Skip to content

필사 모드: Ingress Controller 완전 이해 — Ingress 리소스부터 컨트롤러 동작 원리까지

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며: 왜 Ingress부터 헷갈리는가

Kubernetes를 처음 운영할 때 가장 많이 듣는 질문 중 하나가 "Service로 LoadBalancer를 만들면 되는데 왜 Ingress가 필요하냐"입니다. 작은 클러스터에서 서비스가 한두 개라면 LoadBalancer 타입 Service만으로도 충분합니다. 그러나 서비스가 수십 개로 늘어나면 이야기가 달라집니다. 서비스마다 클라우드 로드밸런서를 하나씩 띄우면 비용이 선형으로 증가하고, 각 로드밸런서마다 별도의 공인 IP와 TLS 인증서를 관리해야 합니다.

Ingress는 이 문제를 해결합니다. 단일 진입점(보통 하나의 로드밸런서) 뒤에서 호스트 이름과 URL 경로를 기준으로 트래픽을 여러 백엔드 서비스로 라우팅합니다. L7(HTTP/HTTPS) 레벨에서 동작하므로 경로 기반 라우팅, 호스트 기반 가상호스팅, TLS 종료를 한 곳에서 처리할 수 있습니다.

그런데 실무에서 가장 큰 혼란은 "Ingress가 두 가지를 동시에 가리킨다"는 점입니다. 하나는 **Ingress 리소스**(API 오브젝트)이고, 다른 하나는 **Ingress Controller**(실제 동작하는 프록시)입니다. 이 둘을 구분하지 못하면 "Ingress를 만들었는데 왜 아무 일도 안 일어나죠?" 같은 함정에 빠집니다. 이 글에서는 이 구분에서 출발해 동작 원리, 리소스 스펙, TLS, 컨트롤러 비교, 그리고 2026년 현재 Gateway API로의 전환 흐름까지 차근차근 다룹니다.

Ingress API vs Ingress Controller — 가장 중요한 구분

먼저 두 개념을 명확히 분리하겠습니다.

- **Ingress 리소스(API 오브젝트)**: "example.com의 /api 경로는 api-service로 보내라"는 라우팅 규칙을 선언적으로 기술한 YAML 명세입니다. 이것 자체는 그저 etcd에 저장된 데이터일 뿐, 어떤 트래픽도 처리하지 않습니다.

- **Ingress Controller**: 클러스터에서 실제로 동작하는 프로그램(보통 reverse proxy)입니다. Ingress 리소스를 watch하다가 변경이 감지되면 자신의 설정을 그에 맞게 생성하고, 들어오는 트래픽을 규칙대로 라우팅합니다.

핵심은 **Ingress 리소스를 만들어도 컨트롤러가 클러스터에 설치되어 있지 않으면 아무 일도 일어나지 않는다**는 점입니다. Deployment를 만들면 컨트롤 플레인이 알아서 파드를 띄우지만, Ingress는 별도로 설치한 서드파티 컨트롤러가 있어야 비로소 의미를 가집니다. 이것이 다른 리소스와 결정적으로 다른 부분입니다.

비유하자면 Ingress 리소스는 "주문서"이고 Ingress Controller는 "주방"입니다. 주문서만 쌓아두고 주방이 없으면 음식은 나오지 않습니다.

대표적인 컨트롤러로는 ingress-nginx, Traefik, HAProxy Ingress, Contour(Envoy 기반), Kong, APISIX 등이 있습니다. 클라우드 제공자들도 자체 컨트롤러(AWS Load Balancer Controller, GKE Ingress 등)를 제공합니다.

동작 흐름: API watch에서 reload까지

Ingress Controller의 내부 동작은 대부분 다음과 같은 제어 루프(control loop)를 따릅니다.

Kubernetes API Server

(1) watch Ingress / Service / Endpoints / Secret

┌───────────────────────────────────────────┐

│ Ingress Controller │

│ │

│ (2) 변경 감지 → 내부 모델 갱신 │

│ (3) 라우팅 설정 파일/객체 생성 │

│ (예: nginx.conf 템플릿 렌더링) │

│ (4) 프록시 reload 또는 동적 reconfigure │

│ │

│ ┌─────────────┐ ┌──────────────┐ │

│ │ Controller │ ───▶ │ Data Plane │ │

│ │ (watcher) │ cfg │ (nginx/envoy) │ │

│ └─────────────┘ └──────────────┘ │

└───────────────────────────────────────────┘

(5) 외부 트래픽 라우팅

Client ─▶ LoadBalancer ─▶ Controller Pod ─▶ Backend Service ─▶ Pod

