4W - 이스티오 메트릭 커스텀, 프로메테우스와 그라파나

개요

이번에는 메트릭을 커스텀하는 방법을 알아본 후에, 메트릭을 시각화하는 방법까지 파헤쳐본다.
여기에서 말하는 메트릭은 주로 서비스 레벨의 메트릭이다.
이 메트릭을 커스텀하기 위해서는 일단 프로메테우스 메트릭에 익숙할 필요가 있다.

사전 지식

이스티오 서비스 레벨 메트릭

이전 문서에서 짧게 다룬 내용에 대해 조금 더 다룬다.

istio_requests_total{
  connection_security_policy="mutual_tls",
  # 이 메트릭은 details라는 서비스로 향하는 트래픽에 대한 정보를 담는다.
  destination_app="details",
  destination_canonical_service="details",
  destination_canonical_revision="v1",
  destination_principal="cluster.local/ns/default/sa/default",
  destination_service="details.default.svc.cluster.local",
  destination_service_name="details",
  destination_service_namespace="default",
  destination_version="v1",
  destination_workload="details-v1",
  destination_workload_namespace="default",
  # 목적지(details) 기준에서 측정한 메트릭이다..
  reporter="destination",
  request_protocol="http",
  response_code="200",
  response_flags="-",
  # 이 메트릭은 productpage라는 서비스로부터 출발한 트래픽에 대한 정보를 담는다.
  source_app="productpage",
  source_canonical_service="productpage",
  source_canonical_revision="v1",
  source_principal="cluster.local/ns/default/sa/default",
  source_version="v1",
  source_workload="productpage-v1",
  source_workload_namespace="default"
} 214

각 엔보이의 메트릭을 종합하여, 서비스 메시의 서비스들끼리 이뤄지는 통신에 대한 메트릭도 제공한다.
이 메트릭은 이스티오를 기본 세팅하고 모니터링했을 때 가장 먼저 마주하게 되는 메트릭으로, 현재 서비스 메시에서 일어나고 있는 트래픽 전반에 대한 정보를 알 수 있게 해준다.
위 예시의 reporter 라벨을 보면 알 수 있듯이, 서비스 간 통신에 두 엔보이가 관여하다보니 하나의 트래픽에 대해서도 2개의 메트릭이 발생하게 된다.
(이것 때문에 재시도 기능을 통해 줄어든 에러율 메트릭을 확인하려면 조금 신경써야 할 부분이 생긴다.)

서비스 레벨 메트릭으로부터 얻을 수 있는 정보는 다음과 같다.

이스티오에서는 별 설정을 하지 않아도 표준으로 세팅된 채 제공되는 메트릭이 몇 가지 있다.[1]

image.png
이것들이 기본 메트릭이고, 이들이 가지는 기본 라벨이 또 있다.
몇 가지만 정리하자면,

metadata:
  annotations:
    service.istio.io/canonical-name: my-app
    service.istio.io/canonical-revision: v2

참고로 canonical은 이렇게 사용자가 워크로드에 명확하게 설정한 값을 기반으로 보여주는데, 설정하지 않았다면 보통 그냥 workload에 나오는 정보와 같다.

Telemetry API

Istio Telemetry는 1.22 버전 이후 메이저로 승격된 관측 가능성 관련 리소스이다.[3]
원래 이스티오에서는 이스티오 전역 설정이나, 워크로드에 어노테이션을 다는 방식으로 관측 가능성 세팅을 지원했다.
이런 방식을 사용성이 너무 제한됐기 때문에 아예 새로운 리소스를 만들어버린 것.

로깅, 메트릭, 트레이싱 등의 설정을 지원한다.
라벨 셀렉팅을 하면 워크로드 단위, 하지 않으면 네임스페이스 단위, 이스티오 루트 네임스페이스에 만들면 전역 설정이 된다.
설정법에 따라서 여러 텔레메트리가 겹치게 되는 경우가 있을 텐데, 이럴 때는 겹치는 설정 부분에 대해서 가장 작은 단위의 설정값이 덮어쓰기한다.

apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata:
  name: foo-tracing-alternate
  namespace: baz
spec:
  selector:
    matchLabels:
      service.istio.io/canonical-name: foo
  tracing:
  - providers:
    - name: "zipkin-alternate"
    randomSamplingPercentage: 10.00
  metrics:
  - providers:
    - name: prometheus
    overrides:
    - match:
        metric: ALL_METRICS
        mode: SERVER
      disabled: true
  accessLogging:
  - providers:
    - name: envoy

라벨 셀렉터로 고르고, 트레이싱이나 메트릭, 로깅 설정을 하면 된다.
라벨을 쓰지 않고 targetRefs로 명시적으로 리소스들을 지정하는 것도 가능하다(waypoint 프록시라면 무조건 이걸 써야 한다).

metrics

  metrics:
  - providers:
    - name: prometheus
    reportingInterval: 5s
    overrides:
	# 서버 쪽 메트릭 비활성화
	- match:
        metric: ALL_METRICS
        mode: SERVER
      disabled: true
	# request_method, request_host 추가(혹은 변경)
    - tagOverrides:
        request_method:
          value: "request.method"
        request_host:
          value: "request.host" # CEL 표현식으로 써야 한다.
    # REQUEST_COUNT 메트릭 제거
    - match:
        metric: REQUEST_COUNT
      tagOverrides:
        response_code:
          operation: REMOVE # REMOVE, UPSERT 가능

