Chapter 23

Ingress와 Gateway API

  • 23.1 Ingress
  • 23.2 Gateway API

외부 트래픽을 클러스터 안 서비스로 깔끔하게 라우팅하는 방법을 다뤄보자. Ingress가 왜 필요한지부터 시작해서, 그 한계를 넘어선 Gateway API까지 이어서 본다.

Ingress가 왜 필요한지부터 시작하자. 온라인 상점 애플리케이션을 Kubernetes에 배포한다고 해. 앱을 파드로 만들고, MySQL 데이터베이스도 파드로 배포해서 ClusterIP 서비스로 연결해. 외부 접근을 위해 NodePort 서비스를 만들면 노드의 높은 포트(예: 30080)에서 접근 가능해. 사용자가 IP와 포트 번호를 매번 치긴 힘드니까 DNS 서버를 설정하고, 포트 80의 요청을 30080으로 프록시하는 프록시 서버를 중간에 넣으면 my-online-store.com으로 접근 가능해져.

클라우드 환경이라면 LoadBalancer 타입 서비스를 쓸 수 있어. Kubernetes가 NodePort 작업은 그대로 하면서, 추가로 클라우드 제공자에게 네트워크 로드 밸런서를 요청해. 로드 밸런서가 자동으로 만들어지고 외부 IP가 생기면, DNS를 거기에 연결하면 돼.

문제는 회사가 성장해서 동영상 스트리밍 같은 새 서비스를 추가할 때야. 새 애플리케이션을 배포하고 LoadBalancer 서비스를 만들면 새 로드 밸런서가 프로비저닝돼. 서비스마다 로드 밸런서가 필요하고, 비용도 늘어나고, URL에 따라 각 로드 밸런서로 트래픽을 라우팅할 또 다른 프록시가 필요해. 새 서비스가 추가될 때마다 로드 밸런서를 재구성해야 하고. SSL까지 설정하려면 애플리케이션 레벨, 로드 밸런서 레벨, 프록시 레벨 중 어디서 할지도 고민이야. 이 모든 걸 Kubernetes 클러스터 안에서 정의 파일 하나로 관리할 수 있다면 좋겠지? 그게 Ingress야.

Ingress는 외부에서 접근 가능한 단일 URL로 애플리케이션에 접근하게 하고, URL 경로에 따라 클러스터 내 여러 서비스로 트래픽을 라우팅하며, SSL도 처리할 수 있어. Kubernetes에 내장된 레이어 7 로드 밸런서라고 생각하면 돼. Ingress가 있더라도 클러스터 외부에서 접근하려면 NodePort나 클라우드 로드 밸런서로 노출해야 하지만, 그건 일회성 설정이고 이후로는 Ingress에서 모든 라우팅과 SSL을 관리해.

Ingress에는 두 가지 구성 요소가 있어. Ingress Controller가 실제 트래픽을 처리하는 로드 밸런서이고, Ingress Resource가 라우팅 규칙을 정의하는 YAML 파일이야. 중요한 건, Kubernetes 클러스터에 기본적으로 Ingress Controller가 포함되어 있지 않다는 거야. 직접 배포해야 해. 리소스만 만들고 동작을 기대하면 안 돼.

Ingress Controller로 사용할 수 있는 솔루션은 여러 가지야. GCE(Google 레이어 7 HTTP 로드 밸런서), nginx, contour, HAProxy, traefik, Istio 등. 현재 Kubernetes 프로젝트에서 공식 지원하는 건 GCEnginx야. 이 컨트롤러들은 단순한 nginx 서버가 아니야. 새로운 Ingress 리소스에 대해 Kubernetes 클러스터를 모니터링하고 그에 따라 자동으로 설정을 업데이트하는 인텔리전스가 내장되어 있어.

