Skip to content
Published on

Ingress 멀티테넌시와 비용 최적화 — 플랫폼 팀의 선택

Authors

들어가며

여러 팀이 하나의 Kubernetes 클러스터를 공유하는 멀티테넌트 환경에서 Ingress는 까다로운 주제입니다. 외부 진입점이 공유 자원이다 보니, "한 팀이 잘못 설정하면 다른 팀이 피해를 본다"는 위험이 상존합니다. 동시에 비용 측면에서는 "테넌트마다 로드밸런서를 따로 두면 청구서가 곱으로 늘어난다"는 현실적 압박이 있습니다.

플랫폼 팀의 과제는 격리(한 팀의 문제가 다른 팀으로 번지지 않게)와 비용 효율(자원을 공유해 청구서를 줄이게)을 동시에 만족시키는 것입니다. 이 둘은 종종 상충합니다. 강한 격리는 자원 분리를 요구하고, 자원 분리는 비용을 키웁니다.

이 글에서는 공유 컨트롤러와 테넌트별 컨트롤러의 트레이드오프를 시작으로, 네임스페이스 격리, 로드밸런서 비용 구조, 리소스 쿼터, 시끄러운 이웃 방지, 보안 경계, 과금/쇼백 모델, 대규모 ingress 운영, 그리고 플랫폼 팀의 셀프서비스 관점까지 멀티테넌트 Ingress 운영과 비용 최적화를 종합적으로 다룹니다.

공유 컨트롤러 vs 테넌트별 컨트롤러

가장 근본적인 결정은 "Ingress Controller를 모든 팀이 공유할 것인가, 팀마다 따로 둘 것인가"입니다.

   공유 컨트롤러 모델                테넌트별 컨트롤러 모델
   ─────────────────               ──────────────────
        외부 LB(1개)                  team-a LB   team-b LB
           │                            │           │
   ┌───────────────┐             ┌──────────┐ ┌──────────┐
   │ 공유 Ingress    │             │ a 컨트롤러 │ │ b 컨트롤러 │
   │ Controller      │             └────┬─────┘ └────┬─────┘
   └───────┬───────┘                   │            │
     ┌─────┼─────┐                   team-a       team-b
   team-a team-b team-c              (강한 격리)   (강한 격리)
   (비용 효율, 약한 격리)

두 모델의 트레이드오프를 정리하면 다음과 같습니다.

항목공유 컨트롤러테넌트별 컨트롤러
로드밸런서 비용낮음(1개 공유)높음(테넌트 수만큼)
격리 강도약함(설정/장애 공유)강함(독립 프로세스)
운영 복잡도낮음(중앙 관리)높음(다수 관리)
시끄러운 이웃 위험높음낮음
블래스트 반경큼(모두 영향)작음(테넌트 한정)
버전 독립성낮음(일괄 업그레이드)높음(개별 업그레이드)

일반적으로 다수의 소규모 팀에는 공유 컨트롤러가, 강한 규제 요구나 매우 큰 트래픽을 가진 소수 테넌트에는 테넌트별 컨트롤러가 유리합니다. 많은 조직은 둘을 섞어, 일반 팀은 공유 컨트롤러를 쓰되 특수 요구 팀만 전용 컨트롤러를 갖는 하이브리드를 채택합니다.

네임스페이스 격리

공유 클러스터의 기본 격리 단위는 네임스페이스입니다. 컨트롤러를 특정 네임스페이스만 감시하게 하거나, 팀별로 IngressClass를 분리하는 방식이 있습니다.

컨트롤러가 특정 네임스페이스만 감시하도록 제한하려면 watch-namespace를 지정합니다.

# Helm values 예시 (ingress-nginx)
controller:
  scope:
    enabled: true
    namespace: "team-a"
  ingressClassResource:
    name: team-a-nginx
    controllerValue: "k8s.io/team-a-nginx"

이렇게 하면 team-a 컨트롤러는 team-a 네임스페이스의 Ingress만 처리하므로, 다른 팀의 잘못된 설정이 이 컨트롤러에 영향을 주지 않습니다. 팀별 IngressClass와 결합하면 라우팅 소유권이 명확해집니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  namespace: team-a
spec:
  ingressClassName: team-a-nginx
  rules:
    - host: team-a.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

공유 컨트롤러를 쓰면서도 격리를 강화하려면, Contour의 HTTPProxy 위임 모델이 우아합니다. 루트 도메인 정의는 플랫폼 팀이 소유하고, 하위 경로 라우팅만 각 팀 네임스페이스로 위임합니다.

# 플랫폼 팀: 루트 정의 + 위임
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: root
  namespace: platform
spec:
  virtualhost:
    fqdn: example.com
  includes:
    - name: team-a-routes
      namespace: team-a
      conditions:
        - prefix: /team-a

로드밸런서 비용

