들어가기 앞서
현재 운영 중인 서비스는 AWS Control Tower 구성으로 되어 있다. Security 계정에 3rd Party 보안 솔루션이 있다. 사용자의 트래픽은 각 계정의 보안 솔루션을 통과하고 최종적으로 Web 서버가 배포되어 있는 계정의 EKS에 먼저 도달한다. EKS 앞에는 NLB와 ALB(istio) 가 있다. 즉, NLB - ALB - EKS 구성으로 트래픽이 전달된다.

문제는 8443 포트의 리스너에서 헬스체크 실패가 발생했다. 최상단의 NLB 에서 시작되는 헬스체크는 EKS 에 도달하여 결과를 반환한다. 즉, NLB의 헬스체크와 ALB의 헬스체크는 모두 EKS 에서 설정한 헬스체크 경로에서 반환한다는 것이다.
Readiness Probe 경로 문제인가
제일 먼저 짚고 넘어가야 할 점은 Readiness Probe 의 경로이다. 기본적으로 Deployment 의 YAML 파일을 열어보면 각 Probe 를 정의할 수 있다. 특히 Readiness Probe 를 정의한다면 헬스 체크 파라미터와 경로 및 Port 를 정의할 수 있다. Probe 가 뭔지 모르겠다면 아래의 주인장이 예전에 포스팅한 블로그 글을 보자!
https://hyukops.tistory.com/58
Kubernetes Pod 헬스 체크 (Probe)
들어가기 앞서 파드가 노드에 스케줄링되는 즉시 해당 노드의 kubelet 은 컨테이너를 실행하고, 그 이후부터는 파드가 존재하는한 컨테이너를 계속 실행한다. 컨테이너의 메인 프로세스가 충돌
hyukops.tistory.com
결론부터 말하자면 Load Balancer의 헬스체크와 Kubernetes의 Probe는 완전히 다른 메커니즘이다. Kubernetes의 liveness 또는 readiness probe는 kubelet이 직접 해당 파드에 접속하여 헬스체크를 수행하는 방식이다. 일반적으로 파드 내부의 컨테이너에서 localhost 기준의 포트나 경로(/health, /ready 등)로 체크하게 되며, 이는 클러스터 내부 통신이기 때문에 외부 의존성 없이 동작한다. 즉, 애플리케이션이 정상적으로 기동되어 있고 설정된 엔드포인트가 응답을 반환하면 정상(Healthy)으로 판단된다.
반면, Load Balancer의 헬스체크는 외부에서 네트워크를 통해 접근하는 방식이다. AWS의 NLB나 ALB를 예로 들면, Load Balancer는 등록된 타겟(예: 파드나 노드)으로 TCP 또는 HTTP(S) 요청을 보내고 응답을 기준으로 상태를 판단한다. 이때 대상 파드가 클러스터 내부에서만 접근 가능한 주소(localhost 등)로만 서비스하고 있다면, Load Balancer는 해당 엔드포인트에 접근할 수 없어 헬스체크가 실패하게 된다. 즉, Probe는 내부에서 바라보는 건강 상태, Load Balancer는 외부에서 바라보는 연결 가능성을 기준으로 한다. 이 차이로 인해 파드는 정상으로 보이지만, Load Balancer에서는 Unhealthy로 판단되는 상황이 발생할 수 있다.
*주의*
Istio에서 사용하는 Gateway와 VirtualService는 Kubernetes 기본 리소스가 아니다. 따라서 kubectl get all -A 명령어를 실행해도 이 두 리소스는 출력되지 않는다.
이유는 간단하다. Gateway와 VirtualService는 Istio가 정의한 Custom Resource Definition(CRD) 이기 때문이다. Kubernetes는 기본적으로 Pod, Service, Deployment 같은 표준 리소스만 get all에 포함해서 보여주므로, CRD 기반 리소스는 명시적으로 요청해야 한다.
따라서 Istio 리소스를 확인하려면 아래와 같은 명령어를 사용해야 한다:
kubectl get gateway -A
kubectl get virtualservice -A
문제점은 어디서?
Istio 에서 헬스 체크를 설정하는 방법 또한 다양하다. IngressGateway (service) 에서 수행할 수도 있고 실제 애플리케이션으로 보내 헬스체크를 수행할 수도 있다. 해당 방법 또한 나중에 포스팅 하도록 하자. 결론부터 말하면 문제점은 VirtualService 에서 찾았다. Istio에서 VirtualService를 구성할 때, hosts와 match 조건을 함께 사용하는 패턴은 특정 트래픽에 대한 정교한 라우팅을 가능하게 한다.
- hosts: 수신 요청의 Host 헤더가 web.application.com인 경우에만 적용.
- match.uri.prefix: URI가 /web으로 시작하는 요청에만 라우팅 규칙을 적용.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: ibj-web-virtual-service
spec:
hosts:
- web.application.com
gateways:
- web-gateway
http:
- match:
- uri:
prefix: /web
route:
- destination:
host: web-service
port:
number: 80
이 설정은 “Host 헤더가 web.application.com이고, URI가 /web로 시작하는 요청”만 web-service로 라우팅되도록 한다.
이러한 방식은 여러 서비스가 동일한 Gateway를 공유하면서도 트래픽을 구분하는 데 유용하다. 하지만 NLB(Network Load Balancer) 는 L4 로드밸런서로, 헬스체크 시 Host 헤더 없이 HTTP 요청을 보낸다.
따라서 VirtualService의 hosts 조건(web.application.com)과 일치하지 않아 요청이 라우팅되지 못하고, IngressGateway는 404 또는 503을 반환한다. 이로 인해 NLB는 해당 엔드포인트를 헬스체크 실패로 판단하게 된다.
NLB 가 헬스체크에 실패한 이유
- NLB는 L4 로드밸런서로, Host 헤더 없이 HTTP 요청을 보냄
- VirtualService는 Host와 URI 조건이 모두 맞아야 라우팅됨
- Host 헤더가 없는 요청은 VirtualService에 매칭되지 않아 404 또는 503 반환
- 따라서 NLB는 헬스 체크 실패로 간주함
Envoy Proxy는 VirtualService에 명시된 조건을 충족하지 못하는 요청에 대해 기본적으로 404 또는 503을 반환
ALB 가 헬스체크에 성공한 이유
- ALB는 L7 로드밸런서로, HTTP 프로토콜 기반 헬스 체크를 보냄
- ALB 헬스 체크는 다음과 같은 특징을 가짐
- 기본적으로 GET / 또는 설정된 경로로 요청
- Host 헤더는 ALB 자체 도메인(예: internal-alb-123.elb.amazonaws.com)으로 포함됨
- Envoy는 VirtualService에 매칭되지 않더라도, 다음 중 하나의 이유로 200 OK를 반환할 수 있음:
- Istio Gateway에 연결된 VirtualService 중 하나가 해당 Host에 일부라도 대응
- Fallback 또는 Envoy 필터 설정이 있어 미매칭 시에도 200을 반환하도록 설정
- 다른 VirtualService에서 ALB의 Host를 처리하고 있을 가능성
즉, ALB는 기본적으로 Host 헤더가 포함되고, Envoy는 그 Host에 대해 기본적인 처리로 200을 반환하고 있을 가능성이 높음
'Service Mesh > Istio 운영 특이사항' 카테고리의 다른 글
| [istio] Default Health Check Route and Port (0) | 2025.10.26 |
|---|---|
| [istio] argo rollout + istio 를 통한 canary 배포 전략 도입 (0) | 2025.10.26 |
| [istio] istioOperator 커스텀 (0) | 2025.08.20 |
| [istio] istio proxy 의 Graceful Startup & Termination (2) | 2025.08.07 |
| [istio] istio 의 Access Log 활성화 (3) | 2025.08.06 |