Kubernetes Gateway API에서의 traffic 전달 논리 구조

Kubernetes Gateway API에서의 traffic 전달 논리 구조

Introduction

Ingress + API Gateway = Kubernetes Gateway API 에서의 논의에 따라 기존 Ingress를 Kubernetes Gateway API로 교체하는 방법을 요약한다. Kubernetes Gateway API 설치는 다음의 문서에 근거한다.

Prerequisite

  • Istio : 현재 Kubernetes Gateway API를 API Gateway 및 Service Mesh 부문까지 포함하여 지원한다(나아가 앞으로 default API로 사용할 것이라고). Istio Ingress Gateway는 설치할 필요 없이 istiod 만 설치하면 된다.
  • KinD : local의 KinD 환경이라 하더라도 Load Balancer 및 GatewayClass, Gateway 리소스 설치 부분을 제외하고는 클라우드 환경과 동일하다.

교체 절차

Ingress 삭제

port 충돌을 막기 위하여 기 설치된 ingress controller를 삭제한다. 한편 ingress resource는 그대로 두어도 무방하다.

Gateway API 설치

1. Kubernetes Gateway API CRD 설치

kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
  { kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v0.8.0" | kubectl apply -f -; }
BASH

위를 실행하면 아래와 같이 gatewayclass resources가 설치되었음을 확인할 수 있다.

kubectl get gatewayclass

# NAME           CONTROLLER                    ACCEPTED   AGE
# istio          istio.io/gateway-controller   True       33d
# istio-remote   istio.io/unmanaged-gateway    True       33d
BASH

2. Load Balancer (KinD: multi node 지원 개발용 Kubernetes 전용)

Gateway API는 Load Balancer를 필요로 한다(참조). 이는 사실 ‘필요’라기보다는 Kubernetes Gateway API 요구사항 중 하나가 역할에 따른 resource 분리임을 고려했을 때, L7 Load Balancer(ingress)가 갖던 역할 통합에서, 역할 분리란 개선 사항으로 이해할 수 있을 듯 하다. 다음은 KinD 공식 문서에서 소개하는 metalLB 설치 절차이다(참조).

  1. metalLB 설치

    # metalLB CRD 설치하기
    kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
    # 완료 기다리기
    kubectl wait --namespace metallb-system \
                    --for=condition=ready pod \
                    --selector=app=metallb \
                    --timeout=90s
    
    BASH
  2. loadbalancer가 사용하는 주소 pool 설정

    1. kind가 docker에서 사용하는주소 pool 얻기

      docker network inspect -f '{{.IPAM.Config}}' kind
      
      # 실행 결과
      # [{172.18.0.0/16  172.18.0.1 map[]} {fc00:f853:ccd:e793::/64  fc00:f853:ccd:e793::1 map[]}]
      
      BASH
    2. 앞서 얻은 주소 범위로 IPAddressPool 설정 및 L2Advertisement resource 설정하기

      Linux와는 달리 Mac이나 Windows의 Docker는 host에서 아래에서 설정하는 주소에 접근이 불가능하다. 이로 인해 아래에서 논할 Gateway로 traffic을 보내기 위한 port forwarding 설정 역시 Linux 환경에서만 동작한다.

      apiVersion: metallb.io/v1beta1
      kind: IPAddressPool
      metadata:
        name: example
        namespace: metallb-system
      spec:
        addresses:
          - 172.18.255.200-172.18.255.250 # 앞서 얻은 CIDR을 참조하여 설정
      ---
      apiVersion: metallb.io/v1beta1
      kind: L2Advertisement
      metadata:
        name: empty
        namespace: metallb-system
      
      YAML

3. Gateway resource 생성

apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: default-gateway
  namespace: cluster
spec:
  gatewayClassName: istio
  addresses:
    - type: IPAddress
      value: 172.18.255.200 # 사용할 Load Balancer의 External IP를 지정
  listeners:
    - name: https-anyflow-net # 443 port에 대한 설정
      hostname: "*.anyflow.net" # subdomain에 대해 wildcard 사용
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret # 기존과 동일하게 Secret으로 인증서 지정
            name: default-tls
            namespace: istio-system
      allowedRoutes:
        namespaces: # 모든 namespace의 HTTPRoute를 수용
          from: All
YAML

위를 설치하면 아래와 같이 Gateway resource가 설치되었음을 확인할 수 있다.

kubectl get gateway -n cluster

# NAME              CLASS   ADDRESS          PROGRAMMED   AGE
# default-gateway   istio   172.18.255.200   True         2d19h
BASH

또한 아래와 같이 gateway의 LoadBalancer type의 서비스가 생성된 것을 볼 수 있다.

kubectl get svc -n cluster

# NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                                                    AGE
# default-gateway-istio   LoadBalancer   10.103.138.151   172.18.255.200   15021:30209/TCP,443:31781/TCP                              2d20h
# ....
YAML

4. HTTPRoute 생성

당연한(?) 이야기이지만 routing 대상 service가 미리 설치되어 있어야 한다. 아래에서는 docserver란 service를 가정한다. 아래 설정은 다음의 규칙을 설정한다.

  • api.anyflow.net domain으로의 traffic에 대해서 설정
  • / path로 들어오는 traffic을 /__apispec/ path로 redirect
  • / prefix인 모든 traffic을 docserver service로의 routing
  • HTTP에서 HTTPS로 영구 redirection
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: docserver
  namespace: cluster2 # 앞선 gateway의 allowedRoutes 설정에 따라 다른 namespace의 service라도 무방
spec:
  parentRefs:
    - name: default-gateway # 앞서 생성한 gateway의 이름을 지정
      namespace: cluster # 앞서 생성한 gateway의 namespace를 지정
  hostnames:
    - api.anyflow.net # routing 대상 domain
  rules:
    - matches:
        - path:
            type: Exact #Implementation-specific, PathPrefix, Exact
            value: /
      filters:
        - type: RequestRedirect
          requestRedirect:
            path:
              type: ReplaceFullPath
              replaceFullPath: /__apispec/ # / path로 들어오는 traffic을 /__apispec/ path로 redirect
            statusCode: 302
    - matches:
        - path:
            type: PathPrefix #Implementation-specific, PathPrefix, Exact
            value: / # / prefix인 모든 traffic을 krakend service로의 routing
      backendRefs:
        - name: docserver
          port: 80
    - filters:
        - type: RequestRedirect
          requestRedirect: # HTTP에서 HTTPS로 영구 redirection
            scheme: https
            statusCode: 301
YAML

이제 해당 호스트에서 curl 등을 사용하여 위 설정의 정상 동작 여부를 테스트해본다. 아래는 이 테스트를 위한 /etc/hosts 파일 예이다.

...
172.18.255.200 api.anyflow.net # 172.18.255.200은 위에서 설정한 Load Balancer IP
...
SHELL

Appendix: Gateway로 traffic을 보내기 위한 port forwarding 설정

본 섹션은 위와 같은 /etc/hosts 파일 수정 없이도, kind 환경에서 host machine으로 들어온 traffic을 Gateway로 보내기 위한 설정을 다룬다.
참고로 Linux와는 달리 Mac이나 Windows의 Docker는 host에서 아래에서 설정하는 주소에 접근이 불가능하기에, 아래는 Linux 환경에서만 동작한다.

핵심은 iptables 명령을 통한 port forwarding 규칙 설정으로, 앞서와 마찬가지로 172.18.255.200 은 위에서 설정한 Load Balancer IP이다. 아래 예제는 443 port만 다루고 있다.

$ sudo iptables -A DOCKER -p tcp -s 0.0.0.0/0 -d 172.18.255.200 --dport 443 -j ACCEPT
$ sudo iptables -t nat -A DOCKER -p tcp --dport 443 -j DNAT --to-destination 172.18.255.200:443
$ sudo iptables -t nat -A POSTROUTING -s 172.18.255.200 -d 172.18.255.200 -p tcp --dport 443 -j MASQUERADE
BASH

Comments