단계별로 보면 다음과 같습니다.

1. **Watch**: 컨트롤러는 API 서버에 watch 연결을 걸어 Ingress, Service, Endpoints(또는 EndpointSlice), TLS Secret 등의 변경을 실시간으로 구독합니다.

2. **모델 갱신**: 변경 이벤트가 오면 컨트롤러 내부에 라우팅 모델을 다시 구성합니다.

3. **설정 생성**: 그 모델을 데이터 플레인이 이해하는 형태로 변환합니다. ingress-nginx라면 nginx.conf를 템플릿으로 렌더링하고, Envoy 기반 컨트롤러라면 xDS 설정을 만듭니다.

4. **적용(reload/reconfigure)**: nginx처럼 설정 파일 기반인 경우 reload를 수행하고, Envoy처럼 동적 설정을 지원하는 경우 무중단으로 reconfigure합니다. ingress-nginx도 동적 backend 변경(엔드포인트 추가/삭제)은 Lua 모듈로 reload 없이 처리합니다.

5. **트래픽 라우팅**: 데이터 플레인이 실제 HTTP 요청을 규칙대로 백엔드 서비스로 전달합니다.

여기서 중요한 점은 **컨트롤러가 트래픽을 백엔드 서비스의 ClusterIP가 아니라 직접 파드 엔드포인트로 보내는 경우가 많다**는 것입니다. ingress-nginx는 기본적으로 Endpoints/EndpointSlice를 watch하여 개별 파드 IP로 부하분산합니다. 그래서 kube-proxy의 iptables 라운드로빈을 거치지 않고 컨트롤러 자체 로드밸런싱(예: ewma, round_robin)을 적용할 수 있습니다.

Ingress 리소스 스펙 상세

이제 Ingress 리소스의 스펙을 필드별로 살펴보겠습니다. 다음은 `networking.k8s.io/v1` 기준의 전형적인 예시입니다.

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: shop-ingress

namespace: shop

spec:

ingressClassName: nginx

defaultBackend:

service:

name: fallback-service

port:

number: 80

rules:

- host: shop.example.com

http:

paths:

- path: /api

pathType: Prefix

backend:

service:

name: api-service

port:

number: 8080

- path: /static

pathType: Prefix

backend:

service:

name: static-service

port:

number: 80

- host: admin.example.com

http:

paths:

- path: /

pathType: Prefix

backend:

service:

name: admin-service

port:

number: 3000

rules와 host

`rules`는 라우팅 규칙의 배열입니다. 각 규칙은 선택적으로 `host`를 가질 수 있습니다. `host`가 지정되면 요청의 Host 헤더가 일치할 때만 그 규칙이 적용됩니다. `host`를 생략하면 모든 호스트에 대해 적용됩니다. 와일드카드 호스트(`*.example.com`)도 지원되지만, 단일 라벨만 매칭한다는 제약이 있습니다. 즉 `*.example.com`은 `a.example.com`에는 매칭되지만 `a.b.example.com`에는 매칭되지 않습니다.

paths와 pathType

각 `host` 아래에는 여러 `paths`가 올 수 있습니다. `pathType`은 경로 매칭 방식을 결정하며 세 가지 값이 있습니다.

- **Prefix**: 경로를 슬래시 단위 요소로 나눠 접두사 매칭합니다. `/api`는 `/api`, `/api/v1`에 매칭되지만 `/apixyz`에는 매칭되지 않습니다.

- **Exact**: 경로가 정확히 일치해야 합니다. 대소문자를 구분합니다.

- **ImplementationSpecific**: 매칭 방식을 컨트롤러 구현에 위임합니다. ingress-nginx는 이 경우 정규식 등 자체 규칙을 적용할 수 있습니다.

`pathType`은 v1 API에서 **필수 필드**입니다. 누락하면 Ingress 생성이 거부됩니다.

backend service와 defaultBackend

`backend`는 트래픽을 보낼 대상을 지정합니다. 서비스 이름과 포트(번호 또는 이름)를 명시합니다. `defaultBackend`는 어떤 규칙에도 매칭되지 않는 요청을 받을 폴백입니다. 지정하지 않으면 매칭되지 않는 요청은 컨트롤러의 기본 404 페이지로 떨어집니다.

resource backend

서비스 대신 리소스(예: 오브젝트 스토리지를 가리키는 커스텀 리소스)를 백엔드로 지정할 수도 있습니다. `backend.service`와 `backend.resource`는 상호 배타적입니다.

IngressClass와 다중 컨트롤러

한 클러스터에 컨트롤러가 둘 이상 설치될 수 있습니다(예: 내부용과 외부용). 이때 "이 Ingress 리소스를 누가 처리할지"를 결정하는 메커니즘이 **IngressClass**입니다.

