/엔지니어/Docker / 컨테이너/Kubernetes Ingress 완전 가이드 —
Docker / 컨테이너고급linuxkubernetesingressnginx

Kubernetes Ingress 완전 가이드 — Nginx Ingress Controller로 외부 트래픽 라우팅

Nginx Ingress Controller를 설치하고 호스트/경로 기반 라우팅, TLS 종료, 리라이트, 인증 등 실전 Ingress 구성을 단계별로 익힙니다.

Kubernetes Ingress 란?

Ingress는 클러스터 외부에서 들어오는 HTTP/HTTPS 트래픽을 내부 Service로 라우팅하는 L7 규칙의 집합입니다. Service의 type: LoadBalancer를 서비스마다 만들면 클라우드 LB가 그만큼 늘어나 비용이 커지지만, Ingress는 단일 진입점에서 호스트명과 경로를 기준으로 여러 Service에 분기할 수 있습니다.

  • Ingress 리소스: "어떤 호스트/경로를 어떤 Service로 보낼지" 선언만 담음
  • Ingress Controller: 그 선언을 실제로 처리하는 리버스 프록시(Nginx, HAProxy, Traefik 등)
  • Ingress 리소스만 만들고 Controller가 없으면 아무 일도 일어나지 않습니다.

Ingress 객체는 명세(스펙)일 뿐이며, 트래픽을 실제로 처리하려면 반드시 Ingress Controller를 별도로 설치해야 합니다. 이 점을 놓치면 "Ingress를 만들었는데 접속이 안 된다"는 함정에 빠집니다.


Nginx Ingress Controller 설치

가장 널리 쓰이는 ingress-nginx(쿠버네티스 공식 프로젝트)를 Helm으로 설치합니다.

Bash
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 \
  --set controller.service.type=LoadBalancer \
  --set controller.metrics.enabled=true

설치 후 컨트롤러와 외부 IP를 확인합니다.

Bash
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx ingress-nginx-controller
# EXTERNAL-IP 가 할당되면 그 IP/도메인으로 트래픽이 들어옵니다

온프레미스(베어메탈)라면 LoadBalancer IP가 안 잡히므로 MetalLB를 함께 쓰거나 controller.service.type=NodePort로 설치합니다.

IngressClass 확인

최신 쿠버네티스에서는 어떤 컨트롤러가 Ingress를 처리할지 ingressClassName으로 지정합니다.

Bash
kubectl get ingressclass
# NAME    CONTROLLER             PARAMETERS   AGE
# nginx   k8s.io/ingress-nginx   <none>       2m

테스트용 백엔드 배포

라우팅을 검증할 두 개의 데모 서비스를 띄웁니다.

YAML
# apps.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-a
spec:
  replicas: 2
  selector: { matchLabels: { app: web-a } }
  template:
    metadata: { labels: { app: web-a } }
    spec:
      containers:
      - name: web
        image: hashicorp/http-echo
        args: ["-text=Hello from A", "-listen=:8080"]
        ports: [{ containerPort: 8080 }]
---
apiVersion: v1
kind: Service
metadata:
  name: web-a
spec:
  selector: { app: web-a }
  ports: [{ port: 80, targetPort: 8080 }]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-b
spec:
  replicas: 2
  selector: { matchLabels: { app: web-b } }
  template:
    metadata: { labels: { app: web-b } }
    spec:
      containers:
      - name: web
        image: hashicorp/http-echo
        args: ["-text=Hello from B", "-listen=:8080"]
        ports: [{ containerPort: 8080 }]
---
apiVersion: v1
kind: Service
metadata:
  name: web-b
spec:
  selector: { app: web-b }
  ports: [{ port: 80, targetPort: 8080 }]
Bash
kubectl apply -f apps.yaml

경로 기반 라우팅

하나의 호스트에서 경로별로 다른 백엔드로 분기합니다.

YAML
# ingress-path.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: demo.example.com
    http:
      paths:
      - path: /a
        pathType: Prefix
        backend:
          service:
            name: web-a
            port: { number: 80 }
      - path: /b
        pathType: Prefix
        backend:
          service:
            name: web-b
            port: { number: 80 }

pathType은 다음 세 가지가 있습니다.

pathType의미
Prefix경로 접두사 매칭 (/a/a, /a/x 모두 매칭)
Exact경로 완전 일치만 매칭
ImplementationSpecific컨트롤러 구현에 위임 (정규식 등)
Bash
kubectl apply -f ingress-path.yaml
# /etc/hosts 또는 DNS 에 demo.example.com → EXTERNAL-IP 매핑 후
curl http://demo.example.com/a   # Hello from A
curl http://demo.example.com/b   # Hello from B

호스트(도메인) 기반 가상호스팅

