스터디/Istio

Istio 스터디 4주차 - <Observability 시각화>

황동리 2025. 5. 3. 19:39
반응형

CloudNet@ 가시다님이 진행하는 Istio 스터디 1기 - 4주차 정리 내용 입니다.

 

앞서 진행했던 주차를 보고 오시면 이해가 빠릅니다.

 

이번 글에서는 Istio의 Observability(관찰 가능성) 을 3가지 방법으로 살펴보겠습니다.

  • Grafana를 사용하여 메트릭 시각화
  • Jaeger를 사용하여 분산 트레이싱 계측
  • kiail를 사용하여 네트워크 호출 그래프 시각화

🖥️ Grafana를 사용하여 메트릭 시각화

우린 앞서 4주차 Observability 첫번째 글에서 실습환경을 구성해놓았습니다.

 

먼저 Grafana 대시보드에 접속 해줍니다.

ID: admin
PW: prom-operator

 

그리고 메트릭 확인을 위한 반복 호출문 작동

🔧 반복 호출문
$ while true; do curl -s http://webapp.istioinaction.io:30000/api/catalog ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done

🔧 대시보드 추가 설정 및 공개 대시보드 검색
$ cd ch8
$ kubectl -n prometheus create cm istio-dashboards \
--from-file=pilot-dashboard.json=dashboards/\
pilot-dashboard.json \
--from-file=istio-workload-dashboard.json=dashboards/\
istio-workload-dashboard.json \
--from-file=istio-service-dashboard.json=dashboards/\
istio-service-dashboard.json \
--from-file=istio-performance-dashboard.json=dashboards/\
istio-performance-dashboard.json \
--from-file=istio-mesh-dashboard.json=dashboards/\
istio-mesh-dashboard.json \
--from-file=istio-extension-dashboard.json=dashboards/\
istio-extension-dashboard.json
$ cd ..

🔧 확인
$ kubectl describe cm -n prometheus  istio-dashboards

🔧 Grafana Operator가 configmap(istio-dashboards)을 마운트(인식) 하도록 레이블 지정
$ kubectl label -n prometheus cm istio-dashboards grafana_dashboard=1

 

Grafana 접속해서 Dashboard 보면 아래와 같이 Istio Dashboard가 생성됩니다.

 

먼저 Control-Plane 대시보드를 살펴보겠습니다.

  • Pilot Version 확인
  • CPU, Memory, Disk, Goroutiones 리소스 사용량 확인
  • Pilot Push Infomation
    - Pilot Pushes: Pilot이 Envoy 프록시에게 전달한 xDS 리소스 수
    - Pilot Errors: Push 중 발생한 에러 수
    - Proxy Push Time: 프록시에게 Config를 푸시할 떄 걸린 시간
    - Confilcts: config 충돌 여부

저희는 앞서 컨트롤 플레인의 메트릭을 긁어가도록 ServiceMonitor 리소스를 설정해두었습니다.

 

그리고 Pilot Pushes -> Explore 로 그래프를 만들어내는 데 사용되는 원본 쿼리를 살펴보겠습니다.

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

 

해당 쿼리를 프로메테우스에서 확인해보겠습니다.

 

pilot_xds_pushes는 Istio 컨트롤 플레인(Pilot)이 Envoy 프록시에게 xDS 리소스를 푸시한 횟수를 의미합니다.

 

그래서 Prometheus 그래프에서 보면, 각 xDS 타입 별로 Push를 진행했던 횟수가 나옵니다.

 

이제 Data-Plane 메트릭을 살펴보겠습니다.

 

Grafana Dashboard에 접속해서 Istio Service Dashboard를 선택해줍니다.

 

빨간색 박스에서 원하는 서비스를 선택하여 살펴 볼 수 있습니다.

 

이제 분산 트레이싱에 대해 알아보겠습니다.

🖥️ Jaeger를 이용하여 분산 트레이싱 해보기

먼저 분산 트레이싱이 왜 필요한지 알아보겠습니다.

📌 분산 트레이싱이란?

하나의 요청이 여러 서비스에 걸쳐 처리될 때, 그 전체 흐름을 추적하는 기술입니다.

즉, 사용자가 어떤 요청을 보냈을 때,

  • 어떤 서비스들이 호출되었고
  • 각 서비스에서의 처리 시간은 얼마였고
  • 어디에서 병목이 생겼는지 등을 시각적으로 보여줍니다.