apiVersion: networking.k8s.io/v1

kind: IngressClass

metadata:

name: nginx

annotations:

ingressclass.kubernetes.io/is-default-class: "true"

spec:

controller: k8s.io/ingress-nginx

Ingress 리소스에서는 `spec.ingressClassName: nginx`로 어떤 클래스를 쓸지 지정합니다. 컨트롤러는 자신이 담당하는 클래스의 Ingress만 처리하고 나머지는 무시합니다. `is-default-class` 어노테이션이 true인 IngressClass가 있으면, `ingressClassName`을 생략한 Ingress는 그 기본 클래스로 자동 할당됩니다.

과거에는 `kubernetes.io/ingress.class` 어노테이션으로 클래스를 지정했지만, 이 방식은 deprecated되었습니다. 신규 환경에서는 `spec.ingressClassName`을 사용하세요. 다중 컨트롤러 운영의 자세한 내용은 별도 글에서 다룹니다.

TLS 설정

Ingress에서 HTTPS를 종료하려면 TLS Secret과 `spec.tls`를 사용합니다.

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: shop-ingress-tls

namespace: shop

spec:

ingressClassName: nginx

tls:

- hosts:

- shop.example.com

secretName: shop-tls

rules:

- host: shop.example.com

http:

paths:

- path: /

pathType: Prefix

backend:

service:

name: shop-service

port:

number: 80

`secretName`이 가리키는 Secret은 `kubernetes.io/tls` 타입이어야 하며 `tls.crt`와 `tls.key`를 담고 있어야 합니다. Secret은 Ingress와 같은 네임스페이스에 있어야 합니다.

apiVersion: v1

kind: Secret

metadata:

name: shop-tls

namespace: shop

type: kubernetes.io/tls

data:

tls.crt: <base64-encoded-cert>

tls.key: <base64-encoded-key>

실무에서는 인증서를 수동으로 관리하지 않고 **cert-manager**를 사용해 Let's Encrypt 등에서 자동 발급/갱신합니다. cert-manager는 ACME 챌린지를 처리하고, 발급된 인증서를 위와 같은 형태의 Secret으로 저장합니다. 그러면 Ingress Controller가 그 Secret을 watch하여 TLS 종료에 사용합니다.

컨트롤러 종류 개요 비교

대표적인 Ingress Controller를 비교하면 다음과 같습니다.

| 컨트롤러 | 데이터 플레인 | 설정 갱신 방식 | 특징 | 비고 |

| --- | --- | --- | --- | --- |

| ingress-nginx | NGINX | nginx.conf reload + Lua 동적 endpoint | 가장 널리 쓰임, 어노테이션 풍부 | 유지보수 모드 흐름, 보안 패치 중심 |

| NGINX Ingress (F5) | NGINX / NGINX Plus | 설정 reload | 상용 지원(Plus), 별도 프로젝트 | ingress-nginx와 다른 프로젝트 |

| Traefik | Traefik | 동적 핫 reconfigure | CRD/어노테이션, 자동 서비스 디스커버리 | 대시보드 제공 |

| HAProxy Ingress | HAProxy | runtime API + reload | 고성능 L4/L7, 정교한 헬스체크 | 세밀한 튜닝 가능 |

| Contour | Envoy | xDS 동적 | HTTPProxy CRD, 위임 모델 | CNCF 프로젝트 |

| Kong | Kong/NGINX | DB-less 동적 | API 게이트웨이 기능, 플러그인 | 인증/레이트리밋 풍부 |

| APISIX | APISIX | etcd 기반 동적 | 플러그인 생태계, 고성능 | Apache 프로젝트 |

| Emissary | Envoy | 동적 | Mapping CRD 기반 | 구 Ambassador |

선택 기준은 보통 다음과 같습니다. 단순 라우팅과 폭넓은 레퍼런스가 필요하면 ingress-nginx, 동적 서비스 디스커버리와 운영 편의성이 중요하면 Traefik, API 게이트웨이 수준의 인증/플러그인이 필요하면 Kong/APISIX, Envoy 기반 정교한 정책이 필요하면 Contour를 고려합니다.

2026년 컨텍스트: Ingress API frozen과 Gateway API 후계 흐름

여기서 반드시 짚어야 할 2026년 현재의 흐름이 있습니다. **Ingress API는 frozen 상태**입니다. 즉 `networking.k8s.io/v1`의 Ingress는 안정적이고 계속 동작하지만, 더 이상 새로운 기능이 추가되지 않습니다. 헤더 기반 라우팅, 트래픽 분할(가중치 기반), 명시적 메서드 매칭 같은 고급 기능은 모두 컨트롤러별 어노테이션으로 우회해야 했고, 이 "어노테이션 지옥"이 Ingress API의 근본적 한계로 지적되어 왔습니다.

