Envoy Gateway 상태와 디버깅 핸드북: Accepted, ResolvedRefs, Programmed의 실제 의미

10 min read

엔지니어링에서 가장 무서운 시나리오는 에러 메시지가 아닙니다. 오히려 이런 경우죠:

YAML을 적용했고 kubectl apply도 성공했는데, 트래픽은 여전히 흐르지 않는다.

이 시점에 spec만 들여다보고 있으면, 마치 찻잎 점을 보는 기분이 들기 시작합니다. Gateway API에서 진짜 진단 정보는 status 안에 있습니다.

이 글의 목표는 하나입니다. 가능한 가장 짧은 디버깅 경로를 주는 것. Gateway, HTTPRoute, Listener가 기대대로 동작하지 않을 때, 어디를 먼저 봐야 하는지 알게 해서 우주 전체의 의미를 의심하는 단계까지 가지 않게 하는 것입니다.

먼저 여기부터: spec을 바꾸기 전에 status를 읽자

가장 유용한 명령어:

kubectl get gateway -A
kubectl get httproute -A
kubectl get grpcroute -A
kubectl describe gateway <name>
kubectl describe httproute <name>
kubectl get gateway <name> -o yaml
kubectl get httproute <name> -o yaml

핵심은 YAML을 백 번 더 읽는 것이 아니라, 컨트롤러가 여러분 설정을 실제로 받아들였는지 확인하는 것입니다.

status를 시험 성적표라고 생각하세요:

  • spec은 여러분이 제출한 답안
  • status는 시스템이 채점한 결과

제출한 답안만 보고, 채점 결과를 안 보는 건 디버깅이 아니라 감정적 위안입니다.

가장 중요한 세 가지 Condition: 이것만 익혀도 대부분 해결된다

Accepted

보통 이 condition은 리소스가 상위 주체에 의해 받아들여졌는지를 나타냅니다. route라면 일반적으로 다음을 뜻합니다:

  • 의도한 Gateway/listener에 성공적으로 attach 되었는가
  • 규칙에 명백한 충돌이나 invalid 설정이 없는가

AcceptedFalse라면 우선 의심할 것:

  • parentRefs가 잘못된 Gateway나 listener를 가리킴
  • hostname/listener 조건이 맞지 않음
  • listener가 이 route 타입을 거부함
  • allowedRoutes에 막힘

ResolvedRefs

이 condition은 리소스 내부에서 참조한 객체들이 성공적으로 resolve 되었는지를 뜻합니다. 예를 들면:

  • backendRefs에서 참조한 Service가 존재하는가
  • certificateRefsSecret을 합법적으로 참조할 수 있는가
  • 네임스페이스 간 참조를 위한 ReferenceGrant가 올바르게 구성되었는가

이 condition은 매우 유용합니다. 많은 설정 실패는 라우팅 로직이 틀려서가 아니라, 참조 대상이 아예 없거나 접근 권한이 없어서 생기기 때문입니다.

Programmed

이 condition은 설정이 구현 계층에 실제로 반영되었는지를 보통 나타냅니다. 이렇게 생각하면 됩니다:

  • 컨트롤러가 설정을 수용했고
  • 그 설정을 데이터 플레인이나 인프라에 밀어 넣었다

Accepted는 이미 True인데도 트래픽이 흐르지 않는다면, 이제 Programmed, Gateway 주소, listener 상태, 하부 Envoy 리소스를 확인해 볼 가치가 있습니다.

💡 리소스마다 노출하는 condition 집합은 조금씩 다를 수 있지만, 직관을 키우기 위해 가장 먼저 익혀야 할 1차 그룹은 Accepted, ResolvedRefs, Programmed입니다.

가장 짧은 디버깅 흐름: 보통 이 순서가 잘 먹힌다

1단계: Gateway가 실제로 떠 있는지 확인

kubectl get gateway -A
kubectl get gateway eg -o yaml

확인할 것:

  • status.addresses에 값이 있는가
  • listener condition 중 이상한 것이 없는가
  • listener의 hostname/protocol/port가 기대와 일치하는가

Gateway에 주소가 없다면, 아무리 예쁜 route YAML도 결국은 창작 글쓰기일 뿐입니다.

2단계: Route가 성공적으로 Attach되었는지 확인

kubectl get httproute -A
kubectl get httproute app-route -o yaml

