0. 들어가며
Istio는 강력한 서비스 메시 기능을 제공하지만, 그만큼 구조가 복잡해 문제가 발생했을 때 원인을 파악하기 어렵다. 특히 네트워크 지연, 응답 실패, 잘못된 라우팅과 같은 문제들은 컨트롤 플레인과 데이터 플레인, 애플리케이션 간의 상호작용 속에서 발생하기 때문에 단순한 로그 확인만으로는 실마리를 찾기 어렵다.
이번 포스팅에서는 Istio에서 자주 발생하는 문제 상황을 재현하고, 이를 디버깅하고 해결하는 과정을 다룬다. VirtualService, DestinationRule 설정 오류부터, Envoy Proxy의 헬스 체크, 지연 응답, 그리고 Prometheus 쿼리 분석까지 운영에서 에서 마주칠 수 있는 다양한 트러블슈팅 방법을 단계별로 정리해 본다.
실습 환경 구성: 설정 누락으로 503 에러 재현하기
Istio에서 자주 마주치는 문제는 대부분 데이터 플레인, 즉 Envoy Proxy의 설정 오류에서 발생한다. 예를 들어, VirtualService를 생성하면서 DestinationRule 없이 Subset을 사용하는 경우가 대표적이다. 배포는 정상적으로 되지만, 트래픽은 원하는 방식으로 흐르지 않고, 결국 503 오류가 발생한다.
Istio Ingress Gateway와 함께 VirtualService를 이용해 트래픽을 v1과 v2 버전으로 각각 20%, 80% 라우팅 하는 실습을 진행한다. 단, 이때 의도적으로 DestinationRule을 생략하여 에러 상황을 유도한다.
# 실습 코드 레포: https://github.com/AcornPublishing/istio-in-action.git
# 샘플 애플리케이션 배포
kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction # catalog v1 배포
kubectl apply -f ch10/catalog-deployment-v2.yaml -n istioinaction # catalog v2 배포
kubectl apply -f ch10/catalog-gateway.yaml -n istioinaction # catalog-gateway 배포
kubectl apply -f ch10/catalog-virtualservice-subsets-v1-v2.yaml -n istioinaction
이 상태에서 Envoy는 정의되지 않은 Subset(version-v1, version-v2)을 찾으려 하고, 결국 다음과 같은 503 에러와 함께 NC (No Cluster) 로그를 출력한다.
kubectl logs -n istio-system -l app=istio-ingressgateway -f
# [로그 예시] "GET /items HTTP/1.1" 503 NC cluster_not_found ...
이 상태에서 curl로 반복 요청을 보내보면, 모든 응답은 동일하게 실패한다.
# 반복 요청으로 상태 확인
for i in {1..100}; do \
curl http://catalog.istioinaction.io:30000/items -w "\nStatus Code %{http_code}\n"; \
sleep .5; \
done
Status Code 503
문제의 원인은 DestinationRule에서 subset을 정의하지 않은 설정 누락이다. 해결 방법은 간단하다. 필요한 subset 정의를 포함한 올바른 DestinationRule을 추가하면 된다.
이처럼 원인을 알고 나면 단순한 실수처럼 보일 수 있지만, Istio의 구조를 충분히 이해하지 못한 상태에서 디버깅을 시작하면 오히려 더 큰 혼란에 빠지기 쉽다.
데이터 플레인은 스스로 판단하거나 설정을 수정하지 않는다. 컨트롤 플레인(istiod)으로부터 전달받은 설정을 그대로 반영할 뿐이다.
즉, 겉보기에 데이터 플레인의 문제처럼 보여도, 실제로는 컨트롤 플레인과의 동기화 문제일 수 있다는 뜻이다.
이처럼 Istio에서 발생하는 문제는 표면적인 증상과 실제 원인이 다를 수 있기 때문에, 정확한 흐름을 따라가는 디버깅 방식이 중요하다.
다음 섹션에서는 이런 문제를 어떻게 파악하고 해결할 수 있을지 다양한 트러블슈팅 방법을 통해 살펴보자.
1. 데이터 플레인 문제 식별하기
1.1 데이터 플레인 문제로 보기 전에 확인할 부분
앞서 언급했듯이, 데이터 플레인은 독립적으로 설정을 바꾸지 않으며 항상 컨트롤 플레인(istiod)에서 받은 설정을 따르게 되어 있다.
따라서 문제가 데이터 플레인에 있는 것처럼 보여도, 실제 원인은 컨트롤 플레인과의 동기화 실패일 수 있다.
결론적으로, 본격적인 트러블슈팅에 앞서 가장 먼저 확인해야 할 것은 Envoy가 Istiod로부터 최신 설정을 제대로 받고 있는지 여부다. 이 과정을 건너뛰면, 진짜 원인을 파악하는 데까지 불필요한 시간이 걸릴 수 있다.
istioctl proxy-status로 snyc 상태 확인
istioctl proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
catalog-6cf4b97d-l44zk.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-8d74787f-ltkhs 1.17.8
catalog-v2-56c97f6db-d74kv.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-8d74787f-ltkhs 1.17.8
catalog-v2-56c97f6db-m6pvj.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-8d74787f-ltkhs 1.17.8
istio-egressgateway-85df6b84b7-2f4th.istio-system Kubernetes SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-8d74787f-ltkhs 1.17.8
istio-ingressgateway-6bb8fb6549-hcdnc.istio-system Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-8d74787f-ltkhs 1.17.8
- SYNCED : Envoy가 istiod로부터 최신 설정을 성공적으로 수신한 상태
- NOT SENT : istiod가 Envoy에게 보낼 설정이 없어서 아무것도 전달되지 않음 (정상일 수 있음)
- STALE : istiod가 업데이트를 보냈지만 Envoy가 이를 확인하지 못한 상태 → 과부하, 커넥션 문제, 또는 Istio 버그 가능성 있음
예시 결과에서 모든 워크로드가 SYNCED 상태를 유지하고 있으므로, 컨트롤 플레인 자체는 정상으로 보인다. 이제 데이터 플레인 설정 오류를 본격적으로 점검해 보자
1.2 Kiali로 잘못된 설정 빠르게 확인하기
데이터 플레인에서 가장 흔하게 발생하는 문제는 잘못된 워크로드 설정이다. 이때 Kiali를 활용하면 시각적으로 빠르게 오류를 파악할 수 있다.
이를 클릭하면 문제가 되는 Istio 리소스(예: VirtualService)의 설정으로 이동하고, 내장된 편집기에서 관련 경고 메시지를 확인할 수 있다.
경고 아이콘 위에 마우스를 올리면 KIA1107 - Subset not found와 같은 메시지가 뜬다. 이는 존재하지 않는 subset을 참조하고 있다는 뜻이다. 보통은 DestinationRule에 해당 subset 정의가 누락되었거나, subset 이름에 오타가 있는 경우다.
Istio는 유효성 검사를 통한 다양한 케이스의 에러상황을 코드를 통해 제공한다.
1.3 istioctl로 정적/동적 설정 오류 분석
잘못 설정된 워크로드를 자동으로 트러블슈팅하는 데 가장 유용한 istioctl 명령어 두 가지는 istioctl analyze와 istioctl describe이다.
istioctl analyze
- 선언형 리소스 정적 분석
istioctl analyze는 설정을 클러스터에 적용하기 전에 미리 검사해 볼 수 있는 정적 분석 도구다. VirtualService, DestinationRule 등 주요 Istio 리소스 정의에 대한 구문 오류, 충돌, 누락 등을 사전에 확인할 수 있다.
istioctl analyze -h
istioctl analyze --list-analyzers
예시 출력
# 분석결과 : 오류 메시지 외에 istio 오류 코드 IST0101 도 제공
istioctl analyze -n istioinaction
Error [IST0101] (VirtualService istioinaction/catalog-v1-v2) Referenced host+subset in destinationrule not found: "catalog.istioinaction.svc.cluster.local+version-v1"
Error [IST0101] (VirtualService istioinaction/catalog-v1-v2) Referenced host+subset in destinationrule not found: "catalog.istioinaction.svc.cluster.local+version-v2"
Error: Analyzers found issues when analyzing namespace: istioinaction.
See https://istio.io/v1.17/docs/reference/config/analysis for more information about causes and resolutions.
에러코드(IST0101)에 대한 자세한 설명 제공 - https://istio.io/v1.17/docs/reference/config/analysis/
istioctl describe
- Envoy 런타임 상태 확인
istioctl describe는 실제 배포된 Envoy 프록시(워크로드) 가 어떤 설정으로 동작 중인지 런타임 상태를 확인할 수 있다. 트래픽 경로, 필터 체인, 리스너 누락 등 문제를 동적으로 추적할 때 유용하다
# 단축키 : experimental(x), describe(des)
istioctl experimental describe -h
istioctl x des pod -n istioinaction $CATALOG_POD1
예시 결과:
Pod: catalog-6cf4b97d-4bh75
Pod Revision: default
Pod Ports: 3000 (catalog), 15090 (istio-proxy)
--------------------
Service: catalog
Port: http 80/HTTP targets pod port 3000
--------------------
Effective PeerAuthentication:
Workload mTLS mode: PERMISSIVE
# Warning 경고 발생 !
Exposed on Ingress Gateway http://192.168.107.2
VirtualService: catalog-v1-v2
WARNING: No destinations match pod subsets (checked 1 HTTP routes)
Warning: Route to subset version-v1 but NO DESTINATION RULE defining subsets!
Warning: Route to subset version-v2 but NO DESTINATION RULE defining subsets!
설정 누락을 해결한 뒤 다시 describe를 실행하면, subset이 정상적으로 인식되는 것을 확인할 수 있다.
# 문제 해결 후 확인(Warning 경고가 사라짐)
istioctl x des pod -n istioinaction $CATALOG_POD1
Pod: catalog-6cf4b97d-4bh75
Pod Revision: default
Pod Ports: 3000 (catalog), 15090 (istio-proxy)
--------------------
Service: catalog
Port: http 80/HTTP targets pod port 3000
DestinationRule: catalog for "catalog.istioinaction.svc.cluster.local"
Matching subsets: version-v1
(Non-matching subsets version-v2)
No Traffic Policy
--------------------
Effective PeerAuthentication:
Workload mTLS mode: PERMISSIVE
Exposed on Ingress Gateway http://192.168.107.2
VirtualService: catalog-v1-v2
Weight 20%
istioctl analyze와 describe는 각각 정적/동적 관점에서 설정 오류를 분석할 수 있는 강력한 도구다. 실수로 인한 설정 누락을 빠르게 식별하고 검증할 수 있으며, 특히 CI/CD 파이프라인에 통합해 자동 검증 도구로도 활용 가능하다.
2. 엔보이 설정에서 수동으로 잘못된 설정 발견하기
앞서 소개한 다양한 방법으로 디버깅해도 문제가 해결되지 않는다면, Envoy 설정 전체를 수동으로 직접 확인해야 한다. 이때는 Envoy 관리 인터페이스와 istioctl proxy-config 명령어를 조합해 트래픽 흐름의 모든 구간을 점검할 수 있다.
2.1 엔보이 관리(admin) 인터페이스
Envoy에는 관리 인터페이스(Admin Interface)가 있으며, 이는 프록시의 내부 설정을 직접 확인하고 일부 상태를 변경할 수 있는 기능을 제공한다. 이 인터페이스는 모든 Envoy 프록시에서 포트 15000을 통해 접근 가능하다.
kubectl port-forward deploy/catalog -n istioinaction 15000:15000
open http://localhost:15000
# 현재 적재한 엔보이 설정 출력 : 데이터양이 많다!
curl -s localhost:15000/config_dump | wc -l
13952
config_dump는 Istio의 모든 설정 파일을 확인할 수 있다. 하지만 출력은 너무 방대해서 사람이 읽기엔 어렵다. 그래서 istioctl은 설정을 더 잘게 나눠 필터링하고 분석할 수 있는 하위 명령어를 제공한다.
2.2 istioctl로 프록시 설정 쿼리하기
istioctl proxy-config
명령어는 Envoy의 xDS API를 통해 워크로드의 상세 설정을 확인할 수 있다. 주요 하위 명령어는 다음과 같다:
- cluster: 클러스터 설정을 가져온다
- endpoint: 엔드포인트 설정을 가져온다
- listener: 리스너 설정을 가져온다
- route: 루트 설정을 가져온다
- secret: 시크릿 설정을 가져온다
1. Enovy 리스너 설정 쿼리 - istioctl proxy-config listner
리스너(listener)는 Envoy가 수신하는 IP:PORT 조합이다. 예를 들어 0.0.0.0:8080은 8080 포트를 수신하고 있다는 뜻이다. Istio에서는 Gateway 리소스에 의해 정의되고, Ingress Gateway에 적용된다
#
istioctl proxy-config listener deploy/istio-ingressgateway -n istio-system
ADDRESS PORT MATCH DESTINATION
0.0.0.0 8080 ALL Route: http.8080 # 8080 포트에 대한 요청은 루트 http.8080에 따라 라우팅하도록 설정된다
0.0.0.0 15021 ALL Inline Route: /healthz/ready*
0.0.0.0 15090 ALL Inline Route: /stats/prometheus*
## 리스터는 8080 포트에 설정돼 있다.
## 그 리스너에서 트래픽은 http.8080 이라는 루트에 따라 라우팅된다.
istio-ingressgateway 서비스는 인그레스 게이트웨이(파드)에 tcp 8080 포트로 전달한다. 즉 8080 포트는 서비스가 전달하는 최종 파드의 포트이다.
2. Enovy 라우터 설정 쿼리 - istioctl proxy-config routes
Istio에서는 들어온 트래픽을 어떤 서비스로 보낼지 결정하는 설정을 Envoy 라우트(Route) 설정이라고 한다.
# http.8080 루트의 트래픽을 어느 클러스터로 라우팅할지 알아내기 위해 설정을 쿼리
istioctl proxy-config routes deploy/istio-ingressgateway -n istio-system --name http.8080
NAME DOMAINS MATCH VIRTUAL SERVICE
http.8080 catalog.istioinaction.io /* catalog-v1-v2.istioinaction
catalog.istioinaction.io로 들어온 모든 요청은 catalog-v1-v2 VirtualService로 라우팅 된다.
라우팅 설정은 VirtualService 리소스를 통해 정의되며, Envoy 라우트로 반영된다
3. Enovy 클러스터 설정 쿼리하기 - istioctl proxy-config cluster
여기서 말하는 클러스터(cluster)는 Kubernetes 클러스터가 아닌, Envoy가 트래픽을 전달할 수 있는 백엔드 서비스 집합이다.
istioctl proxy-config clusters deploy/istio-ingressgateway -n istio-system \
--fqdn catalog.istioinaction.svc.cluster.local --port 80
SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE
catalog.istioinaction.svc.cluster.local 80 - outbound EDS
istioctl proxy-config clusters의 플래그 direction, fqdn, port, subent을 사용하면 특정 클러스터만 출력할 수 있음
잎서, 재현한 에러 상황에서는 DestinationRule을 생성하지 않아, subset을 찾지 못하였다. 따라서 출력 결과에는 subset 정보가 없다.
istioctl proxy-config clusters deploy/istio-ingressgateway -n istio-system \
--fqdn catalog.istioinaction.svc.cluster.local --port 80 --subset version-v1
# 출력 없음 → **클러스터가 존재하지 않음 = 요청 실패**
즉, version-v1 또는 version-v2에 대한 클러스터 정의가 없기 때문에 트래픽 라우팅이 실패하게 된다.
DestinationRule 적용 후 클러스터 재확인하자.
# 문제 해결
cat ch10/catalog-destinationrule-v1-v2.yaml
kubectl apply -f ch10/catalog-destinationrule-v1-v2.yam
istioctl proxy-config clusters deploy/istio-ingressgateway -n istio-system \
--fqdn catalog.istioinaction.svc.cluster.local --port 80
SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE
catalog.istioinaction.svc.cluster.local 80 - outbound EDS catalog.istioinaction
catalog.istioinaction.svc.cluster.local 80 version-v1 outbound EDS catalog.istioinaction
catalog.istioinaction.svc.cluster.local 80 version-v2 outbound EDS catalog.istioinaction
정상적으로 subset을 갖는 클러스터가 생성되었음을 확인할 수 있다.
4. Enovy 클러스터 엔드포인트 쿼리 - istioctl proxy-config endpoints
앞서 확인한 클러스터는 엔드포인트의 집합으로 논리적 그룹이라고 할 수 있다. 하지만 실제로 트래픽이 어느 Pod로 전달되는지까지 확인하려면 Endpoints 정보가 필요하다.
istioctl proxy-config endpoints deploy/istio-ingressgateway -n istio-system \
--cluster "outbound|80|version-v1|catalog.istioinaction.svc.cluster.local"
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.10.0.12:3000 HEALTHY OK outbound|80|version-v1|catalog.istioinaction.svc.cluster.local
kubectl get pod -n istioinaction --field-selector status.podIP=10.10.0.12 -owide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
catalog-6cf4b97d-4bh75 2/2 Running 0 5h32m 10.10.0.12 myk8s-control-plane <none> <none> app=catalog,pod-template-hash=6cf4b97d,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=catalog,service.istio.io/canonical-revision=v1,version=v1
istioctl proxy-config endpoints 명령어는 Envoy가 인식하고 있는 실제 백엔드 Pod의 IP와 포트 목록을 보여준다. 즉, 클러스터가 참조하는 실제 트래픽 전달 대상이 정확히 무엇인지 확인할 수 있다
클러스터 설정이 존재하더라도, 연결 가능한 엔드포인트가 없다면 트래픽은 실제로 전달되지 않는다. 따라서 설정의 마지막 확인 단계로서, endpoints는 실질적인 연결 가능 여부를 점검하는 중요한 지표다.
istioctl proxy-config 명령어는 Envoy 내부 트래픽 흐름을 가장 상세하게 분석할 수 있는 도구다.Listener → Route → Cluster → Endpoint
순으로 설정을 따라가며, 트래픽이 어디에서 막히는지를 정확히 파악할 수 있다.
설정 누락, 리소스 간 불일치, 라우팅 실패 등 복잡한 문제 상황에서도 실제 Envoy에 어떤 설정이 적용됐는지 확인할 수 있는 가장 강력한 방법이다.
2.3 애플리케이션 문제 트러블슈팅하기
Istio는 단순한 서비스 메시 기능을 넘어서, 복잡한 마이크로서비스 환경에서 발생할 수 있는 애플리케이션 수준의 문제를 진단하고 해결할 수 있는 강력한 관측 도구를 제공한다.
서비스 간 통신 지연, 불안정한 엔드포인트, 타임아웃, 라우팅 실패 등은 마이크로서비스 구조에서 자주 발생하는 문제들이다. 이때 단순히 애플리케이션 로그만으로는 원인을 찾기 어려운 경우가 많다. Envoy 프록시가 제공하는 메트릭, 액세스 로그, 라우팅 정보, 응답 플래그 등을 종합적으로 활용하면 문제의 지점을 정밀하게 좁혀갈 수 있다.
일부 워크로드가 느리게 응답하는 상황을 인위적으로 만들고, Istio의 VirtualService, Grafana, Kiali, Envoy 로그 설정 등을 조합해 실제로 어떻게 트러블슈팅할 수 있는지 단계별로 살펴본다
Step 1. 간헐적 응답 지연 상황을 만들어 타임아웃 현상 관찰하기
먼저, 일부 워크로드가 느리게 응답하는 상황을 만들고, Istio가 지정된 타임아웃 이상일 경우 요청을 실패 처리하도록 설정한다.
설정 전, 정상적인 응답을 먼저 확인해 보자.
p99 Latency란?
P99 지연 시간이란, 전체 요청 중 99%가 이 시간 안에 처리됐다는 의미이다.
예를 들어 “P99 = 20ms”라고 하면,
→ 100개의 요청 중 99개는 80밀리 초보다 빨리 끝났다는 의미이다.
간헐적 타임아웃, 리소스 고갈을 파악하는 데 특히 유용하다
catalog v2 파드 중 하나에 지연 설정하여 catalog로 서비스 요청 시, 간혈적으로 통신에 지연이 발생하도록 한다.
# catalog v2 파드 중 첫 번째 파드 이름 변수 지정
CATALOG_POD=$(kubectl get pods -l version=v2 -n istioinaction -o jsonpath={.items..metadata.name} | cut -d ' ' -f1)
echo $CATALOG_POD
catalog-v2-56c97f6db-d74kv
# 해당 파드에 latency (지연) 발생하도록 설정
kubectl -n istioinaction exec -c catalog $CATALOG_POD \
-- curl -s -X POST -H "Content-Type: application/json" \
-d '{"active": true, "type": "latency", "volatile": true}' \
localhost:3000/blowup ;
blowups=[object Object]
트래픽 지속 요청
# 신규 터미널
for in in {1..9999}; do curl http://catalog.istioinaction.io:30000/items -w "\nStatus Code %{http_code}\n"; sleep 1; done
v2가 지연 설정된 만큼 latency 지표가 v1보다 뚜렷하게 상승함을 확인할 수 있음
v2 파드 중 하나가 느리게 응답하도록 설정된 상태에서, Istio VirtualService에 타임아웃을 0.5초로 설정해 보자.
kubectl get vs -n istioinaction
NAME GATEWAYS HOSTS AGE
catalog-v1-v2 ["catalog-gateway"] ["catalog.istioinaction.io"] 6h44m
# 타임아웃(0.5s) 적용
kubectl patch vs catalog-v1-v2 -n istioinaction --type json \
-p '[{"op": "add", "path": "/spec/http/0/timeout", "value": "0.5s"}]'
# 적용확인
kubectl get vs catalog-v1-v2 -n istioinaction -o jsonpath='{.spec.http[?(@.timeout=="0.5s")]}' | jq
...
"timeout": "0.5s"
}
느리게 응답하도록 설정된 파드가 타임아웃 설정으로 인해 실패(5xx)하는 것을 확인할 수 있다.
# 신규 터미널
for in in {1..9999}; do curl http://catalog.istioinaction.io:30000/items -w "\nStatus Code %{http_code}\n"; sleep 1; done
upstream request timeout
Status Code 504
upstream request timeout
Status Code 504
# 로그
kubectl logs -n istio-system -l app=istio-ingressgateway -f
[2025-05-12T11:31:01.942Z] "GET /items HTTP/1.1" 504 UT response_timeout - "-" 0 24 502 - "192.168.107.1" "curl/8.7.1" "3f4e5130-4f1b-906c-837c-f982dd1e259c" "catalog.istioinaction.io:30000" "10.10.0.13:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.7:42972 10.10.0.7:8080 192.168.107.1:57895 -
kubectl logs -n istioinaction -l version=v2 -c istio-proxy -f
[2025-05-12T11:34:52.823Z] "GET /items HTTP/1.1" 0 DC downstream_remote_disconnect - "-" 0 0 500 - "192.168.107.1" "curl/8.7.1" "19528676-f325-9218-84f0-3457e34d3ec7" "catalog.istioinaction.io:30000" "10.10.0.13:3000" inbound|3000|| 127.0.0.6:50303 10.10.0.13:3000 192.168.107.1:0 outbound_.80_.version-v2_.catalog.istioinaction.svc.cluster.local default
Step2: 엔보이 액세스 로그 이해하기 + 엔보이 액세스 로그 형식 바꾸기
기본적으로 이스티오는 프록시가 로그를 TEXT 형식으로 기록하도록 설정하는데, 간결하지만 읽기가 어려워, 키벨류 형식으로 의미를 쉽게 알 수 있는 JSON 형식을 사용하게 설정한다.
# MeshConfig 설정 수정
KUBE_EDITOR="nano" kubectl edit -n istio-system cm istio
...
mesh: |-
accessLogFile: /dev/stdout # 기존 설정되어 있음
accessLogEncoding: JSON # 추가
...
적용 후 로그 확인
kubectl logs -n istio-system -l app=istio-ingressgateway -f | jq
...
{
"upstream_host": "10.10.0.13:3000", # 요청을 받는 업스트림 호스트
"bytes_received": 0,
"upstream_service_time": null,
"response_code_details": "response_timeout",
"upstream_cluster": "outbound|80|version-v2|catalog.istioinaction.svc.cluster.local",
"duration": 501, # 500ms 인 제한 시간 초과
"response_code": 504,
"path": "/items",
"protocol": "HTTP/1.1",
"upstream_transport_failure_reason": null,
"connection_termination_details": null,
"method": "GET",
"requested_server_name": null,
"start_time": "2025-05-09T08:56:38.988Z",
"downstream_remote_address": "172.18.0.1:59052",
"upstream_local_address": "10.10.0.7:57154",
"downstream_local_address": "10.10.0.7:8080",
"bytes_sent": 24,
"authority": "catalog.istioinaction.io:30000",
"x_forwarded_for": "172.18.0.1",
"request_id": "062ad02a-ff36-9dcc-8a7d-68eabb01bbb5",
"route_name": null,
"response_flags": "UT", # 엔보이 응답 플래그, UT(Upstream request Timeout)로 중단됨, '업스트림 요청 제한 시간 초과'
"user_agent": "curl/8.7.1"
}
Step 3: 엔보이 게이트웨이의 로깅 수준 높이기
보다 세부적인 로그가 필요할 경우, Envoy의 로그 레벨을 warning → debug로 올릴 수 있다.
현재 로깅 수준 확인
#
docker exec -it myk8s-control-plane istioctl proxy-config log deploy/istio-ingressgateway -n istio-system
istio-ingressgateway-6bb8fb6549-hcdnc.istio-system:
active loggers:
admin: warning
alternate_protocols_cache: warning
aws: warning
assert: warning
backtrace: warning
cache_filter: warning
client: warning
config: warning
connection: warning # 커넥션 범위에서는 네트워크 계층과 관련된 정보를 기록.
...
http: warning # HTTP 범위에서는 HTTP 헤더, 경로 등 애플리케이션과 관련된 졍보를 기록.
...
router: warning # 라우팅 범위에서는 요청이 어느 클러스터로 라우팅되는지 같은 세부 사항을 기록.
...
주요 로거 설명:
- connection: TCP 계층 관련 로그 (L4)
- http: HTTP 요청/응답 및 헤더 (L7)
- router: 클러스터 라우팅 결정 로그
- pool: 커넥션 풀 동작
connection , http , router , pool 로거의 수준을 debug로 높여보자
docker exec -it myk8s-control-plane istioctl proxy-config log deploy/istio-ingressgateway -n istio-system \
--level http:debug,router:debug,connection:debug,pool:debug
이제 에러 로그를 확인해 보면, 더 상세한 에러 로그를 확인할 수 있다. CONNECTION ID를 활용하면 추적하는 데 있어서 유용하다.
3. 엔보이 텔레메트리로 자신의 애플리케이션 이해하기
마이크로서비스 환경에서는 요청 실패, 성능 저하, 타임아웃 등의 문제가 서비스 간 복잡한 관계 속에서 발생한다.
이런 문제를 정확하게 파악하려면 단순한 애플리케이션 로그만으로는 부족하고, 서비스 메시 내부에서 관측 가능한 텔레메트리 정보가 필요하다.
Istio는 Envoy 프록시를 통해 자동으로 메트릭과 로그를 수집하며, 이를 통해 실패한 요청의 비율, 특정 파드나 버전에서 발생한 문제, 응답 지연이나 연결 종료와 같은 이벤트를 확인할 수 있다.
이번에는 Grafana와 Prometheus를 활용해 실패율을 분석하고, Istio의 디버깅에 유의미한 지표들을 수치로 확인하는 방법을 살펴보자.
3.1 클라이언트/서버 관점에서 실패율 구분하기(Grafana)
Grafana의 Istio Service 대시보드에서는 서비스별 성공률을 클라이언트와 서버 관점에서 각각 확인할 수 있다.
여기서 말하는 클라이언트(source)는 요청을 보낸 측의 Envoy 프록시(다운스트림)이고, 서버(destination)는 요청을 받은 측의 Envoy 프록시(업스트림)를 의미한다.
여기서 말하는 클라이언트(source)는 요청을 발생시킨 서비스 측의 Envoy 프록시(다운스트림)를 의미하고,
서버(destination)는 그 요청을 처리하는 대상 서비스 측의 Envoy 프록시(업스트림)를 의미한다
대시보드에서 catalog.istioinaction.svc.cluster.local 서비스를 선택한 뒤, Reporter를 source와 destination으로 구분해 비교해 보자.
클라이언트의 성공률은 약 70% 수준으로, 30%가량의 요청이 실패하고 있다. 이는 대부분 504 Gateway Timeout 응답이 발생했기 때문이며, 5xx 오류로 집계되어 실패율에 포함된다.
반면 서버(요청을 받은 Envoy 프록시)에서는 성공률이 100%로 표시된다. 서버 입장에서는 5xx 오류가 발생하지 않았기 때문이다.
이러한 차이는 클라이언트가 서버로부터 응답을 받기 전에 연결을 끊는 경우, Envoy가 해당 응답을 코드 0으로 처리하기 때문에 발생한다.
👉 따라서, 정확한 실패율은 클라이언트 관점에서 측정한 값을 기준으로 판단해야 한다.
3.2 프로메테우스를 사용해 영향받는 파드 쿼리 하기(Prometheus)
Grafana 대시보드는 지표를 직관적으로 시각화하는 데는 유용하지만, 세부적인 문제를 파악하기에는 한계가 있다.
예를 들어 “어느 파드에서 오류가 더 자주 발생했는가”, “어떤 응답 코드가 반복되는가”와 같은 질문에는 직접 Prometheus 쿼리를 실행하는 방식이 더 효과적이다.
DC(Downstream Connection termination)란?
서버(Envoy 프록시)가 응답을 보내려는 순간 클라이언트가 먼저 연결을 끊은 상황을 말한다.
이 경우 Envoy 로그에서는 response_flags="DC"로 표시되며, 응답 코드가 0으로 기록된다.
서버 입장에서는 5xx 오류로 처리되지 않지만, 실제로는 실패한 요청으로 간주해야 한다.
이번 예시에서는 catalog 서비스에 대한 요청 중, 서버가 응답을 보내기 전에 클라이언트가 먼저 연결을 끊은 상황, 즉 DC(Downstream Connection termination) 플래그가 설정된 요청을 기준으로 실패율을 분석해 본다.
istio_requests_total 은 Istio에서 가장 기본이 되는 HTTP 요청 메트릭이다.
Envoy 프록시가 수신한 모든 요청에 대해 수집된 카운터 지표로, 요청 횟수를 기반으로 다양한 분석이 가능하다.
sort_desc( # 가장 높은 값부터 내림차순 정렬
sum( # irate 값들을 집계
irate( # 요청 수 초당 증가율
istio_requests_total {
reporter="destination", # 서버(destination) 측에서 보고한 메트릭만 필터링
destination_service=~"catalog.istioinaction.svc.cluster.local", # catalog 가 서버(destination)측인 메트릭만 필터링
response_flags="DC" # DC (다운스트림 커넥션 종료)로 끝난 메트릭만 필터링
}[5m]
)
)by(response_code, pod, version) # 응답 코드(response_code), 대상 pod, 버전(version) 별로 분리 => sum.. 합산
)
쿼리 1: DC 조건에 해당하는 요청 필터링
# 쿼리1
istio_requests_total
istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local"}
istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local",response_flags="DC"}
- reporter="destination": 서버 측 Envoy가 수집한 요청만 필터링
- response_flags="DC": 클라이언트가 먼저 끊은 요청만 추출
쿼리 2: 단위 시간당 DC 발생률 확인
최근 5분 동안 DC 플래그가 붙은 요청이 얼마나 발생했는지 확인
# 쿼리2
istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local",response_flags="DC"}[5m]
irate(istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local",response_flags="DC"}[5m])
sum(irate(istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local",response_flags="DC"}[5m]))
- irate(...): 최근 5분간 초당 요청 증가율 → 요청이 얼마나 자주 실패하고 있는지 수치로 확인
- sum(...): 해당 증가율을 전체 기준으로 집계하여 서비스 전체의 DC 요청량 추이 확인
쿼리 3 파드/버전 단위로 DC 비율 확인
- 어떤 파드와 버전에서 DC 요청이 집중되는지 확인
- by(pod, version): 파드와 버전 단위로 그룹화
- sort_desc(...): 가장 문제가 심각한 파드를 상단에 표시
# 쿼리3
sum(irate(istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local",response_flags="DC"}[5m])) by(response_code, pod, version)
sort_desc(sum(irate(istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local",response_flags="DC"}[5m]))by(response_code, pod, version))
Prometheus 쿼리로 실패 원인 유추해 본다.
- 특정 파드에서 response_code=0, response_flag=DC가 반복된다면?
: 해당 파드에 지연, GC 문제, 애플리케이션 오류가 있을 수 있다. - 특정 버전에서만 DC가 많이 발생한다면?
: 최근 배포된 버전의 결함일 가능성이 높음 - 클러스터 전체에서 DC가 높은 비율로 발생한다면?
: 네트워크 지연, 부하 분산 문제, Istio 타임아웃 설정 등을 의심해야 함
필요하다면 커스텀 메트릭을 추가하거나 Prometheus 클라이언트 라이브러리를 통해 애플리케이션 수준의 지표를 정밀하게 수집하는 것도 좋은 방법이다.
4. 이스티오 구성 요소 트러블 슈팅하기
istio는 프록시와 에이전트, 그리고 컨트롤 플레인 구성 요소들이 다양한 포트를 통해 내부 상태를 노출한다. 각 포트에서 어떤 정보가 노출되는지 파악하면, 복잡한 문제를 훨씬 빠르게 진단할 수 있다.
4.1 데이터 플레인에서 확인할 수 있는 Istio 에이전트 정보
Istio의 사이드카 프록시(Envoy)와 함께 주입되는 파일럿 에이전트(Pilot Agent)는 여러 기능을 담당하며, 이를 위해 다양한 포트를 사용한다.
서비스용 포트(트래픽 처리 및 헬스체크 관련)
포트 | 프로세스 | 설명 |
---|---|---|
15001 | Envoy | 애플리케이션에서 외부로 나가는 트래픽을 Iptables로 리다이렉트 |
15006 | Envoy | 외부에서 애플리케이션으로 들어오는 트래픽을 Iptables로 리다이렉트 |
15020 | Pilot Agent | 프록시 헬스체크, 자체 메트릭 노출, 디버깅 정보 제공 (메모리/CPU 등) |
15021 | Envoy | 사이드카 주입된 파드가 readiness probe를 수행하는 포트 |
15053 | Pilot Agent | Istiod가 구성한 로컬 DNS 프록시 (Kubernetes DNS의 edge case 대응) |
디버깅 및 내부 상태 확인용 포트
포트 | 프로세스 | 설명 |
---|---|---|
15000 | Envoy | Envoy 관리 인터페이스 (config_dump, stats 등) |
15090 | Envoy | Envoy 메트릭 노출 포트 (HTTP/xDS/서킷 브레이커 통계 등) |
15004 | Pilot Agent | 에이전트를 통해 istiod 디버그 엔드포인트를 노출 |
15020 | Pilot Agent | 메트릭 + 디버깅용 엔드포인트 (중복 기능 포함) |
이 포트들의 역할을 명확히 이해하면, 애플리케이션 문제인지, 프록시 문제인지, 아니면 컨트롤 플레인과의 연결 문제인지를 빠르게 구분해 낼 수 있다.
Istio 에이전트의 헬스체크 엔드포인트 확인
Istio는 Envoy에서 직접 헬스체크를 처리하지 않는다. 대신 15021 포트로 들어오는 Readiness Probe 요청을 15020에서 pilot-agent가 처리한다.
pilot-agent는 /app-health/ 경로를 통해 애플리케이션으로 헬스체크를 프록시한다. 이 구조는 Istio가 mutating webhook을 통해 자동으로 설정하며, 애플리케이션 내부 상태에 따라 정상 여부를 판단한다.
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: liveness-http
namespace: istioinaction
spec:
selector:
matchLabels:
app: liveness-http
version: v1
template:
metadata:
labels:
app: liveness-http
version: v1
spec:
containers:
- name: liveness-http
image: docker.io/istio/health:example
ports:
- containerPort: 8001
livenessProbe:
httpGet:
path: /foo
port: 8001
initialDelaySeconds: 5
periodSeconds: 5
EOF
Pilot 디버그 엔드포인트 쿼리하기
pilot-agent는 15020 포트를 통해 디버깅하기 위한 다양한 엔드포인트를 제공한다.
/debug/pprof/* 는 성능 문제, 메모리 누수 등을 디버깅하는 데 도움이 되는 Go 언어 프로파일링 엔드포인트이다.
4.2 컨트롤 플레인(Pilot)에서 확인할 수 있는 정보(ControlZ)
컨트롤 플레인 구성 요소인 istiod는 다양한 포트를 통해 구성 상태를 노출한다.
kubectl -n istio-system exec -it deploy/istiod -- netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:9876 0.0.0.0:* LISTEN
tcp6 0 0 :::8080 :::* LISTEN
tcp6 0 0 :::15017 :::* LISTEN
tcp6 0 0 :::15010 :::* LISTEN
tcp6 0 0 :::15012 :::* LISTEN
tcp6 0 0 :::15014 :::* LISTEN
다양한 디버그 엔드포인트 제공
istiod는 아래와 같은 디버그 엔드포인트를 통해 서비스 메시 상태, 정책 정보, 프록시 동기화 상태 등을 상세히 제공한다.
# [주요 디버그 엔드포인트]
# 현재 알려진 서비스 메시 상태
curl http://localhost:8080/debug/adsz | jq
# 특정 프록시에 대한 엔드포인트 정보
curl http://localhost:8080/debug/edsz?proxyID=webapp.istioinaction
# 인가 정책 확인
curl http://localhost:8080/debug/authorizationz | jq
# 프록시 동기화 상태
curl http://localhost:8080/debug/syncz
CpntrolZ
ControlZ는 Istio Pilot 프로세스의 내부 상태를 실시간으로 확인할 수 있는 관리자용 웹 인터페이스다.
이 인터페이스를 통해 로깅 설정, 메모리 사용량, 환경 변수, 프로세스 정보, 시작 인수, 버전 정보, 메트릭, 시그널 전송 등 다양한 정보를 확인하거나 조정할 수 있다.
주로 디버깅, 운영 중 상태 확인, 설정 확인 등에 유용하며, Pilot 인스턴스의 상태를 빠르게 진단하는 데 효과적이다.
# Istio ControlZ 포트포워딩
kubectl -n istio-system port-forward deploy/istiod 9876
open http://localhost:9876
'Kubernetes > Istio' 카테고리의 다른 글
Istio 시리즈 # 10 - Istio로 구현하는 다중 클러스터 서비스 메시 (Multi-Cluster) (0) | 2025.05.25 |
---|---|
Istio 시리즈 # 9 – Istio 성능 튜닝 가이드(Istio Tuning) (0) | 2025.05.18 |
Istio 시리즈 # 7 – Istio Security로 살펴보는 마이크로서비스 통신 보안 (1) | 2025.05.11 |
Istio 시리즈 # 6 – 메트릭과 트레이싱으로 살펴보는 Istio Observability (1) | 2025.05.04 |
Istio 시리즈 # 5 – 네트워크 복원력 강화하기(Resilience) (0) | 2025.04.27 |