Post

Istio 트러블 슈팅

Istio 트러블 슈팅 방법 살펴보기

들어가며

이번 주차에는 istio를 사용하면서 장애로 이어질 수 있는 유형에 대해 알아보고, 장애가 발생했을 때 어떻게 트러블 슈팅하는지 살펴본다. 실제 운영에 가장 도움이 될 것 같은 주차이다.

실수 유형

CRD 설정 오류

istio는 VirtualService, Gateway, DestinationRule 리소스를 통해 설정을 진행한다. 당연히 해당 리소스를 잘못설정하면 장애가 발생한다.

책에서 살펴보는 장애 유형은 특정 VirtualService에서 subset을 사용하지만, DestinationRule 리소스로 subset을 정의를 생략한 경우에 대해 살펴본다.

실습을 위한 YAML은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
---
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

트래픽을 특정 subset으로 비중을 두어 라우팅한다. 이때 DestinationRule을 생략하고 배포해본다.

이제 해당 서비스에 접근해보자.

1
for i in {1..100}; do curl http://catalog.istioinaction.io:30000/items -w "\nStatus Code %{http_code}\n"; sleep .5;  done

503 에러가 뜨는 걸 볼 수 있다.

로그를 살펴보면, 아래와 같이 GET /items HTTP/1.1" 503 NC cluster_not_found 오류가 발생한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kubectl logs -n istio-system -l app=istio-ingressgateway -f

2025-05-17T15:56:18.338253Z	info	ads	XDS: Incremental Pushing:0 ConnectedEndpoints:0 Version:
2025-05-17T15:56:18.338353Z	info	cache	returned workload trust anchor from cache	ttl=23h59m59.661652788s
2025-05-17T15:56:18.342245Z	info	ads	ADS: new connection for node:istio-ingressgateway-6bb8fb6549-lb7z2.istio-system-1
2025-05-17T15:56:18.342293Z	info	cache	returned workload trust anchor from cache	ttl=23h59m59.657709622s
2025-05-17T15:56:18.342559Z	info	ads	SDS: PUSH request for node:istio-ingressgateway-6bb8fb6549-lb7z2.istio-system resources:1 size:1.1kB resource:ROOTCA
2025-05-17T15:56:18.342805Z	info	ads	ADS: new connection for node:istio-ingressgateway-6bb8fb6549-lb7z2.istio-system-2
2025-05-17T15:56:18.342867Z	info	cache	returned workload certificate from cache	ttl=23h59m59.657135455s
2025-05-17T15:56:18.342938Z	info	ads	SDS: PUSH request for node:istio-ingressgateway-6bb8fb6549-lb7z2.istio-system resources:1 size:4.0kB resource:default
2025-05-17T15:56:19.873533Z	info	Readiness succeeded in 1.620825292s
2025-05-17T15:56:19.874045Z	info	Envoy proxy is ready
[2025-05-17T15:59:54.909Z] "GET /items HTTP/1.1" 503 NC cluster_not_found - "-" 0 0 0 - "172.18.0.1" "curl/8.7.1" "7856f417-c452-9646-b985-dc9f67b84f50" "catalog.istioinaction.io:30000" "-" - - 10.10.0.7:8080 172.18.0.1:60900 - -
[2025-05-17T15:59:55.459Z] "GET /items HTTP/1.1" 503 NC cluster_not_found - "-" 0 0 0 - "172.18.0.1" "curl/8.7.1" "ed2d30dc-3296-9563-8dde-512942bb0489" "catalog.istioinaction.io:30000" "-" - - 10.10.0.7:8080 172.18.0.1:60906 - -
[2025-05-17T15:59:55.994Z] "GET /items HTTP/1.1" 503 NC cluster_not_found - "-" 0 0 0 - "172.18.0.1" "curl/8.7.1" "c47a9812-6a03-9a58-9cb5-77bf4876b3a5" "catalog.istioinaction.io:30000" "-" - - 10.10.0.7:8080 172.18.0.1:60910 - -
[2025-05-17T15:59:56.527Z] "GET /items HTTP/1.1" 503 NC cluster_not_found - "-" 0 0 0 - "172.18.0.1" "curl/8.7.1" "8febf6b3-51fb-9a45-9393-3576812f8fad" "catalog.istioinaction.io:30000" "-" - - 10.10.0.7:8080 172.18.0.1:60924 - -

