Skip to content

필사 모드: IngressClass 심화 — 한 클러스터에서 여러 Ingress Controller 운영하기

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

들어가며: 컨트롤러가 하나로 부족할 때

작은 클러스터에서는 Ingress Controller 하나로 충분합니다. 그러나 운영 규모가 커지면 한 클러스터에 컨트롤러가 둘 이상 필요한 상황이 자연스럽게 생깁니다. 예를 들어 인터넷에 노출되는 외부 트래픽과 사내망에만 노출되는 내부 트래픽을 분리하고 싶을 때, 또는 팀마다 독립적인 진입점을 두고 싶을 때, 혹은 ingress-nginx에서 다른 컨트롤러로 점진적으로 옮기는 중일 때가 그렇습니다.

이때 핵심이 되는 메커니즘이 **IngressClass**입니다. IngressClass는 "이 Ingress 리소스를 어떤 컨트롤러가 처리할지"를 결정합니다. 이 글에서는 IngressClass 리소스의 구조, 기본 클래스, 멀티 컨트롤러 시나리오, 컨트롤러 스코프 설정, 충돌 방지, 운영·비용 고려사항을 실무 관점에서 깊이 있게 다룹니다.

IngressClass 리소스와 기본 클래스

IngressClass는 클러스터 스코프 리소스로, 어떤 컨트롤러 구현이 이 클래스를 담당하는지를 `spec.controller`로 명시합니다.

apiVersion: networking.k8s.io/v1

kind: IngressClass

metadata:

name: nginx-external

spec:

controller: k8s.io/ingress-nginx

apiVersion: networking.k8s.io/v1

kind: IngressClass

metadata:

name: nginx-internal

spec:

controller: k8s.io/ingress-nginx

여기서 두 IngressClass가 같은 `controller` 문자열(`k8s.io/ingress-nginx`)을 쓰는 것에 주목하세요. `controller` 값은 "어떤 종류의 컨트롤러 구현인가"를 가리킬 뿐, 클래스 자체는 이름(`nginx-external`, `nginx-internal`)으로 구분됩니다. 즉 같은 종류의 컨트롤러를 여러 인스턴스로 띄워 각각 다른 클래스를 담당하게 할 수 있습니다.

Ingress 리소스는 `spec.ingressClassName`으로 자신이 어떤 클래스에 속하는지 선언합니다.

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: api-ingress

spec:

ingressClassName: nginx-external

rules:

- host: api.example.com

http:

paths:

- path: /

pathType: Prefix

backend:

service:

name: api-service

port:

number: 80

기본 클래스

IngressClass에 다음 어노테이션을 붙이면 **기본 클래스**가 됩니다.

apiVersion: networking.k8s.io/v1

kind: IngressClass

metadata:

name: nginx-external

annotations:

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

spec:

controller: k8s.io/ingress-nginx

`ingressClassName`을 지정하지 않은 Ingress는 기본 클래스로 자동 할당됩니다. 주의할 점은 **기본 클래스는 클러스터에 하나만 있어야 한다**는 것입니다. 둘 이상을 default로 표시하면 어떤 것이 적용될지 모호해지므로, 멀티 컨트롤러 환경에서는 가급적 모든 Ingress에 `ingressClassName`을 명시하는 편이 안전합니다.

어노테이션 vs spec.ingressClassName

과거에는 클래스를 다음 어노테이션으로 지정했습니다.

metadata:

annotations:

kubernetes.io/ingress.class: nginx

그러나 이 방식은 **deprecated**되었고, 현재 권장 방식은 `spec.ingressClassName` 필드입니다. 둘의 차이는 다음과 같습니다.

| 구분 | kubernetes.io/ingress.class (어노테이션) | spec.ingressClassName (필드) |

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

| 상태 | deprecated | 권장 |

| 검증 | 자유 문자열, 검증 없음 | IngressClass 리소스 존재 여부 검증 가능 |

| 기본 클래스 | 지원 안 함 | is-default-class로 지원 |

| 멀티 컨트롤러 | 동작하나 비표준 | 표준 메커니즘 |

신규 환경에서는 어노테이션을 쓰지 말고 `spec.ingressClassName`을 사용하세요. 다만 일부 컨트롤러는 하위 호환을 위해 여전히 어노테이션을 인식하므로, 마이그레이션 중에는 두 방식이 혼재할 수 있습니다. 이 경우 어떤 방식이 우선되는지는 컨트롤러 구현에 따라 다르니, 한쪽으로 통일하는 것이 혼란을 줄입니다.

컨트롤러 스코프 — 어떤 Ingress를 watch할 것인가

같은 종류의 컨트롤러를 여러 인스턴스로 띄울 때는, 각 인스턴스가 **자신이 담당하는 클래스의 Ingress만** 처리하도록 스코프를 좁혀야 합니다. ingress-nginx는 이를 위한 몇 가지 인자를 제공합니다.

--ingress-class / class 설정

각 컨트롤러 인스턴스에 담당 클래스를 지정합니다. ingress-nginx의 Helm values 예시는 다음과 같습니다.