📌 분산 트레이싱이 필요한 이유

✅ 마이크로서비스 환경의 복잡성 해결

  • 하나의 요청이 여러 마이크로서비스를 거치며 처리됨

예: 프론트엔드 → API Gateway → 인증 서비스 → 결제 서비스 → DB

✅ 병목 및 오류 지점 식별

  • 어느 구간에서 요청이 지연되는지 확인 가능
  • 오류가 발생한 서비스나 호출 경로를 정확히 파악 가능

✅ 성능 개선

  • 느린 서비스 탐지 → 성능 튜닝 포인트 확보

✅ 서비스 간 의존성 파악

  • 서비스 호출 관계를 시각적으로 파악 → 아키텍처 분석에 유리

 

이제 분산 트레이싱은 어떻게 동작하는지 알아보겠습니다.

 

분산 트레이싱이 동작하는 방식 은 기본적으로 요청에 대한 고유한 추적 정보를 서비스 간에 전파하고, 각 서비스에서 트레이스 데이터를 수집하여 분석하는 구조 입니다.


📌 작동 원리 요약

1️⃣ Trace Context 생성

  • 사용자의 요청이 처음 들어오는 지점(예: 프론트엔드, API Gateway) 에서
    - Trace ID: 요청 전체를 식별하는 고유 ID
    - Span ID: 각 서비스 호출 단계를 구분하는 ID
    - Parent Span ID: 어떤 서비스로부터 호출되었는지를 나타냄 이 포함된
    Trace Context를 생성함

2️⃣ Context 전달 (전파)

  • 요청이 다음 서비스로 넘어갈 때,
    이 Trace Context를 HTTP Header(예: traceparent, x-b3-traceid)에 포함시켜 전달함
  • 이를 통해 모든 서비스가 같은 Trace ID 하에 묶임

3️⃣ 각 서비스에서 Span 기록

  • 각 서비스는 요청 처리 시점에 Span 시작, 처리 후 종료
  • Span에는 다음 정보가 포함됨:
    - 시작/종료 시간
    - 수행된 작업 이름 (예: DB 조회, 외부 API 호출)
    - 오류 여부
    - 부모 Span과의 관계 (ParentSpanID)

4️⃣ 트레이싱 백엔드에 전송

  • 각 Span은 백엔드 서버(Jaeger, Zipkin 등) 으로 전송됨
  • 백엔드는 이를 기반으로 하나의 Trace 타임라인으로 시각화

 

Istio는 Span을 분산 트레이싱 엔진으로 보낼 수 있으므로, 별도의 언어 전용 라이브러리나 애플리케이션 전용 설정이 필요 없습니다.

 

이제 분산 트레이싱 시스템을 설치하여 실습해보도록 하겠습니다.

🖥️ Jaeger 설치

🔧 노드 진입 후 설치 진행
$ docker exec -it myk8s-control-plane bash
---
$ cat istio-$ISTIOV/samples/addons/jaeger.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jaeger
  namespace: istio-system
  labels:
    app: jaeger
spec:
  selector:
    matchLabels:
      app: jaeger
  template:
    metadata:
      labels:
        app: jaeger
        sidecar.istio.io/inject: "false"
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "14269"
    spec:
      containers:
        - name: jaeger
          image: "docker.io/jaegertracing/all-in-one:1.35"
          env:
            - name: BADGER_EPHEMERAL
              value: "false"
            - name: SPAN_STORAGE_TYPE
              value: "badger"
            - name: BADGER_DIRECTORY_VALUE
              value: "/badger/data"
            - name: BADGER_DIRECTORY_KEY
              value: "/badger/key"
            - name: COLLECTOR_ZIPKIN_HOST_PORT
              value: ":9411"
            - name: MEMORY_MAX_TRACES
              value: "50000"
            - name: QUERY_BASE_PATH
              value: /jaeger
          livenessProbe:
            httpGet:
              path: /
              port: 14269
          readinessProbe:
            httpGet:
              path: /
              port: 14269
          volumeMounts:
            - name: data
              mountPath: /badger
          resources:
            requests:
              cpu: 10m
      volumes:
        - name: data
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: tracing
  namespace: istio-system
  labels:
    app: jaeger
