CloudNet@에서 진행하는 Istio Study 6주차 10장 내용입니다.
📕 This chapter cover
- Troubleshooting a misconfigured workload
- Detecting and preventing misconfigurations using istioctl and Kiali
- Using istioctl to investigate the service proxy configuration
- Making sense of Envoy logs
- Using telemetry to gain insights into apps
Istio는 VirtualService, DestinationRule 같은 CRD로 누출하며, Envoy 설정으로 변환되어 Data Plane에 적용됩니다.
새 리소스를 적용한 후 데이터 플레인의 동작이 예상과 다를 때 가장 일반적인 원인은 우리가 설정을 잘못한 것이다.

DestinationRule 리소스가 없으면 인그레스 게이트웨이는 부분집합 version-v1과 version-v2에 대한 클러스터 정의가 없으므로, 모든 요청은 실패할 것이다.
# 샘플 애플리케이션 배포
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
# Gateway
cat ch10/catalog-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: catalog-gateway
namespace: istioinaction
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- "catalog.istioinaction.io"
port:
number: 80
name: http
protocol: HTTP
# VirtualService
cat ch10/catalog-virtualservice-subsets-v1-v2.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog-v1-v2
namespace: istioinaction
spec:
hosts:
- "catalog.istioinaction.io"
gateways:
- "catalog-gateway"
http:
- route:
- destination:
host: catalog.istioinaction.svc.cluster.local
subset: version-v1
port:
number: 80
weight: 20
- destination:
host: catalog.istioinaction.svc.cluster.local
subset: version-v2
port:
number: 80
weight: 80
# 확인
kubectl get deploy,svc -n istioinaction
kubectl get gw,vs -n istioinaction
kubectl logs -n istio-system -l app=istio-ingressgateway -f


워크로드와 이벤트 개수가 늘어나는 대규모 클러스터에서는 데이터 플레인을 동기화하는 데 필요한 시간도 비례해 늘어난다.
istioctl proxy-status 로 데이터 플레인이 최신 설정과 동기화했는지 확인

SYNCED : istiod가 보낸 마지막 설정을 엔보이가 확인했다.
NOT SENT : istiod가 아무것도 엔보이로 보내지 않았다. 보통은 istiod가 보낼 것이 없기 때문이다.
STALE : istiod가 엔보이에 업데이트를 보냈지만 확인받지 못했다. 이는 다음 중 하나를 나타낸다.
설정을 받지 못한 stale 상태의 워크로드가 없다.
컨트롤 플레인에 문제가 있을 가능성은 낮으므로 데이터 플레인 구성 요소를 조사해야 한다.
데이터 플레인 구성 요소에서 가장 일반적인 문제는 잘못된 워크로드 설정입니다. 키알리로 빠르게 검증 가능
내장 편집기에서 경고 메시지 확인

다음은 KIA1107 경고의 해결책 부분이다.
키알리 검증은 도움이 되므로, 워크로드가 예상대로 동작하지 않을 때 취하는 첫 조치 중 하나이다.
잘못 설정된 워크로드를 자동으로 트러블슈팅하는 데 가장 유용한 istioctl 명령어 두 가지는 istioctl analyze 와 istioctl describe 이다.

# 이전 명령어 종료 코드 확인
echo $? # (참고) 0 성공
79
출력은 부분집합을 찾지 못했음을 보여준다. 오류 메시지 외에 istio 오류 코드 IST0101 도 제공


#
kubectl port-forward deploy/catalog -n istioinaction 15000:15000
open http://localhost:15000
# 현재 적재한 엔보이 설정 출력 : 데이터양이 많다!
curl -s localhost:15000/config_dump | wc -l
13952

출력은 너무 커서 기본적으로 사람이 읽을 수 없다.

이 API는 프록시에 다음과 같은 영향을 미친다.
- 엔보이 리스너 listeners 는 네트워크 설정(다운스트림 트래픽을 프록시로 허용하는 IP 주소 및 포트 등)을 정의한다.
- 허용된 커넥션에 HTTP 필터 filter 체인이 만들어진다. 체인에서 가장 중요한 필터는 라우터 필터로, 고급 라우팅 작업을 수행한다.
- 엔보이 루트 routes 는 가상 호스트를 클러스터에 일치시키는 규칙 집합이다. 루트는 순서대로 처리된다.
- 일치하는 첫 번째 항목이 트래픽을 워크로드 클러스터로 라우팅하는 데 사용된다.
- 루트는 정적으로 설정할 수 도 있지만, 이스티오에서는 RDS를 사용해 동적으로 설정한다.
- 엔보이 클러스터 clusters 에서, 각 클러스터에는 유사한 워크로드에 대한 엔드포인트 그룹이 있다.
- 부분집합 Subsets 은 클러스터 내에서 워크로드를 더 분할하는 데 사용하며 덕분에 정밀한 트래픽 관리가 가능해진다.
- 엔보이 엔드포인트는 요청을 처리하는 워크로드의 IP 주소를 나타낸다.

