Ingress 를 만든 뒤 나오는 5xx 는 숫자마다 뜻이 다르다. 502 는 백엔드에 연결했지만 응답을 해석하지 못한 것이고, 504 는 응답을 기다리다 시간이 끝난 것이다. 둘을 같은 방법으로 다루면 시간만 버린다. 여기에 더해 /grafana 같은 하위 경로로 서비스를 붙일 때 리다이렉트가 엉키는 문제를 함께 다룬다.
먼저 Ingress 가 실제로 어느 엔드포인트를 가리키는지 확인한다. 엔드포인트가 비어 있으면 503 이지 502 가 아니므로, 502 는 "연결은 됐다" 는 신호다.
kubectl describe ingress <name> -n <ns>
kubectl get endpoints <service> -n <ns>
컨트롤러 파드 안에서 백엔드를 직접 불러 본다. 여기서 성공하면 네트워크가 아니라 프로토콜 문제다.
kubectl exec -n ingress-nginx <controller-pod> -- curl -sv http://<service>.<ns>.svc:<port>/
흔한 원인은 다음과 같다.
| 원인 | 확인 | 조치 |
|---|---|---|
| 백엔드가 HTTPS 인데 Ingress 가 HTTP 로 보냄 | 백엔드 포트가 443 · 8443 · 5601 등 | nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" |
| 서비스 포트와 컨테이너 포트가 어긋남 | kubectl get svc -o yaml 의 targetPort |
서비스 정의 수정 |
| 백엔드가 큰 헤더를 보냄 | 컨트롤러 로그의 upstream sent too big header |
proxy-buffer-size 상향 |
| 백엔드가 기동 중 | 파드 READY 가 0/1 |
readinessProbe 확인 |
백엔드가 자체 서명 인증서를 쓰면 backend-protocol: "HTTPS" 만으로는 부족할 수 있다. ingress-nginx 는 기본적으로 업스트림 인증서를 검증하지 않지만, 검증을 켠 환경에서는 proxy-ssl-verify 관련 설정을 함께 본다.
504 는 대개 백엔드가 살아 있는데 기본 타임아웃(60초) 안에 답을 못 준 것이다. Kibana 나 대시보드 계열처럼 첫 요청에서 무거운 초기화를 하는 서비스에서 자주 본다.
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
| 애너테이션 | 의미 |
|---|---|
proxy-connect-timeout |
백엔드에 TCP 연결을 맺는 데 허용할 시간 |
proxy-send-timeout |
요청을 보내는 동안 허용할 무응답 시간 |
proxy-read-timeout |
응답을 읽는 동안 허용할 무응답 시간 |
타임아웃을 늘리는 것은 증상 완화다. 서비스로는 잘 붙는데 Ingress 로만 504 라면 프로토콜 불일치(위의 backend-protocol)나 컨트롤러 노드에서 백엔드 파드로 가는 경로가 막힌 경우를 먼저 의심한다.
/grafana 로 들어왔는데 /login 으로 튕기는 현상은 Ingress 문제가 아니다. 애플리케이션이 자기가 루트에 있다고 믿고 절대 경로로 리다이렉트를 만들기 때문이다. Ingress 의 rewrite-target 은 들어오는 요청 경로만 바꿔 줄 뿐, 애플리케이션이 만들어 내는 링크와 리다이렉트는 손대지 못한다.
따라서 순서가 중요하다. 먼저 애플리케이션에 자기 기준 경로를 알려 준다.
[server]
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana/
serve_from_sub_path = true
그다음 Ingress 에서 경로를 잘라 준다.
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /grafana(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: grafana
port:
number: 3000
rewrite-target 에 캡처 그룹을 쓰려면 path 에 정규식을 쓸 수 있어야 하므로 pathType 을 ImplementationSpecific 으로 둔다. 컨트롤러에 따라 nginx.ingress.kubernetes.io/use-regex: "true" 가 필요하다.
애플리케이션이 하위 경로를 지원하지 않는다면 경로가 아니라 별도 호스트 이름으로 붙이는 편이 훨씬 안전하다.
설정을 바꾼 뒤에는 애플리케이션이 실제로 그 값을 읽었는지 확인한다. ConfigMap 만 바꾸고 파드를 다시 띄우지 않아 예전 값이 그대로인 경우가 많다.
kubectl exec -n <ns> <pod> -- grep -E 'root_url|serve_from_sub_path' /etc/grafana/grafana.ini
ssl-redirect 는 TLS 가 설정된 Ingress 에서 기본으로 켜진다. HTTP 로 쓰려면 Ingress 에 명시적으로 끈다.
metadata:
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
애너테이션을 껐는데도 리다이렉트된다면 다음을 순서대로 본다.
kubectl get configmap -n ingress-nginx ingress-nginx-controller -o yaml | grep -i redirect
root_url 이 https:// 로 되어 있으면 애플리케이션이 스스로 튕긴다.전역 설정은 클러스터 전체에 영향을 주므로, 특정 서비스만 HTTP 를 허용하려면 전역은 그대로 두고 해당 Ingress 에만 애너테이션을 준다.