spec:
  type: ClusterIP
  ports:
    - name: http-query
      port: 80
      protocol: TCP
      targetPort: 16686
    # Note: Change port name if you add '--query.grpc.tls.enabled=true'
    - name: grpc-query
      port: 16685
      protocol: TCP
      targetPort: 16685
  selector:
    app: jaeger
---
# Jaeger implements the Zipkin API. To support swapping out the tracing backend, we use a Service named Zipkin.
apiVersion: v1
kind: Service
metadata:
  labels:
    name: zipkin
  name: zipkin
  namespace: istio-system
spec:
  ports:
    - port: 9411
      targetPort: 9411
      name: http-query
  selector:
    app: jaeger
---
apiVersion: v1
kind: Service
metadata:
  name: jaeger-collector
  namespace: istio-system
  labels:
    app: jaeger
spec:
  type: ClusterIP
  ports:
  - name: jaeger-collector-http
    port: 14268
    targetPort: 14268
    protocol: TCP
  - name: jaeger-collector-grpc
    port: 14250
    targetPort: 14250
    protocol: TCP
  - port: 9411
    targetPort: 9411
    name: http-zipkin
  selector:
    app: jaeger
    
🔧 설치
$ kubectl apply -f istio-$ISTIOV/samples/addons/jaeger.yaml

🔧 노드에서 빠져나오기
$ exit
---

🔧 Jaeger 설치되었는지 확인
$ kubectl get deploy,pod,svc,ep -n istio-system

🔧 Jaeger 웹 UI 외부 노출
$ kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'

 

Jaeger 웹 UI에 접속해보겠습니다.

 

URL에 localhost:30004 입력

 

이제 분산 트레이싱을 수행 할 수 있도록 Istio 설정을 해보도록 하겠습니다.

🔧 분산 트레이싱 수행 할 수 있도록 Istio 설정

Istio에서 분산 트레이싱은 메시 전체, 네임스페이스, 특정 워크로드 수준으로 설정 할 수 있습니다.

 

방법 1. IstioOperator 리소스 사용

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
spec:
  meshConfig:
    defaultConfig:
      tracing:
        lightstep: {}
        zipkin: {}
        datadog: {}
        stackdriver: {}

 

예를 들어 집킨 호환형인 예거를 사용하려면 아래와 같이 설정합니다.

$ cat ch8/install-istio-tracing-zipkin.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
spec:
  meshConfig:
    defaultConfig:
      tracing:
        sampling: 100
        zipkin:
          address: zipkin.istio-system:9411
          
🔧 기존 설정 확인
$ kubectl get IstioOperator -n istio-system installed-state -o json      

결과
 "tracer": {
                    "datadog": {},
                    "lightstep": {},
                    "stackdriver": {},
                    "zipkin": {}
                },
                
🔧 zipkin 설정 적용
$ docker exec -it myk8s-control-plane bash
---
$ cat << EOF > install-istio-tracing-zipkin.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
spec:
  meshConfig:
    defaultConfig:
      tracing:
        sampling: 100
        zipkin:
          address: zipkin.istio-system:9411
EOF

$ istioctl install -y -f install-istio-tracing-zipkin.yaml
$ exit
---
🔧 설정 적용되었는지 확인
$ kubectl get IstioOperator -n istio-system installed-state -o json

결과
"tracing": {
                    "sampling": 100,
                    "zipkin": {
                        "address": "zipkin.istio-system:9411"
                    }
                }

 

방법 2. istio configmap 설정 - MeshConfig 를 이용한 트레이싱 설정

$ kubectl edit cm -n istio-system istio

 

해당 부분에서 설정을 변경하면 됩니다.

 

방법 3. 워크로드 별로 트레이싱 설정하기 - 파드 Spec에 애노테이션 설정

apiVersion: apps/v1
kind: Deployment
...
spec:
  template:
    metadata:
      annotations:
        proxy.istio.io/config: |
          tracing:
            zipkin:
              address: zipkin.istio-system:9411

 

이런 식으로 파드의 Spec에 애너테이션을 추가 해줍니다.

 

이제 기본 트레이싱 헤더를 살펴보겠습니다.

 

앞서 트레이스를 올바른 위치에 보내도록 분산 트레이싱 엔진과 Istio를 설정해주었습니다.

 

Istio가 오픈트레이싱 헤더와 상관관계 ID를 자동으로 주입한다는 것을 보여주기 위해 Istio Ingress Gateway를 사용하여 외부 httpbin 서비스를 호출하고 헤더를 표시하는 엔드포인트를 호출 해보도록 하겠습니다.