nginx Ingress Controller를 배포하려면 Deployment, ConfigMap(nginx 설정 데이터), Service(외부 노출용 NodePort), ServiceAccount(권한)가 필요해. Deployment에서는 quay.io/kubernetes-ingress-controller/nginx-ingress-controller 이미지를 사용하는데, 이건 Kubernetes에서 Ingress Controller로 쓰도록 특별히 빌드된 이미지야. 이미지 안에서 nginx 프로그램은 /nginx-ingress-controller 경로에 있어서 이걸 명령으로 전달해야 해. 파드 이름과 네임스페이스 환경 변수도 전달하고, 포트 80과 443을 지정해. ConfigMap은 비어 있어도 되는데, 나중에 nginx 설정을 바꾸려면 여기에 추가하면 nginx 설정 파일을 직접 수정할 필요 없이 반영돼.

Ingress Resource는 컨트롤러에 적용되는 규칙 집합이야. 가장 단순한 형태는 모든 트래픽을 하나의 서비스로 보내는 거야:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wear
spec:
  defaultBackend:
    service:
      name: wear-service
      port:
        number: 80

URL 경로 기반으로 라우팅하려면 rules 안에 여러 path를 정의해. 하나의 규칙에 여러 경로를 넣는 거야:

spec:
  rules:
  - http:
      paths:
      - path: /wear
        pathType: Prefix
        backend:
          service:
            name: wear-service
            port:
              number: 80
      - path: /watch
        pathType: Prefix
        backend:
          service:
            name: watch-service
            port:
              number: 80

도메인(호스트) 기반으로 라우팅하려면 각 도메인마다 별도의 규칙을 만들어:

spec:
  rules:
  - host: wear.my-online-store.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wear-service
            port:
              number: 80
  - host: watch.my-online-store.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: watch-service
            port:
              number: 80

URL 분할은 규칙 1개에 경로 여러 개, 도메인 분할은 규칙 여러 개에 경로 각 1개라는 차이를 기억해둬. 각 규칙에서 여러 경로를 지정해서 URL 경로와 도메인 분할을 조합할 수도 있어.

kubectl describe ingress로 상세 정보를 보면 **기본 백엔드(default backend)**라는 게 있어. 어떤 규칙에도 매칭되지 않는 URL로 접근하면 거기로 가는 건데, default-http-backend라는 서비스를 만들어서 404 페이지 같은 걸 보여주면 돼. kubectl get ingress로 목록을 확인하고, kubectl create -f ingress.yaml로 생성해.

Ingress가 이런 편의를 주지만 한계도 분명해. 바로 그 한계를 해결하기 위해 등장한 게 Gateway API야.

Gateway API는 Ingress의 한계를 해결하기 위해 등장한 차세대 Kubernetes 프로젝트야. 레이어 4와 레이어 7 라우팅에 초점을 맞춘 공식 Kubernetes 프로젝트이고, 차세대 인그레스, 로드 밸런싱, 서비스 메시 API를 대표해.

Ingress의 첫 번째 문제는 멀티테넌시 지원 부족이야. 두 서비스가 같은 Ingress 리소스를 공유하는데, 웹 서비스는 A팀이 관리하고 비디오 서비스는 B팀이 관리한다면? Ingress는 단일 리소스니까 한 번에 한 팀만 관리할 수 있어. 여러 팀이 같은 Ingress를 다루면서 서로 조율해야 하니까 충돌이 생기지.

두 번째 문제는 규칙 옵션이 제한적이라는 거야. Ingress는 호스트 매칭이나 경로 매칭 같은 HTTP 기반 규칙만 지원해. TCP/UDP 라우팅, 트래픽 분할, 헤더 조작, 인증, 속도 제한 같은 건 지원하지 않아. 이런 고급 설정은 전부 어노테이션을 통해 컨트롤러에 전달해야 하는데, 문제는 이 어노테이션이 각 컨트롤러(nginx, traefik 등)에 특화되어 있다는 거야. Kubernetes 자체가 이 어노테이션의 유효성을 검증하지 못하고, 그냥 기본 컨트롤러에 전달할 뿐이야. 같은 기능인데 컨트롤러마다 다른 어노테이션을 써야 하고, 컨트롤러를 바꾸면 어노테이션도 다 바꿔야 해.

