Gateway API in NKP

Gateway API เป็นมาตรฐานใหม่ที่จะมาทดแทน Ingress Controller แบบเดิมในการ Expose Kubernetes service ในรูปแบบของ http url เนื่องจากความสามารถของ Ingress Controller มีจำกัดเช่นทำได้แค่ map url path ไปยัง service ภายใน kubernetes ไม่รองรับความต้องการใหม่ๆ สำหรับการเข้าถึง modern application เช่น การจัดการ http header, query parameter routing, traffic splitting เป็นต้น รวมถึงรูปแบบ routing อื่นๆ เช่น GRPC, TLS, TCP และ UDP เป็นต้น

บทความนี้จะมาลองใช้ Gateway API ที่อยู่ใน NKP เพื่อเปรียบเทียบให้เห็นความสามารถที่มากขึ้น โดยจะใช้ wordpress app เป็นตัวอย่าง

script สำหรับ deploy wordpress

YAML
apiVersion: v1
kind: Secret
metadata:
name: mysql-pass
type: Opaque
stringData:
password: nutanix
---
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
storageClassName: nutanix-files
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:latest
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
- name: MYSQL_DATABASE
value: wordpress
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
---
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: ClusterIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
storageClassName: nutanix-files
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:latest
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_NAME
value: wordpress
- name: WORDPRESS_DEBUG
value: "1"
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim

หลังจากติดตั้งแล้ว สร้าง tls secret สำหรับ https service สำหรับให้บริการ wordpress

Shell
# 1. First, check if a TLS secret already exists
kubectl get secrets -n default | grep tls
# 2. If no TLS secret, create a self-signed one for testing
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt \
-subj "/CN=*.192.168.10.42.sslip.io"
kubectl create secret tls wordpress-tls \
--cert=tls.crt --key=tls.key -n default

สร้าง Gateway สำหรับ wordpress application

YAML
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: kommander-gateway
namespace: default
spec:
gatewayClassName: traefik
listeners:
- name: web
protocol: HTTP
port: 8000
hostname: "*.192.168.10.42.sslip.io"
allowedRoutes:
namespaces:
from: All
- name: websecure
protocol: HTTPS
port: 8443
hostname: "*.192.168.10.42.sslip.io"
allowedRoutes:
namespaces:
from: All
tls:
mode: Terminate
certificateRefs:
- name: wordpress-tls

สร้าง http routing service เพื่อให้ผู้ใช้เรียก http/https service มายัง wordpress ได้ โดยจะมีความสัมพันธ์กับ gateway ที่สร้างในข้างต้น ซึ่งกำหนดใน sectionName ตามตัวอย่าง

YAML
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: wordpress
namespace: default
spec:
parentRefs:
- name: kommander-gateway
namespace: default
sectionName: web
- name: kommander-gateway
namespace: default
sectionName: websecure
hostnames:
- wordpress.192.168.10.42.sslip.io
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: wordpress
port: 80

หลังจาก apply เข้าไปยัง kubernetes แล้ว wordpress ก็จะสามารถเรียกได้จาก url : http://wordpress.192.168.10.42.sslip.io

