Gateway API

Делаю:
2026.04.10


https://www.youtube.com/watch?v=q76XVCTDZCY

https://github.com/marcel-dempers/docker-development-youtube-series/tree/master/kubernetes/gateway-api


We need a Kubernetes cluster


$ kind create cluster --name gatewayapi --image kindest/node:v1.35.0


$ kubectl get nodes
NAME                       STATUS   ROLES           AGE   VERSION
gatewayapi-control-plane   Ready    control-plane   97s   v1.35.0


Gateway API CRDs


$ kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/experimental-install.yaml

This will install (Stable Channel):

  • Gateway Classes: kubectl get gatewayclass
  • Gateways: kubectl get gateway
  • HTTP Routes: kubectl get httproute

These APIs are part of the Experimental Channel:

  • TLS Routes: kubectl get tlsroute
  • TCP Routes: kubectl get tcproute
  • UDP Routes: kubectl get udproute

Note: Gateway API is very new, and all of the above is subject to change quite rapidly


Setup some example applications


1) Приложение на python
2) Приложение на go
3) Фронт приложение которое умеет делать запросы к go приложени.


The following will deploy a deployment, service and require configMap and secret for the applications to work.


$ cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: python-deploy
  labels:
    app: python-app
spec:
  selector:
    matchLabels:
      app: python-app
  replicas: 2
  template:
    metadata:
      labels:
        app: python-app
    spec:
      containers:
      - name: python-app
        image: aimvector/python:1.0.0
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
---
kind: Service
apiVersion: v1
metadata:
  name: python-svc
spec:
  selector:
    app: python-app
  ports:
    - protocol: TCP
      port: 5000
      targetPort: 5000
  type: ClusterIP
EOF


$ cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-deploy
  labels:
    app: go-app
spec:
  selector:
    matchLabels:
      app: go-app
  replicas: 2
  template:
    metadata:
      labels:
        app: go-app
    spec:
      containers:
      - name: go-app
        image: aimvector/golang:1.0.0
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
        volumeMounts:
        - name: secret-volume
          mountPath: /secrets/
        - name: config-volume
          mountPath: /configs/
      volumes:
      - name: secret-volume
        secret:
          secretName: go-secret
      - name: config-volume
        configMap:
          name: go-config
---
apiVersion: v1
kind: Secret
metadata:
  name: go-secret
type: Opaque
data:
  secret.json: ewogICAgImFwaV9rZXkiIDogInNvbWVzZWNyZXRnb2VzaGVyZSIKfQo=
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: go-config
data:
  config.json: |
    {
      "environment" : "dev"
    }
---
kind: Service
apiVersion: v1
metadata:
  name: go-svc
spec:
  selector:
    app: go-app
  ports:
    - protocol: TCP
      port: 5000
      targetPort: 5000
  type: ClusterIP
EOF


$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-app
  labels:
    app: web-app
data:
  # The index.html file that NGINX will serve
  index.html: |
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>CORS Demo Client</title>
        <style>
            body { font-family: Arial, sans-serif; margin: 40px; background-color: #f0f4f8; }
            .container { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); max-width: 600px; margin: auto; }
            h1 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
            button { background-color: #3498db; color: white; padding: 10px 15px; border: none; border-radius: 6px; cursor: pointer; margin-top: 15px; font-weight: bold; transition: background-color 0.3s; }
            button:hover { background-color: #2980b9; }
            pre { background-color: #ecf0f1; padding: 15px; border-radius: 6px; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; }
            .status-ok { color: green; font-weight: bold; }
            .status-error { color: red; font-weight: bold; }
        </style>
    </head>
    <body>
        <div class="container">
            <h1>Client-Side CORS Demonstration</h1>
            <button onclick="fetchData()">Make CORS Request</button>
            <h2>Result:</h2>
            <p>Status: <span id="status">Waiting...</span></p>
            <pre id="output"></pre>
        </div>

        <script>
            async function fetchData() {
                const apiEndpoint = 'http://example-app.com/api/go';
                const statusElement = document.getElementById('status');
                const outputElement = document.getElementById('output');

                statusElement.textContent = 'Fetching...';
                outputElement.textContent = 'Request sent to ' + apiEndpoint;

                try {
                    // This public API allows CORS, so the fetch should succeed.
                    const response = await fetch(apiEndpoint);

                    if (!response.ok) {
                        throw new Error(`HTTP error! status: ${response.status}`);
                    }

                    const data = await response.text();

                    statusElement.innerHTML = '<span class="status-ok">SUCCESS (CORS Allowed)</span>';
                    outputElement.textContent = data;

                } catch (error) {
                    // If the API did NOT allow CORS, this catch block would activate,
                    // often showing a generic "Failed to fetch" error in the console.
                    statusElement.innerHTML = '<span class="status-error">FAILURE (Check Browser Console for CORS Error)</span>';
                    outputElement.textContent = 'Error during fetch: ' + error.message;
                    console.error("CORS Request Error:", error);
                }
            }
        </script>
    </body>
    </html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: nginx-web
        image: nginx:stable-alpine
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html-volume
          mountPath: /usr/share/nginx/html/index.html
          subPath: index.html
      volumes:
      - name: html-volume
        configMap:
          name: web-app
          defaultMode: 0644
---
apiVersion: v1
kind: Service
metadata:
  name: web-app
  labels:
    app: web-app
spec:
  type: ClusterIP
  selector:
    app: web-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      name: http
EOF


$ kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
go-deploy-5449f5df89-2mhss       1/1     Running   0          35s
go-deploy-5449f5df89-jswnq       1/1     Running   0          35s
python-deploy-5fc58df78c-px8jx   1/1     Running   0          40s
python-deploy-5fc58df78c-qlmvs   1/1     Running   0          40s
web-app-679f5b5857-qw8r9         1/1     Running   0          26s


$ kubectl get service
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
go-svc       ClusterIP   10.96.167.54    <none>        5000/TCP   58s
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    8m3s
python-svc   ClusterIP   10.96.218.1     <none>        5000/TCP   63s
web-app      ClusterIP   10.96.236.255   <none>        80/TCP     49s


Create test Domains

We also need to imagine we have a domain called example-app.com , so let’s set that up on our hosts file


$ echo "127.0.0.1 example-app.com" | sudo tee -a /etc/hosts
$ echo "127.0.0.1 example-app-go.com" | sudo tee -a /etc/hosts
$ echo "127.0.0.1 example-app-python.com" | sudo tee -a /etc/hosts


HTTPS and TLS


$ sudo apt install -y libnss3-tools


In my video, I generate a test TLS cert using mkcert

$ curl -L https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64 -o mkcert && chmod +x mkcert && sudo mv mkcert /usr/local/bin/

#linux
$ export CAROOT=${PWD}/kubernetes/gateway-api/tls

$ mkcert -key-file kubernetes/gateway-api/tls/key.pem -cert-file kubernetes/gateway-api/tls/cert.pem example-app.com

$ mkcert -install

Now that we have a TLS cert, we can create a Kubernetes secret to store it:

$ kubectl create secret tls secret-tls -n default --cert kubernetes/gateway-api/tls/cert.pem --key kubernetes/gateway-api/tls/key.pem


We need to

  • Adjust our Gateway, to enable the TLS Listener first!
  • Then apply the TLS listener in our HTTP Route to enable TLS, using sectionName


$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: go-route
  namespace: default
spec:
  parentRefs:
  - name: gateway-api
    sectionName: http
    kind: Gateway
  - name: gateway-api
    sectionName: https
    kind: Gateway
  hostnames:
  - example-app.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api/go
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - name: go-svc
      port: 5000
      weight: 1
EOF