그 후계로 등장한 것이 **Gateway API**입니다. Gateway API는 GatewayClass, Gateway, HTTPRoute 등 역할이 분리된 리소스 모델을 제공하며, 표준 스펙 안에서 헤더 매칭, 트래픽 가중치 분할, 크로스 네임스페이스 라우팅 등을 지원합니다. 인프라 운영자(Gateway 관리)와 애플리케이션 개발자(Route 관리)의 책임을 분리하도록 설계된 것이 큰 차이입니다.

또한 그동안 사실상 표준처럼 쓰여온 **ingress-nginx**는 유지보수 모드에 가까운 흐름으로, 신규 기능보다 보안 패치 중심으로 운영되고 있습니다. 따라서 신규 프로젝트라면 처음부터 Gateway API와 그것을 구현하는 컨트롤러(예: Envoy Gateway, Traefik, Contour의 Gateway 지원 등)를 검토하는 것이 합리적입니다.

그렇다고 지금 당장 모든 Ingress를 버리라는 뜻은 아닙니다. Ingress는 여전히 광범위하게 동작하며, 단순한 라우팅에는 충분합니다. 다만 새로 설계한다면 Gateway API를 1차 후보로 두고, 기존 자산은 점진적으로 마이그레이션하는 전략이 권장됩니다.

첫 배포 실습

이제 ingress-nginx로 첫 Ingress를 배포해 보겠습니다. 먼저 컨트롤러를 설치합니다(Helm 사용).

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \

--namespace ingress-nginx --create-namespace

설치 후 컨트롤러가 LoadBalancer 타입 Service로 외부 IP를 받았는지 확인합니다.

kubectl get svc -n ingress-nginx

kubectl get pods -n ingress-nginx

이제 테스트용 애플리케이션과 서비스를 배포합니다.

apiVersion: apps/v1

kind: Deployment

metadata:

name: hello

spec:

replicas: 2

selector:

matchLabels:

app: hello

template:

metadata:

labels:

app: hello

spec:

containers:

- name: hello

image: hashicorp/http-echo

args:

- "-text=hello from ingress"

ports:

- containerPort: 5678

apiVersion: v1

kind: Service

metadata:

name: hello-service

spec:

selector:

app: hello

ports:

- port: 80

targetPort: 5678

그리고 Ingress를 만듭니다.

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: hello-ingress

spec:

ingressClassName: nginx

rules:

- host: hello.example.com

http:

paths:

- path: /

pathType: Prefix

backend:

service:

name: hello-service

port:

number: 80

로컬에서 테스트할 때는 hosts 파일에 컨트롤러 외부 IP를 hello.example.com으로 매핑하거나, curl의 Host 헤더로 흉내냅니다.

curl -H "Host: hello.example.com" http://<EXTERNAL-IP>/

`hello from ingress`가 나오면 성공입니다.

운영과 튜닝 포인트

운영 단계에서 자주 만지는 설정들을 정리합니다. 대부분 ingress-nginx 기준 어노테이션 또는 ConfigMap입니다.

metadata:

annotations:

nginx.ingress.kubernetes.io/proxy-body-size: "50m"

nginx.ingress.kubernetes.io/proxy-read-timeout: "60"

nginx.ingress.kubernetes.io/ssl-redirect: "true"

nginx.ingress.kubernetes.io/rewrite-target: /$2

- **proxy-body-size**: 업로드 크기 제한. 파일 업로드 서비스에서 413 에러가 나면 가장 먼저 확인합니다.

- **proxy-read-timeout**: 백엔드 응답 대기 시간. 장시간 작업이 504로 끊기면 늘립니다.

- **ssl-redirect**: HTTP를 HTTPS로 강제 리다이렉트.

- **rewrite-target**: 경로 재작성. 정규식 캡처 그룹과 함께 씁니다.

ConfigMap으로는 전역 설정(워커 프로세스 수, keepalive, gzip 등)을 조정합니다. 컨트롤러 자체도 리소스 요청/제한, HPA, PodDisruptionBudget을 두어 안정적으로 운영해야 합니다. 컨트롤러는 모든 트래픽이 지나가는 단일 지점이므로, 충분한 복제본과 노드 분산(topologySpreadConstraints)이 중요합니다.

함정과 트러블슈팅

실무에서 자주 마주치는 문제들을 정리합니다.