🔧 Gateway, VirtualService 리소스 배포
$ cat ch8/tracing/thin-httpbin-virtualservice.yaml
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "webapp.istioinaction.io"
    - "httpbin.istioinaction.io"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: thin-httbin-virtualservice
spec:
  hosts:
  - "httpbin.istioinaction.io"
  gateways:
  - coolstore-gateway
  http:
  - route:
    - destination:
        host: httpbin.org
---        
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: external-httpbin-org
spec:
  hosts:
  - httpbin.org 
  ports:
  - number: 80
    name: http
    protocol: HTTP
  location: MESH_EXTERNAL
  resolution: DNS
  
$ kubectl apply -n istioinaction -f ch8/tracing/thin-httpbin-virtualservice.yaml

🔧 생성 확인
$ kubectl get gw,vs,serviceentry -n istioinaction

🔧 도메인 질의를 위한 도메인 명 추가
$ echo "127.0.0.1       httpbin.istioinaction.io" | sudo tee -a /etc/hosts

🔧 도메인 정상 호출 되는 지 확인
$ curl -s http://httpbin.istioinaction.io:30000/headers | jq

결과
{
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.istioinaction.io",
    "User-Agent": "curl/8.5.0",
    "X-Amzn-Trace-Id": "Root=1-6815a5f3-2f86fc821a214b2446c30f73",
    "X-B3-Sampled": "1",
    "X-B3-Spanid": "fa0d47b1a4999d53",
    "X-B3-Traceid": "a7c0aab352f21144fa0d47b1a4999d53",
    "X-Envoy-Attempt-Count": "1",
    "X-Envoy-Decorator-Operation": "httpbin.org:80/*",
    "X-Envoy-Internal": "true",
    "X-Envoy-Peer-Metadata": "ChQKDkFQUF9DT05UQUlORVJTEgIaAAoaCgpDTFVTVEVSX0lEEgwaCkt1YmVybmV0ZXMKHAoMSU5TVEFOQ0VfSVBTEgwaCjEwLjEwLjAuMTYKGQoNSVNUSU9fVkVSU0lPThIIGgYxLjE3LjgKnAMKBkxBQkVMUxKRAyqOAwodCgNhcHASFhoUaXN0aW8taW5ncmVzc2dhdGV3YXkKEwoFY2hhcnQSChoIZ2F0ZXdheXMKFAoIaGVyaXRhZ2USCBoGVGlsbGVyCjYKKWluc3RhbGwub3BlcmF0b3IuaXN0aW8uaW8vb3duaW5nLXJlc291cmNlEgkaB3Vua25vd24KGQoFaXN0aW8SEBoOaW5ncmVzc2dhdGV3YXkKGQoMaXN0aW8uaW8vcmV2EgkaB2RlZmF1bHQKMAobb3BlcmF0b3IuaXN0aW8uaW8vY29tcG9uZW50EhEaD0luZ3Jlc3NHYXRld2F5cwoSCgdyZWxlYXNlEgcaBWlzdGlvCjkKH3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLW5hbWUSFhoUaXN0aW8taW5ncmVzc2dhdGV3YXkKLwojc2VydmljZS5pc3Rpby5pby9jYW5vbmljYWwtcmV2aXNpb24SCBoGbGF0ZXN0CiIKF3NpZGVjYXIuaXN0aW8uaW8vaW5qZWN0EgcaBWZhbHNlChoKB01FU0hfSUQSDxoNY2x1c3Rlci5sb2NhbAouCgROQU1FEiYaJGlzdGlvLWluZ3Jlc3NnYXRld2F5LTk5NmJjNmJiNi05NTh2bgobCglOQU1FU1BBQ0USDhoMaXN0aW8tc3lzdGVtCl0KBU9XTkVSElQaUmt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9pc3Rpby1zeXN0ZW0vZGVwbG95bWVudHMvaXN0aW8taW5ncmVzc2dhdGV3YXkKFwoRUExBVEZPUk1fTUVUQURBVEESAioACicKDVdPUktMT0FEX05BTUUSFhoUaXN0aW8taW5ncmVzc2dhdGV3YXk=",
    "X-Envoy-Peer-Metadata-Id": "router~10.10.0.16~istio-ingressgateway-996bc6bb6-958vn.istio-system~istio-system.svc.cluster.local"
  }
}

 