멀티테넌트 비용에서 가장 큰 변수가 클라우드 로드밸런서입니다. LoadBalancer 타입 서비스 하나마다 클라우드 LB가 프로비저닝되고, 시간당 요금과 처리량 요금이 부과됩니다.

   테넌트별 LB (비용 높음)            공유 LB (비용 낮음)
   ──────────────────              ─────────────────
   team-a → LB1 (월 요금)            모든 팀 → LB1 (월 요금 1개분)
   team-b → LB2 (월 요금)                  │
   team-c → LB3 (월 요금)            공유 컨트롤러가 호스트/경로로 분기
   = LB 요금 x 3                    = LB 요금 x 1

비용을 줄이는 핵심은 LoadBalancer 서비스 개수를 최소화하는 것입니다. 공유 컨트롤러 하나가 단일 LB 뒤에서 호스트/경로 기반으로 모든 테넌트를 라우팅하면, LB 고정 요금을 테넌트들이 분담하게 됩니다. 테넌트별 컨트롤러가 꼭 필요하더라도, 가능하면 공유 LB 뒤에 여러 컨트롤러를 배치하는 구성을 검토할 가치가 있습니다.

리소스 쿼터

공유 컨트롤러 환경에서는 한 테넌트가 자원을 독점해 다른 테넌트를 굶기지 않도록 쿼터를 걸어야 합니다. 네임스페이스 단위 ResourceQuota와 LimitRange를 사용합니다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    count/ingresses.networking.k8s.io: "20"

Ingress 개수 자체에도 쿼터를 걸어, 한 팀이 수천 개의 Ingress를 만들어 컨트롤러의 설정 생성 부담을 폭증시키는 것을 막습니다. 컨트롤러 파드에는 별도로 적정한 requests/limits를 설정해, 트래픽 급증 시에도 안정적으로 동작하도록 합니다.

시끄러운 이웃 방지

공유 컨트롤러의 가장 큰 위험은 한 테넌트의 폭주가 컨트롤러 전체를 마비시키는 것입니다. 이를 막는 장치들입니다.

  • 레이트리밋: 테넌트별 요청 속도 제한으로 한 팀이 컨트롤러 처리량을 독점하지 못하게 합니다.
  • 커넥션 제한: 동시 연결 수 상한.
  • 타임아웃: 느린 백엔드가 워커를 점유하지 않도록 합리적 타임아웃.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  namespace: team-a
  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "100"
    nginx.ingress.kubernetes.io/limit-connections: "50"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "30"
spec:
  ingressClassName: shared-nginx
  rules:
    - host: team-a.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

다만 이런 어노테이션 기반 임의 설정 주입은 보안 이슈가 있을 수 있으므로, 가능하면 플랫폼 팀이 검증한 정책만 허용하는 게이트(정책 엔진)를 두는 것이 좋습니다.

보안 경계

멀티테넌트에서 라우팅은 곧 보안 경계입니다. 한 팀이 다른 팀의 호스트나 경로를 가로채면 트래픽 탈취가 발생합니다. 다음을 강제해야 합니다.

  • 호스트네임 소유권 검증: 팀이 자신에게 할당된 도메인만 Ingress에 쓰도록 정책 엔진(예: 검증 웹훅)으로 강제합니다.
  • IngressClass 강제: 팀은 자신의 IngressClass만 쓸 수 있게 제한합니다.
  • TLS Secret 격리: 인증서 Secret이 네임스페이스를 넘나들지 않게 합니다.
  • 어노테이션 화이트리스트: 위험한 snippet 계열 어노테이션을 차단합니다.

정책 엔진으로 "team-a 네임스페이스의 Ingress는 호스트가 반드시 team-a로 시작해야 한다" 같은 규칙을 강제하면, 호스트 충돌과 탈취를 원천 차단할 수 있습니다.

과금/쇼백 모델

플랫폼 팀이 공유 인프라를 운영할 때, 비용을 테넌트에게 어떻게 배분하느냐가 중요합니다. 두 가지 접근이 있습니다.

  • 차지백(Chargeback): 실제 비용을 테넌트에게 청구. 명확하지만 정밀 계측이 필요합니다.
  • 쇼백(Showback): 청구는 안 하되 각 팀의 사용량/비용을 가시화. 행동 개선을 유도합니다.

공유 LB와 공유 컨트롤러의 고정 비용은 테넌트의 트래픽 비중으로 안분하고, 테넌트별 전용 컨트롤러나 추가 LB는 해당 팀에 직접 귀속시키는 방식이 일반적입니다.

비용 항목배분 방식
공유 LB 고정 요금트래픽 비중으로 안분
공유 컨트롤러 컴퓨트요청 수/대역폭 비중으로 안분
전용 컨트롤러해당 테넌트에 전액 귀속
추가 LB해당 테넌트에 전액 귀속
데이터 전송(egress)테넌트별 egress 계측으로 귀속