overides 필드를 통해 덮어쓰기할 메트릭을 정한다.
메트릭 쪽은 match를 쓸 때 어떤 메트릭을 할 것인지도 세부적으로 지정할 수 있다.
어떤 메트릭 값을 넣을 수 있는지는 서비스 레벨 메트릭 참고.[4]
그래서 그 값에 대해 tagOverrides 필드를 통해 라벨 커스텀을 할 수 있게 되는 것이다.
value 부분은 CEL 표현식으로 작성해야 하는데, 엔보이에서 노출하는 모든 변수를 사용할 수 있다.[5]
이때 서비스 레벨 메트릭 관련해서 노출하고 싶은 정보가 있다면 엔보이 변수 중 filter_state를 이용해주면 된다.
예를 들어 app 라벨을 이용한 메트릭을 커스텀한다면 filter_state.downstream_peer.app, filter_stat.upstream_peer.app를 설정하는 식이다.[6]

EnvoyFilter

엔보이 필터는 말 그대로 엔보이 설정을 직접적으로 만드는 리소스이다.
기본적으로 이스티오에서 제공하는 엔보이 관련 설정 이외에, 지원되지 않고 있는 기능이나 직접적으로 커스텀한 필터를 넣고 싶을 때 직접적으로 엔보이 관련 설정 리소스를 만들고 이것이 엔보이에 적용되도록 할 수 있다.
자유도는 높겠지만 잘못 설정했다가 서비스 메시 전체가 망가질 수도 있기 때문에 조심하게 사용할 필요가 있다.

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: custom-protocol
  namespace: istio-config # 이스티오 세팅 시 설정된 meshConfig에 있다.
spec:
  # 적용할 엔보이 라벨 셀렉팅
  workloadSelector:
    labels:
      app: mysvc
  # 적용할 리소스 유형과 이름
  # Waypoint 프록시는 무조건 이걸로 타겟팅해야 한다고 한다.
  targetRefs:
  - name: waypoint
    kind: Gateway
    group: gateway.networking.k8s.io
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      context: SIDECAR_OUTBOUND # will match outbound listeners in all sidecars
      listener:
        portNumber: 9307
        filterChain:
          filter:
            name: "envoy.filters.network.tcp_proxy"
    patch:
      operation: INSERT_BEFORE
      value:
        # This is the full filter config including the name and typed_config section.
        name: "envoy.extensions.filters.network.mongo_proxy"
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.mongo_proxy.v3.MongoProxy"
          ...
  - applyTo: NETWORK_FILTER # http connection manager is a filter in Envoy
    match:
      # context omitted so that this applies to both sidecars and gateways
      listener:
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: MERGE
      value:
        name: "envoy.filters.network.http_connection_manager"
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
          common_http_protocol_options:
            idle_timeout: 30s

기본적인 설정은 전부 configPatches에서 진행된다.
workloadSelectortargetRefs는 대상이 될 엔보이를 선별하는 필드이고, 여기에 추가적으로 priority 필드로 적용 순서를 명시하는 방법이 있긴 하다.
priority는 위에서 언급한 모든 순서 앞단에서 명시적으로 설정되는 순위라 보면 되겠다.
아무튼 핵심이 되는 configPatches를 중점적으로 보면 된다.

서비스 레벨 메트릭 커스텀

추가적인 서비스 레벨의 메트릭이나 라벨을 넣고 싶다면 위에서 본 Istio EnvoyFilterIstio Telemetry를 통해 메트릭을 등록하는 작업을 해야 한다.
(참고로 두 리소스를 같이 사용할 수 없는데, 결론만 먼저 말하면 1.21부터는 텔레메트리 리소스를 사용하는 게 기본이다.)
이때 개념을 조금 구분해서 이해할 필요가 있다.

서비스 레벨의 메트릭은 결국 엔보이에서 노출되는 메트릭을 조합해서 보여주는 메트릭이란 것을 명심해야 한다.
이때 엔보이에서 기본적으로 노출하는 속성 정보를 속성(attribute)이라고 부르는 것이다.
이걸 프로메테우스 라벨로 메트릭에 노출시키기 위해서는 추가적인 작업이 필요한데, 이걸 차원(dimension)을 추가한다고 표현한다.

그래서 메트릭 커스텀을 한다고 하면 사실 크게 세 가지 방식으로 조작을 분류할 수 있다.

그래서 엔보이에서 먼저 메트릭이나 라벨을 서비스 레벨에서 잡히도록 만들고, 서비스 레벨에서 라벨 커스텀을 한다고 이해하자.
프로메테우스에서는 그냥 라벨이라 부르는 것들을 굳이 속성이라 부르기도 하고 디멘션이라 부르기도 하는 게 다 이유가 있는 거다..
위 방법들에 대해서 각각 실습을 시도했다.

엔보이에서 기본으로 노출하는 메트릭은 이 문서에 담겨있다.[7]
image.png
여기에 추가적으로 이스티오가 자체적으로 추가해주는 라벨 값들이 있다.[3:1]
이들은 통신을 하는 상대에 대한 정보를 나타내기 위한 라벨로, 이들은 엔보이 라벨 중 filter_state의 하위 라벨로서 제공된다.
그래서 filter_state.upstream_peer, filter_state.downstream_peer와 같은 식으로 사용하면 된다.
이밖에도 엔보이가 실행되고 있는 환경, 즉 노드에 대한 정보도 노출하는데 이건 xds.node.metadata라는 필드에 담긴다.
구체적으로 어떤 값이 담기는지 보고 싶다면 엔보이의 관리자 엔드포인트(기본 localhost:15000/server_info)에서 값을 까보자.

버전 업데이트에 따른 변경

스킵 가능