controller:

ingressClassResource:

name: nginx-external

enabled: true

default: false

controllerValue: "k8s.io/ingress-nginx"

ingressClass: nginx-external

electionID: ingress-nginx-external-leader

내부용 컨트롤러는 별도의 values로 배포합니다.

controller:

ingressClassResource:

name: nginx-internal

enabled: true

default: false

controllerValue: "k8s.io/ingress-nginx"

ingressClass: nginx-internal

electionID: ingress-nginx-internal-leader

여기서 `electionID`를 인스턴스마다 다르게 지정하는 것이 중요합니다. 같은 리더 선출 키를 공유하면 두 인스턴스가 서로 간섭할 수 있습니다.

watch-namespace

컨트롤러가 특정 네임스페이스의 Ingress만 watch하도록 제한할 수도 있습니다.

controller:

scope:

enabled: true

namespace: team-a

이렇게 하면 팀 A 컨트롤러는 team-a 네임스페이스의 Ingress만 처리합니다. 클래스 기반 분리와 네임스페이스 기반 분리를 조합하면 더 세밀한 격리가 가능합니다.

멀티 컨트롤러 시나리오

실무에서 자주 등장하는 세 가지 시나리오를 정리합니다.

1. 내부/외부 분리

가장 흔한 패턴입니다. 외부 컨트롤러는 인터넷에 노출되는 LoadBalancer를, 내부 컨트롤러는 사내망 전용 LoadBalancer(또는 internal LB)를 사용합니다.

Internet Internal Network

│ │

▼ ▼

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

│ External LB │ │ Internal LB │

└────────┬─────────┘ └────────┬─────────┘

│ │

▼ ▼

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

│ ingress-nginx │ │ ingress-nginx │

│ class: nginx-external │ │ class: nginx-internal │

└──────────┬───────────┘ └──────────┬───────────┘

│ │

▼ ▼

public services admin/internal services

외부 Ingress는 `ingressClassName: nginx-external`, 내부 Ingress는 `ingressClassName: nginx-internal`로 분리합니다. 이렇게 하면 관리자 페이지 같은 민감한 서비스를 실수로 인터넷에 노출하는 사고를 줄일 수 있습니다.

2. 팀별 분리

멀티테넌트 환경에서 팀마다 독립적인 컨트롤러를 두는 패턴입니다. 각 팀은 자기 클래스(예: team-a-nginx, team-b-nginx)만 사용하고, 컨트롤러도 팀 네임스페이스만 watch하도록 스코프를 좁힙니다. 이렇게 하면 한 팀의 트래픽 폭주나 설정 오류가 다른 팀에 미치는 영향을 줄일 수 있습니다.

3. 마이그레이션

ingress-nginx에서 다른 컨트롤러(예: Traefik, Envoy 기반)로 옮기는 중에는 두 컨트롤러가 잠시 공존합니다. 기존 서비스는 기존 클래스로 두고, 새 서비스나 일부 트래픽만 새 클래스로 옮긴 뒤 점진적으로 전환합니다. 2026년 컨텍스트에서 Gateway API로 옮기는 경우에도, Ingress 컨트롤러와 Gateway 컨트롤러가 한동안 병행 운영됩니다.

충돌 방지

멀티 컨트롤러의 핵심 위험은 **두 컨트롤러가 같은 Ingress를 동시에 처리하거나, 같은 호스트를 중복 노출**하는 것입니다. 다음 원칙으로 방지합니다.

1. **모든 Ingress에 ingressClassName 명시**: 기본 클래스에 의존하지 않으면 "어느 컨트롤러가 잡을지" 모호함이 사라집니다.

2. **기본 클래스는 최대 하나**: default를 둘 이상 표시하지 않습니다.

3. **electionID 분리**: 같은 종류 컨트롤러 인스턴스마다 리더 선출 키를 다르게 합니다.

4. **호스트 중복 금지**: 두 클래스가 같은 호스트를 노출하지 않도록 합니다. 특히 마이그레이션 중에는 컷오버 시점까지 호스트를 분리합니다.

5. **스코프 좁히기**: 필요하면 watch-namespace로 컨트롤러의 관할 범위를 명확히 제한합니다.

비용과 운영 고려

멀티 컨트롤러는 격리와 안전성을 주지만 비용과 운영 복잡도를 동반합니다.

- **로드밸런서 비용**: 컨트롤러마다 LoadBalancer 타입 Service를 두면 클라우드 로드밸런서가 추가됩니다. 내부 컨트롤러는 internal LB로, 또는 단일 LB 뒤에서 호스트로 분기하는 방식으로 비용을 줄일 수 있습니다.

- **리소스 오버헤드**: 각 컨트롤러는 자체 파드, 메모리, CPU를 소비합니다. 컨트롤러 개수가 늘수록 관리 대상도 늘어납니다.

- **운영 일관성**: 컨트롤러마다 버전, 어노테이션 문법, 튜닝 파라미터가 제각각이면 운영이 어려워집니다. 가능하면 같은 종류의 컨트롤러를 여러 클래스로 나누는 편이 운영 일관성에 유리합니다.