NC=No Cluster로 Envoy의 해당 트래픽이 들어오면 라우팅할 Cluster가 없다는 의미이다.

NC와 같은 Envoy 플레그를 가시다님이 아래와 같이 정리해주셨다.

Envoy response flag

RESPONSE_FLAGS

HTTP and TCPUH: No healthy upstream hosts in upstream cluster in addition to 503 response code. • UF: Upstream connection failure in addition to 503 response code. • UO: Upstream overflow (circuit breaking) in addition to 503 response code. • NR: No route configured for a given request in addition to 404 response code, or no matching filter chain for a downstream connection. • URX: The request was rejected because the upstream retry limit (HTTP) or maximum connect attempts (TCP) was reached. • NC: Upstream cluster not found. • DT: When a request or connection exceeded max_connection_duration or max_downstream_connection_duration.

HTTP onlyDC: Downstream connection termination. • LH: Local service failed health check request in addition to 503 response code. • UT: Upstream request timeout in addition to 504 response code. • LR: Connection local reset in addition to 503 response code. • UR: Upstream remote reset in addition to 503 response code. • UC: Upstream connection termination in addition to 503 response code. • DI: The request processing was delayed for a period specified via fault injection. • FI: The request was aborted with a response code specified via fault injection. • RL: The request was ratelimited locally by the HTTP rate limit filter in addition to 429 response code. • UAEX: The request was denied by the external authorization service. • RLSE: The request was rejected because there was an error in rate limit service. • IH: The request was rejected because it set an invalid value for a strictly-checked header in addition to 400 response code. • SI: Stream idle timeout in addition to 408 response code. • DPE: The downstream request had an HTTP protocol error. • UPE: The upstream response had an HTTP protocol error. • UMSDR: The upstream request reached max stream duration. • OM: Overload Manager terminated the request. • DF: The request was terminated due to DNS resolution failure.

istio 기본 필요사항

istio를 처음 구축하는 경우라고하면, 네트워크 설정 오류를 조심해야할 것 같다. 아래처럼 istio가 사용하는 포트가 존재하여 클라우드면 보안 그룹에서 해당 포트를 열어줘야하고, 온프렘이면 운영도구 같이 서버에 기본적으로 탑재된 서비스와 충돌되지 않는지 확인해야 한다.

Port
PortProtocolDescriptionPod-internal only
15000TCPEnvoy admin port (commands/diagnostics)Yes
15001TCPEnvoy outboundNo
15002TCPListen port for failure detectionYes
15004HTTPDebug portYes
15006TCPEnvoy inboundNo
15008HTTP2HBONE mTLS tunnel portNo
15020HTTPMerged Prometheus telemetry from Istio agent, Envoy, and applicationNo
15021HTTPHealth checksNo
15053DNSDNS port, if capture is enabledYes
15090HTTPEnvoy Prometheus telemetryNo

해당 블로그에 잘 정리된 그림도 볼 수 있었다.

image.png

출처: https://www.anyflow.net/sw-engineer/istio-internals-by-port

관련 Pod 권한

아래와 같은 파드 권한이 필요한데, 이를 서버 설정과 충돌하는 지만 체크하면 좋을 것 같다.

  • Application UIDs: UID가 1337이면 안된다. 이는 envoy proxy가 예약해둔 ID이다.
  • NET_ADMIN and NET_RAW 권한이 필요하다. 이는 iptables 설정에 필요하다.

범위 줄이기

만약 장애를 확인하면 원인을 빠르게 찾는 것이 가장 중요하다. 이때 빠르게 문제 범위를 줄이기 위한 방법으로 책에서는 아래와 같은 방식 대로 트러블 슈팅하는 것을 권장한다.

1. 현재 설정과 동일한지 확인(최신 상태 동기화 여부)

우선 데이터 플레인(proxy)가 최신 상태인지 확인한다. 별도 설정을 진행했을 때, 어떤 오류에 의해 설정이 혼자서 다르다면 장애의 원인이 될 수 있다.

  • istioctl proxy-status