어차피 실습 부분에서 다시금 다루는 내용이니 간단하게만 보고 넘어가도 좋다.

1.25 버전을 기준으로 현재라고 치고, 과거의 방식과의 차이점을 간단하게 다룬다.
image.png
과거(위 문서는 1.17 버전)에는 이스티오 관련 메트릭은 그냥 upstream_peer이라고 하는 식으로 지원했다.[4:1]

1.21부터는 엔보이필터를 이용해서 메트릭을 커스텀하는 방식을 지원하지 않는다.[^7]
구체적으로는 두 설정이 충돌하기 때문에 그렇다.[^8]
image.png
이에 따라 과거에는 이스티오 오퍼레이터로 기본 설치할 때 같이 깔리던 여러 엔보이 필터가 최신 버전에서는 기본으로 깔리지 않는다.

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    telemetry:
      enabled: true
      v2:
        enabled: false

이것과 관련해서 아예 이스티오 오퍼레이터를 이용한 설치 방식에서도 세팅 차이가 발생한다.
1.18 이전 버전에서 Istio Telemetry를 사용하려면 위와 같이 세팅을 해야 한다.
현 버전에서는 telemetry라는 필드 자체가 없다.

image.png
기본으로 노출하는 속성 정보에 대한 것도 살짝 차이가 있다.
그 시절의 이스티오에서는 peer 메타데이터 관련해서 다음의 값들을 추가로 노출해주었다.[6:1]
이 속성들은 upstream_peer.istio_version 이런 식으로 접근하는 것이 가능했다.
image.png
그런데 최신 버전에서는 이런 정보를 기본 속성으로 노출해주지 않는다..
또한 이 정보들을 접근하고자 할 때는 filter_state.upstream_peer 이런 식으로 접근해야 한다.
그렇다고 이전의 속성들을 아예 안 보여주는 건 아니고, xds.node.metadata라는 필드로 노출해준다.
엔보이의 런타임 설정 정보에서 볼 수 있도록 바꿨다고 보면 되겠다.

Prometheus Operator

image.png
쿠버네티스 환경에서 프로메테우스를 조금 더 쉽게 관리할 수 있도록 도와주는 오퍼레이터.
프로메테우스에서 활용되는 다양한 설정이나 기능들을 CRD로 만들어서 클러스터적으로 프로메테우스를 관리할 수 있도록 도와준다.

리소스

이 오퍼레이터에는 다양한 CRD가 존재한다.
위에서도 말했듯, 이 CRD들은 대체로 프로메테우스에서 활용되는 기능들을 클러스터 리소스로서도 관리할 수 있도록 만들어진 것들이다.
image.png
크게는 두 가지로 구분을 지을 수 있는데, 이번에 볼 것은 모니터 리소스이다.

서비스 모니터


프로메테우스에서 설정해야 하는 요소들 역시 각각 하나의 리소스로서 관리할 수 있다.
가령 어떤 서비스의 대상이 되는 파드들의 메트릭을 추적하고 싶을 때는 서비스 모니터라는 리소스를 만든다.

kind: Service
apiVersion: v1
metadata:
  name: example-app
  labels:
    app: example-app
spec:
  selector:
    app: example-app
  ports:
  - name: web
    port: 8080
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: example-app
  labels:
    team: frontend
spec:
  selector:
    matchLabels:
      app: example-app
  endpoints:
  - port: web

ServiceMonitor의 경우 이런 식으로 작성한다.
이게 무엇이냐, 서비스 모니터 리소스는 어떤 서비스를 라벨 기반으로 추적한다.
위의 서비스는 자신의 라벨(라벨 셀렉터 부분 말고, 서비스 메타데이터의 라벨이란 것에 유념)로 app: example-app을 가지고 있다.
서비스 모니터는 이 라벨을 추적하여 자신이 메트릭을 수집할 대상이 되는 서비스를 찾아낸다.
이후에 이 서비스가 가지는 엔드포인트슬라이스을 추적하여 실제 트래픽을 받는 파드를 알아내고, 그 파드로부터 메트릭을 긁어온다!
프로메테우스의 서비스 디스커버리 설정에 이렇게 찾아진 파드들이 자동으로 등록되어, 프로메테우스를 통해 해당 파드들이 노출하는 메트릭을 자연스럽게 긁어올 수 있게 된다.

그림으로 보기 편하게 예를 들면 이런 식이다.[8]
서비스 모니터 리소스는 서비스 라벨을 보는 방식이니 서비스의 라벨 셀렉터만 설정한다던가 하는 실수에 유의하자.

실습 진행

프로메테우스 세팅

본격적으로 메트릭을 커스텀하기 전에, 조금이라도 편하게 보기 위해서 먼저 프로메테우스를 세팅한다.
구체적으로는 위에서 프로메테우스 오퍼레이터를 설치하는 것이다.
(더 구체적으로는 그라파나까지 같이 세팅된 kube prom stack을 설치하는 것이다.)

책에 나오는 예엣날 버전의 프로메테우스 설치

가급적 최신 버전들을 이용하면서 스터디 시간에 나오는 이전 버전들과 비교를 하는 것도 내 목표 중 하나인 관계로 나는 이전 버전을 세팅하진 않고 정리만 한다.

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
cat << EOF > prom-values-2.yaml
prometheusOperator:
  tls:
    enabled: false
  admissionWebhooks:
    patch:
      enabled: false

prometheus:
  service:
    type: NodePort
    nodePort: 30001
    
grafana:
  service:
    type: NodePort
    nodePort: 30002
EOF