Gateway API는 이 문제를 세 개의 별도 객체, 세 개의 페르소나로 나눠서 해결해. GatewayClass는 인프라 제공자가 관리하는데, nginx나 traefik 같은 기반 네트워크 인프라가 뭔지 정의해. Gateway는 클러스터 운영자가 관리하는 GatewayClass의 인스턴스야. 그리고 HTTPRoute는 애플리케이션 개발자가 만드는 라우팅 규칙이야.

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: example-class
spec:
  controllerName: example.com/gateway-controller
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: example-class
  listeners:
  - name: http
    protocol: HTTP
    port: 80
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-route
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "www.example.com"
  rules:
  - matches:
    - path:
        value: /login
    backendRefs:
    - name: example-svc
      port: 8080

Ingress에는 HTTPRoute만 있었지만, Gateway API에는 TLSRoute, TCPRoute, UDPRoute, GRPCRoute도 있어. HTTP만 라우팅하는 게 아니라 다양한 프로토콜을 다룰 수 있는 거지.

Ingress와 비교해서 Gateway API가 어떻게 다른지 구체적으로 보자. TLS 설정의 경우, Ingress에서는 기본 TLS는 spec에 정의하지만 HTTP를 HTTPS로 리디렉션하려면 nginx 전용 어노테이션을 써야 했어. Gateway API에서는 리스너 섹션에 HTTPS 엔드포인트, TLS 종료 모드, 인증서 참조가 전부 선언적으로 들어가. 어노테이션이 필요 없어.

**트래픽 분할(카나리 배포)**의 경우, Ingress에서는 nginx 전용 어노테이션으로 "이건 카나리이고 트래픽의 20%를 여기로 보내라"고 설정하는데, 이 설정만 보면 나머지 80%가 어디로 가는지 알 수 없고 nginx에서만 동작해. Gateway API에서는 backendRefs에 서비스 두 개를 넣고 weight를 80과 20으로 명시적으로 설정해. 한 곳에서 전체 그림이 보이고, 어떤 컨트롤러에서든 같은 방식으로 동작해.

CORS 같은 고급 설정도 마찬가지야. Ingress에서는 컨트롤러별로 복잡한 어노테이션을 써야 하지만, Gateway API에서는 응답 헤더 수정자 필터를 사용해서 spec 안에 명시적으로 정의해. 어노테이션 없이, 설정이 가독성 높고 자체 문서화되고, 모든 Gateway API 구현에서 일관되게 동작해.

현재 대부분의 컨트롤러가 Gateway API를 구현했거나 구현 중이야. Amazon EKS, Azure Application Gateway, Contour, Envoy Gateway, Google Kubernetes Engine, HAProxy, Istio, Nginx Gateway Fabric, Traefik Proxy 등이 이미 GA로 구현을 완료했어. 핵심은 어노테이션 없이 선언적으로 모든 설정을 할 수 있고, 모든 컨트롤러에서 동일하게 동작한다는 거야. 컨트롤러를 바꿔도 설정을 다시 작성할 필요가 없어.


정리

23장 읽고 기억할 거 세 가지:

  1. Ingress는 레이어 7 로드 밸런서로, URL 경로/도메인 기반 라우팅과 SSL을 하나의 리소스로 관리하지만, Ingress Controller를 직접 배포해야 동작한다
  2. Gateway API는 GatewayClass(인프라) / Gateway(운영자) / HTTPRoute(개발자)로 역할을 분리해서 멀티테넌시 문제를 해결하고, TLS/TCP/UDP/gRPC 라우팅까지 지원한다
  3. Ingress의 근본 한계는 고급 설정을 컨트롤러별 어노테이션에 의존한다는 건데, Gateway API는 모든 설정을 spec 안에 선언적으로 정의해서 컨트롤러를 바꿔도 설정을 다시 작성할 필요가 없다