루트 http.8080이 80 포트가 아니라 8080포트에서 리스닝하도록 설정
포트 8080이 올라오는 포트인지 확인
kubectl get svc -n istio-system istio-ingressgateway -o yaml | grep "ports:" -A10
ports:
- name: status-port
nodePort: 30840
port: 15021
protocol: TCP
targetPort: 15021
- name: http2
nodePort: 30000
port: 80
protocol: TCP
targetPort: 8080
# http.8080 루트의 트래픽을 어느 클러스터로 라우팅할지 알아내기 위해 설정을 쿼리
docker exec -it myk8s-control-plane 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 의 트래픽 중 URL이 경로 접두사 /*과 일치하는 것이 istioinaction 네임스페이스의 catalog 서비스에 있는 catalog VirtualService 로 라우팅됨을 보여준다.
# 세부 정보 확인
docker exec -it myk8s-control-plane istioctl proxy-config routes deploy/istio-ingressgateway -n istio-system --name http.8080 -o json
...
"routes": [
{
"match": {
"prefix": "/" # 일치해야 하는 라우팅 규칙
},
"route": {
"weightedClusters": {
"clusters": [ # 규칙이 일치할 때 트래픽을 라우팅하는 클러스터
{
"name": "outbound|80|version-v1|catalog.istioinaction.svc.cluster.local",
"weight": 20
},
{
"name": "outbound|80|version-v2|catalog.istioinaction.svc.cluster.local",
"weight": 80
}
],
"totalWeight": 100
},
...
istioctl proxy-config clusters 의 플래그 direction, fqdn, port, subent 을 사용하면 특정 클러스터만 출력할 수 있다.
docker exec -it myk8s-control-plane 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
docker exec -it myk8s-control-plane istioctl proxy-config clusters deploy/istio-ingressgateway -n istio-system \
--fqdn catalog.istioinaction.svc.cluster.local --port 80 --subset version-v1
SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE
subset version-v1 이나 version-v2 용 클러스터는 없었다! ⇒ 이 부분 집합에 대한 클러스터가 없으면 요청은 실패한다.
# istioctl analyze 명령어를 사용해서, 설정할 yaml 파일이 식별한 서비스 메시 오류를 고칠 수 있는지 확인
docker exec -it myk8s-control-plane istioctl analyze /istiobook/ch10/catalog-destinationrule-v1-v2.yaml -n istioinaction
✔ No validation issues found when analyzing /istiobook/ch10/catalog-destinationrule-v1-v2.yaml.
DestinationRule을 적용하면 클러스터 설정의 문제가 고쳐진다
문제 해결
# 문제 해결
cat ch10/catalog-destinationrule-v1-v2.yaml
kubectl apply -f ch10/catalog-destinationrule-v1-v2.yaml
# 확인
docker exec -it myk8s-control-plane 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
CATALOG_POD1=$(kubectl get pod -n istioinaction -l app=catalog -o jsonpath='{.items[0].metadata.name}')
docker exec -it myk8s-control-plane istioctl x des pod -n istioinaction $CATALOG_POD1
docker exec -it myk8s-control-plane istioctl analyze -n istioinaction

엔보이 프록시에는 클러스터 엔드포인트를 발견하기 위한 여러 가지 방법이 있다.
docker exec -it myk8s-control-plane istioctl proxy-config clusters deploy/istio-ingressgateway -n istio-system \
--fqdn catalog.istioinaction.svc.cluster.local --port 80 --subset version-v1 -o json
...
"name": "outbound|80|version-v1|catalog.istioinaction.svc.cluster.local",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {},
"initialFetchTimeout": "0s",
"resourceApiVersion": "V3"
},
"serviceName": "outbound|80|version-v1|catalog.istioinaction.svc.cluster.local"
},
...
edsClusterConfig 가 엔드포인트를 쿼리하는 데 ADS Aggregated Discovery Service 를 사용하도록 설정됐음을 보여준다.
인그레스 게이트웨이에서 클러스터의 엔드포인트를 istioctl proxy-config endpoints 명령어로 수동으로 쿼리하는데 이 정보를 사용할 수 있다.

엔보이 액세스 로그와 메트릭을 사용해 이 문제들 중 일부를 트러블슈팅해본다.

catalog v1 HTTP Request Response Time(ms)에 p99 확인

catalog v2 HTTP Request Response Time(ms)에 p99 확인


# 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]
v2 P90, P99 레이턴스 확인

Istio 에 요청 처리 제한 시간 0.5초가 되도록 VirtualService 설정

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"
}
# 신규 터미널
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-09T08:45:41.636Z] "GET /items HTTP/1.1" 504 UT response_timeout - "-" 0 24 501 - "172.18.0.1" "curl/8.7.1" "cb846eff-07ac-902e-9890-7af478c84166" "catalog.istioinaction.io:30000" "10.10.0.13:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.7:58078 10.10.0.7:8080 172.18.0.1:61108 - -
[2025-05-09T08:45:43.175Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 375 374 "172.18.0.1" "curl/8.7.1" "3f2de0c1-5af2-9a33-a6ac-bca08c1ee271" "catalog.istioinaction.io:30000" "10.10.0.13:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.7:58084 10.10.0.7:8080 172.18.0.1:61118 - -
...
kubectl logs -n istio-system -l app=istio-ingressgateway -f | grep 504
[2025-05-16T06:47:37.938Z] "GET /items HTTP/1.1" 504 UT response_timeout - "-" 0 24 509 - "172.18.0.1" "curl/8.5.0" "996863ed-d0c4-93e5-94c1-7b3951f1f29f" "catalog.istioinaction.io:30000" "10.10.0.3:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.13:39964 10.10.0.13:8080 172.18.0.1:39652 - -
[2025-05-16T06:47:43.816Z] "GET /items HTTP/1.1" 504 UT response_timeout - "-" 0 24 502 - "172.18.0.1" "curl/8.5.0" "bbd3aac8-d8a8-9b59-8187-66adb1f88a42" "catalog.istioinaction.io:30000" "10.10.0.3:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.13:38130 10.10.0.13:8080 172.18.0.1:47108 - -
#
kubectl logs -n istioinaction -l version=v2 -c istio-proxy -f
[2025-05-09T08:42:38.152Z] "GET /items HTTP/1.1" 0 DC downstream_remote_disconnect - "-" 0 0 500 - "172.18.0.1" "curl/8.7.1" "69fef43c-2fea-9e51-b33d-a0375b382d86" "catalog.istioinaction.io:30000" "10.10.0.13:3000" inbound|3000|| 127.0.0.6:36535 10.10.0.13:3000 172.18.0.1:0 outbound_.80_.version-v2_.catalog.istioinaction.svc.cluster.local default
...
catalog v2


Grafana: 500 증가

kubectl logs -n istio-system -l app=istio-ingressgateway -f | grep 504
[2025-05-16T07:17:11.343Z] "GET /items HTTP/1.1" 504 UT response_timeout - "-" 0 24 502 - "172.18.0.1" "curl/8.5.0" "a4c9993f-c6a8-930c-87a1-8ef404d065c1" "catalog.istioinaction.io:30000" "10.10.0.3:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.13:44180 10.10.0.13:8080 172.18.0.1:51328 - -
[2025-05-16T07:17:14.165Z] "GET /items HTTP/1.1" 504 UT response_timeout - "-" 0 24 500 - "172.18.0.1" "curl/8.5.0" "afe6edf7-0ce2-9179-abed-c921b57ce837" "catalog.istioinaction.io:30000" "10.10.0.3:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.13:35200 10.10.0.13:8080 172.18.0.1:51354 - -
[2025-05-16T07:17:17.739Z] "GET /items HTTP/1.1" 504 UT response_timeout - "-" 0 24 507 - "172.18.0.1" "curl/8.5.0" "96a24149-8ddc-95ba-889f-e17b91d07a67" "catalog.istioinaction.io:30000" "10.10.0.3:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.13:54988 10.10.0.13:8080 172.18.0.1:51388 - -
[2025-05-16T07:17:32.277Z] "GET /items HTTP/1.1" 504 UT response_timeout - "-" 0 24 503 - "172.18.0.1" "curl/8.5.0" "0940b9a1-eed7-9db5-ade7-b310b17b1d36" "catalog.istioinaction.io:30000" "10.10.0.3:3000" outbound|80|version-v2|catalog.istioinaction.svc.cluster.local 10.10.0.13:60022 10.10.0.13:8080 172.18.0.1:45930 - -
# 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"
}
...
CATALOG_POD=$(kubectl get pods -l version=v2 -n istioinaction -o jsonpath={.items..metadata.name} | cut -d ' ' -f1)
kubectl get pod -n istioinaction $CATALOG_POD -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
catalog-v2-56c97f6db-h9kjw 2/2 Running 4 (155m ago) 22h 10.10.0.3 myk8s-control-plane <none> <none>
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 : Logs related to layer 4 (transport); TCP connection details
http : Logs related to layer 7 (application); HTTP details
router: Logs related to the routing of HTTP requests
pool : Logs related to how a connection pool acquires or drops a connection’s upstream host
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
# 로그 확인
kubectl logs -n istio-system -l app=istio-ingressgateway -f
k logs -n istio-system -l app=istio-ingressgateway -f > istio-igw-log.txt # 편집기로 열어서 보기
...
# 504 검색
2025-05-09T09:17:17.762027Z debug envoy http external/envoy/source/common/http/filter_manager.cc:967 [C18119][S12425904214070917868] Sending local reply with details response_timeout thread=38
2025-05-09T09:17:17.762072Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:1687 [C18119][S12425904214070917868] encoding headers via codec (end_stream=false):
':status', '504'
'content-length', '24'
'content-type', 'text/plain'
'date', 'Fri, 09 May 2025 09:17:17 GMT'
'server', 'istio-envoy'
thread=38
# 커넥션 ID(C18119)로 다시 검색
## [C18119] new stream # 시작
2025-05-09T09:17:17.262341Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:329 [C18119] new stream thread=38
2025-05-09T09:17:17.262425Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:1049 [C18119][S12425904214070917868] request headers complete (end_stream=true):
':authority', 'catalog.istioinaction.io:30000'
':path', '/items'
':method', 'GET'
'user-agent', 'curl/8.7.1'
'accept', '*/*'
thread=38
## /items 요청이 cluster로 매칭됨
2025-05-09T09:17:17.262445Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:1032 [C18119][S12425904214070917868] request end stream thread=38
2025-05-09T09:17:17.262468Z debug envoy connection external/envoy/source/common/network/connection_impl.h:92 [C18119] current connecting state: false thread=38
025-05-09T09:17:17.262603Z debug envoy router external/envoy/source/common/router/router.cc:470 [C18119][S12425904214070917868] cluster 'outbound|80|version-v2|catalog.istioinaction.svc.cluster.local' match for URL '/items' thread=38
2025-05-09T09:17:17.262683Z debug envoy router external/envoy/source/common/router/router.cc:678 [C18119][S12425904214070917868] router decoding headers:
':authority', 'catalog.istioinaction.io:30000'
':path', '/items'
':method', 'GET'
':scheme', 'http'
'user-agent', 'curl/8.7.1'
'accept', '*/*'
'x-forwarded-for', '172.18.0.1'
'x-forwarded-proto', 'http'
'x-envoy-internal', 'true'
'x-request-id', 'a6bc39e7-9215-950f-96ea-4cb5f6b12deb'
'x-envoy-decorator-operation', 'catalog-v1-v2:80/*'
'x-envoy-peer-metadata', 'ChQKDkFQUF9DT05UQUlORVJTEgIaAAoaCgpDTFVTVEVSX0lEEgwaCkt1YmVybmV0ZXMKGwoMSU5TVEFOQ0VfSVBTEgsaCTEwLjEwLjAuNwoZCg1JU1RJT19WRVJTSU9OEggaBjEuMTcuOAqcAwoGTEFCRUxTEpEDKo4DCh0KA2FwcBIWGhRpc3Rpby1pbmdyZXNzZ2F0ZXdheQoTCgVjaGFydBIKGghnYXRld2F5cwoUCghoZXJpdGFnZRIIGgZUaWxsZXIKNgopaW5zdGFsbC5vcGVyYXRvci5pc3Rpby5pby9vd25pbmctcmVzb3VyY2USCRoHdW5rbm93bgoZCgVpc3RpbxIQGg5pbmdyZXNzZ2F0ZXdheQoZCgxpc3Rpby5pby9yZXYSCRoHZGVmYXVsdAowChtvcGVyYXRvci5pc3Rpby5pby9jb21wb25lbnQSERoPSW5ncmVzc0dhdGV3YXlzChIKB3JlbGVhc2USBxoFaXN0aW8KOQofc2VydmljZS5pc3Rpby5pby9jYW5vbmljYWwtbmFtZRIWGhRpc3Rpby1pbmdyZXNzZ2F0ZXdheQovCiNzZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1yZXZpc2lvbhIIGgZsYXRlc3QKIgoXc2lkZWNhci5pc3Rpby5pby9pbmplY3QSBxoFZmFsc2UKGgoHTUVTSF9JRBIPGg1jbHVzdGVyLmxvY2FsCi8KBE5BTUUSJxolaXN0aW8taW5ncmVzc2dhdGV3YXktNmJiOGZiNjU0OS1oY2RuYwobCglOQU1FU1BBQ0USDhoMaXN0aW8tc3lzdGVtCl0KBU9XTkVSElQaUmt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9pc3Rpby1zeXN0ZW0vZGVwbG95bWVudHMvaXN0aW8taW5ncmVzc2dhdGV3YXkKFwoRUExBVEZPUk1fTUVUQURBVEESAioACicKDVdPUktMT0FEX05BTUUSFhoUaXN0aW8taW5ncmVzc2dhdGV3YXk='
'x-envoy-peer-metadata-id', 'router~10.10.0.7~istio-ingressgateway-6bb8fb6549-hcdnc.istio-system~istio-system.svc.cluster.local'
'x-envoy-expected-rq-timeout-ms', '500'
'x-envoy-attempt-count', '1'
thread=38
## upstream timeout 으로 client 에서 끊음 (disconnect)
2025-05-09T09:17:17.262701Z debug envoy pool external/envoy/source/common/conn_pool/conn_pool_base.cc:265 [C17947] using existing fully connected connection thread=38
2025-05-09T09:17:17.262710Z debug envoy pool external/envoy/source/common/conn_pool/conn_pool_base.cc:182 [C17947] creating stream thread=38
2025-05-09T09:17:17.262736Z debug envoy router external/envoy/source/common/router/upstream_request.cc:581 [C18119][S12425904214070917868] pool ready thread=38
2025-05-09T09:17:17.761697Z debug envoy router external/envoy/source/common/router/router.cc:947 [C18119][S12425904214070917868] upstream timeout thread=38 # 업스트림 서버가 설정된 타임아웃 내에 응답하지 않아 요청이 실패
2025-05-09T09:17:17.761762Z debug envoy router external/envoy/source/common/router/upstream_request.cc:500 [C18119][S12425904214070917868] resetting pool request thread=38
2025-05-09T09:17:17.761776Z debug envoy connection external/envoy/source/common/network/connection_impl.cc:139 [C17947] closing data_to_write=0 type=1 thread=38
2025-05-09T09:17:17.761779Z debug envoy connection external/envoy/source/common/network/connection_impl.cc:250 [C17947] closing socket: 1 thread=38
2025-05-09T09:17:17.761920Z debug envoy connection external/envoy/source/extensions/transport_sockets/tls/ssl_socket.cc:320 [C17947] SSL shutdown: rc=0 thread=38
2025-05-09T09:17:17.761982Z debug envoy pool external/envoy/source/common/conn_pool/conn_pool_base.cc:484 [C17947] client disconnected, failure reason: thread=38
2025-05-09T09:17:17.761997Z debug envoy pool external/envoy/source/common/conn_pool/conn_pool_base.cc:454 invoking idle callbacks - is_draining_for_deletion_=false thread=38
## 504 응답
2025-05-09T09:17:17.762027Z debug envoy http external/envoy/source/common/http/filter_manager.cc:967 [C18119][S12425904214070917868] Sending local reply with details response_timeout thread=38
2025-05-09T09:17:17.762072Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:1687 [C18119][S12425904214070917868] encoding headers via codec (end_stream=false):
':status', '504'
'content-length', '24'
'content-type', 'text/plain'
'date', 'Fri, 09 May 2025 09:17:17 GMT'
'server', 'istio-envoy'
thread=38
2025-05-09T09:17:17.762253Z debug envoy pool external/envoy/source/common/conn_pool/conn_pool_base.cc:215 [C17947] destroying stream: 0 remaining thread=38
2025-05-09T09:17:17.763718Z debug envoy connection external/envoy/source/common/network/connection_impl.cc:656 [C18119] remote close thread=38
2025-05-09T09:17:17.763731Z debug envoy connection external/envoy/source/common/network/connection_impl.cc:250 [C18119] closing socket: 0 thread=38
- 응답이 느린 업스트림의 IP 주소가 액세스 로그에서 가져온 IP 주소와 일치한다는 점이다. 이는 오동작하는 인스턴스가 딱 하나
- 로그 [C17947] client disconnected 에 표시된 대로 클라이언트(프록시)는 업스트림 커넥션을 종료했다.
이는 업스트림 인스턴스가 제한 시간 설정을 초과해 클라이언트(프록시)가 요청을 종료한다는 우리의 예상과 일치한다.
특정 파드에서 tcpdump 후 wireshark 로 분석해보기
kubectl sniff -n istioinaction $CATALOG_POD -i lo
sudo kubectl sniff -n istioinaction $CATALOG_POD -i lo
ERRO[0000] failed to start remote sniffing, stopping wireshark error="executing sniffer failed, exit code: '1'"

