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으로 설치합니다.
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를 확인합니다.
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으로 지정합니다.
kubectl get ingressclass
# NAME CONTROLLER PARAMETERS AGE
# nginx k8s.io/ingress-nginx <none> 2m테스트용 백엔드 배포
라우팅을 검증할 두 개의 데모 서비스를 띄웁니다.
# 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 }]kubectl apply -f apps.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 | 컨트롤러 구현에 위임 (정규식 등) |
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호스트(도메인) 기반 가상호스팅
서브도메인별로 다른 서비스를 노출하는 패턴입니다.
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를 권장합니다.
# 테스트용 자체서명 인증서
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.key2) Ingress에 tls 블록 추가
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로 자동 갱신 (운영 권장)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yamlapiVersion: 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-redirect | HTTP→HTTPS 리다이렉트 |
nginx.ingress.kubernetes.io/proxy-body-size | 업로드 최대 크기 (기본 1m) |
nginx.ingress.kubernetes.io/proxy-read-timeout | 백엔드 응답 타임아웃(초) |
nginx.ingress.kubernetes.io/auth-type | Basic Auth 등 인증 |
nginx.ingress.kubernetes.io/configuration-snippet | 임의 Nginx 설정 삽입 |
rewrite-target 예시 — /api 접두사 제거
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 적용
htpasswd -c auth admin # 비밀번호 입력
kubectl create secret generic basic-auth --from-file=authmetadata:
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"트러블슈팅
# 어떤 라우팅이 적용됐는지 상세 확인
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 | 여러 컨트롤러 중 처리 주체 지정 |
| pathType | Prefix / Exact / ImplementationSpecific |
| TLS | tls 블록 + Secret, 운영은 cert-manager 자동 갱신 |
| 동작 제어 | nginx.ingress.kubernetes.io/* 어노테이션 |
| 디버깅 | describe ingress + 컨트롤러 로그 |
단일 진입점에서 비용을 줄이며 L7 라우팅·TLS·인증을 한곳에 모으는 것이 Ingress의 핵심 가치입니다. 운영에서는 cert-manager로 인증서를 자동화하고, 어노테이션으로 타임아웃·바디 크기 같은 현실적 제약을 반드시 조정하세요.
이 가이드는 AI 도구를 활용해 초안을 구성하고 사람이 명령어·문맥을 검토해 발행했습니다. 운영체제와 도구 버전에 따라 결과가 달라질 수 있으므로 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요.
질문 & 답변 (Q&A)
이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.