kubectl create ns prometheus
helm install prom prometheus-community/kube-prometheus-stack --version 13.13.1 \
-n prometheus -f ch7/prom-values.yaml -f prom-values-2.yaml

일단 프롬 스택을 헬름으로 설치한다.
(참고로 이름이 스택인 이유는 프로메테우스 오퍼레이터와 더불어 그라파나 등 다양한 리소스를 한꺼번에 설치하는 올인원 차트이기 때문이다.)
ch7/prom-values 파일은 무식하게 헬름 values 파일을 갖다 박고 실습에 맞게 수정한 듯한데, 크기가 너무 크고 불편해서 아예 특정 부분들만 덮어씌우는 추가 values 파일을 만들어서 사용한다.
대단한 걸 덮어씌우는 것은 아니고 단순히 사전에 설정한 포트를 사용하고 편의를 위해 tls를 비활성화하는 정도만 세팅한다.

최신 버전 프롬 스택 설치

crds:
  enabled: true
defaultRules:
  create: false
global:
  rbac:
    create: true
    createAggregateClusterRoles: false
    pspEnabled: false

prometheus-windows-exporter:
  prometheus:
    monitor:
      enabled: false
  releaseLabel: false

alertmanager:
  enabled: false

grafana:
  enabled: true
  adminUser: admin
  adminPassword: prom-operator
  sidecar:
    dashboards:
      enabled: true
    datasources:
      enabled: true
      defaultDatasourceEnabled: true
      isDefaultDatasource: true

      name: Prometheus
      uid: prometheus
      ## Set method for HTTP to send query to datasource
      httpMethod: POST

      prometheusServiceName: prometheus-operated
      label: grafana_datasource
      labelValue: "1"

      alertmanager:
        enabled: false

kubernetesServiceMonitors:
  enabled: true

kubeApiServer:
  enabled: false
kubelet:
  enabled: false
kubeControllerManager:
  enabled: false
coreDns:
  enabled: false
kubeEtcd:
  enabled: false
kubeScheduler:
  enabled: false
kubeProxy:
  enabled: false
kubeStateMetrics:
  enabled: false
nodeExporter:
  enabled: false

prometheusOperator:
  enabled: true
  tls:
    enabled: false
  admissionWebhooks:
    patch:
      enabled: false

prometheus:
  service:
    nodePort: 30090
    type: ClusterIP

이건 내가 최신 프롬 스택의 values 파일을 까서 일일히 세팅한 것이다.
책 실습에 나온 설정 부분들을 최대한 비슷하게 세팅했고, 아울러 스터디 시간에 추가적으로 세팅한 부분들도 적용했다.
다만 눈으로 보면서 세팅을 했다보니 조금 다른 부분이 있을 수도 있으니 이 파일을 이용하려면 참고하자.

k create ns prometheus
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prom prometheus-community/kube-prometheus-stack --version 71.1.0 \
-n prometheus -f prom-values-3.yaml
kubectl patch svc -n prometheus prom-kube-prometheus-stack-prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
kubectl patch svc -n prometheus prom-grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 3000, "nodePort": 30002}]}}'

프롬 스택은 그라파나에 대한 상세한 커스텀을 values에서 지원하지 않기에 그라파나 서비스를 바로 NodePort로 세팅하는 것이 불가능하다.
어차피 바로 노드포트로 세팅할 수 없는 김에 프로메테우스 쪽 서비스도 이후에 세팅하는 식으로 진행했다.
image.png
이렇게 세팅이 된다면 성공이다.
image.png
30001 포트로 프메, 30002로 그라파나에 접속할 수 있는 것까지 확인하자.(그라파나 id: admin passwd: prom-operator)

모니터 리소스를 통한 이스티오 메트릭 스크래핑 설정

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: istio-component-monitor
  namespace: prometheus
  labels:
    monitoring: istio-components
    release: prom
spec:
  jobLabel: istio
  targetLabels: [app]
  selector:
    matchExpressions:
    - {key: istio, operator: In, values: [pilot]}
  namespaceSelector:
    any: true
  endpoints:
  - port: http-monitoring # 15014
    interval: 15s

서비스모니터 리소스를 통해 컨트롤 플레인의 메트릭을 먼저 수집해본다.
image.png
만약 이 리소스를 사용하고자 하는 네임스페이스에서 생성하고 이를 적용하게 하려면 프로메테우스 리소스에서 모니터 리소스를 수집하는 네임스페이스에 대한 설정을 추가적으로 해줘야 한다.
image.png
이렇게 서비스 디스커버리에 잡히게 된다면 성공이다.

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: envoy-stats-monitor
  namespace: prometheus
  labels:
    monitoring: istio-proxies
    release: prom