1
2
3
4
5
6
7
8
docker exec -it myk8s-control-plane istioctl proxy-status

NAME                                                   CLUSTER        CDS        LDS        EDS        RDS          ECDS         ISTIOD                    VERSION
catalog-6cf4b97d-5c8hd.istioinaction                   Kubernetes     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-8d74787f-rbnhj     1.17.8
catalog-v2-56c97f6db-phl2v.istioinaction               Kubernetes     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-8d74787f-rbnhj     1.17.8
catalog-v2-56c97f6db-wn9mc.istioinaction               Kubernetes     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-8d74787f-rbnhj     1.17.8
istio-egressgateway-85df6b84b7-hmqvn.istio-system      Kubernetes     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-8d74787f-rbnhj     1.17.8
istio-ingressgateway-6bb8fb6549-lb7z2.istio-system     Kubernetes     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-8d74787f-rbnhj     1.17.8

SYNCED : istiod가 보낸 마지막 설정을 엔보이가 확인한 경우

NOT SENT : istiod가 아무것도 엔보이로 보내지 않은 경우, 보낼 설정이 없을 때도 해당 경우에 포함된다.

STALE : istiod가 프록시에 업데이트를 보냈지만 확인받지 못했다. 이는 다음 중 하나를 나타낸다.

  • istiod가 과부하됐거나, Envoy와 istiod 사이의 커넥션 부족 또는 끊김이거나, 버그가 발생한 경우이다.

만약 최신 상태 동기화여부에 문제가 없다면, 이제는 내가 설정을 잘못했는지 확인해야한다. 이때 가장 좋은 건 운영도구 Kiali 도움을 받는 것이다.

2. Kiali 확인하기

Kiali에 접속해보면, 노란색 경고 표시를 볼 수 있다. 이를 따라가보면 아래와 같이 VirtualService 설정이 잘못되었다고 나온다. 친절하게 설정 중에 어느 것이 잘못되었는지 아래의 사진처럼 나온다.

image.png

Kiali에 Validation 요소는 찾아보면 아래와 같은 것들도 있어 흔히 실수하는 유형을 대부분 잡아줄 것으로 기대된다. Kiali Validation 요소 전문은 Docs에서 확인할 수 있다.

  • KIA0202 – DestinationRule 호스트 미존재
  • KIA0301 – 동일 Host·Port 조합의 Gateway 중복
  • KIA1106 – 동일 Host를 가진 VirtualService 중복

3. istioctl

또 다른 진단도구 중에 istioctl이 있다. istioctl은 우리가 초기 istio 환경을 구성할 때도 자주 사용하는데, analyze 명령어를 통해 현재 상태를 진단할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
docker exec -it myk8s-control-plane istioctl analyze --list-analyzers

* annotations.K8sAnalyzer:
    Checks for misplaced and invalid Istio annotations in Kubernetes resources
* applicationUID.Analyzer:
    Checks invalid application UID
* auth.AuthorizationPoliciesAnalyzer:
    Checks the validity of authorization policies
* deployment.MultiServiceAnalyzer:
    Checks association between services and pods
* deprecation.DeprecationAnalyzer:
    Checks for deprecated Istio types and fields
* destinationrule.CaCertificateAnalyzer:
    Checks if caCertificates is set when TLS mode is SIMPLE/MUTUAL
* envoyfilter.EnvoyPatchAnalyzer:
    Checks an envoyFilters 
* gateway.CertificateAnalyzer:
    Checks a gateway certificate
* gateway.ConflictingGatewayAnalyzer:
    Checks a gateway's selector, port number and hosts
* gateway.IngressGatewayPortAnalyzer:
    Checks a gateway's ports against the gateway's Kubernetes service ports
* gateway.SecretAnalyzer:
    Checks a gateway's referenced secrets for correctness
* injection.Analyzer:
    Checks conditions related to Istio sidecar injection
* injection.ImageAnalyzer:
    Checks the image of auto-injection configured with the running proxies on pods
* injection.ImageAutoAnalyzer:
    Makes sure that Pods and Deployments with `image: auto` are going to be injected
