본문으로 건너뛰기
5.4 Ingress와 Gateway API

5.4 Ingress와 Gateway API

Service는 L4 추상이다. IP와 포트까지만 알고, 그 위로 흐르는 것이 HTTP인지, Host 헤더가 무엇인지, 경로가 /api인지 /admin인지는 보지 않는다. 그래서 HTTP 서비스 열 개를 외부에 노출하려면 LoadBalancer 타입 열 개, 즉 클라우드 로드밸런서 열 대가 필요해진다. 도메인과 경로에 따라 트래픽을 갈라 주는 L7 계층이 있다면 로드밸런서 하나로 전부 수용할 수 있다. 그 역할을 맡는 것이 Ingress이고, 그 후속이 Gateway API다.

리소스와 컨트롤러는 분리되어 있다

Ingress를 처음 쓸 때 가장 흔한 함정부터 짚는다. Ingress 리소스를 만들었는데 아무 일도 일어나지 않는 상황이다. Ingress 리소스는 “이 host의 이 경로는 이 Service로"라는 규칙의 선언일 뿐이고, 그 규칙을 읽어 실제 프록시를 구성하는 Ingress controller는 Kubernetes에 기본 포함되어 있지 않다. 컨트롤러가 없는 클러스터에서 Ingress 리소스는 etcd에 저장된 문서에 지나지 않는다.

컨트롤러는 별도로 설치한다. ingress-nginx가 가장 널리 쓰이고, Traefik, HAProxy 기반 구현, 클라우드 제공자의 관리형 컨트롤러(AWS ALB 연동 등)가 있다. 컨트롤러 자체는 보통 Deployment로 동작하면서 LoadBalancer 타입 Service 하나로 노출된다. 외부 트래픽은 이 하나의 진입점으로 들어와, 컨트롤러가 Ingress 규칙에 따라 백엔드 Service의 Pod로 분배한다.

한 클러스터에 컨트롤러가 여럿일 때 어느 쪽이 이 Ingress를 처리할지는 ingressClassName 필드가 정한다. 각 컨트롤러는 자신을 대표하는 IngressClass 리소스를 두고, Ingress가 그 이름을 참조하는 구조다. 이 필드를 생략하면 기본으로 지정된 IngressClass가 있을 때만 처리되므로, 리소스는 정상인데 어느 컨트롤러도 반응하지 않는 원인이 되곤 한다.

host와 path 라우팅, TLS 종단

Ingress 규칙의 골격은 host와 path의 조합이다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - www.example.com
    secretName: web-tls
  rules:
  - host: www.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80

같은 로드밸런서로 들어온 요청이 Host 헤더와 경로에 따라 다른 Service로 갈라진다. pathType은 Prefix(경로 요소 단위 접두 일치), Exact(완전 일치), ImplementationSpecific(컨트롤러 재량) 세 값이 있고, 여러 규칙이 겹치면 더 긴 경로와 Exact가 우선한다.

TLS는 컨트롤러에서 종단된다. tls.crttls.key를 담은 kubernetes.io/tls 타입 Secret을 secretName으로 참조하면, 컨트롤러가 그 인증서로 HTTPS를 받고 백엔드로는 복호화된 트래픽을 전달한다. 인증서 발급과 갱신을 자동화하려면 cert-manager 같은 도구를 함께 쓰는 것이 보통이다. Secret 리소스 자체는 Part VII. 구성과 시크릿에서 다룬다.

Ingress의 한계

Ingress는 오래 쓰이면서 두 가지 한계가 드러났다. 첫째는 표현력이다. 스펙에 있는 것은 host, path, TLS 종단 정도가 전부인데 실무 요구는 그보다 훨씬 넓다. 경로 재작성, 타임아웃, 요청 크기 제한, 카나리 배포, 세션 고정 같은 기능이 전부 스펙 밖이라 컨트롤러별 annotation으로 해결되어 왔다. ingress-nginx의 nginx.ingress.kubernetes.io/* annotation이 수십 종에 이르는 이유다. annotation은 표준이 아니므로 컨트롤러를 바꾸면 그대로 무효가 되고, 스키마 검증도 없어 오타가 조용히 무시된다.

둘째는 역할의 미분리다. Ingress 하나에 인프라 관심사(어느 로드밸런서, 어느 인증서)와 애플리케이션 관심사(어느 경로를 어느 Service로)가 함께 들어 있다. 로드밸런서와 TLS를 관리하는 플랫폼 팀과 라우팅 규칙을 바꾸고 싶은 애플리케이션 팀이 같은 리소스를 편집해야 하므로 권한을 나누기 어렵다. Ingress API는 1.19에서 GA가 된 뒤 사실상 동결 상태이며, 커뮤니티의 개선 작업은 후속 API로 옮겨 갔다.

Gateway API

Gateway API는 이 두 문제를 리소스 분리로 푼다. 2023년에 v1.0으로 GA가 된 표준으로, 핵심 리소스 세 개가 각자 다른 역할 담당자를 상정한다.

    flowchart TD
  GC["GatewayClass<br/>(구현 제공자)"] --> GW["Gateway<br/>(클러스터 운영자)"]
  GW --> R1["HTTPRoute: 팀 A"]
  GW --> R2["HTTPRoute: 팀 B"]
  R1 --> S1["Service a"]
  R2 --> S2["Service b"]
  

GatewayClass는 어떤 구현체(컨트롤러)를 쓰는지 선언하고 구현 제공자가 관리한다. Gateway는 리스너를 여는 실제 진입점으로, 어느 포트에서 어떤 프로토콜을 받고 어떤 TLS 인증서를 쓰는지를 클러스터 운영자가 정의한다. HTTPRoute는 라우팅 규칙으로, 애플리케이션 팀이 자기 namespace에서 작성해 parentRefs로 Gateway에 연결한다. Gateway 쪽에서는 어느 namespace의 Route를 받아들일지 제한하므로, 인프라 권한과 라우팅 권한이 리소스 경계에서 나뉜다.

표현력 문제는 표준 필드 확충으로 대응한다. 헤더 기반 매칭, 헤더 수정, 트래픽 가중치 분배(카나리), 경로 재작성 같은 것들이 annotation 없이 HTTPRoute의 정식 필드로 들어 있다. HTTP 외에 GRPCRoute 등 프로토콜별 Route 타입도 표준화 단계에 있다.

Gateway API는 Kubernetes 본체에 포함되지 않은 CRD라서 클러스터에 CRD 세트를 설치하고 이를 지원하는 구현체를 함께 설치해야 하며, 기존 Ingress도 계속 지원된다. 신규 구축이라면 Gateway API를 우선 검토하되, 사용하는 컨트롤러와 관리형 서비스가 어느 수준까지 구현했는지 확인하는 것이 먼저다.

정리

  • Ingress는 host와 path 기준 L7 라우팅과 TLS 종단을 선언하는 리소스이고, 별도 설치한 컨트롤러가 있어야만 동작한다.
  • 스펙에 없는 기능을 annotation으로 보완해 온 표현력 문제와 인프라/애플리케이션 역할 미분리 문제를, Gateway API가 GatewayClass, Gateway, HTTPRoute 세 리소스 분리로 해결한다.