- **관측성**: 컨트롤러마다 메트릭/로그를 분리 수집하고, 어느 클래스가 어떤 트래픽을 처리하는지 대시보드로 명확히 합니다.

또한 2026년 현재 ingress-nginx가 유지보수 모드 흐름이라는 점을 고려하면, 새로 멀티 진입점을 설계할 때는 Gateway API의 다중 Gateway 모델도 함께 검토하는 것이 합리적입니다. Gateway API는 여러 Gateway를 GatewayClass로 구분하므로 멀티 진입점을 표준적으로 표현할 수 있습니다.

실습 YAML

내부/외부 두 클래스를 만들고 각각에 Ingress를 붙이는 전체 예시입니다.

apiVersion: networking.k8s.io/v1

kind: IngressClass

metadata:

name: nginx-external

spec:

controller: k8s.io/ingress-nginx

apiVersion: networking.k8s.io/v1

kind: IngressClass

metadata:

name: nginx-internal

spec:

controller: k8s.io/ingress-nginx

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: public-api

namespace: shop

spec:

ingressClassName: nginx-external

rules:

- host: api.example.com

http:

paths:

- path: /

pathType: Prefix

backend:

service:

name: api-service

port:

number: 80

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: admin-panel

namespace: shop

spec:

ingressClassName: nginx-internal

rules:

- host: admin.internal.example.com

http:

paths:

- path: /

pathType: Prefix

backend:

service:

name: admin-service

port:

number: 80

생성 후 확인 명령은 다음과 같습니다.

kubectl get ingressclass

kubectl get ingress -A -o custom-columns=NAME:.metadata.name,CLASS:.spec.ingressClassName,HOSTS:.spec.rules[*].host

함정과 트러블슈팅

**1. Ingress가 어느 컨트롤러에도 잡히지 않는다.**

`ingressClassName`이 실제 존재하는 IngressClass 이름과 다르거나, 기본 클래스가 없어 클래스 미지정 Ingress가 방치된 경우입니다. IngressClass 목록과 Ingress의 클래스 지정을 대조합니다.

kubectl get ingressclass

kubectl describe ingress admin-panel

**2. 두 컨트롤러가 같은 Ingress를 처리한다.**

같은 종류 컨트롤러가 클래스 스코프를 제대로 좁히지 않으면 발생할 수 있습니다. 각 컨트롤러의 `ingressClass` 설정과 `electionID`가 분리되어 있는지 확인합니다.

**3. 기본 클래스가 둘이라 동작이 예측 불가능하다.**

`is-default-class: "true"`가 두 IngressClass에 붙어 있는지 확인하고, 하나만 남깁니다.

kubectl get ingressclass -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations.ingressclass\.kubernetes\.io/is-default-class}{"\n"}{end}'

**4. 내부 서비스가 인터넷에 노출됐다.**

내부용 Ingress에 외부 클래스를 잘못 지정한 경우입니다. 민감 서비스의 `ingressClassName`을 다시 확인하고, 정책(예: OPA/Kyverno)으로 강제하는 것을 고려합니다.

**5. 마이그레이션 중 트래픽이 섞인다.**

기존 클래스와 새 클래스가 같은 호스트를 동시에 노출하면 트래픽이 분산됩니다. 컷오버 전까지 호스트나 가중치로 분리하고, 한 번에 전환합니다.

마치며

멀티 컨트롤러 운영의 출발점은 IngressClass를 정확히 이해하는 것입니다. `spec.controller`로 컨트롤러 종류를, IngressClass 이름으로 인스턴스를 구분하고, Ingress의 `spec.ingressClassName`으로 어느 클래스가 처리할지 명시합니다. 기본 클래스는 하나만 두고, 같은 종류 컨트롤러는 `ingressClass`와 `electionID`로 스코프를 분리하며, 필요하면 watch-namespace로 관할을 좁힙니다.

내부/외부 분리, 팀별 격리, 마이그레이션이라는 대표 시나리오에서 멀티 컨트롤러는 강력한 도구입니다. 다만 로드밸런서 비용과 운영 복잡도라는 대가가 따르므로, 충돌 방지 원칙을 지키고 관측성을 갖추는 것이 중요합니다. 2026년 컨텍스트에서는 ingress-nginx 유지보수 흐름을 감안해, 새 멀티 진입점 설계에는 Gateway API의 다중 Gateway 모델도 함께 검토하시길 권합니다.

참고 자료

- Kubernetes 공식 문서 — Ingress (IngressClass): https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class

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

- ingress-nginx — Multiple controllers: https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/

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

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

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

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

- Kyverno 공식 문서: https://kyverno.io/docs/

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

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

현재 단락 (1/191)

작은 클러스터에서는 Ingress Controller 하나로 충분합니다. 그러나 운영 규모가 커지면 한 클러스터에 컨트롤러가 둘 이상 필요한 상황이 자연스럽게 생깁니다. 예를 들어...

작성 글자: 0원문 글자: 7,496작성 단락: 0/191