* meshnetworks.MeshNetworksAnalyzer:
    Check the validity of MeshNetworks in the cluster
* schema.ValidationAnalyzer.AuthorizationPolicy:
    Runs schema validation as an analyzer on 'AuthorizationPolicy' resources
* schema.ValidationAnalyzer.DestinationRule:
    Runs schema validation as an analyzer on 'DestinationRule' resources
* schema.ValidationAnalyzer.EnvoyFilter:
    Runs schema validation as an analyzer on 'EnvoyFilter' resources
* schema.ValidationAnalyzer.Gateway:
    Runs schema validation as an analyzer on 'Gateway' resources
* schema.ValidationAnalyzer.MeshConfig:
    Runs schema validation as an analyzer on 'MeshConfig' resources
* schema.ValidationAnalyzer.MeshNetworks:
    Runs schema validation as an analyzer on 'MeshNetworks' resources
* schema.ValidationAnalyzer.PeerAuthentication:
    Runs schema validation as an analyzer on 'PeerAuthentication' resources
* schema.ValidationAnalyzer.ProxyConfig:
    Runs schema validation as an analyzer on 'ProxyConfig' resources
* schema.ValidationAnalyzer.RequestAuthentication:
    Runs schema validation as an analyzer on 'RequestAuthentication' resources
* schema.ValidationAnalyzer.ServiceEntry:
    Runs schema validation as an analyzer on 'ServiceEntry' resources
* schema.ValidationAnalyzer.Sidecar:
    Runs schema validation as an analyzer on 'Sidecar' resources
* schema.ValidationAnalyzer.Telemetry:
    Runs schema validation as an analyzer on 'Telemetry' resources
* schema.ValidationAnalyzer.VirtualService:
    Runs schema validation as an analyzer on 'VirtualService' resources
* schema.ValidationAnalyzer.WasmPlugin:
    Runs schema validation as an analyzer on 'WasmPlugin' resources
* schema.ValidationAnalyzer.WorkloadEntry:
    Runs schema validation as an analyzer on 'WorkloadEntry' resources
* schema.ValidationAnalyzer.WorkloadGroup:
    Runs schema validation as an analyzer on 'WorkloadGroup' resources
* service.PortNameAnalyzer:
    Checks the port names associated with each service
* serviceentry.Analyzer:
    Checks the validity of ServiceEntry
* sidecar.DefaultSelectorAnalyzer:
    Validates that there aren't multiple sidecar resources that have no selector
* sidecar.SelectorAnalyzer:
    Validates that sidecars that define a workload selector match at least one pod, and that there aren't multiple sidecar resources that select overlapping pods
* telemetry.ProviderAnalyzer:
    Validates that providers in telemery resource is valid
* virtualservice.ConflictingMeshGatewayHostsAnalyzer:
    Checks if multiple virtual services associated with the mesh gateway have conflicting hosts
* virtualservice.DestinationHostAnalyzer:
    Checks the destination hosts associated with each virtual service
* virtualservice.DestinationRuleAnalyzer:
    Checks the destination rules associated with each virtual service
* virtualservice.GatewayAnalyzer:
    Checks the gateways associated with each virtual service
* virtualservice.JWTClaimRouteAnalyzer:
    Checks the VirtualService using JWT claim based routing has corresponding RequestAuthentication
* virtualservice.RegexAnalyzer:
    Checks regex syntax
* webhook.Analyzer:
    Checks the validity of Istio webhooks

이제 우리 상태를 진단해보자. 아래와 같이 문제가 있는 istioinaction 네임스페이스로 analyze 명령어를 수행하면 다음과 같이 Referenced host+subset in destinationrule not found 현재 문제를 정확하게 진단해준다.

1
2
3
4
5
6
docker exec -it myk8s-control-plane 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.

이는 CLI 도구이기에, istio 설정 파일을 별도의 repo로 관리한다면 CI 부분에 넣어두어 검증해도 좋을 것 같다.

수동 확인(envoy)

위에서 문제를 찾을 수 없다면, 이젠 Envoy에 대한 정보를 바탕으로 직접 디버깅을 해야한다.