นอกจากจะทำ path routing แล้ว Gateway API ยังสามารถทำ routing ในรูปแบบอื่นๆ ได้ดังนี้

  1. Header-Based Routing
    rules:
    - matches:
    - headers:
    - name: X-Version
    value: v2
    backendRefs:
    - name: wordpress-v2
    port: 80

    2. Method-Based Routing

    rules:
    - matches:
    - method: POST
    path:
    type: PathPrefix
    value: /api
    backendRefs:
    - name: api-write
    port: 80

    3. Query Parameter Routing

    rules:
    - matches:
    - queryParams:
    - name: env
    value: debug
    backendRefs:
    - name: debug-service
    port: 80

    4. Traffic Splitting (Canary/Blue-Green)

    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /
    backendRefs:
    - name: wordpress-v1
    port: 80
    weight: 90
    - name: wordpress-v2
    port: 80
    weight: 10

    5. Request Header Modification

    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /
    filters:
    - type: RequestHeaderModifier
    requestHeaderModifier:
    add:
    - name: X-Custom-Header
    value: my-value
    set:
    - name: Host
    value: internal-service
    remove:
    - X-Unwanted-Header
    backendRefs:
    - name: wordpress
    port: 80

    6. Response Header Modification

    filters:
    - type: ResponseHeaderModifier
    responseHeaderModifier:
    add:
    - name: X-Frame-Options
    value: DENY
    set:
    - name: Cache-Control
    value: no-cache

    7. URL Redirect

    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /old-blog
    filters:
    - type: RequestRedirect
    requestRedirect:
    scheme: https
    hostname: new-blog.example.com
    port: 443
    statusCode: 301

    8. URL Rewrite

    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /api/v1
    filters:
    - type: URLRewrite
    urlRewrite:
    hostname: backend-api.internal
    path:
    type: ReplacePrefixMatch
    replacePrefixMatch: /v1
    backendRefs:
    - name: api-service
    port: 80

    9. Request Mirroring

    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /
    filters:
    - type: RequestMirror
    requestMirror:
    backendRef:
    name: wordpress-shadow
    port: 80
    backendRefs:
    - name: wordpress
    port: 80

    10. Multiple Matches Combined

    rules:
    - matches:
    - path:
    type: PathPrefix
    value: /api
    headers:
    - name: X-Version
    value: v2
    method: POST
    backendRefs:
    - name: api-v2
    port: 80

    นอกจากนี้ยังมี Router type อื่นๆ เช่น GRPCRoute, TLSRoute, TCPRoute และ UDPRoute ตามตัวอย่าง

    1. GRPCRoute
    YAML
    apiVersion: gateway.networking.k8s.io/v1
    kind: GRPCRoute
    metadata:
    name: grpc-route
    namespace: default
    spec:
    parentRefs:
    - name: my-gateway
    sectionName: grpc
    hostnames:
    - grpc.example.com
    rules:
    # Route by service name
    - matches:
    - method:
    service: myapp.UserService
    method: GetUser
    backendRefs:
    - name: user-service
    port: 50051
    # Route by header
    - matches:
    - headers:
    - name: x-api-version
    value: v2
    backendRefs:
    - name: api-v2
    port: 50051
    # Traffic splitting for gRPC
    - matches:
    - method:
    service: myapp.OrderService
    backendRefs:
    - name: order-service-v1
    port: 50051
    weight: 80
    - name: order-service-v2
    port: 50051
    weight: 20
    ---
    # Gateway listener for gRPC
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
    name: my-gateway
    spec:
    gatewayClassName: traefik
    listeners:
    - name: grpc
    protocol: HTTPS
    port: 8443
    hostname: "grpc.example.com"
    tls:
    mode: Terminate
    certificateRefs:
    - name: grpc-tls-secret
    allowedRoutes:
    kinds:
    - kind: GRPCRoute
    namespaces:
    from: All

    2. TLSRoute (TLS Passthrough)

    YAML
    apiVersion: gateway.networking.k8s.io/v1alpha2
    kind: TLSRoute
    metadata:
    name: tls-passthrough
    namespace: default
    spec:
    parentRefs:
    - name: my-gateway
    sectionName: tls
    hostnames:
    - secure.example.com
    rules:
    - backendRefs:
    - name: secure-backend
    port: 8443
    ---
    # Gateway listener for TLS passthrough
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
    name: my-gateway
    spec:
    gatewayClassName: traefik
    listeners:
    - name: tls
    protocol: TLS
    port: 8443
    hostname: "secure.example.com"
    tls:
    mode: Passthrough # TLS is NOT terminated, passed directly to backend
    allowedRoutes:
    kinds:
    - kind: TLSRoute
    namespaces:
    from: All

    3. TCPRoute

    YAML
    apiVersion: gateway.networking.k8s.io/v1alpha2
    kind: TCPRoute
    metadata:
    name: tcp-database
    namespace: default
    spec:
    parentRefs:
    - name: my-gateway
    sectionName: postgres
    rules:
    - backendRefs:
    - name: postgres-service
    port: 5432
    ---
    # Another TCPRoute for Redis
    apiVersion: gateway.networking.k8s.io/v1alpha2
    kind: TCPRoute
    metadata:
    name: tcp-redis
    namespace: default
    spec:
    parentRefs:
    - name: my-gateway
    sectionName: redis
    rules:
    - backendRefs:
    - name: redis-service
    port: 6379
    ---
    # Gateway with TCP listeners
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
    name: my-gateway
    spec:
    gatewayClassName: traefik
    listeners:
    - name: postgres
    protocol: TCP
    port: 5432
    allowedRoutes:
    kinds:
    - kind: TCPRoute
    namespaces:
    from: All
    - name: redis
    protocol: TCP
    port: 6379
    allowedRoutes:
    kinds:
    - kind: TCPRoute
    namespaces:
    from: All

    4. UDPRoute

    YAML
    apiVersion: gateway.networking.k8s.io/v1alpha2
    kind: UDPRoute
    metadata:
    name: udp-dns
    namespace: default
    spec:
    parentRefs:
    - name: my-gateway
    sectionName: dns
    rules:
    - backendRefs:
    - name: coredns
    port: 53
    ---
    # Another UDPRoute for game server
    apiVersion: gateway.networking.k8s.io/v1alpha2
    kind: UDPRoute
    metadata:
    name: udp-game
    namespace: default
    spec:
    parentRefs:
    - name: my-gateway
    sectionName: game
    rules:
    - backendRefs:
    - name: game-server
    port: 27015
    ---
    # Gateway with UDP listeners
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
    name: my-gateway
    spec:
    gatewayClassName: traefik
    listeners:
    - name: dns
    protocol: UDP
    port: 53
    allowedRoutes:
    kinds:
    - kind: UDPRoute
    namespaces:
    from: All
    - name: game
    protocol: UDP
    port: 27015
    allowedRoutes:
    kinds:
    - kind: UDPRoute
    namespaces:
    from: All