K8s Ingress 라우팅 심화

Kubernetes Ingress란?

Kubernetes Ingress는 클러스터 외부에서 내부 Service로 HTTP/HTTPS 트래픽을 라우팅하는 API 오브젝트다. NodePort나 LoadBalancer Service와 달리, 호스트 기반·경로 기반 라우팅을 단일 진입점에서 처리할 수 있다. 하지만 Ingress 리소스만으로는 아무 일도 일어나지 않는다. 반드시 Ingress Controller가 필요하다.

Ingress Controller 선택

Ingress 리소스는 선언이고, 실제 동작은 Controller가 담당한다. 대표적인 선택지를 비교해보자.

Controller 프록시 엔진 특징
NGINX Ingress NGINX 가장 범용적, 커뮤니티 표준
Traefik 자체 엔진 자동 인증서, 미들웨어 체인
HAProxy Ingress HAProxy 고성능 TCP/HTTP 로드밸런싱
Istio Gateway Envoy 서비스 메시 통합
Gateway API 다양 차세대 표준, 역할 분리

프로덕션에서는 NGINX Ingress Controller가 가장 많이 사용된다. 이 글에서도 NGINX 기반으로 설명한다.

경로 기반 라우팅

하나의 도메인에서 경로별로 다른 Service로 트래픽을 분배하는 가장 기본적인 패턴이다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /users(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: user-service
            port:
              number: 3000
      - path: /orders(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: order-service
            port:
              number: 3000

rewrite-target: /$2 어노테이션은 캡처 그룹을 사용해 경로를 재작성한다. /users/123 요청이 user-service에는 /123으로 전달된다. 이 패턴을 모르면 백엔드 서비스에서 404가 발생하는 흔한 실수를 하게 된다.

호스트 기반 라우팅

서브도메인별로 서비스를 분리하는 패턴이다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-host-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
  - host: admin.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 3000
  - host: ws.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: websocket-service
            port:
              number: 8080

TLS 인증서 설정

HTTPS는 선택이 아니라 필수다. Ingress에서 TLS를 종료하는 두 가지 방법을 살펴보자.

수동 Secret 등록

# TLS Secret 생성
kubectl create secret tls example-tls 
  --cert=tls.crt 
  --key=tls.key 
  -n default

# Ingress에 적용
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    - admin.example.com
    secretName: example-tls
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

cert-manager 자동 발급

Let’s Encrypt와 cert-manager를 연동하면 인증서 발급·갱신이 완전 자동화된다.

# ClusterIssuer 정의
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          class: nginx

# Ingress에 어노테이션 추가
metadata:
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - api.example.com
    secretName: api-tls-auto  # cert-manager가 자동 생성

Rate Limiting

API 남용을 방지하는 속도 제한을 Ingress 레벨에서 설정할 수 있다.

metadata:
  annotations:
    # 초당 요청 수 제한
    nginx.ingress.kubernetes.io/limit-rps: "10"
    # 분당 요청 수 제한
    nginx.ingress.kubernetes.io/limit-rpm: "300"
    # 동시 연결 수 제한
    nginx.ingress.kubernetes.io/limit-connections: "5"
    # 제한 초과 시 응답 코드
    nginx.ingress.kubernetes.io/limit-rate-after: "10m"
    # 버스트 허용량 (rps * burst-multiplier)
    nginx.ingress.kubernetes.io/limit-burst-multiplier: "3"

IP 기반 제한이 기본이며, X-Forwarded-For 헤더로 실제 클라이언트 IP를 식별한다. CDN 뒤에 있다면 use-forwarded-headers ConfigMap 설정이 필수다.

CORS·보안 헤더

프론트엔드-백엔드 분리 아키텍처에서 CORS 설정은 Ingress에서 중앙 관리하는 것이 효율적이다.

metadata:
  annotations:
    # CORS 설정
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization, Content-Type"
    nginx.ingress.kubernetes.io/cors-max-age: "86400"
    
    # 보안 헤더
    nginx.ingress.kubernetes.io/configuration-snippet: |
      more_set_headers "X-Frame-Options: DENY";
      more_set_headers "X-Content-Type-Options: nosniff";
      more_set_headers "X-XSS-Protection: 1; mode=block";
      more_set_headers "Strict-Transport-Security: max-age=31536000";

WebSocket·gRPC 프록시

실시간 통신 프로토콜도 Ingress에서 처리할 수 있다.

# WebSocket
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/upstream-hash-by: "$remote_addr"

# gRPC
metadata:
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"

WebSocket은 기본 60초 타임아웃을 늘려야 하고, upstream-hash-by로 같은 클라이언트가 같은 Pod에 연결되도록 해야 한다. gRPC는 반드시 TLS가 필요하며 backend-protocol: GRPC를 명시해야 한다.

Canary 배포

NGINX Ingress의 canary 어노테이션으로 트래픽을 점진적으로 전환할 수 있다.

# 기존 Ingress (stable)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-stable
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-stable
            port:
              number: 8080
---
# Canary Ingress (새 버전)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    # 가중치 기반: 10% 트래픽
    nginx.ingress.kubernetes.io/canary-weight: "10"
    # 헤더 기반: X-Canary: always 시 무조건 canary로
    nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
    # 쿠키 기반: 특정 사용자만 canary
    nginx.ingress.kubernetes.io/canary-by-cookie: "canary-cookie"
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-canary
            port:
              number: 8080

우선순위는 header → cookie → weight 순이다. 헤더가 설정되면 가중치는 무시된다. QA 팀은 헤더로 canary에 직접 접근하고, 일반 사용자는 가중치로 점진 노출하는 전략이 실무에서 흔하다.

커스텀 에러 페이지

metadata:
  annotations:
    nginx.ingress.kubernetes.io/custom-http-errors: "404,502,503"
    nginx.ingress.kubernetes.io/default-backend: error-page-service

# error-page-service는 요청 헤더에서 원래 상태 코드를 읽음
# X-Code: 원래 HTTP 상태 코드
# X-Original-URI: 원래 요청 경로

성능 튜닝 어노테이션

metadata:
  annotations:
    # 프록시 버퍼 크기 (큰 응답 헤더 처리)
    nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
    # 요청 본문 크기 제한 (파일 업로드)
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
    # 백엔드 연결 타임아웃
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    # Gzip 압축
    nginx.ingress.kubernetes.io/enable-gzip: "true"
    nginx.ingress.kubernetes.io/gzip-min-length: "1000"
    # 로드밸런싱 알고리즘
    nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"

디버깅과 트러블슈팅

# Ingress 상태 확인
kubectl get ingress -A
kubectl describe ingress app-ingress

# NGINX 설정 확인 (실제 생성된 nginx.conf)
kubectl exec -n ingress-nginx deploy/ingress-nginx-controller 
  -- cat /etc/nginx/nginx.conf | grep -A 20 "api.example.com"

# 실시간 로그
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller -f

# 백엔드 연결 테스트
kubectl exec -n ingress-nginx deploy/ingress-nginx-controller 
  -- curl -s http://user-service.default.svc:3000/health

가장 흔한 실수는 ingressClassName 누락, Service 포트 불일치, pathType 오설정이다. describe 명령에서 Events 섹션을 반드시 확인하자.

정리

기능 핵심 어노테이션
경로 재작성 rewrite-target
TLS 자동화 cert-manager.io/cluster-issuer
Rate Limiting limit-rps, limit-connections
Canary 배포 canary, canary-weight, canary-by-header
WebSocket/gRPC proxy-read-timeout, backend-protocol

Ingress는 단순한 라우터가 아니라 TLS 종료, 속도 제한, CORS, Canary 배포, 보안 헤더까지 담당하는 클러스터의 관문이다. 어노테이션 기반 설정을 숙지하면 별도 API Gateway 없이도 상당한 트래픽 제어가 가능하다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux