Service_Mesh/Istio

[Istio-6주차] 운영 및 튜닝 (이론, 실습)

lati-tech 2025. 5. 15. 01:44

10장 데이터 플레인 트러블 슈팅하기

배경 설명

  • 잘못 설정한 워크로드 트러블슈팅하기 Troubleshooting a misconfigured workload
  • istioctl과 키알리로 잘못된 설정 감지 및 방지하기 Detecting and preventing misconfigurations using istioctl and Kiali
  • istioctl을 사용해 서비스 프록시 설정 조사하기 Using istioctl to investigate the service proxy configuration
  • 엔보이 로그 이해하기 Making sense of Envoy logs
  • 텔레메트리를 사용해 앱에 대한 통찰력 얻기 Using telemetry to gain insights into apps


  • 들어가며: 데이터 플레인 문제 해결
    • Istio 서비스 프록시가 예기치 못하게 작동할 경우 해결 방안에 대해 조명 필요
    • Istio의 역할
      • 네트워크 통신 조명: 이슈 발생 시 확인 가능
      • 타임아웃, 재시도, 서킷 브레이커 등의 복원 기능 배치: 애플리케이션에서 네트워크 이슈 자동 대응
    • Istio에서 요청 처리에 참여하는 구성 요소
      • 예기치 못한 문제가 발생하면, 구성 요소(체인 리소스)가 원인일 수 있음
      • 다만, 시스템 전체적으로 장애 발생 시 모든 구성 요소 디버깅은 시간 부족 
      • istiod: 데이터 플레인원하는 상태 desired state 로 동기화되도록 보장
      • 인그레스 게이트웨이: 트래픽을 클러스터로 허용
      • 서비스 프록시
        • 접근 제어 기능 제공
        • 다운스트림에서 로컬 애플리케이션으로 흐르는 트래픽 처리
      • 애플리케이션 (그 자체)
        • 요청 처리
        • 또 다른 업스트림 서비스로 체인을 이어나가는 다른 서비스에 요청

 

  1. k8s(1.23.17) 배포 : NodePort(30000 HTTP, 30005 HTTPS)
    더보기

     

    # istio-in-action 실습 코드 다운로드
    git clone https://github.com/AcornPublishing/istio-in-action
    cd istio-in-action/book-source-code-master
    pwd # 각자 자신의 pwd 경로
    code .
    
    # 아래 extramounts 생략 시, myk8s-control-plane 컨테이너 sh/bash 진입 후 직접 git clone 가능
    kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - <<EOF
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    nodes:
    - role: control-plane
      extraPortMappings:
      - containerPort: 30000 # Sample Application (istio-ingrssgateway) HTTP
        hostPort: 30000
      - containerPort: 30001 # Prometheus
        hostPort: 30001
      - containerPort: 30002 # Grafana
        hostPort: 30002
      - containerPort: 30003 # Kiali
        hostPort: 30003
      - containerPort: 30004 # Tracing
        hostPort: 30004
      - containerPort: 30005 # Sample Application (istio-ingrssgateway) HTTPS
        hostPort: 30005
      - containerPort: 30006 # TCP Route
        hostPort: 30006
      - containerPort: 30007 # kube-ops-view
        hostPort: 30007
      extraMounts: # 해당 부분 생략 가능
      - hostPath: /Users/gasida/Downloads/istio-in-action/book-source-code-master # 각자 자신의 pwd 경로로 설정
        containerPath: /istiobook
    networking:
      podSubnet: 10.10.0.0/16
      serviceSubnet: 10.200.0.0/22
    EOF
    
    # 설치 확인
    docker ps
    
    # 노드에 기본 툴 설치
    docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
    
    # (옵션) kube-ops-view
    helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
    helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ="Asia/Seoul" --namespace kube-system
    kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
    
    ## kube-ops-view 접속 URL 확인
    open "http://localhost:30007/#scale=1.5"
    open "http://localhost:30007/#scale=1.3"
    
    # (옵션) metrics-server
    helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
    helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
    kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server

     

  2. istio 1.17.8 설치 - Docs , Install , profile
    더보기
    # myk8s-control-plane 진입 후 설치 진행
    docker exec -it myk8s-control-plane bash
    -----------------------------------
    # (옵션) 코드 파일들 마운트 확인
    tree /istiobook/ -L 1
    혹은
    git clone ... /istiobook
    
    # istioctl 설치
    export ISTIOV=1.17.8
    echo 'export ISTIOV=1.17.8' >> /root/.bashrc
    
    curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
    cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
    istioctl version --remote=false
    
    # demo 프로파일 컨트롤 플레인 배포
    istioctl install --set profile=demo --set values.global.proxy.privileged=true -y
    
    # 보조 도구 설치
    kubectl apply -f istio-$ISTIOV/samples/addons
    
    # 빠져나오기
    exit
    -----------------------------------
    
    # 설치 확인 : istiod, istio-ingressgateway, crd 등
    kubectl get istiooperators -n istio-system -o yaml
    kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
    kubectl get cm -n istio-system istio -o yaml
    kubectl get crd | grep istio.io | sort
    
    # 실습을 위한 네임스페이스 설정
    kubectl create ns istioinaction
    kubectl label namespace istioinaction istio-injection=enabled
    kubectl get ns --show-labels
    
    # istio-ingressgateway 서비스 : NodePort 변경 및 nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집)
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 443, "targetPort": 8443, "nodePort": 30005}]}}'
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
    kubectl describe svc -n istio-system istio-ingressgateway
    
    # NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
    kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
    kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
    kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
    kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
    
    # Prometheus 접속 : envoy, istio 메트릭 확인
    open http://127.0.0.1:30001
    
    # Grafana 접속
    open http://127.0.0.1:30002
    
    # Kiali 접속 1 : NodePort
    open http://127.0.0.1:30003
    
    # (옵션) Kiali 접속 2 : Port forward
    kubectl port-forward deployment/kiali -n istio-system 20001:20001 &
    open http://127.0.0.1:20001
    
    # tracing 접속 : 예거 트레이싱 대시보드
    open http://127.0.0.1:30004

 


  • The most common mistake: A misconfigured data plane : 환경 설정하기 (실습)
    • Istio의 서비스 프록시 설정
      • 사람이 읽을 수 있는 형식인 VirtualService, DestinationRule 등과 같은 CRD로 누출
      • CRD 설정은 Envoy 설정으로 변환돼 데이터 플레인적용
    • 새 리소스를 적용한 후 데이터 플레인의 동작이 예상과 다르다면? 관리자가 설정을 잘못한 것
    • 데이터 플레인을 잘못 설정했을 때 트러블슈팅하는 방법: 예제
      • 구성
        • Gateway 리소스: Ingress Gateway로 트래픽을 허용
        • VirtualService 리소스
          • 요청 중 20% 부분집합 version-v1으로 라우팅
          • 나머지 80%를 version-v2로 라우팅
      • 문제점
        • DestinationRule 리소스가 없음
          • Ingress Gateway는 부분집합 version-v1과 version-v2에 대한 클러스터 정의가 되어있지 않음
          • 현재 설정 상 모든 요청은 실패할 예정 (트러블슈팅에 적합)

    • Istio는 배포했지만 다른 애플리케이션 구성 요소를 배포하지 않았다고 가정하기
      • 샘플 애플리케이션 배포 (Deployment, Service, Gateway, VirtualService 정리 필요)
        # 샘플 애플리케이션 배포
        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
      • 통신 확인 : 부분집합 설정 누락으로 503 ‘Service Unavailable’ 출력 - Envoy
        # 로그 확인 : NC - NoClusterFound : Upstream cluster not found.
        kubectl logs -n istio-system -l app=istio-ingressgateway -f
        [2025-05-09T01:54:51.145Z] "GET /items HTTP/1.1" 503 NC cluster_not_found - "-" 0 0 0 - "172.18.0.1" "curl/8.7.1" "90a7d941-cbc4-91ae-9da1-bc95695d5c50" "catalog.istioinaction.io:30000" "-" - - 10.10.0.7:8080 172.18.0.1:64130 - -
        
        # 반복 호출 시도
        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

        요청 실행
        503 NC cluster_not_found 발생
        • Envoy response flag - 링크
          %RESPONSE_FLAGS%
          • 더보기

            HTTP and TCP

            HTTP only

            • DC: 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. 

 


  • 들어가며: 데이터 플레인 디버깅부터 하지 말 것
    • 일상적인 운영: 데이터 플레인 문제를 처리가 일반적
    • 바로 데이터 플레인 디버깅에 착수할수도 있으나
      컨트롤 플레인 문제를 추정 원인에서 빠르게 배제하는 것이 더욱 중요
    • 컨트롤 플레인의 주요 기능이 데이터 플레인을 최신 설정으로 동기화하는 것임을 감안한다면?
      • 첫 단계는 컨트롤 플레인과 데이터 플레인이 동기화된 상태인지 확인하는 것

 

    1. 데이터 플레인이 최신 상태인지 확인하는 방법 How to verify that the data plane is up to date
      • 데이터 플레인 설정은 설계상 궁극적으로 일관성을 가짐
        • 컨트롤 플레인과 동기화하기 전까지는
          환경(서비스, 엔드포인트, 상태)이나 설정의 변화가 데이터 플레인에 즉시 반영 X

        • ex) 컨트롤 플레인에서 특정 서비스의 개별 엔드포인트 IP 주소를 데이터 플레인으로 보낼 때 (서비스 내의 각 파드 IP 주소와 대강 동일)
          • 엔드포인트 중 어느 하나가 비정상이 되면, 쿠버네티스가 이를 인지하고 파드를 비정상으로 표시하는 데 시간 소요
          • 컨트롤 플레인도 특정 시점에 문제를 인지하고 엔드포인트를 데이터 플레인에서 제거
          • 위 과정을 통해 컨트롤 플레인은 최신 설정으로 돌아오며, 프록시 설정도 다시 일관된 상태 유지

        • 아래) 데이터 플레인을 업데이트하기 위해 발생하는 이벤트를 시각화
          워크로드가 비정상이 된 후 데이터 플레인 구성 요소의 설정이 업데이트될 때까지 일련의 이벤트
          1. kubelet은 주기적으로 Node 내에서 실행 중인 Pod의 상태를 확인
          2. Kubernetes API서버는 상태 확인을 실패한 Pod를 통보 받음
          3. API 서버가 모든 이해 당사자들에게 알림
          4. istiod가 데이터 플레인을 업데이트해 설정에서 엔드포인트를 제거
          5. 건강하지 않은 인스턴스로는 더 이상 트래픽이 전송되지 않음

            워크로드와 이벤트 개수가 늘어나는 대규모 클러스터에서는 데이터 플레인을 동기화하는 데 필요한 시간도 비례하여 증가
        • 데이터 플레인이 최신 설정과 동기화했는지 확인 (istioctl proxy-status)
          docker exec -it myk8s-control-plane 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

          istioctl proxy-status 로 데이터 플레인의 동기화 여부 확인
          • SYNCED: istiod가 보낸 마지막 설정을 Envoy가 확인함
          • NOT SENT: istiod가 아무것도 Envoy로 보내지 않음 (보통은 istiod가 보낼 것이 없기 때문)
          • STALE : istiod가 Envoy에 업데이트를 보냈지만 확인받지 못함
            • 원인
              • istiod가 과부하
              • Envoy와 istiod 사이의 커넥션 부족 또는 끊김
              • Istio 버그

        • 위 실습을 통해 확인한 사항
          • proxy-status 확인 시 설정을 받지 못한 stale 상태의 워크로드 없음
        • 확인해야 할 사항
          • 컨트롤 플레인에 문제가 있을 가능성은 낮으므로 데이터 플레인 구성 요소를 조사해야 함
          • 데이터 플레인 구성 요소에서 가장 일반적인 문제잘못된 워크로드 설정.
            • Kiali를 사용하면 빠르게 설정 검증 가능
    2. 키알리로 잘못된 설정 발견하기 Discovering misconfigurations with Kiali
      • 잘못된 설정 발견
        1. 대시보드 Overview 확인
        2. istioinaction 네임스페이스에 경고 표시
        3. 경고 아이콘 클릭 → 내장 편집기에서 경고 메시지 확인 - Docs
          ㅇㅅㅇ
          KIA1107 - Subset not found

          경고 아이콘 위로 마우스를 올리면 경고 메시지 ‘KIA1107 Subnet not found’ 확인 가능
           (Kiali 공식 문서 참고 - Docs)
          • KIA1107 경고의 해결책: 존재하지 않은 부분집합을 가리키는 루트 수정 필요
            • 부분집합 이름의 오타를 수정해야 할 수 있음
            • DestinationRule 에서 빠트린 부분집합이 있을 수 있음
          • Kiali 검증은 이슈 파악이 손쉬워 워크로드가 예상대로 동작하지 않을 때 취하는 첫 조치 중 하나
    3. istioctl로 잘못된 설정 발견하기 Discovering misconfigurations with istioctl
      • istio 명령어 중 오설정된 워크로드 트러블슈팅에 유용한 2가지 명령어
        1. istio analyze
        2. istio describe
      • istioctl 로 Istio 설정 분석하기 ANALYZING ISTIO CONFIGURATIONS WITH ISTIOCTL
        • istioctl analyze
          • Istio 설정을 분석하는 강력한 진단 도구
          • 이미 문제가 발생한 클러스터에 실행하여 검사 가능
          • 리소스를 잘못 구성하는 것을 방지하고자 클러스터에 적용하기 전에 설정이 유효한지 검사 가능
          • 여러 분석기를 실행할 수 있음 - 각 분석기는 특정 문제를 감지하는 데 특화됨
          • 쉬운 확장성
        • istioctl analyze로 감지된 문제 살펴보기
          # analyze help docs 확인
          docker exec -it myk8s-control-plane istioctl analyze -h
          
          # 진단 가능한 analyzer 목록 보기
          docker exec -it myk8s-control-plane istioctl analyze --list-analyzers
          ...
          
          # istioctl analyze 명령어로 istioinaction 네임스페이스 문제 진단
          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.
          
          # 이전 명령어 종료 코드 확인
          echo $? # (참고) 0 성공
          79
          진단 가능한 analyzer 목록 보기
          istioctl analyze 명령어로 istioinaction 네임스페이스 문제 진단

          • IST0101 에러가 확인됨
            • Istio 리소스(VirtualService)가 참조하는 다른 리소스(DestinationRule의 서브셋)가 존재하지 않음을 확인할 수 있음
            • 참고 문서 - IstioDocs , 0101

      • 워크로드별로 설정 오류 찾기 DETECTING WORKLOAD-SPECIFIC MISCONFIGURATIONS
        • istioctl describe
          • 워크로드별 설정을 기술하는데 사용
          • istio 설정(워크로드 하나에 직간접적으로 영향)을 분석해 요약 내용을 출력
          • 요약 내용은 다음 질문에 답변을 제공 (워크로드 관련)
            • 이 워크로드는 서비스 메시의 일부인가?
            • 어떤 VirtualService 와 DestinationRule 이 적용되는가?
            • 상호 인증 트래픽을 요구하는가?
        • istioctl describe 로 설정 오류 확인
          # catalog pod 이름 변수화
          kubectl get pod -n istioinaction -l app=catalog -o jsonpath='{.items[0].metadata.name}'
          CATALOG_POD1=$(kubectl get pod -n istioinaction -l app=catalog -o jsonpath='{.items[0].metadata.name}')
          
          # catalog pod istioctl experimental describe로 확인
          # 단축키 : experimental(x), describe(des)
          docker exec -it myk8s-control-plane istioctl experimental describe -h
          docker exec -it myk8s-control-plane istioctl x des pod -n istioinaction $CATALOG_POD1
          Pod: catalog-6cf4b97d-l44zk
             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
          
          
          Exposed on Ingress Gateway http://172.18.0.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!
          
          
          # 문제 해결(destinationrule 적용) 후 확인
          cat ch10/catalog-destinationrule-v1-v2.yaml
          kubectl apply -f ch10/catalog-destinationrule-v1-v2.yaml
          docker exec -it myk8s-control-plane istioctl x des pod -n istioinaction $CATALOG_POD1
          Pod: catalog-6cf4b97d-l44zk
             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://172.18.0.2
          VirtualService: catalog-v1-v2 # 이 파드로 트래픽을 라우팅하는 VirtualService
             Weight 20%
          
          # 다음 점검 방법을 위해 오류 상황으로 원복
          kubectl delete -f ch10/catalog-destinationrule-v1-v2.yaml

          istioctl x des pod -n istioinaction $CATALOG_POD1 로

          문제 해결(destinationrule 적용) 후 확인 Matching subsets: version-v1 - 일치하는 부분집합 (Non-matching subsets version-v2) - 일치하지 않은 부분집합



      • CI/CD 파이프라인에서 istio 설정 검증 예시 (istioctl analyze와 istioctl describe를 활용)
        • 명령어
          # 로컬 YAML 파일 검증
          istioctl analyze --use-kube=false samples/bookinfo/networking/bookinfo-gateway.yaml
          
          # 라이브 클러스터와 YAML 파일 조합 검증
          istioctl analyze samples/bookinfo/networking/bookinfo-gateway.yaml samples/bookinfo/networking/destination-rule-all.yaml
          
          # 특정 경고 억제
          istioctl analyze --namespace default --suppress "IST0102=Namespace default"
          
          # 모든 네임스페이스 분석
          istioctl analyze --all-namespaces
          
          # 디렉토리 내 모든 YAML 파일 분석
          istioctl analyze --recursive my-istio-config/


        • GitHub Actions
          name: Istio YAML 분석
          
          on:
            push:
              paths:
                - 'istio-configs/**.yaml'
            pull_request:
          
          jobs:
            istio-analyze:
              runs-on: ubuntu-latest
          
              steps:
              - name: Checkout repository
                uses: actions/checkout@v3
          
              - name: Install istioctl
                run: |
                  curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.22.0 sh -
                  export PATH="$PATH:./istio-1.22.0/bin"
                  echo "PATH=$PATH:./istio-1.22.0/bin" >> $GITHUB_ENV
          
              - name: Run istioctl analyze
                run: |
                  # --use-kube=false를 지정하면 클러스터 없이도 로컬 파일만으로 분석 가능
                  istioctl analyze ./istio-configs --use-kube=false --output json > analyze-report.json || true
                  cat analyze-report.json
          
              - name: Fail if issues found
                run: |
                  if grep -q '"Level":"Error"' analyze-report.json; then
                    echo "Istio analyze found errors"
                    exit 1
                  fi


          name: Istio YAML Validation
          
          on:
            pull_request:
              branches:
                - main
          
          jobs:
            validate-istio:
              runs-on: ubuntu-latest
              steps:
                # 리포지토리 체크아웃
                - name: Checkout code
                  uses: actions/checkout@v3
          
                # Istio 설치
                - name: Install istioctl
                  run: |
                    curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.25.2 sh -
                    mv istio-1.25.2/bin/istioctl /usr/local/bin/
                    istioctl version
          
                # YAML 파일 검증
                - name: Run istioctl analyze
                  run: |
                    istioctl analyze --use-kube=false ./istio-config/*.yaml
                  continue-on-error: false
          
                # 클러스터 접근 설정 (필요한 경우)
                - name: Setup kubeconfig
                  run: |
                    echo "${{ secrets.KUBECONFIG }}" > kubeconfig
                    export KUBECONFIG=kubeconfig
          
                # 라이브 클러스터 분석
                - name: Run istioctl analyze with cluster
                  run: |
                    istioctl analyze ./istio-config/bookinfo-gateway.yaml
          
                # 파드 구성 확인 (배포 후)
                - name: Run istioctl describe
                  run: |
                    POD_NAME=$(kubectl get pods -l app=ratings -o jsonpath='{.items[0].metadata.name}')
                    istioctl describe pod $POD_NAME


        • 과정 중 istioctl describe 사용  (디버깅용으로 로그 출력)
          ...
          - name: Describe Istio service
            run: |
              istioctl x describe svc reviews.default.svc.cluster.local || true
        • GitLab CI
          # .gitlab-ci.yml
          
          stages:
            - lint
            - build
            - deploy
          
          variables:
            ISTIO_VERSION: "1.20.0" # 사용하는 Istio 버전에 맞게 조정
          
          istioctl_analyze:
            stage: lint
            image: 
              name: gcr.io/google.com/cloudsdk/cloud-sdk:latest # kubectl, istioctl이 포함된 이미지 사용 (또는 커스텀 이미지)
              entrypoint: [""] # Entrypoint를 재정의하여 쉘 스크립트 실행
            before_script:
              # istioctl 다운로드 및 설치 (만약 이미지에 포함되어 있지 않다면)
              - apt-get update && apt-get install -y curl
              - curl -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIO_VERSION sh -
              - export PATH=$PWD/istio-$ISTIO_VERSION/bin:$PATH
            script:
              - echo "Analyzing Istio YAML files..."
              # 모든 Istio 관련 YAML 파일이 있는 디렉토리를 지정
              # 여기서는 project/istio-config/ 아래에 모든 Istio 설정 파일이 있다고 가정
              - istioctl analyze project/istio-config/
              # 또는 특정 파일만 지정
              # - istioctl analyze project/istio-config/gateway.yaml project/istio-config/virtualservice.yaml
              - if [ $? -ne 0 ]; then
                  echo "Istio configuration analysis failed. Please check the reported errors."
                  exit 1
                fi
              - echo "Istio configuration analysis passed."
            only:
              - merge_requests # PR/MR 시에만 실행
              - main           # main 브랜치 커밋 시에도 실행
        • Jenkins 파이프라인
          pipeline {
              agent any
          
              environment {
                  ISTIO_VERSION = '1.22.0'
                  ISTIOCTL_PATH = "${env.WORKSPACE}/istio-${ISTIO_VERSION}/bin"
              }
          
              stages {
                  stage('Checkout') {
                      steps {
                          git 'https://your-repo-url.com/your-istio-config-repo.git'
                      }
                  }
          
                  stage('Install istioctl') {
                      steps {
                          sh '''
                              curl -L https://istio.io/downloadIstio | ISTIO_VERSION=${ISTIO_VERSION} sh -
                              export PATH=$PATH:${ISTIOCTL_PATH}
                          '''
                      }
                  }
          
                  stage('Run istioctl analyze') {
                      steps {
                          sh '''
                              export PATH=$PATH:${ISTIOCTL_PATH}
                              istioctl analyze ./istio-configs --use-kube=false --output json > analyze-report.json || true
                              cat analyze-report.json
                              if grep -q '"Level":"Error"' analyze-report.json; then
                                  echo "Istio analyze found errors"
                                  exit 1
                              fi
                          '''
                      }
                  }
              }
          }


  • 들어가며: 데이터 플레인의 잘못된 설정, 혹은 문제 파악 후에도 이슈 해소가 되지 않았을 경우
    • Envoy 설정 전체에 대해 수동 조사 필요

 

      1. Envoy 관리(admin) 인터페이스 Envoy administration interface
        • 프록시의 특정 부분(로그 수준 증가 등)을 수정하는 기능 수행
        • Envoy 설정을 노출함
        • 모든 서비스 프록시에서 포트 15000으로 접근 가능
          # Envoy 관리 인터페이스 포트포워딩 (15000)
          kubectl port-forward deploy/catalog -n istioinaction 15000:15000
          open http://localhost:15000
          
          # 현재 적재한 엔보이 설정 출력 : 데이터양이 많다!
          curl -s localhost:15000/config_dump | wc -l
            13952

          admin 페이지
          config_dump (총 13952 라인)
        • Envoy configuration은 사람을 위한 것이 아니라 기계를 위한 것 (출력이 너무 커서 기본적으로 사람이 읽을 수 없음)
        • 참고 영상: IstioCon  https://www.youtube.com/watch?v=nNTpfXSCLKs
          다운로드


          로그 관리 영역




        • istioctl은 방대하고 다양한 Config 특성 상 출력을 작은 뭉치로 필터링하는 도구 제공 - 가독성 및 이해도 증가
        • Envoy Admin 인터페이스 - https://www.envoyproxy.io/docs/envoy/latest/operations/admin
      2. istioctl 로 프록시 설정 쿼리하기 Querying proxy configurations using istioctl
        • istioctl proxy-config 명령어 사용
          - 엔보이 xDS API를 기반으로 워크로드의 프록시 설정을 가져오고 필터링 가능
          • cluster : 클러스터 설정을 가져온다
          • endpoint : 엔드포인트 설정을 가져온다
          • listener : 리스너 설정을 가져온다
          • route : 루트 설정을 가져온다
          • secret : 시크릿 설정을 가져온다
        • 요청을 라우팅하기 위한 엔보이 API의 상호작용 THE INTERACTION OF ENVOY APIS TO ROUTE A REQUEST
          • 요청 라우팅을 설정하는 Envoy API
          • 요청 라우팅 Envoy API가 프록시에 미치는 영향
            • Envoy 리스너 listeners: 네트워크 설정을 정의
              • 다운스트림 트래픽을 프록시로 허용하는 IP 주소 및 포트 등을 정의
            • 허용된 커넥션에 HTTP 필터 filter 체인 생성
              • 체인의 라우터 필터 - 고급 라우팅 작업을 수행 (가장 중요)
            • Envoy 루트 routes: 가상 호스트를 클러스터에 일치시키는 규칙 집합 (루트는 순서대로 처리)
              • 일치하는 첫 번째 항목이 트래픽을 워크로드 클러스터로 라우팅
              • Istio에서는 RDS를 사용해 루트를 동적으로 설정 (Istio 아닌 환경에서는 정적 설정도 가능)
            • 각 Envoy 클러스터 clusters 의 엔드포인트 그룹
              • 각 클러스터에는 유사한 워크로드에 대한 엔드포인트 그룹 존재
              • 부분집합 Subsets: 클러스터 내에서 워크로드를 더 분할하는 데 사용 - 정밀한 트래픽 관리 가능
            • Envoy 엔드포인트: 요청을 처리하는 워크로드의 IP 주소


        • 엔보이 리스터 설정 쿼리하기 QUERYING THE ENVOY LISTENER CONFIGURATION
          • Ingress Gateway NodePort 30000 포트로 도착하는 트래픽이 클러스터로 허용되는 지 확인
            • 트래픽을 허용하는 것은 Envoy 리스너의 역할
            • Istio에서는 Gateway 리소스를 설정
          • Gateway의 리스너 설정을 쿼리하고 80 포트에서 트래픽이 허용되는 지 확인
            # Ingress Gateway의 리스너 설정을 쿼리
            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 # 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 이라는 루트에 따라 라우팅된다.

            Gateway 리스너 설정 쿼리 (8080 포트에 리스너 설정됨)
          • 80이 아닌 8080으로 리스닝되어 있는 점 확인
          • 8080이 설정된 포트인 지 확인
            # Ingress Gateway 에서 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

            • 외부 트래픽
              • 트래픽이 nodePort 30000 포트에 인입
              • Ingress Gateway(Pod)에 tcp 8080 포트로 전달(도달) (istio-ingressgateway 서비스)
            • 내부 트래픽(k8s 클러스터 내부)
              • clusterIP혹은 서비스명으로 tcp 80 요청
              • Ingress Gateway(Pod)에 tcp 8080 포트로 전달(도달)
            • 확인한 사항
              • 트래픽을 Ingress Gateway로 허용하는 리스너 존재 확인
              • 리스너의 라우팅은 루트 http.8080이 수행하는 점 확인


        • Envoy 루트(Route) 설정 쿼리하기 QUERYING THE ENVOY ROUTE CONFIGURATION
          • Envoy 루트 설정: 트래픽을 라우팅할 클러스터를 결정하는 규칙 집합을 정의
          • Istio의 Envoy 루트
            • VirtualService 리소스로 설정
            • 클러스터는 디스커비리로 자동 설정되거나, DestinationRule 리소스로 정의됨
          • http.8080 루트의 트래픽을 어느 클러스터로 라우팅할지 알아내기 위해 설정을 쿼리해보기
            • 세부 정보 확인
              호스트 catalog.istioinaction.io 의 트래픽 중 URL이 경로 접두사 /*과 일치하면
              istioinaction 네임스페이스의 catalog 서비스에 있는 catalog VirtualService 로 라우팅 됨
              # 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
                                          },
              ...



              • 클러스터 출력:  {DIRECTION} | {PORT} | {SUBSET} | {FQDN} → 루트가 일치할 때 트래픽을 수신하는 클러스터가 둘
                • outbound|80|version-v1|catalog.istioinaction.svc.cluster.local
                • outbound|80|version-v2|catalog.istioinaction.svc.cluster.local
        • Envoy 클러스터 설정 쿼리하기 QUERYING THE ENVOY CLUSTER CONFIGURATION
          • Envoy 클러스터 설정은 요청을 라우팅할 수 있는 백엔드 서비스를 정의
            • 클러스터는 부하를 여러 인스턴스나 엔드포인트에 분산
            • 엔드포인트(보통 IP 주소): 최종 사용자 트래픽을 처리하는 개별 워크로드 인스턴스
          • istioctl을 사용하면 Ingress Gateway가 알고 있는 클러스터를 쿼리할 수 있음
            • 다만, 클러스터는 수가 많음 - 라우팅할 수 있는 모든 백엔드 서비스마다 하나씩 설정됨
          • istioctl proxy-config clusters 의 플래그 direction, fqdn, port, subset 을 사용하면 특정 클러스터만 출력 가능
            • 플래그에서 사용할 정보는 앞서 가져온 클러스터 이름에 포함됨
              클러스터 이름을 구성하는 요소: 방향 ❘ 포트 ❘ 부분집합 ❘ FQDN 정보를 플래그에서 사용
          • 클러스터 중 한 개 쿼리
            # 클러스터 중 fqdn이 catalog.istioinaction.svc.cluster.local 이고 port가 80인 것 쿼리
            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  
            
            # 클러스터 중 앞의 조건은 동일하되 subset이 version-v1인 것 쿼리
            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
            
            # 클러스터 중 앞의 조건은 동일하되 subset이 version-v2인 것 쿼리
            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-v2

            클러스터 쿼리. catalog의 fqdn 및 port 80인 클러스터는 쿼리되지만, subset이 version-v1이나 v2인 것은 미확인

            • 부분 집합 subset version-v1 이나 version-v2 용 클러스터는 없음
              이 부분 집합에 대한 클러스터가 없으면 요청은 실패

          • 정상적인 설정 전, 설정할 yaml 파일이 식별한 서비스 메시 오류를 고칠 수 있는지 확인
            (istioctl analyze 명령어 사용)
            # 해당 파일이 없을 경우 'copy & paste'로 작성 후 진행
            docker exec -it myk8s-control-plane cat /istiobook/ch10/catalog-destinationrule-v1-v2.yaml
            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
            
            # 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.

            yaml 파일 적용을 시뮬레이션 시 서비스 메시 오류 해소 확인 ✔ 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
            
            # 호출 확인
            curl http://catalog.istioinaction.io:30000/items
            curl http://catalog.istioinaction.io:30000/items
             
            쿼리 시 subset이 version-v1, version-v2인 EDS 확인, 검증 오류 해소 및 정상 조회 확인


        • 클러스터는 어떻게 설정되는가? HOW CLUSTERS ARE CONFIGURED
          • Envoy 프록시에는 클러스터 엔드포인트를 발견하기 위한 여러 가지 방법이 있음
          • 사용 중인 방법 확인
            • istioctl에서 version-v1 클러스터를 JSON 형식으로 출력해보기
              # catalog version-v1 클러스터를 json 형식으로 출력
              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"
                      },
              ...

              catalog version-v1 클러스터의 edsClusterConfig 설정 보기
               
              • 출력 내용
                • edsClusterConfig 가 엔드포인트를 쿼리하는 데 ADS Aggregated Discovery Service 를 사용하도록 설정 되어있음
                • 서비스 이름 outbound|80|version-v1|catalog.istioinaction.svc.cluster.local 은 ADS를 쿼리할 때 엔드포인트용 필터로 사용


        • 엔보이 클러스터 엔드포인트 쿼리하기 QUERYING ENVOY CLUSTER ENDPOINTS
          • 앞에서는 Envoy 프록시가 서비스 이름으로 ADS를 쿼리하도록 설정된 것 확인
          • 서비스 이름을 통해 Ingress Gateway에서 클러스터의 엔드포인트를 수동으로 쿼리 가능
            (istioctl proxy-config endpoints 명령어)
            # 엔드포인트 정보 확인 : IP 정보
            docker exec -it myk8s-control-plane 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
            
            # 해당 IP 쿼리로 실제 워크로드가 있는지 확인
            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
            catalog-6cf4b97d-l44zk   2/2     Running   0          5h58m   10.10.0.12   myk8s-control-plane   <none>           <none>

            status.podIP가 10.10.0.12 인 워크로드 존재

          • 엔드포인트 정보에서 확인된 IP로 실제 워크로드가 확인 되었음
          • 트래픽을 워크로드로 라우팅하도록 서비스 프록시를 설정하는 Envoy API 리소스 체인 전체가 완성됨


      3. 애플리케이션 문제 트러블슈팅하기 Troubleshooting application issues
        • 마이크로서비스 기반 애플리케이션에서 서비스 프록시가 생성하는 로그와 메트릭의 이점
          • 이슈 트러블슈팅에 도움이 됨
            • 성능 병목을 일으키는 서비스 디스커버리
            • 빈번하게 실패하는 엔드포인트 식별
            • 성능 저하 감지
        • Envoy 액세스 로그와 메트릭을 사용해, 이 문제들 중 일부를 트러블슈팅
          (트러블슈팅 임의 발생 시키기)


        • 간헐적으로 제한 시간을 초과하는 느린 워크로드 준비하기 SETTING UP AN INTERMITTENTLY SLOW WORKLOAD THAT TIMES OUT
          • 설정 전 정상 통신 환경 상태 확인
            # 신규 터미널
            for in in {1..9999}; do curl http://catalog.istioinaction.io:30000/items -w "\nStatus Code %{http_code}\n"; sleep 1; done

            • kiali: catalog - 100% 성공


            • kiali : catalog 에 v2 링크 클릭 후 오른쪽 탭 메뉴 하단에 HTTP Request Response Time(ms)에 p99 확인 → 4.96ms


            • Grafana - Istio Mesh 대시보드


          • 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

            응답 지연 설정

            • Grafana - Istio Mesh 대시보드 : v2 에 P90, P99 레이턴스 확인 , v1 과 비교
              v2 레이턴시 p90: 800 ms, p99: 980 ms
            • kiali: catalog v2
              p99: 960 ms
            • Istio 에 요청 처리 제한 시간 0.5초가 되도록 VirtualService 설정
              2가지 변경 사항 : catalog v2 중 파드 1대는 간헐적으로 느린 응답을 하고, istio-proxy 가 요청 0.5초 이상 시 시간 초과 발생

              # VirtualService 확인 (istioinaction)
              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
              ..
              
              # ingressgateway 로그 확인
              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
              ...
              
              # label이 version=v2인 proxy 로그 확인 
              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
              ...

              타임아웃(0.5s) 적용 및 확인

              오류 확인
              504 발생 확인 - 전체 ingress gateway 로그 중

              504 발생 확인 - v2

              • kiali : catalog v2
              • Grafana - Istio Mesh 대시보드 : 500 응답 증가, v2 에 Success Rate 75% 확인



        • 엔보이 액세스 로그 이해하기 + 엔보이 액세스 로그 형식 바꾸기
          • Istio는 프록시가 로그를 TEXT 형식으로 기록하도록 기본 설정 (간결하지만, 읽기는 어려움)
          • JSON 형식을 사용하게 설정: 값이 키와 연결돼 의미를 알 수 있음
            # 형식 설정 전 로그 확인
            kubectl logs -n istio-system -l app=istio-ingressgateway -f | grep 504
            ...
            
            # MeshConfig 설정 수정
            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"
            }
            ...
            
            # slow 동작되는 파드 IP로 느린 동작 파드 확인!
            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-d74kv   2/2     Running   0          7h11m   10.10.0.13   myk8s-control-plane   <none>           <none>

            기존 로그 확인
            JSON 인코딩 설정

            로그 아웃풋 변경 확인

            jq 확인
            slow 동작되는 Pod IP로 느린 동작 Pod 확인

            • 필요 시, Envoy 프록시의 로깅 수준 상향으로 더욱 자세한 로그 확인 가능


        • 엔보이 게이트웨이의 로깅 수준 높이기 INCREASING THE LOGGING LEVEL FOR THE INGRESS GATEWAY
          • 현재 로깅 수준 확인
            # 로깅 수준 조회
            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 # 라우팅 범위에서는 요청이 어느 클러스터로 라우팅되는지 같은 세부 사항을 기록.
            ...

            현재 로깅 수준 확인

            • 사용할 수 있는 로깅 수준: none, error, warning, info, debug 
            • 각 범위에 로깅 수준을 서로 다르게 지정할 수 있음
            • Envoy 가 만들어내는 로그의 관심 영역의 로깅 수준만 레벨 상향 가능.
              • connection: 4계층(전송) 관련 로그, TCP 연결 세부 정보
              • http: 7계층(애플리케이션) 관련 로그, HTTP 세부 정보
              • router: HTTP 요청 라우팅과 관련된 로그
              • pool: 연결 풀이 연결의 업스트림 호스트를 획득하거나 삭제하는 방법과 관련된 로그
          • connection , http , router , pool 로거의 수준을 debug 로 상향
            # 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
            
            # 로그 확인
            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
          • 두 가지 중요한 발견
            1. 응답이 느린 업스트림의 IP 주소가 액세스 로그에서 가져온 IP 주소와 일치함
              - 오동작하는 인스턴스가 딱 하나라는 심증을 더욱 굳힘

            2. 로그 [C17947] client disconnected 에 표시된 대로 클라이언트(프록시)는 업스트림 커넥션을 종료함
              - 업스트림 인스턴스가 제한 시간 설정을 초과해 클라이언트(프록시)가 요청을 종료한다는 예상과 일치함


      4. ksniff (or tcpdump)로 네트워크 트래픽 검사 Inspect network traffic with ksniff
        • Pod 및 서비스 정보 확인
          Flow 예시

          # 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
          
          # catalog 서비스 정보 확인
          kubectl get svc,ep -n istioinaction
          NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
          service/catalog   ClusterIP   10.200.1.178   <none>        80/TCP    10h
          
          NAME                ENDPOINTS                                         AGE
          endpoints/catalog   10.10.0.12:3000,10.10.0.13:3000,10.10.0.14:3000   10h
          
          # istio-proxy 에서 기본 정보 확인 (tcpdump 는 생략 - sniff 로 캡처함.)
          kubectl exec -it -n istioinaction $CATALOG_POD -c istio-proxy -- sudo whoami
          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



          slow pod 및 catalog 서비스 기본 정보 확인
          istio-proxy 기본 정보 확인


          1. 특정 Pod에서 sniff 후 wireshark로 분석

            wsl2에서 wireshark 권한 오류 해결https://askubuntu.com/questions/748941/im-not-able-to-use-wireshark-couldnt-run-usr-bin-dumpcap-in-child-process

            이후 sniff로 slow pod dump 캡처
            # https://askubuntu.com/questions/748941/im-not-able-to-use-wireshark-couldnt-run-usr-bin-dumpcap-in-child-process
            # wsl2에서 권한 오류 해결 방안
            ##  wireshark "couldn't run /usr/bin/dumpcap in child process" [duplicate]
            # dumpcap 에 others 실행 권한 추가!!
            sudo dpkg-reconfigure wireshark-common
            sudo chmod +x /usr/bin/dumpcap
            # → root 없이도 실행 및 캡처 가능
            
            # sniff로 Pod dump 캡처 (lo)
            kubectl sniff -n istioinaction $CATALOG_POD -i lo
            
            # 실행되지 않을 경우 -p 옵션 (previledge) 옵션 추가 필요!
            # istio-proxy 에 eth0 에서 패킷 덤프
            kubectl sniff -n istioinaction $CATALOG_POD -c istio-proxy -i eth0 tcp port 3000
            
            # istio-proxy 에 lo 에서 패킷 덤프
            kubectl sniff -n istioinaction $CATALOG_POD -c istio-proxy -i lo
            
            # istio-proxy 에 tcp port 3000 에서 패킷 덤프
            kubectl sniff -n istioinaction $CATALOG_POD -c istio-proxy -i any tcp port 3000

            sniff 로 패킷 캡처 - catalog pod (lo)


          2. Wireshark 에서 TLS 암호 통신 확인 : istio-ingressgateway → [ (캡처 지점) istio-proxy ⇒ catalog application ]
            • Client Hello (SNI 확인) : EDS 의 클러스터 이름으로 접속
              outbound_.80_.version-v2_.catalog.istioinaction.svc.cluster.local
              SNI 내 서버 이름 값이 EDS의 클러스터 이름인 이유: https 통신 시 EDS 기준 요청에 대한 통제를 하기 위한 것으로 보인다.

              암호화된 내용 확인 : Encrypted Application Data 에 값 확인


          3. Wireshark 에서 평문 통신 확인 : istio-ingressgateway → [ istio-proxy (캡처 지점) catalog application ]

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


            GET /items 패킷에서 우클릭 후 Follow → TCP Stream 클릭해서 해당 스트림(TCP) 필터링

            tcp.stream eq 24 and http 로 필터링


            • Statistics → Flow Graph 확인 : 정상적으로 GET 요청과 200 응답 확인
              tcp.stream eq 24 and http 로 필터링

              tcp.stream eq 24으로 확인 (tcp+http)



            • 필터 (tcp.stream == 24 and http) 사용 함. (숫자 - 24 은 각자 스트림 필터링 값 입력 필요)

          4. istio-proxy 가 timeout 으로 먼저 종료된 것 확인 (catalog v2의 늦은 응답)
            Flow 예시

            • 필터 ((tcp.stream == 24 and http) or tcp.flags == 0x0011 or tcp.flags == 0x0004) : TCP RST, FIN/ACK 플래그 필터링 추가

              TCP RST로 연결 종료 확인

              • No. 301번에서 요청 후 0.5초 이상 응답이 없으니 (366번)368번에 istio-ingressgateway istio-proxy 가 TCP RST 로 연결 종료
                • 즉, 현재 구성 상 istio-ingressgw → catalog 이므로,
                  istio-ingressgw 가 TCP Timeout 후 종료 처리한 것을 알 수 있음
              • 이후 369번은 catalog v2 istio-proxy 가 FIN/ACK를 applcation 에게 전달 이후 연결 종료


            • TCP control flags - 커넥션의 특정 상태 표시
              • Synchronization (SYN): 커넥션을 새로 수립하는 데 사용
              • Acknowledgment (ACK): 패킷 수신이 성공했음을 확인하는 데 사용
              • Finish (FIN): 커넥션 종료를 요청하는 데 사용

            • kiali 확인


            • jaeger ui


        • 네트워크 트래픽 검사로 클라이언트가 커넥션 종료를 시작했으며, 서버에서 요청 응답이 느렸음을 확인.

 


  1. Grafana에서 실패한 요청 비율 찾기 Finding the rate of failing requests in Grafana
    • Grafana - Istio Service 대시보드 ⇒ Service(catalog.istioinaction..) , Reporter(source) 선택

      • 클라이언트 성공률은 요청 중 70% 정도(스샷은 79%)로 30% 정도 실패. ⇒ Client 응답에 5xx가 30% 정도 있음
        상태 코드 504 (’Gateway timeout’)로 표기되어 클라이언트 측 실패율에 반영

      • 서버 성공률100%, 즉 서버 문제는 아님 ⇒ Server 응답에는 5xx 없음.
        Envoy 프록시가 다운스트림 종료 요청에 대한 응답 코드를 0으로 표시하며,
        이는 5xx 응답이 아니라서 실패율에 포함되지 않음

      • [Ingress Gateway : 응답 플래그 UT, 상태 코드 504] ⇒ (요청 타임아웃) ⇒ [catalog v2 : 응답 플래그 DC, 상태 코드 0]
        클라이언트와 서버가 설정한 응답 플래그와 응답 코드의 차이점


    • 올바른 값은 클라이언트가 보고하는 성공률
    • 실패율이 2-30%이므로 빠른 확인 필요
      • 다만, Grafana 대시보드는 단지 catalog 서비스에 속한 모든 워크로드(v1,v2) 성공률만 확인 가능하였음
      • 이슈 있는 단일 인스턴스 식별을 위해 좀 더 상세한 출력이 필요

  2. 프로메테우스를 사용해 영향받는 파드 쿼리하기 Querying the affected Pods using Prometheus
    • 그라파나 대시보드 정보 부족으로 프로메테우스에 직접 쿼리
    • ex) Pod 별 실패율에 대한 메트릭 쿼리
      • destination 이 보고한 요청
      • destination 서비스가 catalog인 요청
      • 응답 플래그가 DC(다운스트림 커넥션 종료)인 요청 ⇒ 서버 입장에서는 응답을 하려는데, 클라이언트가 먼저 끊어 버린 것!
        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
        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"}
        
        # 쿼리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]))
        
        # 쿼리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))

        그래프를 보면 오직 워크로드 하나만 실패를 보고하고 있음을 보여줌
        (response_flags="DC" 인 Pod는 version="v2" 밖에 없음. response_code 는 0)

      • 쿼리를 조금 수정해서 확인 : 여러 개 Pod 중 catalog v2 만 응답 코드 0 기록 확인
        sort_desc(sum(irate(istio_requests_total{reporter="destination", destination_service=~"catalog.istioinaction.svc.cluster.local"}[5m]))by(response_code, pod, version))
        여러 개 Pod 중 catalog v2 만 응답 코드 0


    • Istio 표준 메트릭에 필요한 정보가 없으면, 커스텀 메트릭 추가 가능 (이전 블로그 글 7장 7.4 참고)
    • 혹은 프로메테우스 클라이언트 라이브러리를 사용해 애플리케이션 모니터링 임의 설정 가능


10 장에서는 데이터 플레인 트러블슈팅을 훑어봄

  • 요약
    • istioctl 명령어를 사용해 서비스 메시와 서비스 프록시에 대한 통찰력을 얻을 수 있음
      • proxy-status: 데이터 플레인 동기화 상태의 개요 확인 가능
      • analyze: 서비스 메시 설정을 분석
      • describe: 요약을 가져오고 서비스 프록시 설정을 검증
      • proxy-config: 서비스 프록시 설정을 쿼리하고 수정
    • istioctl analyze 명령을 사용해 클러스터에 적용하기 전에 설정 검증 가능
    • Kiali와 그 검증 기능을 사용해 일반적인 설정 실수 파악 가능
    • 장애 상황을 살펴보려면, 프로메테우스와 수집한 메트릭을 사용해야 함
    • kubectl sniff(tcpdump)를 사용해 영향을 받는 Pod의 네트워크 트래픽을 캡처 가능
    • istioctl proxy-config log 명령어를 사용해 Envoy 프록시의 로깅 수준 개선 가능

 


Internal Architecture by Port : Istiod(컨트롤플레인) + Istio Proxy(데이터플레인) 도식화 - Blog

 

  1. Istio 에이전트(DataPlane 위치에서 확인)가 노출하는 정보 Information exposed by the Istio agent (실습)
    • 실습 환경 초기화
      # 기존 리소스 삭제
      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


      반복 호출
    • istio sidecar기능
      • 헬스체크 Health checking
        • 프록시로서의 Envoy는 트래픽을 처리할 수 있는 즉시 준비 상태이지만,
          서비스 메시의 관점에서 보면 충분하지 않음
          • 프록시가 트래픽을 처리하기 전에 설정을 받았는지, ID를 할당받았는지 등의 더 많은 확인이 필요하다.
      • 메트릭 수집 및 노출 Metrics collection and exposure
        • 서비스 내에서 메트릭을 생성하는 세 가지 구성 요소: Application, Agent, Envoy Proxy
        • Agent는 다른 구성 요소의 메트릭을 집계해 노출
      • DNS resolution 해석
      • 인바운드 및 아웃바운드 트래픽 라우팅
      • .. etc


    • 서비스 노출 포트 확인
      kubectl -n istioinaction exec -it deploy/webapp -c istio-proxy -- netstat -tnl
      Active Internet connections (only servers)
      Proto Recv-Q Send-Q Local Address           Foreign Address         State      
      tcp        0      0 127.0.0.1:15000         0.0.0.0:*               LISTEN     
      tcp        0      0 127.0.0.1:15004         0.0.0.0:*               LISTEN     
      tcp        0      0 0.0.0.0:15021           0.0.0.0:*               LISTEN     
      tcp        0      0 0.0.0.0:15021           0.0.0.0:*               LISTEN     
      tcp        0      0 0.0.0.0:15001           0.0.0.0:*               LISTEN     
      tcp        0      0 0.0.0.0:15001           0.0.0.0:*               LISTEN     
      tcp        0      0 0.0.0.0:15006           0.0.0.0:*               LISTEN     
      tcp        0      0 0.0.0.0:15006           0.0.0.0:*               LISTEN     
      tcp        0      0 0.0.0.0:15090           0.0.0.0:*               LISTEN     
      tcp        0      0 0.0.0.0:15090           0.0.0.0:*               LISTEN     
      tcp6       0      0 :::15020                :::*                    LISTEN     
      tcp6       0      0 :::8080                 :::*                    LISTEN   
      
      # 포트별 프로세스 확인 : 파일럿에이전트, 엔보이
      kubectl -n istioinaction exec -it deploy/webapp -c istio-proxy --  ss -tnlp
      State                Recv-Q               Send-Q                             Local Address:Port                                Peer Address:Port               Process                                            
      LISTEN               0                    4096                                   127.0.0.1:15000                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=18))                    
      LISTEN               0                    4096                                   127.0.0.1:15004                                    0.0.0.0:*                   users:(("pilot-agent",pid=1,fd=11))               
      LISTEN               0                    4096                                     0.0.0.0:15021                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=25))                    
      LISTEN               0                    4096                                     0.0.0.0:15021                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=24))                    
      LISTEN               0                    4096                                     0.0.0.0:15001                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=36))                    
      LISTEN               0                    4096                                     0.0.0.0:15001                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=35))                    
      LISTEN               0                    4096                                     0.0.0.0:15006                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=38))                    
      LISTEN               0                    4096                                     0.0.0.0:15006                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=37))                    
      LISTEN               0                    4096                                     0.0.0.0:15090                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=23))                    
      LISTEN               0                    4096                                     0.0.0.0:15090                                    0.0.0.0:*                   users:(("envoy",pid=21,fd=22))                    
      LISTEN               0                    4096                                           *:15020                                          *:*                   users:(("pilot-agent",pid=1,fd=7))                
      LISTEN               0                    4096                                           *:8080                                           *:*               
      
      # istio-proxy 컨테이너에 Readiness Probe 정보 확인 : 15021 헬스체크 포트
      kubectl describe pod -n istioinaction -l app=webapp | grep Readiness:
          Readiness:  http-get http://:15021/healthz/ready delay=1s timeout=3s period=2s #success=1 #failure=30

      Pilot Agent , Envoy , Rediness Probe 정보 확인


    • Agent 및 Envoy 프록시의 포트와 각 포트 기능
      • 서비스용 포트 Ports facing other services
        • 15020: (파일럿 에이전트 프로세스) 여러 기능 제공
          • 메트릭을 집계하고 노출. 메트릭 종류는 다음과 같음
            • 프록시의 15090 포트에 쿼리한 메트릭
            • 애플리케이션 메트릭(설정한 경우)
            • 자체 메트릭
          • Envoy 및 DNS 프록시를 헬스 체크
            • 이 엔드포인트에서 애플리케이션도 헬스 체크하도록 프록시를 설정할 수 있지만,
              보통은 가상머신과 같이 쿠버네티스가 아닌 워크로드에만 사용
          • Istio 개발 팀에 유용한 파일럿 에이전트 디버깅용 엔드포인트
            • 메모리 정보, CPU 프로파일링 등과 같은 정보를 노출
        • 15021: (엔보이 프로세스)
          • Sidecar 가 주입된 Pod는 이 포트에서 트래픽을 받을 준비가 됐는지 확인하도록 설정
          • Envoy 프록시헬스 체크를 15020 포트의 파일럿 에이전트로 라우팅하며, 실제 헬스 체크는 여기서 일어남
        • 15053: (파일럿 에이전트 프로세스)
          • 쿠버네티스 DNS 해석이 충분하지 않은 에지 케이스를 해결하기 위해 istiod가 구성한 로컬 DNS 프록시
        • 15001: (엔보이 프로세스)
          • 애플리케이션에서 나가는 트래픽은 Iptable 규칙에 의해 일단 이 포트로 리다이렉트되며,
            이후 프록시가 트래픽을 서비스로 라우팅
        • 15006: (엔보이 프로세스)
          • 애플리케이션으로 들어오는 트래픽은 Iptable 규칙에 의해 일단 이 포트로 리다이렉트되며,
            여기서 로컬 애플리케이션 라우팅
      • 에이전트 디버깅 및 내부 상태 조사에 유용한 포트 useful for debugging and introspecting the agent
        • 15000: (엔보이 프로세스)
          • 엔보이 프록시 관리 인터페이스
        • 15090: (엔보이 프로세스)
          • 엔보이 프록시 메트릭을 노출 (xDS 통계, 커넥션 통계, HTTP 통계, 이상값 outlier 통계, 헬스 체크 통계, 서킷 브레이커 통계 등)
        • 15004: (파일럿 에이전트 프로세스)
          • 에이전트를 통해 이스티오 파일럿 디버그 엔드포인트를 노출.
          • 파일럿과의 연결 문제를 디버깅하는 데 유용.
        • 15020: (파일럿 에이전트 프로세스)
          • 파일럿 에이전트 디버기용 엔드포인트들을 노출.


    • 1.1 이스티오 에이전트를 조사하고 트러블슈팅하기 위한 엔드포인트들 Endpoints to introspect and troubleshoot the Istio agent
      • 15020 포트의 엔드포인트들
        • /healthz/ready : Envoy 및 DNS 프록시에서 일련의 검사를 수행
          • 워크로드가 클라이언트 요청을 처리할 준비가 됐는지 확인하기 위한 것
        • /stats/prometheus : Envoy 프록시와 애플리케이션의 메트릭을 자체 메트릭과 병합하고 긁어갈 수 있도록 노출
        • /quitquitquit  : 파일럿 에이전트의 프로세스를 종료
        • /app-health/ : Istio 프록시 사이드카의 환경 변수 ISTIO_KUBE_APP_PROBERS로 정의한 애플리케이션 프로브를 실행
          • 애플리케이션이 쿠버네티스 Probe를 정의하면,
            istiod mutating 웹훅이 정보를 추출해 이 환경 변수로 Probe를 설정 - Docs
          • 그러므로 에이전트는 이 경로로의 쿼리를 애플리케이션으로 리다이렉트 함
            #
            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/liveness-http -c istio-proxy -- curl -s localhost:15020/app-health/liveness-http/livez -v
            
            # 실습 확인 후 삭제
            kubectl delete deploy liveness-http -n istioinaction
          • /debug/ndsz : istiod가 NDS Name Discovery Service API로 DNS 프록시에 설정한 호스트네임들을 나열
          • /debug/pprof/* : 성능 문제, 메모리 누수 등을 디버깅하는 데 도움이 되는 Go 언어 프로파일링 엔드포인트. - Docs
            • 기본 경로 localhost:15020/debug/pprof 에 쿼리해 디버그 엔드포인트의 전체 목록을 확인 가능
            • 출력은 HTML이며 브라우저에서 보는 것이 가장 용이 (로컬호스트 포트 포워딩).
            • 프로파일링 엔드포인트는 Istio 개발자와 관련 있음 (Istio  사용자는 신경 쓸 필요 없음)

        • 접근 확인
          #
          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
          open http://localhost:15020/debug/pprof # 혹은 웹 브라우저에서 열기







    • 1.2 Istio 에이전트를 통해 이스티오 파일럿 디버그 엔드포인트들 쿼리하기
      • Agent는 기본적으로 15004 포트에서 몇 가지 istiod 디버그 엔드포인트들을 노출
        • 디버그 엔드포인트들에 한 요청은 xDS 이벤트 형태로 안전하게 istiod로 전달하며,
          이 xDS 이벤트는 에이전트에서 컨트롤 플레인으로의 연결 상태를 확인 할 수 있는 좋은 방법임
        • ex) 노출된 엔드포인트들 중 하나로 워크로드의 동기화 상태를 쿼리 가능
      • 이를 보려면, 프록시 중 하나의 셸 커넥션을 가져와서 파일럿 에이전트의 15004 포트에 /debug/sync 엔드포인트로 요청해볼 수 있음
        #
        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





      • 노출된 정보는 Istio 파일럿 디버그 엔드포인트들이 노출하는 정보의 부분집합
      • 또한 istioctl에 새로 추가된 istioctl x internal-debug 명령어가 동일한 엔드포인트를 노출

    • Istio 파일럿(ControlPlane 영역에서 확인)이 노출하는 정보 Information exposed by the Istio Pilot
    • 파일럿은 서비스 메시를 검사하고 디버깅하기 위한 정보들도 노출
    • 이 정보는 서비스 메시 운영자는 물론이고 외부 서비스들에도 유용
    • Istio 파일럿이 열어둔 포트를 확인
      #
      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
      ...





      노출된 Istio 파일럿 포트와 그 기능

      • 서비스용 포트
        • 15010 : xDS API 및 인증서 발급을 평문으로 노출한다. 트래픽을 스니핑할 수 있으므로 이 포트는 사용하지 않는 것이 좋다.
        • 15012 : 15010 포트와 노출하는 정보는 같지만 보안을 적용한다. 이 포트는 TLS를 사용해 ID를 발급하여, 후속 요청은 상호 인증된다.
        • 15014 : 11장에서 다룬 것과 같은 컨트롤 플레인 메트릭을 노출한다.
        • 15017 : 쿠버네티스 API 서버가 호출하는 웹훅 서버를 노출한다.
          • 쿠버네티스 API 서버는 새로 만들어진 파드에 사이드카를 주입하고, Gateway나 VirtualServie 같은 이스티오 리소스를 검증하기 위해 호출한다.
      • 디버깅 및 검사 포트
        • 8080 : 이스티오 파일럿 디버그 엔드포인트를 노출한다.
        • 9876 : istiod 프로세스에 대한 검사 정보를 노출한다.

    • 2.1 Istio 파일럿 디버그 엔드포인트 The Istio Pilot debug endpoints
      • Istio 파일럿 디버그 엔드포인트는 파일럿이 알고 있는 전체 서비스 메시의 설정과 상태를 노출한다.
      • 엔드포인트는 다음과 같은 질문들에 답한다.
        • 프록시는 동기화됐는가? Are the proxies synchronized?
        • 프록시에 대한 마지막 푸시는 언제 수행됐는가? When was the last push to a proxy performed?
        • xDS API의 상태는 어떤가? What’s the state of the xDS APIs?
      • 디버그 엔드포인트로 접근
        #
        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"
          },
        ...






      • 디버그 엔드포인가 노출될 경우 오용될 수 있는 민감 정보가 포함돼 있음
      • 운영 환경에서는 Istio를 설치할 때 환경 변수 ENABLE_DEBUG_ON_HTTPfalse 로 설정해 디버그 엔드포인트 비활성화를 권장
      • 이렇게 하면 해당 엔드포인트에 의존하는 도구가 제 역할을 할 수 없지만,
        향후 릴리스에서는 이러한 엔드포인트가 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 : 이 파일럿이 관리하는 프록시들을 표시한다.
        • 또한 프록시로 보낸 최신 논스 nonce 와 응답받은 최신 논스도 보여준다. 이 둘이 동일하면 프록시의 설정이 최신인 것이다.


    • 2.2 ControlZ 인터페이스
      • Istio 파일럿에는 파일럿 프로세스의 현재 상태와 몇 가지 사소한 설정 가능성을 확인 할 수 있는 관리자 인터페이스가 함께 제공됨
      • 이 인터페이스는 아래 표 D.1 에서 다룬 것 처럼 파일럿 인스턴스와 관련된 정보를 빠르게 조회할 수 있다.페이지 설명
        페이지 설명
        로깅 범위 Logging Scopes 이 프로세스에 대한 로깅은 범위별로 구성돼 있어 범위별로 로깅 단계를 별도로 설정할 수 있다.
        메모리 사용량 Memory Usage 이 정보는 Go 런타임에서 수집되며 이 프로세스의 메모리 소비량을 나타낸다.
        환경 변수 Environment Variables 이 프로세스에 정의된 환경 변수 집합이다.
        프로세스 정보 Process Information 이 프로세스에 대한 정보다.
        명령줄 인수 Command-Line Arguments 이 프로세스를 시작할 때 사용한 명령줄 인수 집합이다.
        버전 정보 Version Info 바이너리(예: 이스티오 파일럿 1.7.3)와 Go 런타임(go 1.14.7)에 대한 정보다.
        메트릭 Metrics 파일럿에서 노출하는 메트릭을 가져오는 방법 중 하나다.
        시그널 Signals 실행 중인 프로세스에 SUGUSR1 시그널을 보낼수 있다.
      • 접속 확인
        # ControlZ 인터페이스 포트포워딩
        kubectl -n istio-system port-forward deploy/istiod 9876
        open http://localhost:9876



 

 


 

11장 튜닝

배경 설명

  • 컨트롤 플레인 성능에 영향을 미치는 요소 이해하기 Understanding the factors of control-plane performance
  • 성능 모니터링 방법 알아보기 How to monitor performance
  • 주요 성능 메트릭 알아보기 What are the key performance metrics
  • 성능 최적화 방법 이해하기 Understanding how to optimize performance


  • 들어가며 : 컨트롤 플레인 성능 최적화
    • 데이터 플레인 문제 해결을 다룬 앞 장에서는 프록시 설정 및 동작 문제를 진단하는 데 사용할 수 있는 디버깅 도구를 상세 확인
      • 서비스 프록시 설정을 이해하면 예상과 다를 때 문제를 해결에 용이
    • 11장에서는 컨트롤 플레인 성능 최적화초점, 다음 사항을 확인
      • 컨트롤 플레인이 어떻게 서비스 프록시를 설정하는지
      • 서비스 프록시 설정 과정을 느리게 만드는 요인이 무엇인지
      • 서비스 프록시 설정 과정을 어떻게 모니터링하는지
      • 성능을 향상시키기 위해 조정할 수 있는 변수는 무엇인지

 

 

 


  • 들어가며 : 유령 워크로드와 대응 방안
    • 컨트롤 플레인
      • 서비스 메시의 두뇌
      • 서비스 메시 운영자를 위해 API를 노출
    • 컨트롤 플레인 API 사용 시
      • 메시의 동작을 조작
      • 각 워크로드 인스턴스에 함께 배포된 서비스 프록시를 설정
    • 서비스 메시 운영자(즉, 우리)가 이 API에 요청을 하는 것이 메시의 동작과 설정에 영향을 미치는 유일한 방법은 아님

    • 컨트롤 플레인은 런타임 환경의 세부적인 내용들을 추상화
      • 어떤 서비스가 존재하는지(서비스 디스커버리)
      • 어떤 서비스가 정상인지
      • 오토스케일링 이벤트
    • 컨트롤 플레인은 쿠버네티스의 이벤트를 수신하고, 원하는 새 상태를 반영하고자 설정을 업데이트
      • 이 상태 조정 절차는 올바르게 동작하는 메시를 유지하기 위해 계속됨
      • 시기적절하게 일어나는 것이 중요
    • 컨트롤 플레인이 상태 조정 절차를 적시에 하지 못한다면, 그 때마다 예기치 못한 결과로 이어짐
      • 워크로드는 이미 바뀐 상태로 설정돼 있기 때문
      • 성능 저하될 때 발생하는 흔한 증상을 ‘유령 워크로드 phantom workload’ 라고 지칭
        • 이미 사라진 엔드포인트로 트래픽을 라우팅하도록 서비스가 설정돼 있으므로 요청이 실패
      • 유령 워크로드의 개념
        오래된 설정으로 트래픽을 '유령 워크로드' 로 라우팅

        1. 비정상이 된 워크로드가 이벤트를 트리거
        2. 업데이트가 지연되면 서비스가 낡은 설정을 지님
        3. 오래된? 설정 때문에 서비스가 트래픽이 존재하지 않은 워크로드로 라우팅
      • 데이터 플레인의 궁극적 일관성eventually consistent 성질 덕분에 설정이 잠깐 낡은 것은 크 문제되지 않음 (다른 보호 기체를 사용할 수 있기 때문)
        • ex) 네트워크 문제로 요청이 실패하면 요청은 기본적으로 두 번 재시도되므로, 아마도 다른 정상 엔드포인트가 처리
      • 또 다른 교정 방법: 이상값 감지
        • 엔드포인트로 보낸 요청이 실패했을 때 클러스터에서 엔드포인트를 배제하는 것
      • 그러나 지연이 몇 초를 넘어가면 최종 사용자에게 부정적인 영향을 미칠 수 있으므로 반드시 배제 필요

 

  1. 데이터 플레인 동기화 단계 이해하기 Understanding the steps of data-plane synchronization : 디바운스와 스로틀링
    • 데이터 플레인을 원하는 상태로 동기화하는 과정 (여러 단계로 수행)
      1. 컨트롤 플레인은 쿠버네티스에서 이벤트를 수신
      2. 이벤트는 Envoy 설정으로 변환되어,
        데이터 플레인의 서비스 프록시로 푸시
    • 데이터 플레인 동기화 과정을 이해하면 컨트롤 플레인 성능을 미세 조정하고 최적화할 때 이뤄지는 의사결정 시 도움을 받을 수 있음
    • 들어오는 변경 사항에 맞춰 데이터 플레인을 동기화하는 단계
      최신 설정을 워크로드에 푸시하는 작업의 순서

      1. 들어오는 이벤트가 동기화 과정을 시작
      2. istiodDiscoveryServer 구성 요소가 이 이벤트들을 수신
        • 성능을 향상시키기 위해, 푸시 대기열에 이벤트를 추가하는 작업을 일정 시간 미루고 그 동안의 후속 이벤트를 병합해 일괄 처리
        • 이를 ‘디바운스 debounce 한다’고 지칭 - 디바운스는 시간을 잡아먹는 작업이 너무 자주 실행되지 않도록 도움
      3. 지연 시간이 만료되면, DiscoveryServer가 병합된 이벤트를 푸시 대기열에 추가
        • 푸시 대기열은 처리 대기 중인 푸시 목록을 유지 관리
      4. istiod 서버는 동시처리되는 푸시 요청 개수제한 throttle 함
        • throttle는 처리 중인 항목이 더 빨리 처리되도록 보장하고 CPU 시간이 작업 간 콘텍스트 스위칭에 낭비되는 것을 방지
      5. 처리된 항목은 Envoy 설정으로 변환돼 워크로드로 푸시됨

    • 디바운스와 스로틀링은 성능을 향상시키기 위해 설정 가능한 것
    • 디바운스(디바운싱 debouncing)와 스로틀링 throttling 이라는 두 가지 방법을 사용해 과부하되지 않도록 스스로를 보호하는 방법 확인

  2. 성능을 결정짓는 요소 Factors that determine performance : 변경 속도, 할당된 리소스, 업데이트할 워크로드 개수, 설정 크기
    • 동기화 프로세스를 잘 이해하면 - 컨트롤 플레인의 성능에 영향을 미치는 요소를 자세히 설명할 수 있음
      컨트롤 플레인 성능에 영향을 주는 속성들


      • 변경 속도 The rate of changes
        • 변경 속도가 빠를수록 데이터 플레인을 동기화 상태로 유지하는 데 더 많은 처리가 필요
      • 할당된 리소스 Allocated resources
        • 수요가 istiod에 할당된 리소스를 넘어서면 작업을 대기열에 넣어야하므로 업데이트 배포가 느려짐
      • 업데이트할 워크로드 개수 Number of workloads to update
        • 더 많은 워크로드에 업데이트를 배포하려면 네트워크 대역폭과 처리 능력이 더 많이 필요
      • 설정 크기 Configuration size
        • 더 큰 엔보이 구성을 배포하려면 처리 능력과 네트워크 대역폭이 더 많이 필요
    • 위 요소들에 부함한 성능 최적화 확인
    • 성능 최적화를 위해 프로메테우스가 istiod에서 수집한 메트릭을 시각화한 그라파나 대시보드(8장에서 준비함)를 사용해 병목 지점을 판단해야 함

 


  • 들어가며: 컨트롤 플레인 모니터링
    • istiod는 핵심 성능 지표의 지속 시간 및 빈도를 측정하는 메트릭을 노출
      • 리소스 사용률
      • 수신 또는 발신 트래픽으로 인한 부하
      • 오류 비율 등
    • 메트릭을 통해 확인할 수 있는 것
      • 제어 평면의 성능
      • 이슈가 될 리소스 파악
      • 올바르게 동작하지 않는 것을 어떻게 트러블슈팅해야 하는지 파악
    • Istio 공식 문서에 노출되는 메트릭 기재 (메트릭 개수는 방대) -  Docs
    • 핵심 메트릭 식별 및 네 가지 황금 신호에 대략 맞도록 메트릭 정리 필요

 

  1. 컨트롤 플레인의 네 가지 황금 신호 The four golden signals of the control plane (실습)
    1. 들어가며 : 실습 환경 초기화
      • 네 가지 황금 신호 (구글 SRE 책에서 정의)
        • 서비스가 어떻게 동작하는지에 대한 외부의 시각을 이해하기 위해 모니터링해야 하는 네 가지 주요 메트릭
        • 특정 서비스가 자신의 서비스 수준 목표 SLO 에서 벗어난 경우, 네 가지 황금 신호 메트릭을 통해 원인을 분석하는 통찰력을 얻을 수 있음
        • 네 가지 신호
          • 지연 시간
          • 포화도
          • 오류
          • 트래픽
        • 컨트롤 플레인의 메트릭을 빠르게 살펴보려면 다음 명령어로 쿼리
          # 실습 환경 준비
          kubectl -n istioinaction apply -f services/catalog/kubernetes/catalog.yaml
          kubectl -n istioinaction apply -f ch11/catalog-virtualservice.yaml
          kubectl -n istioinaction apply -f ch11/catalog-gateway.yaml
          
          # 확인
          kubectl get deploy,gw,vs -n istioinaction
          
          # 반복 설정 해두기
          while true; do curl -s http://catalog.istioinaction.io:30000/items ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
          
          
          # 컨트롤 플레인 메트릭 확인
          kubectl exec -it -n istio-system deploy/istiod -- curl localhost:15014/metrics
          # HELP citadel_server_csr_count The number of CSRs received by Citadel server.
          # TYPE citadel_server_csr_count counter
          citadel_server_csr_count 3
          ...

          실습 서비스 배포 확인
          서비스 반복 조회 - while

          컨트롤 플레인 메트릭 확인



      • 지연 시간: 데이터 플레인을 업데이트하는 데 필요한 시간 LATENCY: THE TIME NEEDED TO UPDATE THE DATA PLANE
        • 지연 시간 신호를 사용 시 알 수 있는 것
          • 서비스가 어떻게 동작하는지를 서비스 외부의 최종 사용자 관점에서 확인 가능
        • 지연 시간 증가가 의미하는 것
          • 서비스의 성능이 저하된 것
          • 성능 저하의 원인이 무엇인지는 알 수 없음 (원인을 알려면 다른 신호 조사 필요)
        • Istio 컨트롤 플레인에서 지연 시간 측정 방법
          • 컨트롤 플레인이 데이터 플레인에 업데이트를 얼마나 빠르게 배포하는지로 측정
          • 지연 시간을 측정하는 주요 메트릭
            • pilot_proxy_convergence_time 
            • 보조적으로 동기화 절차 중 대부분의 시간을 소비하는 단계의 이해를 돕는 보조 메트릭도 두 가지 존재
              • pilot_proxy_queue_time
              • pilot_xds_push_time
          • 동기화 단계 중 지연 시간 측정 메트릭이 다루는 부분
            전체 지연 시간 중 각 메트릭이 다루는 부분들

            1. pilot_proxy_convergence_time : 프록시 푸시 요청이 대기열에 안착한 순간부터 워크로드에 배포되기까지 전체 과정의 지속 시간을 측정
            2. pilot_proxy_queue_time : 워커가 처리할 때까지 푸시 요청이 대기열에서 기다린 시간을 측정
              • 푸시 대기열에서 상당한 시간이 걸리는 경우, istiod를 수직으로 확장해 동시 처리 능력을 높일 수 있음
            3. pilot_xds_push_time : Envoy 설정워크로드로 푸시하는 데 필요한 시간을 측정
              • 시간이 늘어나면, 전송되는 데이터양 때문에 네트워크 대역폭이 과부하된 것
              • 설정 업데이트 크기와 워크로드별 변화 빈도를 줄임으로써 이 상황을 상당히 개선할 수 있음


          • pilot_proxy_convergence_time 은 그라파나 대시보드에서 시각화 가능
            (Istio Control Plane 대시보드 - Proxy Push Time 참고)

            • 그래프 확인 시 Push의 99.9%는 워크로드에 배포하는 데 걸리는 시간이 100ms미만 (이상적)
              histogram_quantile(0.5,   sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))
              histogram_quantile(0.9,   sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))
              histogram_quantile(0.99,  sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))
              histogram_quantile(0.999, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))
            • (참고) 프로메테우스 쿼리 : le (누적 카운트) “less than or equal”
              pilot_proxy_convergence_time_bucket
              # le="0.1": 0.1초 이하로 동기화 완료된 프록시가 10개
              # le="1": 1초 이하로 완료된 프록시가 누적 20개
              # le="+Inf": 모든 프록시 포함 → 누적 41개
              ...
              
              pilot_proxy_convergence_time_bucket[1m]
              rate(pilot_proxy_convergence_time_bucket[1m])
              sum(rate(pilot_proxy_convergence_time_bucket[1m]))
              sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le)
              
              histogram_quantile(0.5, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))
              histogram_quantile(0.9, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))
              ...



          • 그라파나 대시보드에 2개의 패널(메트릭) 추가

            • 대시보드 편집 및 패널 2개 추가하여 배치
            • Proxy Queue Time : PromQL - pilot_proxy_queue_time
              histogram_quantile(0.5, sum(rate(pilot_proxy_queue_time_bucket[1m])) by (le))
              histogram_quantile(0.9, sum(rate(pilot_proxy_queue_time_bucket[1m])) by (le))
              histogram_quantile(0.99, sum(rate(pilot_proxy_queue_time_bucket[1m])) by (le))
              histogram_quantile(0.999, sum(rate(pilot_proxy_queue_time_bucket[1m])) by (le))
            • XDS Push Time : PromQL - pilot_xds_push_time_bucket
              histogram_quantile(0.5, sum(rate(pilot_xds_push_time_bucket[1m])) by (le))
              histogram_quantile(0.9, sum(rate(pilot_xds_push_time_bucket[1m])) by (le))
              histogram_quantile(0.99, sum(rate(pilot_xds_push_time_bucket[1m])) by (le))
              histogram_quantile(0.999, sum(rate(pilot_xds_push_time_bucket[1m])) by (le))
            • 패널 배치
          • 메시에 워크로드를 추가하면 관련 메트릭에서 지연 시간 점진적 증가
          • 약간의 증가는 당연하나, 지연 시간이 허용할 수 있는 임계값를 넘어가면 얼럿 트리거 필요
          • 다음 기준으로 임계값 고려 권장
            • Warning 심각도 severity : 10초 이상 동안 지연 시간이 1초를 초과하는 경우
            • Critical 심각도 severity : 10초 이상 동안 지연 시간이 2초를 초과하는 경우
          • 지연 시간이 늘어났다는 것은 컨트롤 플레인 성능이 저하됐음을 알리는 가장 좋은 지표이지만,
            성능 저하 원인에 대한 정보는 확인 불가
          • 저하 원인을 알아보려면 다른 메트릭 파악 필요


      • 포화도: 컨트롤 플레인이 얼마나(CPU, MEM 리소스) 가득 차 있는가? SATURATION: HOW FULL IS THE CONTROL PLANE?
        • 포화도 메트릭리소스 사용량을 나타냄
          • 사용률이 90% 이상이면 서비스는 포화된 것이거나 곧 포화됨
          • istiod가 포화되면? 배포 업데이트가 느려짐
            • 푸시 요청이 대기열에서 더 오래 처리를 기다리기 때문
        • 포화는 보통 가장 제한적인 리소스 때문에 일어남
          • istiodCPU 집중적이므로, 보통은 CPU가 가장 먼저 포화됨
          • CPU 사용률 측정 패널 확인
            CPU 패널 확인

            • container_cpu_usage_seconds_total : 쿠버네티스 컨테이너가 보고하는 (istiod 파드) CPU 사용률을 측정 - Docs
              # Cumulative cpu time consumed by the container in core-seconds
              container_cpu_usage_seconds_total
              container_cpu_usage_seconds_total{container="discovery"}
              container_cpu_usage_seconds_total{container="discovery", pod=~"istiod-.*|istio-pilot-.*"}
              sum(irate(container_cpu_usage_seconds_total{container="discovery", pod=~"istiod-.*|istio-pilot-.*"}[1m]))
              container_cpu_usage_seconds_total{container="discovery"}

            • process_cpu_seconds_total : istiod 계측이 보고하는 (istiod 파드) CPU 사용률을 측정.
              # Total user and system CPU time spent in seconds
              process_cpu_seconds_total{app="istiod"}
              irate(process_cpu_seconds_total{app="istiod"}[1m])

              process_cpu_seconds_total{app="istiod"}


            • (참고) kubectl top 파드/컨테이너 리소스 사용 확인
              kubectl top pod -n istio-system -l app=istiod --containers=true
              POD                     NAME        CPU(cores)   MEMORY(bytes)   
              istiod-8d74787f-cqhs2   discovery   3m           62Mi            
              
              kubectl top pod -n istioinaction --containers=true
              POD                      NAME          CPU(cores)   MEMORY(bytes)   
              catalog-6cf4b97d-5jtzt   catalog       0m           20Mi            
              catalog-6cf4b97d-5jtzt   istio-proxy   6m           46Mi
              
              #
              kubectl resource-capacity -n istioinaction -c -u -a
              kubectl resource-capacity -n istioinaction -c -u   
              NODE                  POD                      CONTAINER     CPU REQUESTS   CPU LIMITS    CPU UTIL   MEMORY REQUESTS   MEMORY LIMITS   MEMORY UTIL                                                                                                                                       
              myk8s-control-plane   *                        *             10m (0%)       2000m (25%)   7m (0%)    40Mi (0%)         1024Mi (8%)     67Mi (0%)
              myk8s-control-plane   catalog-6cf4b97d-5jtzt   *             10m (0%)       2000m (25%)   7m (0%)    40Mi (0%)         1024Mi (8%)     67Mi (0%)
              myk8s-control-plane   catalog-6cf4b97d-5jtzt   catalog       0m (0%)        0m (0%)       0m (0%)    0Mi (0%)          0Mi (0%)        21Mi (0%)
              myk8s-control-plane   catalog-6cf4b97d-5jtzt   istio-proxy   10m (0%)       2000m (25%)   7m (0%)    40Mi (0%)         1024Mi (8%)     47Mi (0%)
              
              #
              kubectl get pod -n istio-system -l istio.io/rev=default
              kubectl resource-capacity -n istio-system -c -u
              kubectl resource-capacity -n istio-system -c -u -a -l istio.io/rev=default
              kubectl resource-capacity -n istio-system -c -u -l istio.io/rev=default
              NODE                  POD                                     CONTAINER     CPU REQUESTS   CPU LIMITS    CPU UTIL   MEMORY REQUESTS   MEMORY LIMITS   MEMORY UTIL                                                                                                                                                      
              myk8s-control-plane   *                                       *             30m (0%)       4000m (50%)   27m (0%)   180Mi (1%)        2048Mi (17%)    164Mi (1%)
              myk8s-control-plane   istio-egressgateway-85df6b84b7-m4699    *             10m (0%)       2000m (25%)   9m (0%)    40Mi (0%)         1024Mi (8%)     49Mi (0%)
              myk8s-control-plane   istio-egressgateway-85df6b84b7-m4699    istio-proxy   10m (0%)       2000m (25%)   9m (0%)    40Mi (0%)         1024Mi (8%)     49Mi (0%)
              myk8s-control-plane   istio-ingressgateway-6bb8fb6549-k4ln6   *             10m (0%)       2000m (25%)   11m (0%)   40Mi (0%)         1024Mi (8%)     50Mi (0%)
              myk8s-control-plane   istio-ingressgateway-6bb8fb6549-k4ln6   istio-proxy   10m (0%)       2000m (25%)   11m (0%)   40Mi (0%)         1024Mi (8%)     50Mi (0%)
              myk8s-control-plane   istiod-8d74787f-cqhs2                   *             10m (0%)       0m (0%)       8m (0%)    100Mi (0%)        0Mi (0%)        66Mi (0%)
              myk8s-control-plane   istiod-8d74787f-cqhs2                   discovery     10m (0%)       0m (0%)       8m (0%)    100Mi (0%)        0Mi (0%)        66Mi (0%)

              istioinaction(서비스) 네임스페이스 리소스 capacity 조회

              istio-system (시스템) 네임스페이스 리소스 capacity 조회


              프로메테우스 CPU / 메모리 사용률 계산: 참고 -  https://themapisto.tistory.com/55



          • CPU 사용률 메트릭을 시각화하는 그래프 (예시)

            • 이 그래프는 istiod 에서 가장 일반적인 사용 패턴을 나타냄
            • 대부분의 시간이 유휴 idle 시간이다가, 서비스가 배포될 때 컴퓨팅 요청이 급증
              •  istiod가 엔보이 설정을 생성모든 워크로드로 푸시하기 때문
            • 컨트롤 플레인이 포화되면 리소스가 부족한 것이므로, 할당량을 다시 생각해야 함
              • 컨트롤 플레인 동작을 최적화하기 위해 다른 접근법을 시도해 왔다가 포화에 이르렀다면,
                리소스를 늘리는 것최선의 선택일 것

      • 트래픽: 컨트롤 플레인의 부하는 어느 정도인가? TRAFFIC: WHAT IS THE LOAD ON THE CONTROL PLANE?
        • 트래픽: 시스템이 겪는 부하를 측정
          • ex) 웹 애플리케이션에서 부하초당 요청 수 (rps) 로 정의
        • Istio 컨트롤 플레인의 트래픽 측정 
          • 수신 트래픽(설정 변경 형태)
          • 송신 트래픽(데이터 플레인으로 변경 푸시)
        • 성능을 제한하는 요인을 찾으려면 양방향 트래픽 모두 측정 필요
        • 측정치 기반하여 성능 개선 여러 방식 시도 가능
          Pilot Pushes - 푸시 빈도 / XDS Active Connections - 컨트롤 플레인이 관리하는 엔드포인트 개수.
        • 수신 트래픽에 대한 메트릭
          • pilot_inbound_updates
            • 각 istiod 인스턴스가 설정 변경을 수신한 횟수
          • pilot_push_triggers 
            • 푸시를 유발한 전체 이벤트 횟수
            • 푸시 원인은 서비스, 엔드포인트, 설정 중 하나
              • 여기서 설정이란 Gateway나 VirtualService 같은 istio 커스텀 리소스
          • pilot_services 
            • 파일럿이 인지하고 있는 서비스 개수를 측정
            • 파일럿이 인지하는 서비스 개수가 늘어날수록, 이벤트를 수신할 때 Envoy 설정을 만들어내는 데 필요한 처리가 더 많아짐
              • 따라서 istiod가 수신 트래픽 때문에 받는 부하량이 결정되는데 중요한 역할
              • avg(pilot_virt_services{app="istiod"}) # istio vs 개수: kubectl get vs -A --no-headers=true | wc -l avg(pilot_services{app="istiod"}) # k8s service 개수: kubectl get svc -A --no-headers=true | wc -l
        • 발신 트래픽에 대한 메트릭
          • pilot_xds_pushes
            • 리스너, 루트, 클러스터, 엔드포인트 업데이트와 같이 컨트롤 플레인이 수행하는 모든 유형의 푸시를 측정
            • Istio Control Plane - Pilot Pushes 에서 확인 가능

              sum(irate(pilot_xds_pushes{type="cds"}[1m]))
              sum(irate(pilot_xds_pushes{type="eds"}[1m]))
              sum(irate(pilot_xds_pushes{type="lds"}[1m]))
              sum(irate(pilot_xds_pushes{type="rds"}[1m]))


          • pilot_xds
            • 워크로드로의 전체 커넥션 개수를 파일럿 인스턴스별로 표시
            • Istio Control Plane - ADS Monitoring 에서 확인 가능
              avg(pilot_virt_services{app="istiod"})   # istio vs 개수: kubectl get vs -A --no-headers=true | wc -l
              avg(pilot_services{app="istiod"})        # k8s service 개수: kubectl get svc -A --no-headers=true | wc -l
              
              # docker exec -it myk8s-control-plane istioctl proxy-status
              pilot_xds
              pilot_xds{app="istiod"}
              sum(pilot_xds{app="istiod"})
              sum(pilot_xds{app="istiod"}) by (pod)


          • envoy_cluster_upstream_cx_tx_bytes_total
            • 네트워크로 전송된 설정 크기를 측정
            • Istio Control Plane - XDS Requests Size 패널의 Legend: XDS Request Bytes Average 로 확인 가능
              # rx
              max(rate(envoy_cluster_upstream_cx_rx_bytes_total{cluster_name="xds-grpc"}[1m]))
              quantile(0.5, rate(envoy_cluster_upstream_cx_rx_bytes_total{cluster_name="xds-grpc"}[1m]))
              
              # tx
              max(rate(envoy_cluster_upstream_cx_tx_bytes_total{cluster_name="xds-grpc"}[1m]))
              quantile(.5, rate(envoy_cluster_upstream_cx_tx_bytes_total{cluster_name="xds-grpc"}[1m]))


          • 수신 트래픽과 송신 트래픽을 구분하면, 포화의 원인과 사용할 수 있는 완화책 명확하게 식별 가능
            • 포화가 수신 트래픽 때문에 발생
              • 원인: 성능 병목은 변화율로 인함
              • 해결책: 이벤트 배치 처리 늘리기 or 스케일 업
            • 포화가 송신 트래픽 때문에 발생
              • 해결책
                • 각 파일럿이 관리하는 인스턴스가 줄어들 수 있도록 컨트롤 플레인을 스케일 아웃
                • 혹은 모든 워크로드에 대해 사이드카 리소스를 정의
      • 오류: 컨트롤 플레인의 실패율은 어떻게 되는가? ERRORS: WHAT IS THE FAILURE RATE IN THE CONTROL PLANE?
        • 오류: isiotd의 실패율 
          • 일반적으로 서비스가 포화 상태에 이르러 성능이 저하됐을 때 발생
        • Istio Control Plane - Pilot Errors 패널에 시각화됨
          # 각 쿼리 패턴에 Legend 확인
          Legend(Rejected CDS Configs) : sum(pilot_xds_cds_reject{app="istiod"}) or (absent(pilot_xds_cds_reject{app="istiod"}) - 1)
          Legend(Rejected EDS Configs) : sum(pilot_xds_eds_reject{app="istiod"}) or (absent(pilot_xds_eds_reject{app="istiod"}) - 1)
          Legend(Rejected RDS Configs) : sum(pilot_xds_rds_reject{app="istiod"}) or (absent(pilot_xds_rds_reject{app="istiod"}) - 1)
          Legend(Rejected LDS Configs) : sum(pilot_xds_lds_reject{app="istiod"}) or (absent(pilot_xds_lds_reject{app="istiod"}) - 1)
          Legend(Write Timeouts) : sum(rate(pilot_xds_write_timeout{app="istiod"}[1m]))
          Legend(Internal Errors) : sum(rate(pilot_total_xds_internal_errors{app="istiod"}[1m]))
          Legend(Config Rejection Rate) : sum(rate(pilot_total_xds_rejects{app="istiod"}[1m]))
          Legend(Push Context Errors) : sum(rate(pilot_xds_push_context_errors{app="istiod"}[1m]))
          Legend(Push Timeouts) : sum(rate(pilot_xds_write_timeout{app="istiod"}[1m]))

          메트릭 설명
          pilot_total_xds_rejects 설정 푸시 거부 횟수
          pilot_xds_’cds/lds/rds/cds’_reject pilot_total_xds_rejects 메트릭의 부분집합. 어느 API 푸시가 거부됐는지 수사망을 좁히는 데 유용함
          pilot_xds_write_timeout push를 시작할 때 발생한 오류와 타임아웃의 합계
          pilot_xds_push_context_errors 엔보이 설정을 생성하는 동안 발생한 이스티오 파일럿 오류 횟수. 주로 이스티오 파일럿의 버그와 관련

 


  • 들어가며 : 컨트롤 플레인 성능의 변수
    • 컨트롤 플레인의 성능 요인 되짚기
      • 클러스터/환경의 변화 속도
      • 리소스 할당량
      • 관리하는 워크로드 개수
      • 관리 워크로드로 푸시하는 설정 크기 
    • 위 성능 요인 중 하나라도 병목되면, 성능 개선 방안 필요 (여러 가지가 있음)
      컨트롤 플레인 성능을 개선할 수 있는 방법
    • 컨트롤 플레인 성능의 변수
      • 서비스 메시와 관련 없는 이벤트 무시하기
      • 이벤트 배치 처리 기간을 좀 더 늘려 데이터 플레인 업데이트에 필요한 푸시 횟수 줄이기 
      • 다음 방법으로 리소스 추가 할당
        • istiod Deployment 스케일 아웃하기 : 관리하는 워크로드를 파일럿 인스턴스들에 나눠 부하를 경감
        • istiod Deployment 스케일 업하기 : 엔보이 설정 생성 속도를 높이고 더 많은 푸시 요청을 동시에 처리
      • 워크로드가 관련 있는 설정을 컨트롤 플레인에게 알리는 사이드카 설정을 정의해 관련 있는 업데이트만 워크로드로 푸시하기. (2가지 이점)
        • 해당 프로세스에 필요한 최소한의 요청만을 보냄으로써 서비스 프록시에 보내는 설정 크기를 줄일 수 있음
        • 이벤트 하나로 업데이트되는 프록시 개수를 줄일 수 있음

 

    1. 워크스페이스 준비하기 : 실습 환경 준비 - 더미 워크로드와 서비스 생성
      • istiod에게 관리할 워크로드를 주기 위해 catalog 워크로드와 더미 워크로드 10개 생성
        # 실습 환경 준비 : 11.2.1 에서 이미 설정함
        kubectl -n istioinaction apply -f services/catalog/kubernetes/catalog.yaml
        kubectl -n istioinaction apply -f ch11/catalog-virtualservice.yaml
        kubectl -n istioinaction apply -f ch11/catalog-gateway.yaml
        kubectl get deploy,gw,vs -n istioinaction
        
        # 반복 설정 해두기
        while true; do curl -s http://catalog.istioinaction.io:30000/items ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
        
        # 모니터링
        while true; do kubectl top pod -n istio-system -l app=istiod --containers=true ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
        POD                     NAME        CPU(cores)   MEMORY(bytes)   
        istiod-8d74787f-cqhs2   discovery   7m           65Mi            
        2025-05-11 15:04:34
        
        POD                     NAME        CPU(cores)   MEMORY(bytes)   
        istiod-8d74787f-cqhs2   discovery   27m          82Mi            
        2025-05-11 15:04:36
        ...
        
        
        # 더미 워크로드 10개 생성
        cat ch11/sleep-dummy-workloads.yaml
        ...
        apiVersion: v1
        kind: Service
        ...
        spec:
          ports:
          - port: 80
            name: http
          selector:
            app: sleep
        ---
        apiVersion: apps/v1
        kind: Deployment
        ...
            spec:
              serviceAccountName: sleep
              containers:
              - name: sleep
                image: governmentpaas/curl-ssl
                command: ["/bin/sleep", "3650d"]
                imagePullPolicy: IfNotPresent
        ...
        
        kubectl -n istioinaction apply -f ch11/sleep-dummy-workloads.yaml
        
        
        # 확인
        kubectl get deploy,svc,pod -n istioinaction
        ...
        
        docker exec -it myk8s-control-plane istioctl proxy-status
        NAME                                                   CLUSTER        CDS        LDS        EDS        RDS          ECDS         ISTIOD                    VERSION
        catalog-6cf4b97d-5jtzt.istioinaction                   Kubernetes     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-8d74787f-cqhs2     1.17.8
        istio-egressgateway-85df6b84b7-m4699.istio-system      Kubernetes     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-8d74787f-cqhs2     1.17.8
        istio-ingressgateway-6bb8fb6549-k4ln6.istio-system     Kubernetes     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-8d74787f-cqhs2     1.17.8
        sleep-6f8cfb8c8f-2nfrm.istioinaction                   Kubernetes     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-8d74787f-cqhs2     1.17.8
        ...
        
        #
        docker exec -it myk8s-control-plane istioctl proxy-config cluster deploy/catalog.istioinaction --fqdn sleep.istioinaction.svc.cluster.local
        docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction
        10.10.0.16:80                                           HEALTHY     OK                outbound|80||sleep.istioinaction.svc.cluster.local
        10.10.0.17:80                                           HEALTHY     OK                outbound|80||sleep.istioinaction.svc.cluster.local
        10.10.0.18:80                                           HEALTHY     OK                outbound|80||sleep.istioinaction.svc.cluster.local
        10.10.0.19:80                                           HEALTHY     OK                outbound|80||sleep.istioinaction.svc.cluster.local
        ...





      • 그라파나 대시보드: Last 5 minutes, Last 15 minutes


      • 이 정도는 파일럿에게 아직도 견딜만한 상황
      • 몇 가지 더미 서비스로 Envoy 설정을 부풀려 상황을 악화시키기 : svc 200개, vs 200개, gw 200개
        # Envoy 설정을 부풀려 상황을 악화시키기 : svc 200개, vs 200개, gw 200개
        cat ch11/resources-600.yaml
        cat ch11/resources-600.yaml | wc -l
            9200
        
        # 각각 200개
        cat ch11/resources-600.yaml | grep 'kind: Service' | wc -l
        cat ch11/resources-600.yaml | grep 'kind: Gateway' | wc -l
        cat ch11/resources-600.yaml | grep 'kind: VirtualService' | wc -l
             200
        
        # 배포 : svc 200개, vs 200개, gw 200개
        kubectl -n istioinaction apply -f ch11/resources-600.yaml
        
        
        # 확인
        kubectl get deploy,svc,pod -n istioinaction
        ...
        
        # k8s service 개수 202개
        kubectl get svc -n istioinaction --no-headers=true | wc -l 
             202
        
        kubectl get gw,vs -n istioinaction
        ...
        
        #
        docker exec -it myk8s-control-plane istioctl proxy-status
        docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/catalog.istioinaction
        docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction
        docker exec -it myk8s-control-plane istioctl proxy-config cluster deploy/catalog.istioinaction
        docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction








      • 그라파나 대시보드: Last 15 minutes

        • istiod 인스턴스 하나가 인그레스 및 이그레스 게이트웨이를 포함해 워크로드(istio-proxy 동작)를 13개 관리하며, 서비스는 총 600개(svc + vs + gw) 인지하도록 만듬
        • 서비스 개수는 엔보이 설정을 만드는 데 필요한 처리량을 늘리고, 워크로드로 보내야 하는 설정을 부풀림


    2. 최적화 전 성능 측정하기 Measuring performance before optimizations Sidecar
      • 들어가며: 테스트 실행
        • 이제 테스트로 컨트롤 플레인 성능을 판단할 것
        • 테스트는 서비스를 반복적으로 만들어 부하를 생성하고, 프록시에 설정을 업데이트하는 데 걸리는 지연 시간P99 값푸시 개수를 측정

        • P99 이해하기
          • P99(또는 percentile 백분위 99)는 업데이트 전파 중 가장 빠른 99%의 최대 지연 시간을 측정
          • ex) ‘P99 지연 시간이 80ms이다’는 요청 중 99%가 80ms 보다 빠르게 전파됐음을 의미
            • 각 요청이 정확히 어떻게 분포하는지는 알지 못하며, 대부분은 수 ms 범위일 수 있음
            • 그러나 가장 빠른 99%만을 고려할 때 가장 느린 요청도 80ms안에 처리됐음을 알 수 있음

        • (첫 번째) 테스트를 10회 반복하되, 반복 사이에 2.5초 간격 두기
          (변경을 흩뿌려 배치 처리되는 상황을 피하려는 것)
          • bin/performance-test.sh : 파일 수정 해두기!
            $GATEWAY:30000/items
            #!/bin/bash
            
            main(){
              ## Pass input args for initialization
              init_args "$@"
            
              SLEEP_POD=$(kubectl -n istioinaction get pod -l app=sleep -o jsonpath={.items..metadata.name} -n istioinaction | cut -d ' ' -f 1)
            
              PRE_PUSHES=$(kubectl exec -n istio-system deploy/istiod -- curl -s localhost:15014/metrics | grep pilot_xds_pushes | awk '{total += $2} END {print total}') 
            
              if [[ -z "$PRE_PUSHES" ]]; then
                echo "Failed to query Pilot Pushes from prometheus."
                echo "Have you installed prometheus as shown in chapter 7?"
                exit 1
              fi
            
              echo "Pre Pushes: $PRE_PUSHES"
            
              INDEX="0"
              while [[ $INDEX -lt $REPS ]]; do
                SERVICE_NAME="service-`openssl rand -hex 2`-$INDEX" 
            
                create_random_resource $SERVICE_NAME &
                sleep $DELAY
                INDEX=$[$INDEX+1]
              done
            
              ## Wait until the last item is distributed
              while [[ "$(curl --max-time .5 -s -o /dev/null -H "Host: $SERVICE_NAME.istioinaction.io" -w ''%{http_code}'' $GATEWAY:30000/items)" != "200" ]]; do 
                # curl --max-time .5 -s -o /dev/null -H "Host: $SERVICE_NAME.istioinaction.io" $GATEWAY/items
                sleep .2
              done
            
              echo ==============
            
              sleep 10
            
              POST_PUSHES=$(kubectl exec -n istio-system deploy/istiod -- curl -s localhost:15014/metrics | grep pilot_xds_pushes | awk '{total += $2} END {print total}')
            
              echo
              
              LATENCY=$(kubectl -n istioinaction exec -it $SLEEP_POD -c sleep -- curl "$PROM_URL/api/v1/query" --data-urlencode "query=histogram_quantile(0.99, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))" | jq  '.. |."value"? | select(. != null) | .[1]' -r)
            
              echo "Push count:" `expr $POST_PUSHES - $PRE_PUSHES`
              echo "Latency in the last minute: `printf "%.2f\n" $LATENCY` seconds" 
            }
            
            create_random_resource() {
              SERVICE_NAME=$1
              cat <<EOF | kubectl apply -f -
            ---
            kind: Gateway
            apiVersion: networking.istio.io/v1alpha3
            metadata:
              name: $SERVICE_NAME
              namespace: $NAMESPACE
            spec:
              servers:
                - hosts:
                    - "$SERVICE_NAME.istioinaction.io"
                  port:
                    name: http
                    number: 80
                    protocol: HTTP
              selector:
                istio: ingressgateway
            ---
            apiVersion: v1
            kind: Service
            metadata:
              labels:
                app: catalog
              name: $SERVICE_NAME
              namespace: $NAMESPACE
            spec:
              ports:
              - name: http
                port: 80
                protocol: TCP
                targetPort: 3000
              selector:
                app: catalog
            ---
            apiVersion: networking.istio.io/v1alpha3
            kind: VirtualService
            metadata: 
              name: $SERVICE_NAME
              namespace: $NAMESPACE
            spec:
              hosts:
              - "$SERVICE_NAME.istioinaction.io"
              gateways:
              - "$SERVICE_NAME"
              http:
              - route:
                - destination:
                    host: $SERVICE_NAME.istioinaction.svc.cluster.local
                    port:
                      number: 80
            ---
            EOF
            }
            
            help() {
                cat <<EOF
            Poor Man's Performance Test creates Services, Gateways and VirtualServices and measures Latency and Push Count needed to distribute the updates to the data plane.
                   --reps         The number of services that will be created. E.g. --reps 20 creates services [0..19]. Default '20'
                   --delay        The time to wait prior to proceeding with another repetition. Default '0'
                   --gateway      URL of the ingress gateway. Defaults to 'localhost'
                   --namespace    Namespace in which to create the resources. Default 'istioinaction'
                   --prom-url     Prometheus URL to query metrics. Defaults to 'prom-kube-prometheus-stack-prometheus.prometheus:9090'
            EOF
                exit 1
            }
            
            init_args() {
              while [[ $# -gt 0 ]]; do
                  case ${1} in
                      --reps)
                          REPS="$2"
                          shift
                          ;;
                      --delay)
                          DELAY="$2"
                          shift
                          ;;
                      --gateway)
                          GATEWAY="$2"
                          shift
                          ;;
                      --namespace)
                          NAMESPACE="$2"
                          shift
                          ;;
                      --prom-url)
                          PROM_URL="$2"
                          shift
                          ;;
                      *)
                          help
                          ;;
                  esac
                  shift
              done
            
              [ -z "${REPS}" ] &&  REPS="20"
              [ -z "${DELAY}" ] &&  DELAY=0
              [ -z "${GATEWAY}" ] &&  GATEWAY=localhost
              [ -z "${NAMESPACE}" ] &&  NAMESPACE=istioinaction
              [ -z "${PROM_URL}" ] &&  PROM_URL="prom-kube-prometheus-stack-prometheus.prometheus.svc.cluster.local:9090"
            }
            
            main "$@"




          • 여러 개의 임의 서비스 리소스를 생성 → Istio의 xDS Push 횟수 증가량 측정, Prometheus에서 프록시 구성 수렴 시간(latency) 확인 ⇒ 최종적으로 Push 성능과 latency를 평가
            # (참고) 호출
            curl -H "Host: catalog.istioinaction.io" localhost:30000/items
            
            # 확인
            kubectl get svc -n istioinaction --no-headers=true | wc -l
            kubectl get gw -n istioinaction --no-headers=true | wc -l
            kubectl get vs -n istioinaction --no-headers=true | wc -l
            
            # :30000 포트 정보 추가해둘것!
            cat bin/performance-test.sh
            ...
            Poor Man's Performance Test creates Services, Gateways and VirtualServices and measures Latency and Push Count needed to distribute the updates to the data plane.
                   --reps         The number of services that will be created. E.g. --reps 20 creates services [0..19]. Default '20'
                   --delay        The time to wait prior to proceeding with another repetition. Default '0'
                   --gateway      URL of the ingress gateway. Defaults to 'localhost'
                   --namespace    Namespace in which to create the resources. Default 'istioinaction'
                   --prom-url     Prometheus URL to query metrics. Defaults to 'prom-kube-prometheus-stack-prometheus.prometheus:9090'
            ...
            
            # 성능 테스트 스크립트 실행!
            ./bin/performance-test.sh --reps 10 --delay 2.5 --prom-url prometheus.istio-system.svc.cluster.local:9090
            Pre Pushes: 335
            ...
            ateway.networking.istio.io/service-00a9-9 created
            service/service-00a9-9 created
            virtualservice.networking.istio.io/service-00a9-9 created
            ==============
            
            Push count: 510 # 변경 사항을 적용하기 위한 푸시 함수
            Latency in the last minute: 0.45 seconds # 마지막 1분 동안의 지연 시간
            
            
            # 확인
            kubectl get svc -n istioinaction --no-headers=true | wc -l
            kubectl get gw -n istioinaction --no-headers=true | wc -l
            kubectl get vs -n istioinaction --no-headers=true | wc -l






          • (두 번째) 딜레이 없이 실행
            # 성능 테스트 스크립트 실행 : 딜레이 없이
            ./bin/performance-test.sh --reps 10 --prom-url prometheus.istio-system.svc.cluster.local:9090
            Push count: 51
            Latency in the last minute: 0.47 seconds
            
            # 확인
            kubectl get svc -n istioinaction --no-headers=true | wc -l
            kubectl get gw -n istioinaction --no-headers=true | wc -l
            kubectl get vs -n istioinaction --no-headers=true | wc -l



            그라파나:



          • (세 번째) 딜레이 좀 더 늘려서 실행
            # 성능 테스트 스크립트 실행 : 딜레이 5초로 늘려서 실행
            ./bin/performance-test.sh --reps 10 --delay 5 --prom-url prometheus.istio-system.svc.cluster.local:9090
            Push count: 510
            Latency in the last minute: 0.43 seconds

            Push Coun: 510



          • 테스트 정리
            • (첫 번째) 테스트에 따르면, 현재 설정으로는 479회의 푸시P99 지연 시간 4.84로 수행
              Push count: 479
              Latency in the last minute: 4.84 seconds
            • (두 번째) 테스트에 따르면, 서비스 간의 간격을 없애면, 푸시 횟수와 지연 시간 모두 떨어지는 것을 볼 수 있음 - 이벤트가 배치처리되어 더 적은 작업량으로 처리되기 때문
              Push count: 206
              Latency in the last minute: 0.50 seconds
      • 사이드카를 사용해 푸시 횟수 및 설정 크기 줄이기 REDUCING CONFIGURATION SIZE AND NUMBER OF PUSHES USING SIDECARS
        • 마이크로서비스 환경에서는 한 서비스가 다른 서비스에 의존하게 되는 경우가 잦지만,
          한 서비스가 다른 모든 가용 서비스에 접근해야 하는 것도 드문 일 (아니면 적어도 이런 상황을 피하려고는 함)
        • 기본적으로 Istio는 각 서비스가 어떤 접근이 필요한지 알 수 없음
          • 기본값은 모든 서비스 프록시가 메시의 모든 워크로드를 알도록 함
          • 이로 인해 프록시의 설정이 쓸데없이 부풀려진다는 점을 알 수 있음
        • ex) catalog 워크로드의 설정 크리 계산
          # catalog pod의 설정 dump 크기 계산
          CATALOG_POD=$(kubectl -n istioinaction get pod -l app=catalog -o jsonpath={.items..metadata.name} | cut -d ' ' -f 1)
          kubectl -n istioinaction exec -ti $CATALOG_POD -c catalog -- curl -s localhost:15000/config_dump > /tmp/config_dump
          du -sh /tmp/config_dump
          1.8M    /tmp/config_dump
          
          #
          docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/catalog.istioinaction
          docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction
          docker exec -it myk8s-control-plane istioctl proxy-config cluster deploy/catalog.istioinaction
          
          #
          docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction
          docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction | wc -l
               275


          • 지금 설정 크기가 대략 2MB (엄청 많음)
            • 워크로드가 200개인 중간 클러스터만 되어도 엔보이 설정이 400MB로 늘어남
              • 이로 인해 연산 성능, 네트워크 대역폭, 메모리가 더욱 많이 필요
              • 이 설정이 모든 사이드카 프록시에 저장되기 때문
      • Sidecar 리소스 
        • 이런 문제를 해결하기 위해 Sidecar 리소스를 사용
          • 사이드카 프록시에 드나드는 트래픽의 설정을 세밀하게 조정 가능
        • 예시 리소스
          apiVersion: networking.istio.io/v1beta1
          kind: Sidecar
          metadata:
            name: default
            namespace: istioinaction
          spec:
            workloadSelector:
              labels:
                app: foo
            egress:
            -hosts:
             - "./bar.istioinaction.svc.cluster.local"
             - "istio-system/*"
            outboundTrafficPolicy:
              mode: REGISTRY_ONLY


          • workloadSelector
            • 사이드카 설정을 적용할 워크로드를 제한
          • ingress
            • 애플리케이션에 들어오는 트래픽 처리를 지정
              • 생략 시 Istio는 Pod 정의를 조회해 서비스 프록시를 자동으로 설정
          • egress
            • 사이드카를 거치는 외부 서비스로의 송신 트래픽 처리를 지정
              • 생략 시
                • 좀 더 일반적인 사이드카에서 egress 설정을 상속 (있는 경우).
                • 위 설정이 없는 경우: 다른 모든 서비스에 접근할 수 있도록 설정하는 기본 동작으로 대처
          • outboundTrafficPolicy : 송신 트래픽 처리 시 모드 지정. 
            • REGISTRY_ONLY 모드 : 워크로드가 설정한 서비스에만 트래픽을 보낼 수 있게 함
            • ALLOW_ANY 모드 : 어디로든 트래픽 송신을 허용


        • Sidecar 리소스를 워크로드에 적용하면, 컨트롤 플레인egress 필드를 사용해 워크로드가 어떤 서비스들에 접근해야 하는지 판단
          • 덕분에 컨트롤 플레인은 관련 있는 설정과 업데이트를 파악하고 해당 프록시로만 보낼 수 있음
          • 그 결과, 다른 모든 서비스에 도달하는 방법에 대한 설정을 모두 생성하고 배포하는 일을 방지해 ‘CPU, 메모리, 네트워크 대역폭 소모’를 줄일 수 있음

      • 메시 범위 사이드카 설정으로 더 나은 기본값 정의하기 DEFINING BETTER DEFAULTS WITH A MESH-WIDE SIDECAR CONFIGURATIO
        그림 출처 https://netpple.github.io/docs/istio-in-action/Istio-ch11-performance

        • 모든 서비스 프록시로 전송되는 엔보이 설정을 줄여 컨트롤 플레인 성능을 개선할 수 있는 가장 쉬운 방법
          • 트래픽 송신을 istio-system 네임스페이스의 서비스로만 허용하는 사이드카 설정을 메시 범위로 정의하는 것
          • 기본값을 이렇게 정의하면, 최소 설정으로 메시 내 모든 프록시가 컨트롤 플레인에만 연결하도록 하고 다른 서비스로의 연결 설정은 모두 삭제 가능
          • 이 방식은 서비스 소유자를 올바른 길로 유도
            • 워크로드용 사이드카 정의를 좀 더 구체적으로 정의할 수 있음
            • 서비스에 필요한 트래픽 송신을 모두 명시적으로 기술하게 함(강제 유도)으로써 워크로드가 프로세스에 필요한 관련 설정을 최소한으로 수신할 수 있게 함
        • 다음 사이드카 정의를 사용하면 설정 가능
          • 메시 내 모든 서비스 사이드카가 istio-system 네임스페이스에 있는 Istio 서비스로만 연결하도록 설정
            (메트릭을 수집할 수 있드록 프로메테우스 네임스페이스도 연결)
          • 컨트롤 플레인은 서비스 프록시가 istio-system / prometheus 네임스페이스의 서비스로 연결할 수 있는 최소한의 설정만 갖도록 업데이트됨
          • 적용 시 catalog 워크로드의 엔보이 설정 크기는 현저히 줄어야 함
            # cat ch11/sidecar-mesh-wide.yaml
            apiVersion: networking.istio.io/v1beta1
            kind: Sidecar
            metadata:
              name: default # istio-system 네임스페이스의 사이드카는 메시 전체에 적용된다.
              namespace: istio-system # 위 설명 동일.
            spec:
              egress:
              - hosts:
                - "istio-system/*" # istio-system 네임스페이스의 워크로드만 트래픽 송신을 할 수 있게 설정한다.
                - "prometheus/*"   # 프로메테우스 네임스페이스도 트래픽 송신을 할 수 있게 설정한다.
              outboundTrafficPolicy:
                mode: REGISTRY_ONLY # 모드는 사이드카에 설정한 서비스로만 트래픽 송신을 허용한다
          • 테스트를 위해 sample nginx 배포 후 테스트
            # 테스트를 위해 샘플 nginx 배포
            cat << EOF | kubectl apply -f -
            apiVersion: apps/v1
            kind: Deployment
            metadata:
              name: nginx
              labels:
                app: nginx
            spec:
              replicas: 1
              selector:
                matchLabels:
                  app: nginx
              template:
                metadata:
                  labels:
                    app: nginx
                spec:
                  containers:
                  - name: nginx
                    image: nginx:alpine
                    ports:
                    - containerPort: 80
            ---
            apiVersion: v1
            kind: Service
            metadata:
              name: nginx
            spec:
              selector:
                app: nginx
              ports:
              - protocol: TCP
                port: 80
                targetPort: 80
              type: ClusterIP
            EOF
            
            # catalog 에서 nginx 서비스 접속 확인
            docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction | grep nginx
            docker exec -it myk8s-control-plane istioctl proxy-config cluster deploy/catalog.istioinaction | grep nginx                                        
            docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction | grep nginx
            10.10.0.26:80                                           HEALTHY     OK                outbound|80||nginx.default.svc.cluster.local
            
            kubectl exec -it deploy/catalog -n istioinaction -- curl nginx.default | grep title
            <title>Welcome to nginx!</title>
            
            
            # istio-system, prometheus 네임스페이스만 egress 허용 설정
            kubectl -n istio-system apply -f ch11/sidecar-mesh-wide.yaml
            kubectl get sidecars -A
            
            # catalog 에서 nginx 서비스 접속 확인
            docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction | grep nginx
            docker exec -it myk8s-control-plane istioctl proxy-config cluster deploy/catalog.istioinaction | grep nginx                                        
            docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction | grep nginx
            kubectl exec -it deploy/catalog -n istioinaction -- curl nginx.default | grep title
            
            # envoy config 크기 다시 확인!
            CATALOG_POD=$(kubectl -n istioinaction get pod -l app=catalog -o jsonpath={.items..metadata.name} | cut -d ' ' -f 1)
            kubectl -n istioinaction exec -ti $CATALOG_POD -c catalog -- curl -s localhost:15000/config_dump > /tmp/config_dump
            du -sh /tmp/config_dump
            520K    /tmp/config_dump
             
            sample nginx 배포 및 catalog 에서 접근 시 접근됨
            istio-system, prometheus 네임스페이스만 egress 설정 허용 후: catalog에서 nginx 접근 불가 / envoy config 크기 464k로 감소


          • 설정 크기가 2MB에서 464KB로 대폭 감소
            • 이점은 그것뿐이 아님 - 이제부터 컨트롤 플레인은 푸시를 더 적게 할 것
          • 성능 테스트로 확인
            # 성능 테스트 스크립트 실행!
            ./bin/performance-test.sh --reps 10 --delay 2.5 --prom-url prometheus.istio-system.svc.cluster.local:9090
            ...
            Push count: 88 # 변경 사항을 적용하기 위한 푸시 함수
            Latency in the last minute: 0.10 seconds # 마지막 1분 동안의 지연 시간
            
            # 확인
            kubectl get svc -n istioinaction --no-headers=true | wc -l
            kubectl get gw -n istioinaction --no-headers=true | wc -l
            kubectl get vs -n istioinaction --no-headers=true | wc -l

            Push count: 83 / Latency 0.95 로 감소

            • 예상대로 푸시 횟수와 지연 시간 모두 감소
              • 이 성능 향상은 메시 범위 Sidecar 리소스를 정의하는 것이 얼마나 중요한지 나타냄
              • 메시의 운영 비용을 절감
              • 성능을개선
              • 플랫폼의 사용자(테넌트 tenant)들에게 워크로드에 송신 트래픽을 명시적으로 정의하는 좋은 습관을 심어줄 수 있음
            • 기존 클러스터에서는 서비스 중단을 방지하기 위해 플랫폼의 사용자들과 신중히 협의해야 함
              • 구체적으로는 그들이 좀 더 구체적인 Sidecar 리소스로 워크로드의 송신 트래픽을 먼저 정의하도록 해야 함
              • 송신 트래픽 설정 후 메시 범위에 디폴트 사이드카 설정을 적용 가능
              • 항상 스테이징 환경에서 변경 사항 테스트 필요
            • 사이드카 설정 범위(scope)
              • mesh-wide 메시 범위 사이드카
                • 메시 내 모든 워크로드에 적용돼 기본값을 정의할 수 있음
                • 다른 규칙을 명시적으로 지정하지 않는 한 트래픽 송신을 제한하는 식
                • 메시 범위 사이드카 설정을 만들려면, Istio를 설치한 네임스페이스(우리의 경우 istio-system)에 적용하면 됨
                • 메시 범위 사이드카의 이름 컨벤션은 default
              • namespace-wide 네임스페이스 범위 사이드카
                • 메시 범위보다 좀 더 구체적
                • 메시 범위 설정을 덮어 씀
                • 네임스페이스 범위 사이드카 설정을 만들려면, workloadSelector 필드를 정의하지 않고 원하는 네임스페이스에 적용해야 함
                • 네임스페이스 범위 사이드카의 이름 컨벤션은 default
              • workload-specific 워크로드별 사이드카
                • workloadSelector 속성에 부합하는 특정 워크로드를 목표로 함
                • 가장 구체적인 설정
                • 메시 범위와 네임스페이스 범위 설정 모두를 덮어 씀
            • 정책 관리 - https://youtu.be/4sJd6PIkP_s?si=xEMYgy7YC5TsOAJg&t=466
    3. 이벤트 무시하기: 디스커버리 셀렉터로 디스커버리 범위 줄이기 meshConfig.discoverySelectors
      • Istio 컨트롤 플레인은 기본적으로 K8S 모든 네임스페이스의 파드, 서비스와 기타 리소스의 생성 이벤트를 감지할 수 있음
        • 대규모 클러스터에서 이런 동작은 컨트롤 플레인에 부담을 줄 수 있음
        • 데이터 플레인을 최신 상태로 유지하기 위해 모든 이벤트마다 엔보이 설정을 처리하고 생성하기 때문
      • Istio 1.10에는 네임스페이스 디스커버리 셀렉터 discovery selector 기능 추가
        • 컨트롤 플레인 부담을 줄이기 위해 생긴 기능
        • istiod 컨트롤 플레인이 감시할 이벤트를 세밀하게 조정할 수 있음
        • 워크로드와 엔드포인트를 감시할 네임스페이스를 명시적으로 지정할 수 있음
        • 네임스페이스 셀렉터 접근법을 사용하면 동적으로 네임스페이스와 해당 네임스페이스의 각 워크로드를 포함하거나, 메시에서 처리하지 않도록 제외할 수 있음
        • K8S 클러스터에 메시 내 워크로드가 절대 라우팅하지 않을 워크로드가 많거나, 계속 변하는 워크로드가 있는 경우(스파크 잡 spark job 이 계속 생성되고 종료되는 등) 특히 유용
          • 이 경우 컨트롤 플레인이 이 워크로드들에서 만들어지는 이벤트를 무시하게 만들고 싶을 것
        • IstioOperator 파일을 사용해 시작 시 디스커버리 셀렉터 기능을 활성화 가능
          apiVersion: install.istio.io/v1alpha1
          kind: IstioOperator
          metadata:
            namespace: istio-system
          spec:
            meshConfig:
              discoverySelectors: # 디스커버리 셀렉터 활성화
                - matchLabels:
                    istio-discovery: enabled # 사용할 레이블 지정
          • 컨트롤 플레인이 처리할 네임스페이스istio-discovery: enabled 레이블이 있는 것으로 한정
            (네임스페이스에 이 레이블이 없으면 무시)
        • 네임스페이스 대부분을 포함하고 소규모만 제외하려는 경우: 레이블 비교 표현식을 사용
          apiVersion: install.istio.io/v1alpha1
          kind: IstioOperator
          metadata:
            namespace: istio-system
          spec:
            meshConfig:
              discoverySelectors:
                - matchExpressions:
                  - key: istio-exclude
                    operator: NotIn
                    values:
                      - "true"
          • (참고) discoverySelectors - Docs
            • Istio가 사이드카의 구성 업데이트를 계산할 때 고려하는 네임스페이스 세트를 지정하는 Kubernetes selector들의 목록

        • istio-exclude: true 레이블이 있는 네임스페이스제외하도록 업데이트
          (모든 항목을 살피는 기존 동작을 방해하지 않음)
          #
          cat ch11/istio-discovery-selector.yaml
          
          #
          docker exec -it myk8s-control-plane cat /istiobook/ch11/istio-discovery-selector.yaml
          docker exec -it myk8s-control-plane istioctl install -y -f /istiobook/ch11/istio-discovery-selector.yaml
          
          #
          kubectl get istiooperators.install.istio.io -A -o json
          ...
                          "meshConfig": {
                              "accessLogEncoding": "JSON",
                              "accessLogFile": "/dev/stdout",
                              "defaultConfig": {
                                  "proxyMetadata": {}
                              },
                              "discoverySelectors": [
                                  {
                                      "matchExpressions": [
                                          {
                                              "key": "istio-exclude",
                                              "operator": "NotIn",
                                              "values": [
                                                  "true"
          ...





          • 간단 실습 해보기
            #
            kubectl create ns new-ns
            kubectl label namespace new-ns istio-injection=enabled
            kubectl get ns --show-labels
            
            # 테스트를 위해 샘플 nginx 배포
            cat << EOF | kubectl apply -n new-ns -f -
            apiVersion: apps/v1
            kind: Deployment
            metadata:
              name: nginx
              labels:
                app: nginx
            spec:
              replicas: 1
              selector:
                matchLabels:
                  app: nginx
              template:
                metadata:
                  labels:
                    app: nginx
                spec:
                  containers:
                  - name: nginx
                    image: nginx:alpine
                    ports:
                    - containerPort: 80
            ---
            apiVersion: v1
            kind: Service
            metadata:
              name: nginx
            spec:
              selector:
                app: nginx
              ports:
              - protocol: TCP
                port: 80
                targetPort: 80
              type: ClusterIP
            EOF
            
            # 확인
            kubectl get deploy,svc,pod -n new-ns
            docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep nginx
            10.10.0.26:80                                           HEALTHY     OK                outbound|80||nginx.default.svc.cluster.local
            10.10.0.27:80                                           HEALTHY     OK                outbound|80||nginx.new-ns.svc.cluster.local
            
            # 설정
            kubectl label ns new-ns istio-exclude=true
            kubectl get ns --show-labels
            
            # 다시 확인
            docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep nginx
            10.10.0.26:80                                           HEALTHY     OK                outbound|80||nginx.default.svc.cluster.local



            • discoverySelectors를 사용해 디스커버리 범위를 관련 있는 네임스페이스로 줄였는데도 컨트롤 플레인이 여전히 포화 상태인 경우
              • 각 이벤트를 개별적으로 해결하는 대신 이벤트를 배치 처리해 묶음으로 해결하는 것을 고려
    4. 이벤트 배치 처리 및 푸시 스로틀링 속성 Event-batching and push-throttling properties
      • 들어가며: 디바운스 기반
        • 데이터 플레인 설정을 바꾸는 런타임 환경 이벤트는 보통 운영자가 제어할 수 없는 것
        • 컨트롤 플레인이 감지해 데이터 플레인 프록시를 조정하는 이벤트 (기본적)
          • 새로운 서비스가 온라인 상태가 되는 것
          • 복제본 스케일 아웃
          • 서비스가 비정상이 되는 것 같은 이벤트
        • 운영자가 (어느 정도) 제어할 수 있는 이벤트
          • 업데이트를 얼마나 지연해서 배치 처리할 지
            • 배치 처리하면, 이벤트를 한 묶음으로 처리함으로써 엔보이 설정을 한 번만 만들어 데이터 플레인 프록시로 한 번에 푸시할 수 있다는 이점이 있음
        • 이벤트 수신이 서비스 프록시로 변경 사항을 푸시하는 작업지연시키는 방법(디바운스)
          • 디바운스 기반을 더 늘리면?
            • 지연 기간에서 제외됐던 마지막 이벤트도 배치에 포함시켜 모든 이벤트를 하나의 배치로 합침으로써 하나의 요청으로 푸시할 수 있게 됨
              이벤트가 어떻게 병합돼 푸시되는지를 보여주는 순서도
          • 반대로 기간을 줄이면?
            • 업데이트가 더 빠르게 수행되는 것이 보장됨
            • 그러나 그렇게 하면 컨트롤 플레인이 미처 배포할 수 없을 정도로 푸시 요청이 많아짐
            • 푸시 요청들은 푸시 대기열에서 스로틀링돼 대기 시간 증가로 이어짐

      • 배치 기간과 푸시 스로틀링을 정의하는 환경 변수 ENVIRONMENT VARIABLES THAT DEFINE THE BATCHING PERIOD AND PUSH THROTTLING
        • 배치 기간을 정의하는 환경 변수 - Docs
          • PILOT_DEBOUNCE_AFTER
            • 이벤트를 푸시 대기열에 추가하는 디바운스할 시간을 지정
            • 기본값은 100ms
              • 컨트롤 플레인이 이벤트를 받았을 때 푸시 대기열에 추가하는 행동을 100ms 디바운스한다는 것을 의미
            • 이 기간 동안에 추가로 발생하는 이벤트는 앞서 발생한 이벤트에 통합돼 작업이 다시 디바운스
            • 이 기간 동안 이벤트가 발생하지 않으면, 결과 배치가 푸시 대기열에 추가돼 처리할 준비가 됨
            • ex) 100ms (기본값) 이내에 새로운 이벤트가 없으면 queue에 추가하고, 있으면 merge 후 다시 100ms 동안 대기 + 단, 최대 PILOT_DEBOUNCE_MAX 이내에서 허용
          • PILOT_DEBOUNCE_MAX
            • 이벤트 디바운스를 허용할 최대 간을 지정
            • 이 시간이 지나면 현재 병합된 이벤트가 푸시 대기열에 추가됨
            • 기본값은 10초
          • PILOT_ENABLE_EDS_DEBOUNCE
            • 엔드포인트 업데이트가 디바운스 규칙을 준수할지, 우선권을 줘 푸시 대기열에 즉시 배치할지를 지정
            • 기본값은 true
              • 엔드포인트 업데이트도 디바운스된다는 의미
          • PILOT_PUSH_THROTTLE
            • istiod가 동시에 처리하는 푸시 요청 개수를 지정
            • 기본값은 100개의 동시 푸시
            • CPU 사용률이 낮은 경우, 스로틀 값을 높여서 업데이트를 더 빠르게 할 수 있다.

        • 배치 기간 환경 변수 사용 시의 일반적인 지침 general guidance
          • 컨트롤 플레인이 포화 상태이고 수신 트래픽이 성능 병목을 야기하는 경우: 이벤트 배치 처리 증가시키기
          • 업데이트 전파를 더 빠르게 하는 것이 목표일 때: 이벤트 배치 처리를 줄이고 동시에 푸시하는 개수를 증가시킴
            (단, 이 방식은 컨트롤 플레인이 포화 상태가 아닐 때만 권장)
          • 컨트롤 플레인이 포화 상태이고 송신 트래픽이 성능 병목인 경우: 동시에 푸시하는 개수를 감소시키기
          • 컨트롤 플레인이 포화 상태가 아니거나, 스케일 업을 했고 빠른 업데이트를 원하는 경우: 동시에 푸시하는 개수를 증가시키기



      • 배치 기간 늘리기 INCREASING THE BATCHING PERIOD
        • 배치의 효과를 보여주기 위해 PILOT_DEBOUNCE_AFTER 값을 말도 안 되게 높은 값인 2.5초로 지정하기 (기본값은 100ms == 0.1초)  - 운영에서는 이러한 설정은 적절하지 않음!!
          # myk8s-control-plane 진입 후 설치 진행
          docker exec -it myk8s-control-plane bash
          -----------------------------------
          # demo 프로파일 컨트롤 플레인 배포 시 적용
          istioctl install --set profile=demo --set values.pilot.env.PILOT_DEBOUNCE_AFTER="2500ms" --set values.global.proxy.privileged=true --set meshConfig.accessLogEncoding=JSON -y
          exit
          -----------------------------------
          
          #
          kubectl get deploy/istiod -n istio-system -o yaml
          ...
                  - name: PILOT_DEBOUNCE_AFTER
                    value: 2500ms
          ...
          
          # 성능 테스트 스크립트 실행!
          ./bin/performance-test.sh --reps 10 --delay 2.5 --prom-url prometheus.istio-system.svc.cluster.local:9090
          Push count: 28 # 변경 사항을 적용하기 위한 푸시 함수
          Latency in the last minute: 0.10 seconds # 마지막 1분 동안의 지연 시간





          • PILOT_DEBOUNCE_MAX 로 정의한 한계값을 넘지 않는 한 모든 이벤트는 병합돼 푸시 큐에 더해짐
            • 덕분에 푸시 횟수가 현저히 줄어듬(28회)
          • 엔보이 설정을 만들고 워크로드로 푸시하는 추가 작업을 모두 피해 CPU 사용률과 네트워크 대역폭 소모가 줄어듬

      • 지연 시간 메트릭은 디바운스 기간을 고려하지 않는다! LATENCY METRICS DO NOT ACCOUNT FOR THE DEBOUNCE PERIOD!
        최신 설정을 워크로드에 푸시하는 작업의 순서

        • 디바운스 기간을 늘린 후 지연 시간 메트릭에 푸시 배포가 10ms 걸린 것으로 나타났지만, 실제는 그렇지 않음
          • 지연 시간 메트릭이 측정하는 기간은 푸시 요청이 푸시 대기열에 추가된 시점부터 시작됨
          • 즉, 이벤트들이 디바운드되는 동안 업데이트는 전달되지 않음
        • 따라서 업데이트를 푸시하는 시간은 늘어났지만, 이는 지연 시간 메트릭에서는 나타나지 않게 됨! 
          • 이렇게 이벤트를 너무 오래 디바운스해 지연 시간이 늘어나면 성능이 낮을 때와 마찬가지로 설정이 낡게(오래되게)
          • 따라서 배치 속성을 조정할 때는 한 번에 너무 크게 변경하는 것보다는 조금씩 변경하는 것이 좋음
            • 데이터 플레인은 보통 늦은 엔드포인트 업데이트에 영향을 받음
            • 환경 변수 PILOT_ENABLE_EDS_DEBOUNCE false로 설정 시 
              엔드포인트 업데이트 디바운스 기간을 건너뛰어 지연되지 않음을 보장할 수 있음


      • 컨트롤 플레인에 리소스 추가 할당하기 ALLOCATING ADDITIONAL RESOURCES TO THE CONTROL PLAN
        • Sidecar 리소스를 정의하고 discovery selectors를 사용하고 배치를 설정한 후 성능 향상시키기
          • 컨트롤 플레인에 리소스를 더 할당하는 것이 유일한 방법
        • 컨트롤 플레인에 리소스를 더 할당하는 방법
          • istiod 인스턴스를 추가해 스케일 아웃
          • 모든 istiod 인스턴스에 리소스를 추가로 제공해 스케일 업
        • 성능 병목 원인에 따라  스케일 인/아웃 여부 결정됨
          • 송신 트래픽이 병목일 때는 스케일 아웃
            • istiod 인스턴스당 관리하는 워크로드가 많을 때만 일어남
            • 스케일 아웃은 istiod 인스턴스가 관리하는 워크로드 개수를 감소시킴
          • 수신 트래픽이 병목일 때는 스케일 업
            • Envoy 설정을 생성하는 데 리소스(Service, VS, DR 등)을 많이 처리할 때만 일어남
            • 스케일 업하면 istiod 인스턴스에 처리 능력을 더 제공

        • 복제본 2 스케일 아웃과 리소스 스케일 업
          #
          kubectl get pod -n istio-system -l app=istiod
          kubectl describe pod -n istio-system -l app=istiod
          ...
              Requests:
                cpu:      10m
                memory:   100Mi
          ...
          
          kubectl resource-capacity -n istio-system -u -l app=istiod
          NODE                  CPU REQUESTS   CPU LIMITS   CPU UTIL   MEMORY REQUESTS   MEMORY LIMITS   MEMORY UTIL
          myk8s-control-plane   10m (0%)       0m (0%)      8m (0%)    100Mi (0%)        0Mi (0%)        90Mi (0%)
          
          
          # myk8s-control-plane 진입 후 설치 진행
          docker exec -it myk8s-control-plane bash
          -----------------------------------
          # demo 프로파일 컨트롤 플레인 배포 시 적용
          istioctl install --set profile=demo \
          --set values.pilot.resources.requests.cpu=1000m \
          --set values.pilot.resources.requests.memory=1Gi \
          --set values.pilot.replicaCount=2 -y
          
          exit
          -----------------------------------
          
          #
          kubectl get pod -n istio-system -l app=istiod
          NAME                      READY   STATUS    RESTARTS   AGE
          istiod-5485dd8c48-6ngdc   1/1     Running   0          11s
          istiod-5485dd8c48-chjsz   1/1     Running   0          11s
          
          kubectl resource-capacity -n istio-system -u -l app=istiod
          NODE                  CPU REQUESTS   CPU LIMITS   CPU UTIL    MEMORY REQUESTS   MEMORY LIMITS   MEMORY UTIL
          myk8s-control-plane   2000m (25%)    0m (0%)      119m (1%)   2048Mi (17%)      0Mi (0%)        107Mi (0%)
          
          kubectl describe pod -n istio-system -l app=istiod
          ...
              Requests:
                cpu:      1
                memory:   1Gi
          ...
        • 컨트롤 플레인 성능 최적화의 요점
          • 항상 워크로드에 사이드카 설정을 정의해야 함 - 이것만으로도 대부분의 이점을 얻을 수 있음
          • 컨트롤 플레인이 포화 상태인데 이미 리소스를 많이 할당한 경우에만 이벤트 배치를 수정하기
          • 병목이 송신 트래픽일 때 istiod 스케일 아웃
          • 병목이 수신 트래픽일 때 istiod 스케일 업


      • Istiod Deployment 오토스케일링 Autoscaling istiod deployment
        • 오토스케일링은 일반적으로 리소스 소모를 최적화할 수 있음
          • Isito 컨트롤 플레인과 같이 부하가 급증할 수 있는 워크로드의 경우에는 특히 최적화 가능
        • 그러나 현재로서는 istiod에 효과적이지 않음
          • istiod가 워크로드와 30분 커넥션을 시작하기 때문
            • 이 커넥션은 ADS로 프록시를 설정하고 업데이트하는 데 사용하는 것
          • (참고) MaxServerConnectionAge by GPT - Github
            • MaxServerConnectionAge
              • Istio에서 Envoy 프록시가 오래된 연결을 자동으로 종료시키도록 설정하는 값
              • gRPC Keepalive 정책에 가까운 기능으로, 연결의 수명 최대치를 정의
            • Sidecar ProxyConfig 설정에 적용
              apiVersion: networking.istio.io/v1alpha3
              kind: ProxyConfig
              metadata:
                name: example-proxy-config
                namespace: istio-system
              spec:
                proxyMetadata:
                  ISTIO_META_PROXY_CONFIG: |
                    concurrency: 2
                    terminationDrainDuration: 5s
                    tracing:
                      sampling: 100
                    connectionPool:
                      tcp:
                        maxServerConnectionAge: 30m
              • 이 설정은 Envoy가 수신한 TCP 연결을 최대 30분까지만 유지하도록 제한
              • 그 후에는 연결을 graceful 하게 종료하여 새 연결이 만들어지게 유도
            • DestinationRule에서 설정
              apiVersion: networking.istio.io/v1alpha3
              kind: DestinationRule
              metadata:
                name: my-service-dr
                namespace: my-namespace
              spec:
                host: my-service.my-namespace.svc.cluster.local
                trafficPolicy:
                  connectionPool:
                    tcp:
                      maxServerConnectionAge: 300s  # 연결 최대 수명 300초 (5분)
            • 왜 사용?
              • L7 프록시의 연결 수를 주기적으로 리셋해 리소스 누수 방지
              • 장시간 열린 연결로 인한 부하 집중을 해소
              • 로드밸런서/업스트림 변경 시 새 연결 유도
              • 가비지 커넥션, idle 상태 유지 문제를 방지
        • 따라서 새로이 생성된 istiod 복제본은 서비스 프록시와 종전 파일럿 사이의 커넥션이 만료될 때까지는 아무런 부하를 받지 않음
          • 아무런 부하를 받지 않으니 새 istiod 복제본은 축소됨
          • 신규 istiod 복제본 축소로 인해, Deployment가 반복적으로 확장됐다가 축소되는 퍼덕거림(flapping)이 일어남
            flapping
          • 현재로서 오토스케일링을 구성하는 가장 좋은 방법:
            • 점진적인 부하 증가에 맞추는 것 (며칠, 몇주, 심지어는 몇 달 단위에 걸쳐서 맞춰야 함)
            • 성능을 지속적으로 모니터링하고 Deployment 스케일링 결정을 내려야 하는 인적 자원의 부담을 줄일 수 있음
          • ‘2025 Optimise Your Mesh With Istio Sidecar - 사이드카 적용 경험 공유(개발팀 협업) - Youtube

  • 들어가기
    • 성능을 튜닝하기 전에 Istio는 성능이 정말 좋다는 것을 명심해야 함
    • Istio 팀은 다음과 같은 파라미터로 모든 릴리스를 테스트함
      • 엔보이 설정을 부풀리는 쿠버네티스 서비스 1,000개 Kubernetes services that bloat the Envoy configuration
      • 동기화해야 하는 워크로드 2,000개 workloads that need to be synchronized
      • 서비스 메시 전체에서 초당 요청 70,000개 requests per second in the entire service mesh
    • 이 정도 부하로도 메시 전체를 동기화하는 Istio 파일럿 인스턴스 하나가 겨우 가상 코어 하나와 메모리 1.5GB만을 사용
    • 대부분의 운영 환경 클러스터에는 복제본 셋에 vCPU 2개와 2GB 정도쯤되는 적당한 할당으로도 충분


  • 컨트롤 플레인 성능 튜닝 가이드라인 Performance tuning guidelines
    • 성능 문제인지 확인
      • 데이터 플레인에서 컨트롤 플레인으로 연결이 제대로 이뤄지고 있는가?
      • 플랫폼 문제인가? 이를테면 쿠버네티스에서 API 서버가 정상인가?
      • 변경 범위를 지정하도록 Sidecar 리소스를 정의했는가?
    • 성능 병목 지점을 파악 - 수집된 지연 시간, 포화도, 트래픽에 대한 메트릭을 사용해 튜닝 결정
      • 컨트롤 플레인이 포화 상태도 아닌데 지연 시간이 증가하면 리소스가 최적으로 활용되지 않고 있다는 것을 나타낸다.
        • 더 많은 푸시가 동시에 처리되도록 동시 푸시 임계값을 늘릴 수 있다.
      • 사용률은 낮지만 부하가 걸렸을 때 빠르게 포화 상태가 되면 변경 사항이 매우 폭발적임을 나타낸다.
        • 즉, 변경 사항이 없는 기간이 길다가 짧은 시간에 이벤트가 급증하는 것이다.
        • 이스티오 파일럿의 복제본 수를 늘리거나, 업데이트를 미룰 여지가 있는 경우 배치 속성을 조정한다.
      • 변경은 점진적으로 수행하자. 병목을 파악한 후 점진적으로 변경하자.
        • 예를 들어, 컨트롤 플레인이 긴 시간 동안 계속해서 이벤트를 받는 경우에는 디바운스 기간을 두배, 심지어는 네 배로 늘리고 싶은 유혹이 있을 수 있다.
        • 하지만 그렇게 하면 데이터 플레인이 낡기 쉽다. 대신 설정을 10~30% 범위에서 늘리거나 줄이는 등 조금만 바꾸자.
        • 그런 다음, 며칠 동안 이점(또는 성능 저하)를 지켜보고 새로운 데이터를 바탕으로 결정을 내리자.
      • 안전은 최우선으로 생각하자.
        • 이스티오 파일럿은 메시 전체의 네트워크를 관리하므로, 다운타임은 중단으로 이어지기 십상이다.
        • 컨트롤 플레인에 할당하는 리소스는 항상 관대하게 잡고, 복제본을 절대 2개 밑으로 내리지 말자.
        • 또한 안전을 최우선으로 생각하자.
      • 버스트 가능한 burstable 가상머신을 사용하는 것을 고려하자.
        • 이스티오 파일럿은 CPU 리소스가 계속 필요하지 않으므로 버스트성 성능 요구 사항이 있다.

 


  • 멈춰버린 세계: 네트워크 통신 불가를 해결하기 위한 여정(feat. Istio) | 2024 당근 테크 밋업 : istio-proxy 에 CPU Limit 제거하기 - Link




  • Isito 환경에서 MSA 서비스 간 호출시 Connection Reset 이슈 해결 사례 : tcp max_connect_attempts 1 → 5 시도 증가 - Blog
    {{- if .Capabilities.APIVersions.Has "networking.istio.io/v1alpha3/EnvoyFilter" }}
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: max-connect-attempts
      namespace: istio-system
    spec:
      configPatches:
      - applyTo: NETWORK_FILTER
        match:
          listener:
            filterChain:
              filter:
                name: envoy.filters.network.tcp_proxy
        patch:
          operation: MERGE
          value:
            name: envoy.filters.network.tcp_proxy
            typed_config:
              '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
              max_connect_attempts: 5
    {{- end }}
  • Istio 도입시 겪었던 Error : holdApplicationUntilProxyStarts: true - Blog
    • Pod 시작시 Network Error ⇒ holdApplicationUntilProxyStarts: true 설정 - Docs
    • Pod 종료시 Network Error ⇒ terminationDrainDuration 설정 혹은 EXIT_ON_ZERO_ACTIVE_CONNECTIONS 설정
      •  terminationDrainDuration 
        • he amount of time allowed for connections to complete on proxy shutdown. On receiving SIGTERM or SIGINTistio-agent tells the active Envoy to start gracefully draining, discouraging any new connections and allowing existing connections to complete. It then sleeps for the terminationDrainDuration and then kills any remaining active Envoy processes. If not set, a default of 5s will be applied.
      • EXIT_ON_ZERO_ACTIVE_CONNECTIONS
        • When set to true, terminates proxy when number of active connections become zero during draining
    • Pod가 종료될 때 커넥션이 비정상적으로 종료되는 경우 : EXIT_ON_ZERO_ACTIVE_CONNECTIONS - Blog
    • Istio를 통한 header기반 API 라우팅/호출 시 cors preflight request 이슈 트러블슈팅 : http.corsPolicy - Blog
    • Istio xDS로 인한 connection 끊김 이슈 : excludeEgressPorts - Blog