Envoy의 구성요소는 아래와 같다. 이제 하나씩 해당 요소를 디버깅할 예정이다. Envoy에 들어가 직접 쿼리를 날리기보단 istioctl을 통해 진행한다.

  • 리스너(Listeners)
  • 라우트(Routes)
  • 클러스터(Cluster)

Listeners

istio-ingressgateway의 리스너 설정을 확인한다.

1
2
3
4
5
6
docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/istio-ingressgateway -n istio-system

ADDRESS PORT  MATCH DESTINATION
0.0.0.0 8080  ALL   Route: http.8080
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

해당 proxy로는 8080포트로 트래픽이 인입된다. 이것이 우리가 설정한 서비스 규칙과 맞는지 확인한다.

1
2
3
4
5
6
7
8
9
kubectl get svc -n istio-system  istio-ingressgateway -o yaml | grep "ports:" -A10

  ports:
...
  - name: http2
    nodePort: 30000
    port: 80
    protocol: TCP
    targetPort: 8080

아래와 같이 targetPort: 8080으로 NodePort 혹은 ClusterIP:80로 들어오는 트래픽이 파드에 8080 포트로 라우팅된다는 것을 알 수 있다.

ingress gateway 파드로 들어오는 과정엔 문제가 없다. 이제 게이트웨이에서 특정 서비스로 향하는 라우팅을 확인해본다.

http.8080으로 들어오는 트래픽에 대한 라우팅 정보를 조회한다.

catalog.istioinaction.io 도메인인에 대한 모든 트래픽은 catalog-v1-v2.istioinaction VirtualService로 향한다.

1
2
3
4
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

여기까진 문제가 없다. 더 자세하게 라우팅 규칙을 살펴보자.

{DIRECTION} | {PORT} | {SUBSET} | {FQDN} 형식으로 출력된다.

2개의 라우팅 요소를 볼 수 있다.

"outbound|80|version-v1|catalog.istioinaction.svc.cluster.local"

"outbound|80|version-v2|catalog.istioinaction.svc.cluster.local"

1
2
3
4
5
6
7
8
9
10
11
12
docker exec -it myk8s-control-plane istioctl proxy-config routes deploy/istio-ingressgateway -n istio-system --name http.8080 -o json
...
"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
    }
],

이제 해당 FQDN을 살펴보면 다음과 같이 SUBSET이 없는 것을 볼 수 있다.

1
2
3
4
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      

이제 문제 해결을 위한 새로운 DestionalRule을 작성하고, 이를 istioctl analyze를 통해 적용했을 때 문제가 없는지 확인해본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: catalog
  namespace: istioinaction
spec:
  host: catalog.istioinaction.svc.cluster.local
  subsets:
  - name: version-v1
    labels:
      version: v1
  - name: version-v2
    labels:
      version: v2

명령어 수행결과 ✔ No validation issues found로 클러스터가 정상 동작한다는 것을 확인한다.

1
2
3
docker exec -it myk8s-control-plane istioctl analyze /DestinationRule.yaml -n istioinaction

✔ No validation issues found when analyzing /DestinationRule.yaml.

이제 해당 리소스를 배포하면, 문제는 정상적으로 해결된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
curl http://catalog.istioinaction.io:30000/items

[
  {
    "id": 1,
    "color": "amber",
    "department": "Eyewear",
    "name": "Elinor Glasses",
    "price": "282.00"
  },
  {
    "id": 2,
    "color": "cyan",
    "department": "Clothing",
    "name": "Atlas Shirt",
    "price": "127.00"
  },
  {
    "id": 3,
    "color": "teal",
    "department": "Clothing",
    "name": "Small Metal Shoes",
    "price": "232.00"
  },
  {
    "id": 4,
    "color": "red",
    "department": "Watches",
    "name": "Red Dragon Watch",
    "price": "232.00"
  }
]%                                                                                                                 

마치며

이번 주차에서는 실제 istio를 사용하면서 자주하는 실수와 장애가 발생했을 때 트러블 슈팅하는 방법에 대해 알아봤다. 다음 포스팅에서는 istio를 튜닝하는 방법에 대해 살펴볼 예정이다.

This post is licensed under CC BY 4.0 by the author.