spec:
  selector:
    matchExpressions:
    - {key: istio-prometheus-ignore, operator: DoesNotExist}
  namespaceSelector:
    any: true
  jobLabel: envoy-stats
  podMetricsEndpoints:
  - path: /stats/prometheus
    interval: 15s
    relabelings:
    - action: keep
      sourceLabels: [__meta_kubernetes_pod_container_name]
      regex: "istio-proxy"
    - action: keep
      sourceLabels: [__meta_kubernetes_pod_annotationpresent_prometheus_io_scrape]
    - sourceLabels: [
    __address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
      action: replace
      regex: \d+)?;(\d+
      replacement: $1:$2
      targetLabel: __address__
    - action: labeldrop
      regex: "__meta_kubernetes_pod_label_(.+)"
    - sourceLabels: [__meta_kubernetes_namespace]
      action: replace
      targetLabel: namespace
    - sourceLabels: [__meta_kubernetes_pod_name]
      action: replace
      targetLabel: pod_name

다음은 데이터 플레인의 메트릭을 수집해보자.
위 예시는 어떤 네임스페이스에 있던 메트릭 수집 무시 관련 라벨이 안 붙어 있는 파드들의 메트릭을 긁어온다.
메트릭 엔드포인트에 대한 리라벨링을 진행하는데, 대충 설명하자면,

image.png
이 녀석도 위와 마찬가지로 서비스 디스커버리 탭에 추가가 되면 성공이다.
이제 프록시 레벨, 서비스 레벨의 메트릭도 프로메테우스를 통해 볼 수 있게 됐다.

메트릭 커스텀 - 1.25 버전 기준

1.25 버전 기준으로는 엔보이필터를 이용해 메트릭을 커스텀하는 방식은 deprectated됐고, 대신 telemetry 리소스를 사용해야 한다.
엔보이 필터로 하는 것이 불가능한 것은 아니지만, 엔보이 필터 자체가 이스티오에서 제공되지 않는 기능들을 커스텀할 때 사용하는 최후의 보루이기에 에러를 해결하기도 어려운지라 가능한 telemetry를 사용하는 것이 아주 강력하게 권장된다.
기술 부채 제조기 이스티오

기존 메트릭에 추가 라벨 설정

istio_requests_total{connection_security_policy="mutual_tls", container="istio-proxy", destination_app="webapp", destination_canonical_revision="latest", destination_canonical_service="webapp", destination_cluster="Kubernetes", destination_principal="spiffe://cluster.local/ns/istioinaction/sa/webapp", destination_service="webapp.istioinaction.svc.cluster.local", destination_service_name="webapp", destination_service_namespace="istioinaction", destination_workload="webapp", destination_workload_namespace="istioinaction", instance="10.10.0.7:15020", job="prometheus/envoy-stats-monitor", namespace="istioinaction", pod="webapp-7c96945758-5c69g", pod_name="webapp-7c96945758-5c69g", reporter="destination", request_protocol="http", response_code="200", response_flags="-", source_app="istio-ingressgateway", source_canonical_revision="latest", source_canonical_service="istio-ingressgateway", source_cluster="Kubernetes", source_principal="spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account", source_version="unknown", source_workload="istio-ingressgateway", source_workload_namespace="istio-system"}

먼저 기존 istio_request_total 메트릭의 라벨은 이런 상태이다.
여기에는 request_protocol이 들어가 있다.

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: demo
  values:
    telemetry:
      v2:
        prometheus:
          configOverride:
            inboundSidecar:
              metrics:
              - name: requests_total
                dimensions:
                  upstream_proxy_version: upstream_peer.istio_version
                  source_mesh_id: node.metadata['MESH_ID']
                tags_to_remove:
                - request_protocol
            outboundSidecar:
              metrics:
              - name: requests_total
                dimensions:
                  upstream_proxy_version: upstream_peer.istio_version
                  source_mesh_id: node.metadata['MESH_ID']
                tags_to_remove:
                - request_protocol
            gateway:
              metrics:
              - name: requests_total
                dimensions:
                  upstream_proxy_version: upstream_peer.istio_version
                  source_mesh_id: node.metadata['MESH_ID']
                tags_to_remove:
                - request_protocol

책에서 안내하는 이전 버전의 방식은 위와 같이 이스티오 오퍼레이터에 telemtry 필드에 설정을 넣는 것이다.
그런데 이 방식은 1.25 버전 현재 아예 불가능하다.

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: add-dimension-tags
  namespace: istioinaction
spec:
  metrics:
  - providers:
      - name: prometheus
    overrides:
      - match:
          metric: REQUEST_COUNT
          mode: CLIENT_AND_SERVER
        disabled: false
        tagOverrides:
          upstream_proxy_version:
            operation: UPSERT
            value: upstream_peer.istio_version
          source_mesh_id:
            operation: UPSERT
            value: node.metadata['MESH_ID']
          request_protocol:
            operation: REMOVE

당시 알파 단계였던 텔레메트리 리소스 양식을 활용해서 만드는 방법은 이렇다.[10]
미리 말하지만 이것도 현재 버전에서는 먹히지 않는다.

istio_requests_total{connection_security_policy="mutual_tls", container="istio-proxy", destination_app="webapp", destination_canonical_revision="latest", destination_canonical_service="webapp", destination_cluster="Kubernetes", destination_principal="spiffe://cluster.local/ns/istioinaction/sa/webapp", destination_service="webapp.istioinaction.svc.cluster.local", destination_service_name="webapp", destination_service_namespace="istioinaction", destination_workload="webapp", destination_workload_namespace="istioinaction", instance="10.10.0.8:15020", job="prometheus/envoy-stats-monitor", namespace="istioinaction", pod="webapp-7c96945758-8xtdj", pod_name="webapp-7c96945758-8xtdj", reporter="destination", response_code="200", response_flags="-", source_app="istio-ingressgateway", source_canonical_revision="latest", source_canonical_service="istio-ingressgateway", source_cluster="Kubernetes", source_mesh_id="unknown", source_principal="spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account", source_version="unknown", source_workload="istio-ingressgateway", source_workload_namespace="istio-system", upstream_proxy_version="unknown"}

image.png
이걸 적용해서 만들어지는 값을 보면, 일단 request_protocol 라벨이 없어지고 source_mesh_id, upstream_proxy_version 라벨이 추가되는 것을 확인할 수 있다.
그러나 막상 보면 두 값이 unknown으로 표시된다.
image.png
그 시절의 이스티오에서는 peer 메타데이터 관련해서 다음의 값들을 추가로 노출해주었다.[11]
image.png
그런데 최신 버전에서는 조금도 비슷한 정보가 없다..
엔보이쪽에서 노출된다는 속성값들을 찾아봐도 이스티오 버전에 대한 값을 노출할 수 있는 방법은 보이지 않는다.
애초에 엔보이의 설정 버전에 큰 변화(메이저 버전이 3으로 올라감)가 생기면서 이스티오도 빠르게 발맞추다보니 메트릭 관련해서 상당히 이전 버전과 비슷하게 세팅하는 방법들에 대한 자료가 많지 않은 것 같다.
최근 블로그 글을 보면 죄다 gateway api, ambient 모드에만 치중이 돼있어 api에 생긴 중대한 변화에 대한 마이그레이션 포인트들을 인지하기 힘들다.
image.png
문서에서는 node metadata도 노출하긴 한다고 하는데, 막상 다른 자료들을 보더라도 구체적으로 어떻게 노출한다고 하는지 알긴 힘들다.
image.png
혼자 눈물의 똥꼬쇼를 하다보니 조금씩 단서들이 나오긴 했다.
엔보이에서 자체적으로 노출해주는 값 중에서 node 관련된 것이 있기는 하다.
image.png
node 관련해 노출하는 엔보이의 필드들은 다음과 같다.
image.png
위의 값들을 토대로 넣을 값을 수정해보자.
image.png
예제에서 나온 방식으로 정확하게 적용된다고 보기는 힘들지만, 아무튼 값을 출력할 수 있기는 하다.
그렇다면 책의 내용처럼 프록시 버전과 메시ID를 정확하게 볼 수 있는 방법은 없을까?

keti catalog-5899bbc954-mqpbt -- curl http://localhost:15000/server_info | fx

image.png
답을 찾고자 한다면, 언젠가는 찾기 마련이다.
엔보이를 알아두면 유용한 이유가 바로 이런 것이다!
엔보이 어드민 엔드포인트로 요청을 날려보면 엔보이에 대한 각종 정보를 볼 수 있는데, 이때 이스티오와 관련된 메타데이터는 node.metadata쪽으로 들어가는 것을 확인할 수 있다.

apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata:
  name: add-extra-label
  namespace: istio-system
spec:
  metrics:
    - providers:
        - name: prometheus
      overrides:
        - match:
            metric: REQUEST_COUNT
          tagOverrides:
            request_protocol:
              operation: REMOVE
            upstream_proxy_version:
              operation: UPSERT
              value: "string(xds.node.metadata.ISTIO_VERSION)"
            source_mesh_id:
              operation: UPSERT
              value: "xds.node.metadata.MESH_ID"

이를 기반으로 텔레메트리 리소스를 만들어본다.
image.png
이렇게 하면 드디어 원하는 값들을 담은 라벨을 볼 수 있게 된다!
image.png
참고로 해당 설정은 엔보이 리스너 설정 쪽으로 들어가게 된다(사진은 삽질하면서 중간에 찍은 거라 값이 조금 다르게 쓰여있다).

새로운 메트릭 만들기 - 보류

새로운 메트릭을 만들고자 할 때는 텔레메트리가 아니라 다른 리소스로 설정을 해줘야 한다.[12]
근데 속성을 추가로 만드는 것에 대한 예시는 있는데, 어째 메트릭을 새로 만드는 예시가 없다.

직접 만들어야 하나..?
이스티오에는 관리하지 않은지 오래 됐지만 그래도 기본적으로 활용할 만한 와즘 플러그인들을 모아두긴 했다.[13]
그러나 여기에도 메트릭을 추가하는 것과 관련된 것은 보이지 않는다.

열심히 찾아봤지만, 역시 메트릭을 추가하는 것과 관련된 마땅한 예제, 자료가 존재하지 않았다.
아무래도 정말로 직접 와즘 플러그인을 만드는 방향을 생각해야 할 것으로 보인다.

향후에 텔레메트리 api에서 새로운 메트릭을 추가할 수 있도록 양식이 변경될 가능성이 있을까 모르겠다.

새로운 속성 만들기

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: attribute-gen-example
  namespace: istioinaction
spec:
  configPatches:
  ## Sidecar Outbound 
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_OUTBOUND
      listener:
        filterChain:
          filter:
            name: envoy.filters.network.http_connection_manager
            subFilter:
              name: istio.stats
      proxy:
        proxyVersion: ^1\.13.*
    patch:
      operation: INSERT_BEFORE
      value:
        name: istio.attributegen
        typed_config:
          '@type': type.googleapis.com/udpa.type.v1.TypedStruct
          type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
          value:
            config:
              configuration:
                '@type': type.googleapis.com/google.protobuf.StringValue
                value: |
                  {
                    "attributes": [
                      {
                        "output_attribute": "istio_operationId", # 속성 이름
                        "match": [
                         {
                           "value": "getitems", # 속성 값
                           "condition": "request.url_path == '/items' && request.method == 'GET'"
                         },
                         {
                           "value": "createitem",
                           "condition": "request.url_path == '/items' && request.method == 'POST'"
                         },     
                         {
                           "value": "deleteitem",
                           "condition": "request.url_path == '/items' && request.method == 'DELETE'"
                         }                                             
                       ]
                      }
                    ]
                  }
              vm_config:
                code:
                  local:
                    inline_string: envoy.wasm.attributegen
                runtime: envoy.wasm.runtime.null

새로운 속성을 만들고자 할 때는 원래 엔보이필터 리소스를 이용하는 방식이 기본이었다.
image.png
그러나 현재 이걸 세팅하면 지속적으로 에러가 발생한다.
image.png
istiod 로그를 보면 wasm 플러그인을 만드는데 실패한다고 표시되고 있다.

그렇다면 어떻게 새로운 속성을 추가할 수 있는가?
이스티오에서는 어차피 와즘을 통해 새로운 필터를 주입하는 방식으로 엔보이 필터를 사용하는 기능을 확장하는 케이스가 많은 만큼, 아예 WasmPlugin이란 리소스를 통해 와즘을 넣는 방식을 제공하고 있다.
다행이라 해야 할지, 현재 attributegen이란 형태의 와즘 바이너리가 현재 제공되고 있어서 이건 코드를 만들지 않고 설정하는 게 가능하다.[14]

apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
  name: istio-attributegen-filter
spec:
  url: https://storage.googleapis.com/istio-build/proxy/attributegen-359dcd3a19f109c50e97517fe6b1e2676e870c4d.wasm
  imagePullPolicy: Always
  phase: AUTHN
  pluginConfig:
    attributes:
    - output_attribute: "istio_operationId"
	  match: 
	    - value: "getitems", # 속성 값
		  condition: "request.url_path == '/items' && request.method == 'GET'"
		- value: "createitem",
		  condition: "request.url_path == '/items' && request.method == 'POST'"
		- value: "deleteitem",
		  condition: "request.url_path == '/items' && request.method == 'DELETE'"

image.png
해당 리소스를 적용하면 엔보이 listener 쪽 http 필터가 추가된다.
configDiscovery를 통해 구체적인 설정 정보를 받아가도록 세팅돼있는데, 이건 ecds 설정을 봐야 한다.

istioctl pc ecds webapp-76774fdc79-jb7rj -ojson | fx

image.png
설정이 이렇게 들어간 것을 확인할 수 있다.
image.png
그 다음부터는 삽질하면서 어떻게 해당 메트릭을 노출할 수 있는지 알아봤다.
결론적으로는, 문서에 나온 그대로 filter_statewasm.istio_operationId를 키로 접근해야 값이 나온다.

apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata:
  name: custom-tags
  namespace: istio-system
spec:
  metrics:
    - overrides:
        - match:
            metric: REQUEST_COUNT
            mode: CLIENT_AND_SERVER
          tagOverrides:
            request_operation:
              value: filter_state['wasm.istio_operationId']
      providers:
        - name: prometheus

최종적으로는 이런 식으로 만들어주었다.

curl -s http://$SIMPLEWEB:30000/items --resolve "$SIMPLEWEB:30000:127.0.0.1"

새로운 속성을 지정할 때 url 경로가 /items여야 하도록 설정했기 때문에, 테스트를 할 때도 당연히 해당 경로를 넣어서 요청해야 한다.
image.png
성공적으로 해당 라벨에 원한 값이 추가됐다!
image.png
주의사항으로, 텔레메트리 리소스들의 설정이 함께 설정이 되지 않기 때문에 기존 리소스를 반드시 삭제하고 해당 리소스를 적용해야 한다.[15]

그라파나 세팅

이제 메트릭을 시각화하는 툴, 그라파나를 간단하게 보면서 실습을 마친다.
이건 깊게 파지는 않을 것이다.
이전에 프로메테우스 스택을 설치하면서 자연스럽게 그라파나도 같이 세팅이 완료됐다.
image.png
추가적으로 세팅해줄 만한 건 이스티오를 위한 대시보드들을 그라파나에 넣어주는 것 뿐이다.

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 label -n prometheus cm istio-dashboards grafana_dashboard=1

라벨로 grafana_dashboard를 넣은 컨피그맵에 대해 그라파나는 자동으로 대시보드 파일로 인식하고 이를 업로드해준다.
책 실습 코드에 이스티오를 위한 대시보드 파일들이 담겨있으므로 이걸 바로 등록해서 보도록한다.
image.png
이렇게 표시된다면 완료.

대시보드 확인

먼저 컨트롤 플레인 대시보드를 본다.
image.png
버전에 대한 정보를 왜 그래프로 표시했는지는 모르겠다..굳이..?
image.png
컨트롤 플레인에서 사용된 리소스 사용량도 확인할 수 있다.
image.png
설정 동기화에 대한 정보도 확인할 수 있는데, pilot 에러는 이전에 메트릭 설정을 엔보이 필터로 넣으려다 발생한 에러에 대한 값이다.

image.png
서비스 메시 대시보드에는 전체 리소스들에 대한 정보와 트래픽 성공 실패 여부가 간단하게 담겨 있다.

image.png
성능 대시보드쪽을 보면 데이터가 제대로 나오지 않는 것을 볼 수 있다.
image.png
이건 쿼리문을 보면 이유를 알 수 있는데, 위의 메트릭은 프로메테우스 오퍼레이터를 설치할 때 기본 Recording Rule과 리라벨링으로 설정되는 메트릭이다.
처음 헬름 설치 시 해당 설정을 하지 않도록 했기 때문에 이 메트릭을 볼 수 없는 것이다.

image.png
다음으로 서비스 대시보드를 본다.

sum(irate(istio_tcp_received_bytes_total{reporter="destination", destination_service=~"webapp.istioinaction.svc.cluster.local"}[1m]))

tcp 데이터가 나오지 않아서 뭔가 했는데, TCP 메트릭은 http 통신 시 세팅되는 TCP를 제외하고 정말 TCP로서 연결되는 데이터가 있을 때만 출력된다고 한다.

image.png
wasm 관련 대시보드도 존재한다.

avg(envoy_wasm_vm_null_active)

image.png
그런데 여기는 쿼리 데이터 자체가 잘못된 케이스이다.
새 버전으로 올라오면서 아무래도 메트릭 값에 변화가 생긴 것으로 추정된다.
image.png
처음 알았는데, 컨피그맵으로 등록된 대시보드는 수정이 제대로 반영이 안 된다.
그래서 간단하게 대시보드를 파서 쿼리를 다시 짰는데, 아무튼 wasm 관련한 데이터도 제대로 시각화할 수 있다.

image.png
마지막으로 워크로드 대시보드에서도 데이터를 확인할 수 있다.

결론

이스티오에서는 광범위한 메트릭을 노출해주고, 이것들을 커스텀할 수 있도록 다양한 방법을 지원한다.
여태 봤을 때는 커스텀을 하기보다는 그냥 있는 메트릭이나 잘 정제하는 게 이스티오를 좋게 활용하는 방향이라는 생각이 든다.
다만 그럼에도 커스텀을 해야 하는 케이스가 있을 수 있을 텐데, 만약 새로운 메트릭을 추가해야 하는 경우라면 이스티오의 확장 리소스인 엔보이 필터, 와즘 플러그인을 세팅해야 하므로 깊은 숙련도와 개발 역량이 요구된다고 볼 수 있겠다.

이전 글, 다음 글

다른 글 보기

이름 index noteType created
1W - 서비스 메시와 이스티오 1 published 2025-04-10
1W - 간단한 장애 상황 구현 후 대응 실습 2 published 2025-04-10
1W - Gateway API를 활용한 설정 3 published 2025-04-10
1W - 네이티브 사이드카 컨테이너 이용 4 published 2025-04-10
2W - 엔보이 5 published 2025-04-19
2W - 인그레스 게이트웨이 실습 6 published 2025-04-17
3W - 버츄얼 서비스를 활용한 기본 트래픽 관리 7 published 2025-04-22
3W - 트래픽 가중치 - flagger와 argo rollout을 이용한 점진적 배포 8 published 2025-04-22
3W - 트래픽 미러링 패킷 캡쳐 9 published 2025-04-22
3W - 서비스 엔트리와 이그레스 게이트웨이 10 published 2025-04-22
3W - 데스티네이션 룰을 활용한 네트워크 복원력 11 published 2025-04-26
3W - 타임아웃, 재시도를 활용한 네트워크 복원력 12 published 2025-04-26
4W - 이스티오 메트릭 확인 13 published 2025-05-03
4W - 이스티오 메트릭 커스텀, 프로메테우스와 그라파나 14 published 2025-05-03
4W - 오픈텔레메트리 기반 트레이싱 예거 시각화, 키알리 시각화 15 published 2025-05-03
4W - 번외 - 트레이싱용 심플 메시 서버 개발 16 published 2025-05-03
5W - 이스티오 mTLS와 SPIFFE 17 published 2025-05-11
5W - 이스티오 JWT 인증 18 published 2025-05-11
5W - 이스티오 인가 정책 설정 19 published 2025-05-11
6W - 이스티오 설정 트러블슈팅 20 published 2025-05-18
6W - 이스티오 컨트롤 플레인 성능 최적화 21 published 2025-05-18
8W - 가상머신 통합하기 22 published 2025-06-01
8W - 엔보이와 iptables 뜯어먹기 23 published 2025-06-01
9W - 앰비언트 모드 구조, 원리 24 published 2025-06-07
9W - 앰비언트 헬름 설치, 각종 리소스 실습 25 published 2025-06-07
7W - 이스티오 메시 스케일링 26 published 2025-06-09
7W - 엔보이 필터를 통한 기능 확장 27 published 2025-06-09

관련 문서

이름 noteType created
2024-04-07(일) - 2024-06-13
Prometheus Operator knowledge 2025-03-30
Prometheus knowledge 2025-02-26
Prometheus-Adapter knowledge 2025-03-04
4주차 - 관측 가능성 project 2025-02-23
4W - 프로메테우스 스택을 통한 EKS 모니터링 published 2025-02-28
E-이스티오 컨트롤 플레인 성능 최적화 topic/explain 2025-05-18
E-이스티오 메시 스케일링 topic/explain 2025-06-08

참고


  1. https://istio.io/latest/docs/reference/config/metrics/ ↩︎

  2. https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage ↩︎

  3. https://istio.io/latest/blog/2024/v1-apis/ ↩︎ ↩︎

  4. https://istio.io/latest/docs/reference/config/telemetry/#MetricSelector-IstioMetric ↩︎ ↩︎

  5. https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes ↩︎

  6. https://istio.io/latest/docs/tasks/observability/metrics/customize-metrics/#use-expressions-for-values ↩︎ ↩︎

  7. https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes#request-attributes ↩︎

  8. https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/img/custom-metrics-elements.png ↩︎

  9. https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config ↩︎

  10. https://netpple.github.io/docs/istio-in-action/Istio-ch7-observability#741-configuring-existing-metrics ↩︎

  11. https://istio.io/v1.17/docs/tasks/observability/metrics/customize-metrics/ ↩︎

  12. https://istio.io/latest/docs/tasks/observability/metrics/classify-metrics/ ↩︎

  13. https://github.com/istio-ecosystem/wasm-extensions ↩︎

  14. https://istio.io/latest/docs/tasks/observability/metrics/classify-metrics/ ↩︎

  15. https://istio.io/latest/docs/tasks/observability/telemetry/ ↩︎