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
apiVersion: v1kind: Secretmetadata: name: mysql-passtype: OpaquestringData: password: nutanixapiVersion: v1kind: Servicemetadata: name: wordpress-mysql labels: app: wordpressspec: ports: - port: 3306 selector: app: wordpress tier: mysql clusterIP: NoneapiVersion: v1kind: PersistentVolumeClaimmetadata: name: mysql-pv-claim labels: app: wordpressspec: storageClassName: nutanix-files accessModes: - ReadWriteOnce resources: requests: storage: 20GiapiVersion: apps/v1kind: Deploymentmetadata: name: wordpress-mysql labels: app: wordpressspec: 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-claimapiVersion: v1kind: Servicemetadata: name: wordpress labels: app: wordpressspec: ports: - port: 80 selector: app: wordpress tier: frontend type: ClusterIPapiVersion: v1kind: PersistentVolumeClaimmetadata: name: wp-pv-claim labels: app: wordpressspec: storageClassName: nutanix-files accessModes: - ReadWriteOnce resources: requests: storage: 20GiapiVersion: apps/v1kind: Deploymentmetadata: name: wordpress labels: app: wordpressspec: 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
# 1. First, check if a TLS secret already existskubectl get secrets -n default | grep tls# 2. If no TLS secret, create a self-signed one for testingopenssl 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
apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: kommander-gateway namespace: defaultspec: 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 ตามตัวอย่าง
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: name: wordpress namespace: defaultspec: 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 ในรูปแบบอื่นๆ ได้ดังนี้
- 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 ตามตัวอย่าง
- GRPCRoute
apiVersion: gateway.networking.k8s.io/v1kind: GRPCRoutemetadata: name: grpc-route namespace: defaultspec: 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 gRPCapiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: my-gatewayspec: 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)
apiVersion: gateway.networking.k8s.io/v1alpha2kind: TLSRoutemetadata: name: tls-passthrough namespace: defaultspec: parentRefs: - name: my-gateway sectionName: tls hostnames: - secure.example.com rules: - backendRefs: - name: secure-backend port: 8443# Gateway listener for TLS passthroughapiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: my-gatewayspec: 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
apiVersion: gateway.networking.k8s.io/v1alpha2kind: TCPRoutemetadata: name: tcp-database namespace: defaultspec: parentRefs: - name: my-gateway sectionName: postgres rules: - backendRefs: - name: postgres-service port: 5432# Another TCPRoute for RedisapiVersion: gateway.networking.k8s.io/v1alpha2kind: TCPRoutemetadata: name: tcp-redis namespace: defaultspec: parentRefs: - name: my-gateway sectionName: redis rules: - backendRefs: - name: redis-service port: 6379# Gateway with TCP listenersapiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: my-gatewayspec: 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
apiVersion: gateway.networking.k8s.io/v1alpha2kind: UDPRoutemetadata: name: udp-dns namespace: defaultspec: parentRefs: - name: my-gateway sectionName: dns rules: - backendRefs: - name: coredns port: 53# Another UDPRoute for game serverapiVersion: gateway.networking.k8s.io/v1alpha2kind: UDPRoutemetadata: name: udp-game namespace: defaultspec: parentRefs: - name: my-gateway sectionName: game rules: - backendRefs: - name: game-server port: 27015# Gateway with UDP listenersapiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: my-gatewayspec: 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