서브도메인별로 다른 서비스를 노출하는 패턴입니다.

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

TLS 종료 (HTTPS)

Ingress에서 TLS를 종료하면 백엔드는 평문 HTTP로 통신하고, 외부에는 HTTPS만 노출됩니다.

1) 인증서를 Secret으로 등록

테스트는 자체서명, 운영은 cert-manager + Let's Encrypt를 권장합니다.

Bash
# 테스트용 자체서명 인증서
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key -out tls.crt \
  -subj "/CN=demo.example.com/O=demo"

kubectl create secret tls demo-tls --cert=tls.crt --key=tls.key

2) Ingress에 tls 블록 추가

YAML
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"   # HTTP→HTTPS 강제
spec:
  ingressClassName: nginx
  tls:
  - hosts: [demo.example.com]
    secretName: demo-tls
  rules:
  - host: demo.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend: { service: { name: web-a, port: { number: 80 } } }

cert-manager로 자동 갱신 (운영 권장)

Bash
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
YAML
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef: { name: letsencrypt-prod }
    solvers:
    - http01:
        ingress: { ingressClassName: nginx }

Ingress에 cert-manager.io/cluster-issuer: letsencrypt-prod 어노테이션만 달면 인증서가 자동 발급·갱신됩니다.


자주 쓰는 Nginx 어노테이션

ingress-nginx의 동작은 어노테이션으로 세밀하게 제어합니다.

어노테이션용도
nginx.ingress.kubernetes.io/rewrite-target경로 재작성 (정규식 캡처 $1 사용)
nginx.ingress.kubernetes.io/ssl-redirectHTTP→HTTPS 리다이렉트
nginx.ingress.kubernetes.io/proxy-body-size업로드 최대 크기 (기본 1m)
nginx.ingress.kubernetes.io/proxy-read-timeout백엔드 응답 타임아웃(초)
nginx.ingress.kubernetes.io/auth-typeBasic Auth 등 인증
nginx.ingress.kubernetes.io/configuration-snippet임의 Nginx 설정 삽입

rewrite-target 예시 — /api 접두사 제거

YAML
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: demo.example.com
    http:
      paths:
      - path: /api(/|$)(.*)
        pathType: ImplementationSpecific
        backend: { service: { name: web-a, port: { number: 80 } } }

/api/users 요청이 백엔드에는 /users로 전달됩니다.

Basic Auth 적용

Bash
htpasswd -c auth admin           # 비밀번호 입력
kubectl create secret generic basic-auth --from-file=auth
YAML
metadata:
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"

트러블슈팅

Bash
# 어떤 라우팅이 적용됐는지 상세 확인
kubectl describe ingress app-ingress

# 컨트롤러 로그 — 502/503, 인증서 오류 추적
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller -f

# 생성된 실제 nginx.conf 덤프
kubectl exec -n ingress-nginx deploy/ingress-nginx-controller -- cat /etc/nginx/nginx.conf
증상원인 / 점검
404 Not Found (nginx)host/path 불일치 또는 ingressClassName 누락
503 Service Unavailable백엔드 Pod가 없거나 Service selector 불일치
TLS 인증서 미적용tls.secretName 오타, Secret이 같은 네임스페이스에 없음
502 Bad Gateway백엔드 포트(targetPort) 불일치, 컨테이너 미기동

Ingress 리소스와 백엔드 Service는 같은 네임스페이스에 있어야 합니다. cross-namespace 라우팅은 기본 Ingress 스펙으로 불가능하며 ExternalName Service 등 우회가 필요합니다.


정리

항목핵심
Ingress 리소스호스트/경로 → Service 라우팅 규칙 선언
Ingress Controller규칙을 실제로 처리하는 프록시 (필수 설치)
ingressClassName여러 컨트롤러 중 처리 주체 지정
pathTypePrefix / Exact / ImplementationSpecific
TLStls 블록 + Secret, 운영은 cert-manager 자동 갱신
동작 제어nginx.ingress.kubernetes.io/* 어노테이션
디버깅describe ingress + 컨트롤러 로그

단일 진입점에서 비용을 줄이며 L7 라우팅·TLS·인증을 한곳에 모으는 것이 Ingress의 핵심 가치입니다. 운영에서는 cert-manager로 인증서를 자동화하고, 어노테이션으로 타임아웃·바디 크기 같은 현실적 제약을 반드시 조정하세요.

#kubernetes#ingress#nginx#tls#routing
편집 안내 · Editorial Note

이 가이드는 AI 도구를 활용해 초안을 구성하고 사람이 명령어·문맥을 검토해 발행했습니다. 운영체제와 도구 버전에 따라 결과가 달라질 수 있으므로 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요.

질문 & 답변 (Q&A)

이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.