**1. Ingress를 만들었는데 ADDRESS가 비어 있다.**

컨트롤러가 설치되지 않았거나, `ingressClassName`이 잘못되어 어떤 컨트롤러도 그 Ingress를 잡지 않은 경우입니다. `kubectl get ingress`로 ADDRESS 컬럼을 확인하고, `kubectl describe ingress`의 이벤트를 봅니다.

kubectl get ingress

kubectl describe ingress hello-ingress

**2. 503 Service Temporarily Unavailable.**

백엔드 서비스의 셀렉터가 파드와 맞지 않아 Endpoints가 비어 있을 가능성이 높습니다. 다음으로 확인합니다.

kubectl get endpoints hello-service

비어 있으면 서비스 셀렉터와 파드 라벨, 그리고 파드의 readiness probe 상태를 점검합니다.

**3. 404 Not Found가 컨트롤러에서 떨어진다.**

host 헤더가 규칙의 `host`와 다르거나, `pathType`/경로 매칭이 의도와 다를 때입니다. `Prefix`와 `Exact`의 차이, 와일드카드 호스트의 단일 라벨 제약을 다시 확인합니다.

**4. rewrite-target이 의도대로 동작하지 않는다.**

캡처 그룹 정규식과 경로 정의가 어긋난 경우입니다. ingress-nginx에서 `rewrite-target`을 쓸 때는 경로를 정규식 캡처 형태로 정의하고, `use-regex` 어노테이션과 함께 검증합니다.

**5. TLS가 적용되지 않는다.**

Secret 타입이 `kubernetes.io/tls`가 아니거나, Secret이 Ingress와 다른 네임스페이스에 있거나, `spec.tls.hosts`와 `rules.host`가 불일치하는 경우입니다. cert-manager를 쓴다면 Certificate와 CertificateRequest, Order 리소스의 상태를 추적합니다.

**6. 컨트롤러 reload가 잦아 지연이 생긴다.**

Ingress/Service/Endpoints 변경이 빈번하면 설정 reload가 자주 발생할 수 있습니다. ingress-nginx는 동적 endpoint 갱신으로 대부분 reload를 피하지만, 어노테이션이나 호스트 규칙 변경은 reload를 유발합니다. 변경을 묶어서 배포하고, 컨트롤러 로그에서 reload 빈도를 모니터링합니다.

kubectl logs -n ingress-nginx deploy/ingress-nginx-controller | grep -i reload

마치며

Ingress를 제대로 다루려면 가장 먼저 **Ingress 리소스(선언)와 Ingress Controller(실행)의 구분**을 체화해야 합니다. 그 위에서 API watch → 설정 생성 → reload로 이어지는 제어 루프를 이해하면, 라우팅이 왜 동작하는지 또는 왜 동작하지 않는지를 구조적으로 추론할 수 있습니다.

리소스 스펙(rules/paths/pathType/backend/defaultBackend), IngressClass를 통한 컨트롤러 선택, TLS Secret 연동까지 익히면 기본적인 운영은 충분히 해낼 수 있습니다. 다만 2026년 현재는 Ingress API가 frozen이고 Gateway API가 후계 표준으로 자리잡는 전환기입니다. 기존 Ingress 자산은 안정적으로 유지하되, 신규 설계에서는 Gateway API를 적극 검토하시길 권합니다. 다음 단계로는 Gateway API로의 마이그레이션과 다중 컨트롤러 운영을 살펴보면 좋습니다.

참고 자료

- Kubernetes 공식 문서 — Ingress: https://kubernetes.io/docs/concepts/services-networking/ingress/

- Kubernetes 공식 문서 — Ingress Controllers: https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/

- ingress-nginx 공식 문서: https://kubernetes.github.io/ingress-nginx/

- Traefik 공식 문서: https://doc.traefik.io/traefik/

- HAProxy 공식 문서: https://www.haproxy.com/documentation/

- Project Contour 공식 문서: https://projectcontour.io/docs/

- Kong Ingress Controller 문서: https://docs.konghq.com/kubernetes-ingress-controller/

- Apache APISIX Ingress 문서: https://apisix.apache.org/docs/ingress-controller/getting-started/

- Gateway API 공식 사이트: https://gateway-api.sigs.k8s.io/

- cert-manager 공식 문서: https://cert-manager.io/docs/

현재 단락 (1/252)

Kubernetes를 처음 운영할 때 가장 많이 듣는 질문 중 하나가 "Service로 LoadBalancer를 만들면 되는데 왜 Ingress가 필요하냐"입니다. 작은 클러스터에...

작성 글자: 0원문 글자: 10,643작성 단락: 0/252