우선순위:

  • status.parents
  • Accepted
  • ResolvedRefs
  • observedGeneration

observedGeneration은 꽤 볼 가치가 있습니다. 이 값이 metadata.generation을 따라잡지 못했다면, 컨트롤러가 아직 최신 설정 버전을 처리하지 않았을 수 있습니다.

3단계: 참조한 리소스가 실제로 존재하는지 확인

이 단계는 다른 어떤 단계보다 자주 건너뛰어집니다:

kubectl get svc -A
kubectl get secret -A
kubectl get referencegrant -A

흔한 문제:

  • 백엔드 서비스 이름이 틀림
  • 포트가 틀림
  • Secret이 생각한 네임스페이스에 없음
  • 네임스페이스 간 참조에 ReferenceGrant가 없음

ResolvedRefsFalse라면 이 경로를 따라 추적하세요.

4단계: 데이터 플레인과 컨트롤러 확인

Gateway와 Route 상태가 대체로 멀쩡해 보이는데도 트래픽이 흐르지 않는다면, 더 아래를 봐야 합니다:

kubectl get pods -n envoy-gateway-system
kubectl logs -n envoy-gateway-system deployment/envoy-gateway

이 시점에는 "규칙이 받아들여졌는가"를 디버깅하는 것이 아니라, 컨트롤 플레인과 데이터 플레인이 정상 동작하는지를 보는 단계입니다.

egctl이 설치돼 있다면 상태를 빠르게 보는 데 도움이 됩니다. 하지만 그것이 없어도 kubectl get/describe -o yaml만으로 대부분의 문제는 해결할 수 있습니다.

가장 흔한 실패 패턴 네 가지

1. Route는 맞는데, 의도한 Listener에 Attach되지 않았다

증상:

  • YAML은 맞아 보임
  • 그런데 트래픽이 규칙에 들어오지 않음

확인할 것:

  • parentRefs.name
  • parentRefs.sectionName
  • Gateway listener 이름이 정확히 일치하는지

멀티 listener 환경에서는 sectionName 오타 하나로도 디버깅 세션 하나를 통째로 날릴 수 있습니다.

2. Accepted는 성공했는데 ResolvedRefs가 실패한다

보통 이건 attach 자체는 가능한데, 참조된 무언가에 문제가 있다는 뜻입니다. 가장 흔한 원인:

  • 백엔드 Service가 존재하지 않음
  • Secret 이름이 틀림
  • 네임스페이스 간 참조가 승인 없이 이뤄짐

이 실패 유형이 가장 짜증 나는 이유는 거의 다 온 것처럼 보이는데, 실제로는 참조 계층에서 멈춰 있기 때문입니다.

3. Gateway에는 Listener가 있는데 기대한 주소가 없다

이건 로컬 클러스터나 LoadBalancer 구현이 없는 환경에서 자주 보입니다. Gateway API가 고장 난 것이 아니라, 아래 인프라가 외부 주소를 프로비저닝하지 못하는 것입니다.

선택지:

  • 클러스터에 LoadBalancer 기능이 있는지 확인
  • port-forward로 라우팅 경로를 검증

4. YAML은 반영됐지만 요청이 404 / 503을 반환한다

이 단계에서는 보통 한 필드만 틀린 것이 아니라, 체인 어딘가가 끊어진 경우가 많습니다:

  • 잘못된 Host header
  • 어떤 규칙과도 매칭되지 않는 path
  • 백엔드 Pod 비정상
  • Service selector가 어떤 Pod도 선택하지 못함

Ingress 문제는 프록시 실패처럼 보여도 실제로는 백엔드가 준비되지 않은 경우가 많습니다. 모든 걸 프록시 탓으로 돌리지는 마세요. 그쪽도 이미 할 일이 많습니다.

한 줄 요약

Gateway API를 가장 효과적으로 디버깅하는 방법은 YAML을 계속 바꾸는 것이 아니라, status를 읽는 법을 익히는 것입니다. Accepted는 주문이 접수됐는지 알려주고, ResolvedRefs는 참조가 해결됐는지 알려주며, Programmed는 설정이 실제로 반영됐는지 알려줍니다. 이 세 계층을 분리해서 보면 문제 해결 속도가 크게 올라갑니다.

다음 단계

이로써 10편짜리 Envoy Gateway 시리즈가 완성됩니다. 시리즈 전체 지도를 다시 보고 싶다면:

👉 시리즈 개요