결과를 보면, /headers 엔드포인트로 GET 요청을 하면 요청에 사용한 요청 헤더를 반환하여, x-b3-* 집킨 헤더가 요청에 자동으로 붙은 것을 확인 할 수 있습니다.

 

해당 집킨 헤더들은 스팬을 만드는 데 쓰이며 예거로 보내집니다.

 

이제 Jaeger UI에서 분산 트레이싱 데이터를 확인해보겠습니다.

 

하나의 트레이스를 클릭해서 세부적인 정보를 확인해보겠습니다.

  • 트레이스 콘텍스트 및 헤더 전파하기 Propagating trace context and headers : 올바른 동작을 위해 아래와 같은 헤더 전파 필요
    • x-request-id
    • x-b3-traceid
    • x-b3-spanid
    • x-b3-parentspanid
    • x-b3-sampled
    • x-b3-flags
    • x-ot-span-context
    • ⇒ 즉, 애플리케이션 코드가 요청을 받아 처리를 시작할 때 이 헤더와 그 값을 저장했다가 애플리케이션이 수행해야 하는 모든 발신 요청에 삽입해야 합니다. 이 작업은 프록시에서 자동으로 수행할 수 없습니다.

이제 트레이스 샘플링, 강제 트레이스, 트레이스 태그 커스터마이징 해보겠습니다.

메시의 트레이스 샘플링 비율 조정하기

🔧 샘플링 비율 조정하기
$ KUBE_EDITOR="vim" kubectl edit -n istio-system cm istio
---
sampling: 10
---

🔧 샘플링 적용
$ kubectl rollout restart deploy -n istio-system istio-ingressgateway

 

이렇게 설정을 해주면 서비스 메시 내 모든 워크로드에서 샘플링 비율이 10%로 변경됩니다.

 

그리고 이렇게 전역 설정 대신 파드에 애노테이션을 달아서 워크로드 별로도 설정할 수 있습니다.

🔧 파드 템플릿에 애노테이션 추가
$ cat ch8/webapp-deployment-zipkin.yaml
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      annotations:
        proxy.istio.io/config: |
          tracing:
            sampling: 10 # 퍼센트 직접 수정 후 적용 해보세요!
            zipkin:
              address: zipkin.istio-system:9411
              
$ kubectl apply -f ch8/webapp-deployment-zipkin.yaml -n istioinaction

 

보면 여러번 요청을 했지만 트레이싱 결과가 몇 개 없는 것을 확인 할 수 있습니다.

클라이언트에서 트레이싱 강제하기

운영 환경에서는 트레이스의 샘플링 비율을 최소한으로 설정한 후 문제가 있을 때만 특정 워크로드에 대해 활성화 하는 것이 좋습니다.

 

가볍게 실습해보도록 하겠습니다.

🔧 애플리케이션 요청에 "x-envoy-force-trace" 헤더 추가해 강제로 스팬과 트레이스를 Istio가 포착하도록 요청
$ curl -s -H "x-envoy-force-trace: true" http://webapp.istioinaction.io:30000/api/catalog -v
$ curl -s -H "x-envoy-force-trace: true" http://webapp.istioinaction.io:30000/api/catalog -v
$ curl -s -H "x-envoy-force-trace: true" http://webapp.istioinaction.io:30000/api/catalog -v

 

보면 샘플링 비율과 관계없이 Jaeger에 바로 잡히는 것을 확인할 수 있습니다.

트레이스의 태그 커스터마이징 하기

스팬에 태그를 추가하는 것은 애플리케이션이 트레이스에 추가 메타데이터를 첨부하는 방법 입니다.

🔧 webapp 서비스의 스팬에 커스텀 태그를 추가하려면, 해당 파드에 애노테이션에 추가를 하면됩니다.
$ cat ch8/webapp-deployment-zipkin-tag.yaml
  template:
    metadata:
      annotations:
        proxy.istio.io/config: |
          tracing:
            sampling: 100
            customTags:
              custom_tag: # 커스텀 태그의 키
                literal:
                  value: "Test Tag" # 커스텀 태그의 값
            zipkin:
              address: zipkin.istio-system:9411

# webapp 에 커스텀 태그 적용
$ kubectl apply -n istioinaction -f ch8/webapp-deployment-zipkin-tag.yaml

 