# slow 파드 정보 확인
CATALOG_POD=$(kubectl get pods -l version=v2 -n istioinaction -o jsonpath={.items..metadata.name} | cut -d ' ' -f1)
kubectl get pod -n istioinaction $CATALOG_POD -owide
# istio-proxy 에서 기본 정보 확인
kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- sudo whoami
kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- tcpdump -h
kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- ip -c addr
kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- ip add show dev eth0
kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- ip add show dev lo
# istio-proxy 에 eth0 에서 패킷 덤프
kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- sudo tcpdump -i eth0 tcp port 3000 -nnq
kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- sudo tcpdump -i eth0 tcp port 3000 -nn
kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- sudo tcpdump -i eth0 tcp port 3000

Client Hello (SNI 확인) : EDS 의 클러스터 이름으로 접속
outbound.80.version-v2_.catalog.istioinaction.svc.cluster.local



istio-proxy 가 HTTPS를 복호화해서 평문으로 애플리케이션으로 요청 : x-envoy, x-b3 등 헤더 추가 확인

Statistics → Flow Graph 확인 : 정상적으로 GET 요청과 200 응답 확인



No. 38번에서 요청 후 0.5초 이상 응답이 없으니 (42번)44번에 istio-ingressgateway istio-proxy 가 TCP RST 로 연결 종료
⇒ 즉, 현재 구성 상 istio-ingressgw → catalog 이므로, istio-ingressgw 가 TCP Timeout 후 종료 처리함
이후 45번은 catalog v2 istio-proxy 가 FIN/ACK를 applcation 에게 전달 이후 연결 종료