Prometheus 메트릭에서 테넌트(네임스페이스/호스트) 라벨로 요청 수와 대역폭을 집계하면 쇼백 리포트를 자동화할 수 있습니다.

대규모 Ingress 운영

테넌트와 Ingress가 수백, 수천 개로 늘어나면 컨트롤러 자체가 병목이 됩니다. 대규모 운영의 핵심 포인트입니다.

  • 설정 reload 부담: nginx 계열은 Ingress가 바뀔 때마다 설정을 다시 생성/reload합니다. Ingress가 많으면 reload가 잦고 무거워집니다. Envoy 계열(xDS)은 동적 갱신이라 이 부담이 적습니다.
  • 컨트롤러 샤딩: 너무 많은 Ingress를 한 컨트롤러가 감당하기 어려우면, 네임스페이스/IngressClass 기준으로 컨트롤러를 샤딩합니다.
  • 메모리/CPU 스케일: Ingress 수에 비례해 컨트롤러 자원 요구가 늘어나므로 모니터링 후 적절히 증설합니다.
  • 상태 가시화: reload 시간, 설정 생성 지연, 큐 길이 같은 컨트롤러 내부 메트릭을 관찰합니다.

수천 개 규모에서는 reload 기반보다 동적 갱신(Envoy 계열) 컨트롤러가 운영상 유리한 경우가 많습니다.

플랫폼 팀 관점: 셀프서비스

플랫폼 팀의 목표는 "각 팀이 플랫폼 팀에 매번 요청하지 않고도 안전하게 Ingress를 만들 수 있게" 하는 것입니다. 이를 위한 패턴입니다.

  • 골든 패스 제공: 검증된 IngressClass, 기본 TLS 발급(cert-manager), 기본 정책이 갖춰진 템플릿을 제공합니다.
  • 정책으로 가드레일: 정책 엔진으로 호스트 소유권, 어노테이션 화이트리스트, IngressClass 강제 등 안전 규칙을 자동 검증합니다.
  • 쇼백으로 책임감 부여: 각 팀이 자기 비용/사용량을 보게 해 자율적 최적화를 유도합니다.
  • 추상화된 인터페이스: 팀은 호스트와 백엔드만 선언하고, 복잡한 컨트롤러 설정은 플랫폼이 감춥니다. Gateway API의 역할 분리(GatewayClass/Gateway는 플랫폼, HTTPRoute는 앱 팀)가 이 모델에 잘 맞습니다.

Gateway API는 본래 멀티테넌트와 역할 분리를 염두에 두고 설계되어, 플랫폼 팀의 셀프서비스 모델에 자연스럽게 들어맞습니다. Ingress가 frozen된 흐름에서, 멀티테넌트 플랫폼이라면 Gateway API 도입을 적극 검토할 만합니다.

사례 시나리오

가상의 조직 사례로 정리해 봅니다. 50개 팀이 하나의 클러스터를 공유합니다.

  • 일반 팀(45개): 단일 공유 컨트롤러 + 단일 공유 LB. 네임스페이스 쿼터, 호스트 소유권 정책, 레이트리밋 가드레일을 적용. 비용은 트래픽 비중으로 쇼백.
  • 고트래픽 팀(3개): 시끄러운 이웃을 피하기 위해 전용 컨트롤러. 단, 공유 LB 뒤에 배치해 추가 LB 비용은 회피.
  • 규제 팀(2개): 강한 격리 요구로 전용 컨트롤러 + 전용 LB. 비용은 전액 해당 팀 귀속.

이렇게 하이브리드로 가면, 다수 팀의 비용 효율을 유지하면서도 특수 요구를 충족하고, 비용 귀속도 공정해집니다.

마치며

멀티테넌트 Ingress 운영은 격리와 비용 효율이라는 상충하는 목표 사이의 균형 잡기입니다. 핵심 원칙을 정리하면, 첫째, 기본은 공유 컨트롤러 + 공유 LB로 비용을 낮추고, 특수 요구 팀만 전용으로 분리하는 하이브리드입니다. 둘째, 네임스페이스 격리, 쿼터, 레이트리밋, 호스트 소유권 정책으로 시끄러운 이웃과 보안 경계 문제를 가드레일로 막습니다. 셋째, 쇼백으로 각 팀에 비용 가시성과 책임감을 주어 자율적 최적화를 유도합니다.

그리고 2026년 흐름에서, 멀티테넌트 플랫폼을 새로 설계한다면 역할 분리가 내장된 Gateway API를 셀프서비스 기반으로 삼는 것을 적극 권합니다. Ingress가 frozen된 지금, 멀티테넌시는 Gateway API가 가장 빛나는 영역 중 하나입니다.

참고 자료