Jaeger UI에서 확인해보면, custom_tag=Test Tag 가 추가된 것을 확인 할 수 있습니다.

백엔드 분산 트레이싱 엔진 커스터마이징하기

분산 트레이싱의 마지막으로 분산 트레이싱 엔진에 연결하는 백엔드 설정을 어떻게 수행하는지 알아보겠습니다.

🔧 집킨 기반 트레이싱 엔진의 기본 트레이싱 설정 알아보기
$ docker exec -it myk8s-control-plane bash
---
$ istioctl pc bootstrap -n istioinaction deploy/webapp -o json | jq .bootstrap.tracing

결과
{
  "http": {
    "name": "envoy.tracers.zipkin",
    "typedConfig": {
      "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
      "collectorCluster": "zipkin",
      "collectorEndpoint": "/api/v2/spans",
      "traceId128bit": true,
      "sharedSpanContext": false,
      "collectorEndpointVersion": "HTTP_JSON"
    }
  }
}

$ exit

 

현재 기본 설정을 살펴보면,

  • tracing enging은 zipkin 으로 설정
  • span은 /api/v2/spans 으로 전달
  • json 엔드포인트로 처리

이제 설정을 커스터마이징 해보겠습니다.

🔧 부트스트랩 설정을 덮어 쓰려는 워크로드가 있는 네임스페이스에 configmap 적용
$ cat ch8/istio-custom-bootstrap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: istio-custom-zipkin
data:
  custom_bootstrap.json: |
    {
      "tracing": {
        "http": {
          "name": "envoy.tracers.zipkin",
          "typedConfig": {
            "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
            "collectorCluster": "zipkin",
            "collectorEndpoint": "/zipkin/api/v1/spans",
            "traceId128bit": "true",
            "collectorEndpointVersion": "HTTP_JSON"
          }
        }
      }
      
$ kubectl apply -n istioinaction -f ch8/istio-custom-bootstrap.yaml

🔧 변경된 configmap을 참조하는 Deployment 리소스와 파드 템플릿에 애노테이션 추가
$ cat ch8/webapp-deployment-custom-boot.yaml
  template:
    metadata:
      annotations:
        sidecar.istio.io/bootstrapOverride: "istio-custom-zipkin" # 부트스트랩 설정을 istio-custom-zipkin 사용
        proxy.istio.io/config: |
          tracing:
            sampling: 10
            zipkin:
              address: zipkin.istio-system:9411
      labels:
        app: webapp
        
$ kubectl apply -n istioinaction -f ch8/webapp-deployment-custom-boot.yaml

🔧 변경된 설정 확인
$ docker exec -it myk8s-control-plane bash
---
$ istioctl pc bootstrap -n istioinaction deploy/webapp -o json | jq .bootstrap.tracing

결과
{
  "http": {
    "name": "envoy.tracers.zipkin",
    "typedConfig": {
      "@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
      "collectorCluster": "zipkin",
      "collectorEndpoint": "/zipkin/api/v1/spans",
      "traceId128bit": true,
      "collectorEndpointVersion": "HTTP_JSON"
    }
  }
}

$ exit
---

🔧 호출 테스트
$ for in in {1..10}; do curl -s http://webapp.istioinaction.io:30000/api/catalog ; sleep 0.5; done

 

호출 테스트를 하고 Jaeger UI에서 확인해보면 collectorEndpoint에 잘못된 경로 설정이 되어서 webapp Span이 출력 되지 않습니다.

🔧 다음 실습을 위해 설정 원복
$ kubectl apply -n istioinaction -f services/webapp/kubernetes/webapp.yaml

🖥️ Kiail를 사용한 시각화

이제 kiail 를 사용하여 서비스 간의 통신 상황을 시각적 으로 확인해보겠습니다.

🔧 kiail 설치

🔧 helm repo 추가 및 설치
$ helm repo add kiali https://kiali.org/helm-charts
$ helm repo update 
$ helm install --namespace kiali-operator --create-namespace --version 1.63.2 kiali-operator kiali/kiali-operator

🔧 kiail-operator 설치 확인
$ kubectl get pod -n kiali-operator

결과
NAME                             READY   STATUS    RESTARTS   AGE
kiali-operator-584858fb7-xvq7s   1/1     Running   0          37s

🔧 앞서 배포했던 프로메테우스와 Jaeger에 연결할 수 있도록 설정
$ cat ch8/kiali.yaml
apiVersion: kiali.io/v1alpha1
kind: Kiali
metadata:
  namespace: istio-system
  name: kiali
spec:
  istio_namespace: "istio-system"  
  istio_component_namespaces:
    prometheus: prometheus
  auth:    
    strategy: anonymous # 익명 접근 허용
  deployment:
    accessible_namespaces:
    - '**'
  external_services:    
    prometheus: # 클러스터 내에서 실행 중인 프로메테우스 설정
      cache_duration: 10
      cache_enabled: true
      cache_expiration: 300
      url: "http://prom-kube-prometheus-stack-prometheus.prometheus:9090"    
    tracing: # 클러스터 내에서 실행 중인 예거 설정
      enabled: true
      in_cluster_url: "http://tracing.istio-system:16685/jaeger"
      use_grpc: true

🔧 키알리 인스턴스(대시보드) 설치
$ kubectl apply -f ch8/kiali.yaml

🔧 kiail 대시보드 외부 접속하기 위해 Nodeport 변경
$ kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'

🔧 트래픽 확인을 위한 반복 호출
$ while true; do curl -s http://webapp.istioinaction.io:30000/api/catalog ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done

 

이제 kiail 대시보드 접속 해보도록 하겠습니다.

 

URL에 localhost:30003 으로 들어가줍니다.

 

kiail에 접속해서 하나씩 살펴보면,

  • Overview dashboard: 각 네임스페이스에서 실행 중인 애플리케이션 표기

  • Graph: 서비스 메시의 트래픽 흐름을 보여주는 그래프
    - 트래픽의 이동과 흐름 Traversal and flow of traffic
    - 바이트 수, 요청 개수 등 Number of bytes, requests, and so on
    - 여러 버전에 대한 여러 트래픽 흐름(예: 카나리 릴리스나 가중치 라우팅)
    - 초당 요청 수 Requests/second; 총량 대비 여러 버전의 트래픽 비율
    - 네트워크 트래픽에 기반한 애플리케이션 상태 health
    - HTTP/TCP 트래픽
    - 빠르게 식별할 수 있는 네트워크 실패

  • Workload: k8s 클러스터 내에서 실행 중인 서비스의 실제 실행 단위인 Pod 레벨의 리소스 상태를 보여줍니다.

그리고 Workload와 Applicatio의 차이점을 비교해보자면, 아래와 같습니다.

 

 

구분 Workload Application
의미 Kubernetes에서 Pod을 생성하는 리소스 (예: Deployment) 동일 네임스페이스 내에서 동일한 app 라벨을 가진 Pod들의 논리적 집합
단위 개별 실행 단위 (1개의 Deployment, 1개의 StatefulSet 등) 서비스 관점의 묶음 (같은 앱 라벨을 갖는 여러 Workload 포함 가능)
기준 실제 Kubernetes 리소스 (컨트롤러 중심) app 라벨 값 중심의 논리적 그룹
예시 simple-web Deployment 1개 app=simple-web 라벨을 가진 모든 Workload (v1, v2 등)
목적 리소스 수준에서 상태/트래픽 확인 서비스 수준에서 전체 흐름/메트릭 추적에 적합
사용 예 Pod 상태 확인, 사이드카 주입 여부 확인 등 트래픽 버전별 분포, Canary 분석 등
-------- -------------------------------------------- ----------------------------------------------

 

🔎 쉽게 설명하면:

  • Workload는 실제로 Kubernetes에 배포된 실행 단위
    - 예: Deployment/simple-web-v1, Deployment/simple-web-v2
  • Application은 같은 애플리케이션 이름(app)으로 묶인 그룹
    - 예: app=simple-web 라벨이 붙은 모든 버전의 Pod

이렇게 kiail를 사용하면

  • 서비스 메쉬의 가시성 확보
  • 실시간 모니터링, 요청량, 지연 시간, 에러율 등 트래픽 메트릭 시각화
  • 사이드카 누락, 사이클, 잘못된 라우팅 등 구조 문제 탐지 및 알림
  • Gateway, VirtualService, DestinationRule 등 istio 리소스를 한눈에 확인/편집 가능

Kiail를 도입하면 istio를 더 쉽게 운영하고 디버깅 할 수 있습니다.

 

이번 주 스터디는 여기 까지 입니다.

 

감사합니다.

반응형