클라이언트와 서버가 설정한 응답 플래그와 응답 코드의 차이점
그라파나 대시보드의 정보가 부족하면 프로메테우스에 직접 쿼리할 수 있다.
기준을 충족하는 메트릭을 쿼리
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.. 합산
)

-> 오직 워크로드 하나만 실패를 보고 하고 있음을 보여준다.
sort_desc(sum(irate(istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local"}[5m]))by(response_code, pod, version))

- istioctl 명령어를 사용해 서비스 메시와 서비스 프록시에 대한 통찰력을 얻는다.
- proxy-status 는 데이터 플레인 동기화 상태의 개요를 보여준다.
- analyze는 서비스 메시 설정을 분석한다.
- describe는 요약을 가져오고 서비스 프록시 설정을 검증한다.
- proxy-config는 서비스 프록시 설정을 쿼리하고 수정한다.
- istioctl analyze 명령을 사용해 클러스터에 적용하기 전에 설정을 검증할 수 있다.
- 키알리와 그 검증 기능을 사용해 일반적인 설정 실수를 잡아낼 수 있다.
- 장애 상황을 살펴보려면 프로메테우스와 수집한 메트릭을 사용하자.
- ksniff(tcpdump)를 사용해 영향을 받는 파드의 네트워크 트래픽을 캡처할 수 있다.
- istioctl proxy-config log 명령어를 사용해 엔보이 프록시의 로깅 수준을 높일 수 있다.

https://www.anyflow.net/sw-engineer/istio-internals-by-port
실습 환경 초기화
# 기존 리소스 삭제
kubectl delete -n istioinaction deploy,svc,gw,vs,dr,envoyfilter --all
# 샘플 애플리케이션 배포
kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction
# 확인
kubectl get gw,vs -n istioinaction
curl -s http://webapp.istioinaction.io:30000/api/catalog | jq
# 신규 터미널 : 반복 접속
while true; do curl -s http://webapp.istioinaction.io:30000/api/catalog ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
서비스 노출 포트

에이전트 디버깅 및 내부 상태 조사에 유용한 포트
/healthz/ready : 엔보이 및 DNS 프록시에서 일련의 검사를 수행한다./stats/prometheus : 엔보이 프록시와 애플리케이션의 메트릭을 자체 메트릭과 병합하고 긁어갈 수 있도록 노출한다./quitquitquit : 파일럿 에이전트의 프로세스를 종료시킨다./app-health/ : 이스티오 프록시 사이드카의 환경 변수 ISTIO_KUBE_APP_PROBERS로 정의한 애플리케이션 프로브를 실행한다.#
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
#
kubectl get pod -n istioinaction -l app=liveness-http
kubectl describe pod -n istioinaction -l app=liveness-http
...
Containers:
liveness-http:
Container ID: containerd://edaf01bff5d553e03290b3d44f60bb26958319e615a27a9b38309aad9b2df477
Image: docker.io/istio/health:example
Image ID: docker.io/istio/health@sha256:d8a2ff91d87f800b4661bec5aaadf73d33de296d618081fa36a0d1cbfb45d3d5
Port: 8001/TCP
Host Port: 0/TCP
State: Running
Started: Sat, 10 May 2025 16:58:35 +0900
Ready: True
Restart Count: 0
Liveness: http-get http://:15020/app-health/liveness-http/livez delay=5s timeout=1s period=5s #success=1 #failure=3
...
istio-proxy:
Container ID: containerd://d4b0955372bdb7b3e1490eb3f290c6c6f5a9f2691eabea4cebafaafa8be85fc9
Image: docker.io/istio/proxyv2:1.17.8
Image ID: docker.io/istio/proxyv2@sha256:d33fd90e25c59f4f7378d1b9dd0eebbb756e03520ab09cf303a43b51b5cb01b8
Port: 15090/TCP
...
Readiness: http-get http://:15021/healthz/ready delay=1s timeout=3s period=2s #success=1 #failure=30
Environment:
...
ISTIO_META_POD_PORTS: [
{"containerPort":8001,"protocol":"TCP"}
]
ISTIO_META_APP_CONTAINERS: liveness-http
ISTIO_META_CLUSTER_ID: Kubernetes
ISTIO_META_NODE_NAME: (v1:spec.nodeName)
ISTIO_META_INTERCEPTION_MODE: REDIRECT
ISTIO_META_WORKLOAD_NAME: liveness-http
ISTIO_META_OWNER: kubernetes://apis/apps/v1/namespaces/istioinaction/deployments/liveness-http
ISTIO_META_MESH_ID: cluster.local
TRUST_DOMAIN: cluster.local
ISTIO_KUBE_APP_PROBERS: {"/app-health/liveness-http/livez":{"httpGet":{"path":"/foo","port":8001,"scheme":"HTTP"},"timeoutSeconds":1}}
kubectl get pod -n istioinaction -l app=liveness-http -o json | jq '.items[0].spec.containers[0].livenessProbe.httpGet'
{
"path": "/app-health/liveness-http/livez",
"port": 15020,
"scheme": "HTTP"
}

#
kubectl exec -n istioinaction deploy/webapp -c istio-proxy -- curl -s localhost:15020/healthz/ready -v
# webapp 워크로드의 병합된 통계 확인 : istio_agent로 시작하는 메트릭(에이전트에서 온 것) + envoy로 시작하는 메트릭(프록시에서 온 것)
kubectl exec -n istioinaction deploy/webapp -c istio-proxy -- curl -s localhost:15020/stats/prometheus
## 응답에서는 istio_agent로 시작하는 메트릭(에이전트에서 온 것)과 envoy로 시작하는 메트릭(프록시에서 온 것)을 볼 수 있는데,
## 이는 이 둘이 병합됐음을 보여준다.
#
kubectl exec -n istioinaction deploy/webapp -c istio-proxy -- curl -s localhost:15020/quitquitquit
#
kubectl exec -n istioinaction deploy/webapp -c istio-proxy -- curl -s localhost:15020/debug/ndsz
#
kubectl port-forward deploy/webapp -n istioinaction 15020:15020

kubectl exec -n istioinaction deploy/webapp -c istio-proxy -- curl -s localhost:15004/debug/syncz -v
kubectl exec -n istioinaction deploy/webapp -c istio-proxy -- curl -s localhost:15004/debug/syncz | jq
...
"@type": "type.googleapis.com/envoy.service.status.v3.ClientConfig",
"node": {
"id": "catalog-6cf4b97d-fbftr.istioinaction", # 워크로드 ID
"metadata": {
"CLUSTER_ID": "Kubernetes"
}
},
"genericXdsConfigs": [
{
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"configStatus": "SYNCED" # xDS API는 최신 상태로 동기화됬다
},
{
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"configStatus": "SYNCED" # xDS API는 최신 상태로 동기화됬다
},
{
"typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
"configStatus": "SYNCED" # xDS API는 최신 상태로 동기화됬다
},
{
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"configStatus": "SYNCED" # xDS API는 최신 상태로 동기화됬다
},
...
docker exec -it myk8s-control-plane istioctl x internal-debug -h
docker exec -it myk8s-control-plane istioctl x internal-debug syncz

istioctl x internal-debug 명령어가 동일한 엔드포인트를 노출한다.파일럿은 서비스 메시를 검사하고 디버깅하기 위한 정보들도 노출한다.
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 :::15017 :::* LISTEN
tcp6 0 0 :::15014 :::* LISTEN
tcp6 0 0 :::15012 :::* LISTEN
tcp6 0 0 :::15010 :::* LISTEN
tcp6 0 0 :::8080 :::* LISTEN
# pilot-discovery 프로세스 확인
kubectl -n istio-system exec -it deploy/istiod -- ss -tnlp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.1:9876 0.0.0.0:* users:(("pilot-discovery",pid=1,fd=8))
LISTEN 0 4096 *:15017 *:* users:(("pilot-discovery",pid=1,fd=12))
LISTEN 0 4096 *:15014 *:* users:(("pilot-discovery",pid=1,fd=9))
LISTEN 0 4096 *:15012 *:* users:(("pilot-discovery",pid=1,fd=10))
LISTEN 0 4096 *:15010 *:* users:(("pilot-discovery",pid=1,fd=11))
LISTEN 0 4096 *:8080 *:* users:(("pilot-discovery",pid=1,fd=3))
#
kubectl describe pod -n istio-system -l app=istiod
...
Containers:
discovery:
Container ID: containerd://f13d7ad8a32cc0cecf47392ef426ea4687ce12d1abf64b5a6d2a60c2f8934e04
Image: docker.io/istio/pilot:1.17.8
Image ID: docker.io/istio/pilot@sha256:cb9e7b1b1c7b8dcea37d5173b87c40f38a5ae7b44799adfdcf8574c57a52ad2c
Ports: 8080/TCP, 15010/TCP, 15017/TCP
Host Ports: 0/TCP, 0/TCP, 0/TCP
Args:
discovery
--monitoringAddr=:15014
--log_output_level=default:info
--domain
cluster.local
--keepaliveMaxServerConnectionAge
30m
...
Readiness: http-get http://:8080/ready delay=1s timeout=5s period=3s #success=1 #failure=3
Environment:
REVISION: default
JWT_POLICY: third-party-jwt
PILOT_CERT_PROVIDER: istiod
POD_NAME: istiod-8d74787f-ltkhs (v1:metadata.name)
POD_NAMESPACE: istio-system (v1:metadata.namespace)
SERVICE_ACCOUNT: (v1:spec.serviceAccountName)
KUBECONFIG: /var/run/secrets/remote/config
PILOT_TRACE_SAMPLING: 100
PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_OUTBOUND: true
PILOT_ENABLE_PROTOCOL_SNIFFING_FOR_INBOUND: true
ISTIOD_ADDR: istiod.istio-system.svc:15012
PILOT_ENABLE_ANALYSIS: false
CLUSTER_ID: Kubernetes
...

이스티오 파일럿 디버그 엔드포인트는 파일럿이 알고 있는 전체 서비스 메시의 설정과 상태를 노출한다.
엔드포인트는 다음과 같은 질문들에 답한다.
디버그 엔드포인트 접근
kubectl -n istio-system port-forward deploy/istiod 8080
open http://localhost:8080/debug
# 파일럿이 알고 있는 서비스 메시 상태
## 클러스터, 루트, 리스너 설정
curl -s http://localhost:8080/debug/adsz | jq
## 이 파일럿이 관리하는 모든 프록시에 대한 푸시를 트리거한다.
curl -s http://localhost:8080/debug/adsz?push=true
Pushed to 4 servers
## /debug/edsz=proxyID=<pod>.<namespace> : 프록시가 알고 있는 엔드포인트들
curl -s http://localhost:8080/debug/edsz=proxyID=webapp.istioninaction
## /debug/authorizationz : 네임스페이스에 적용되는 인가 정책 목록
curl -s http://localhost:8080/debug/authorizationz | jq
# 파일럿이 알고 있는 데이터 플레인 설정을 나타내는 엔드포인트
## 이 파일럿 인스턴스에 연결된 모든 엔보이의 버전 상태 : 현재 비활성화되어 있음
curl -s http://localhost:8080/debug/config_distribution
Pilot Version tracking is disabled. It may be enabled by setting the PILOT_ENABLE_CONFIG_DISTRIBUTION_TRACKING environment variable to true
## 이스티오 파일럿의 현재 알려진 상태에 따라 엔보이 설정을 생성한다.
curl -s http://localhost:8080/debug/config_dump?=proxyID=webapp.istioninaction
## 이 파일럿이 관리하는 프록시들을 표시한다.
curl -s http://localhost:8080/debug/syncz | jq
...
{
"cluster_id": "Kubernetes",
"proxy": "webapp-7685bcb84-lwsvj.istioinaction",
"istio_version": "1.17.8",
"cluster_sent": "ff5e6b2c-e857-4e12-b17e-46ad968567f4",
"cluster_acked": "ff5e6b2c-e857-4e12-b17e-46ad968567f4",
"listener_sent": "7280c908-010d-4788-807f-7138e74fe72e",
"listener_acked": "7280c908-010d-4788-807f-7138e74fe72e",
"route_sent": "2a1916c3-9c05-4ce5-8cfa-d777105b9205",
"route_acked": "2a1916c3-9c05-4ce5-8cfa-d777105b9205",
"endpoint_sent": "dffacd32-2674-4e39-8e76-17016ff32514",
"endpoint_acked": "dffacd32-2674-4e39-8e76-17016ff32514"
},
...

디버그 엔드포인가 노출될 경우 오용될 수 있는 민감 정보가 포함돼 있다.
운영 환경에서는 이스티오를 설치할 때 환경 변수 ENABLE_DEBUG_ON_HTTP 를 false 로 설정해 디버그 엔드포인트를 비활성화를 권장한다.
이렇게 하면 해당 엔드포인트에 의존하는 도구가 제 역할을 할 수 없지만, 향후 릴리스에서는 이러한 엔드포인트가 xDS를 통해 안전하게 노출됩니다.
/debug/adsz : 클러스터, 루트, 리스너 설정/debug/adsz?push=true : 이 파일럿이 관리하는 모든 프록시에 대한 푸시를 트리거한다./debug/edsz=*proxyID*=*<pod>.<namespace>* : 프록시가 알고 있는 엔드포인트들/debug/authorizationz : 네임스페이스에 적용되는 인가 정책 목록/debug/config_distribution : 이 파일럿 인스턴스에 연결된 모든 엔보이의 버전 상태/debug/config_dump?proxyID=<pod>.<namespace> : 이스티오 파일럿의 현재 알려진 상태에 따라 엔보이 설정을 생성한다./debug/syncz : 이 파일럿이 관리하는 프록시들을 표시한다.| 페이지 | 설명 |
|---|---|
| 로깅 범위 | 이 프로세스에 대한 로깅은 범위별로 구성돼 있어 범위별로 로깅 단계를 별도로 설정할 수 있다. |
| 메모리 사용량 | 이 정보는 Go 런타임에서 수집되며 이 프로세스의 메모리 소비량을 나타낸다. |
| 환경 변수 | 이 프로세스에 정의된 환경 변수 집합이다. |
| 프로세스 정보 | 이 프로세스에 대한 정보다. |
| 명령줄 인수 | 이 프로세스를 시작할 때 사용한 명령줄 인수 집합이다. |
| 버전 정보 | 바이너리(예: 이스티오 파일럿 1.7.3)와 Go 런타임(go 1.14.7)에 대한 정보다. |
| 메트릭 | 파일럿에서 노출하는 메트릭을 가져오는 방법 중 하나다. |
| 시그널 | 실행 중인 프로세스에 SUGUSR1 시그널을 보낼수 있다. |
kubectl -n istio-system port-forward deploy/istiod 9876

이스티오 파일럿을 디버깅 해야 할 때 로깅 범위 변경 사용 가능.