Istio 사이드카 및 게이트웨이의 Prometheus 스크래핑 보안

Istio 사이드카 및 게이트웨이의 Prometheus 스크래핑 보안 (Securing Prometheus Scraping for Istio Sidecar and Gateway)

이 작업은 Prometheus와 Istio 상호 TLS(mTLS)를 사용해서 사이드카 모드에서 Istio 워크로드와 게이트웨이 메트릭을 안전하게 스크래핑하는 방법을 보여드려요. 기본적으로 Prometheus는 워크로드와 게이트웨이 메트릭을 평문 HTTP로 스크래핑해요.

출처: Istio 문서

본문

이 작업은 Istio mTLS를 통해 Prometheus를 사용해 Istio 사이드카 및 게이트웨이 메트릭을 안전하게 스크래핑하는 방법을 보여드려요. 기본적으로 Prometheus는 Istio 워크로드와 게이트웨이에서 메트릭을 평문 HTTP로 스크래핑해요. 이 작업에서는 메트릭이 상호 인증된 TLS 연결을 통해 안전하게 스크래핑되도록 Istio와 Prometheus를 구성해요. 이 문서는 사이드카와 게이트웨이가 노출하는 Envoy 및 Istio 생성 텔레메트리에 초점을 맞춰요. 애플리케이션 메트릭을 포함한 일반적인 Prometheus와 Istio 통합에 대해서는 Prometheus 통합 문서를 참고하세요.

[!note] Istio 1.31부터 보안 mTLS 메트릭 포트를 네이티브로 활성화할 수 있어요. 이전 Istio 버전은 레거시 해결 방법 섹션을 참고하세요.

기본 메트릭 스크래핑 이해하기 (Understand default metrics scraping)

기본적으로 Istio는 /stats/prometheus 엔드포인트에서 메트릭을 노출해요.

  • 워크로드 메트릭은 사이드카 텔레메트리 포트(15020) 또는 Envoy 전용 포트(15090)에서 제공돼요.
  • 게이트웨이 메트릭은 게이트웨이 파드 텔레메트리 포트에서 제공돼요.
  • 이 엔드포인트는 상호 TLS로 보호되지 않으며, HTTPS로 직접 스크래핑하는 것은 권장되지 않아요. 이 작업의 접근 방식은 전용 mTLS 보호 리스너를 추가해서 Prometheus가 암호화되고 상호 인증된 연결로 스크래핑하도록 해요.

시작하기 전에 (Before you begin)

  • default 프로필을 사용해서 클러스터에 Istio를 설치하세요.

mTLS 스크래핑을 위한 Prometheus 구성하기 (Configure Prometheus for mTLS scraping)

보안 포트를 스크래핑할 때 Prometheus는 메시 CA가 신뢰하는 유효한 인증서를 제시해야 해요. 해당 자격 증명을 프로비저닝하는 가장 간단한 방법은 Prometheus 파드에 Istio 사이드카를 주입하고 OUTPUT_CERTS를 사용해서 공유 볼륨에 워크로드 인증서를 쓰는 거예요. prometheus-secure-metrics 샘플(samples/addons/extras/prometheus-secure-metrics.yaml)은 사이드카 주입, 인증서 내보내기, mTLS 스크래핑 잡이 미리 구성된 samples/addons/prometheus.yaml의 독립형 대체품이에요.

  1. mTLS 스크래핑이 미리 구성된 Prometheus를 배포하세요.
$ kubectl apply -n istio-system -f @samples/addons/extras/prometheus-secure-metrics.yaml@
$ kubectl rollout status deployment/prometheus -n istio-system

샘플은 표준 Prometheus 애드온과 비교해 다음 주요 설정을 구성해요.

  • sidecar.istio.io/inject: "true" 레이블 - Prometheus 파드의 "false" 기본값을 오버라이드해서 사이드카 주입을 활성화해요.
  • OUTPUT_CERTS: /etc/istio-certs - 사이드카가 공유 볼륨에 워크로드 인증서, 키, 루트 CA를 쓰도록 지시해서 Prometheus가 mTLS 스크래핑에 사용할 수 있게 해요.
  • INBOUND_CAPTURE_PORTS: "" - 사이드카가 인바운드 Prometheus 트래픽을 가로채지 못하게 해요. 사이드카는 인증서 프로비저닝에만 사용돼요.
  • sidecar.istio.io/userVolumeMount - istio-proxy 컨테이너에 인증서 볼륨을 마운트해서 인증서를 쓸 수 있게 해요. 같은 볼륨을 prometheus-server에도 마운트해서 읽을 수 있게 해요. 두 마운트 모두 필요해요.
  • 스크래핑 잡 - ConfigMap에는 두 개의 미리 구성된 mTLS 스크래핑 잡(istio-secure-merged-metrics는 포트 15092, istio-secure-envoy-metrics는 포트 15091)이 있고, prometheus.istio.io/secure-port와 prometheus.istio.io/secure-envoy-port 어노테이션으로 파드를 발견해요.

[!note] 사이드카 기반 인증서 프로비저닝의 대안으로, Istio를 cert-manager와 통합해서 Prometheus용 인증서를 프로비저닝할 수 있어요. 이 모델에서는 Istio 사이드카가 필요하지 않아요.

  1. Prometheus 파드에 Istio 사이드카가 주입되고 실행 중인지 확인하세요.
$ kubectl get pod -n istio-system -l app.kubernetes.io/name=prometheus
NAME                          READY   STATUS    RESTARTS   AGE
prometheus-6c647c84c8-gpxt4   3/3     Running   0          75s

네이티브 mTLS 메트릭 포트 활성화하기 (Istio 1.31+) (Enable native mTLS metrics ports)

Istio 1.31은 모든 Envoy 프록시(사이드카와 게이트웨이 프록시 모두)에 mTLS 보호 정적 부트스트랩 리스너를 직접 주입하는 두 환경 변수를 도입했어요. | Variable | Default | Description | | ENVOY_SECURE_METRICS_PORT | 0 (disabled) | Envoy 전용 stats 포트(15090)로 프록시하는 mTLS 리스너를 추가해요 | | ENVOY_SECURE_MERGED_METRICS_PORT | 0 (disabled) | 병합 메트릭 포트(15020, 앱과 에이전트 stats 포함)로 프록시하는 mTLS 리스너를 추가해요 | 설정하면 Envoy가 부트스트랩 시 구성된 리스너를 추가해요. 스크래퍼는 메시 CA가 신뢰하는 인증서를 제시해야 해요. 이는 (위에서 프로비저닝한 것 같은) Istio 워크로드 인증서 또는 cert-manager 같은 신뢰된 CA가 발급한 인증서일 수 있어요.

사이드카 워크로드에서 활성화하기

이 예시는 워크로드로 httpbin을 사용해요. 아래 매니페스트는 Deployment에 보안 메트릭 어노테이션을 추가한 httpbin 샘플을 기반으로 해요.

  1. 보안 메트릭 포트가 활성화된 httpbin을 배포하세요.
$ kubectl label namespace default istio-injection=enabled --overwrite
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 8080
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
      annotations:
        proxy.istio.io/config: |
          proxyMetadata:
            ENVOY_SECURE_METRICS_PORT: "15091"
            ENVOY_SECURE_MERGED_METRICS_PORT: "15092"
        prometheus.io/path: "/stats/prometheus"
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/mccutchen/go-httpbin:v2.15.0
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 8080
EOF
  • ENVOY_SECURE_METRICS_PORT(15091)은 Envoy 전용 stats용 mTLS 리스너 포트예요.
  • ENVOY_SECURE_MERGED_METRICS_PORT(15092)는 병합 메트릭(Envoy + 애플리케이션 + 에이전트)용 mTLS 리스너 포트예요.
  1. 아래 검증 단계에 사용할 환경 변수를 설정하세요.
$ export HTTPBIN_POD=$(kubectl get pod -n default -l app=httpbin -o jsonpath='{.items[0].metadata.name}')
$ export HTTPBIN_IP=$(kubectl get pod -n default -l app=httpbin -o jsonpath='{.items[0].status.podIP}')
$ export PROM_POD=$(kubectl get pod -n istio-system -l app.kubernetes.io/name=prometheus -o jsonpath='{.items[0].metadata.name}')
  1. httpbin 사이드카에 보안 리스너가 구성됐는지 확인하세요.
$ istioctl proxy-config listeners "$HTTPBIN_POD" -n default | grep -E "15090|15091|15092"
0.0.0.0       15090 ALL                                                                                     Inline Route: /stats/prometheus*
0.0.0.0       15091 Trans: tls                                                                              Inline Route: /stats/prometheus*
0.0.0.0       15092 Trans: tls                                                                              Inline Route: /stats/prometheus*, /metrics*

포트 15091과 15092의 Trans: tls는 mTLS 리스너가 활성화됐음을 확인해요.

게이트웨이에서 활성화하기

게이트웨이 프록시는 같은 pilot-agent 부트스트랩 경로를 사용하므로 같은 변수가 동일하게 동작해요. [Istio APIs] 1. 인그레스 게이트웨이 Deployment를 패치하세요.

$ kubectl patch deployment istio-ingressgateway -n istio-system --type=strategic --patch-file=/tmp/gateway-secure-metrics-patch.yaml
$ kubectl rollout status deployment/istio-ingressgateway -n istio-system
$ cat <<EOF > /tmp/gateway-secure-metrics-patch.yaml
spec:
  template:
    metadata:
      annotations:
        prometheus.istio.io/secure-port: "15092"
        prometheus.io/path: "/stats/prometheus"
    spec:
      containers:
      - name: istio-proxy
        env:
        - name: ENVOY_SECURE_METRICS_PORT
          value: "15091"
        - name: ENVOY_SECURE_MERGED_METRICS_PORT
          value: "15092"
EOF
$ export GW_POD=$(kubectl get pod -n istio-system -l app=istio-ingressgateway -o jsonpath='{.items[0].metadata.name}')

인그레스 게이트웨이에 보안 리스너가 구성됐는지 확인하세요.

$ istioctl proxy-config listeners "$GW_POD" -n istio-system | grep -E "15090|15091|15092"
0.0.0.0   15090 ALL        Inline Route: /stats/prometheus*
0.0.0.0   15091 Trans: tls Inline Route: /stats/prometheus*
0.0.0.0   15092 Trans: tls Inline Route: /stats/prometheus*, /metrics*

포트 15091과 15092의 Trans: tls는 게이트웨이에서 mTLS 리스너가 활성화됐음을 확인해요.

[Gateway API] 1. Gateway 리소스를 패치해서 보안 리스너를 활성화하세요.

$ kubectl patch gateway istio-ingressgateway -n istio-system --type=merge --patch-file=/tmp/gateway-api-secure-metrics-patch.yaml
$ cat <<EOF > /tmp/gateway-api-secure-metrics-patch.yaml
spec:
  infrastructure:
    annotations:
      proxy.istio.io/config: |
        proxyMetadata:
          ENVOY_SECURE_METRICS_PORT: "15091"
          ENVOY_SECURE_MERGED_METRICS_PORT: "15092"
      prometheus.istio.io/secure-port: "15092"
      prometheus.io/path: "/stats/prometheus"
EOF
$ export GW_POD=$(kubectl get pod -n istio-system -l gateway.networking.k8s.io/gateway-name=istio-ingressgateway -o jsonpath='{.items[0].metadata.name}')

게이트웨이 파드에 보안 리스너가 구성됐는지 확인하세요.

$ istioctl proxy-config listeners "$GW_POD" -n istio-system | grep -E "15090|15091|15092"
0.0.0.0   15090 ALL        Inline Route: /stats/prometheus*
0.0.0.0   15091 Trans: tls Inline Route: /stats/prometheus*
0.0.0.0   15092 Trans: tls Inline Route: /stats/prometheus*, /metrics*

포트 15091과 15092의 Trans: tls는 게이트웨이에서 mTLS 리스너가 활성화됐음을 확인해요.

완전히 강화된 설정 (Fully hardened setup)

완전히 강화된 배포를 위해 보안 포트를 METRICS_LOCALHOST_ACCESS_ONLY와 결합하세요. 이는 기본 평문 포트(15090과 15020)를 localhost로 제한해서 mTLS 리스너만 외부에서 접근 가능한 유일한 스크래핑 표면으로 만들어요.

$ cat <<EOF > /tmp/httpbin-hardened-patch.yaml
spec:
  template:
    metadata:
      annotations:
        proxy.istio.io/config: |
          proxyMetadata:
            ENVOY_SECURE_METRICS_PORT: "15091"
            ENVOY_SECURE_MERGED_METRICS_PORT: "15092"
            METRICS_LOCALHOST_ACCESS_ONLY: "true"
        prometheus.io/path: "/stats/prometheus"
EOF
$ kubectl patch deployment httpbin -n default --type=merge --patch-file=/tmp/httpbin-hardened-patch.yaml

[!note] METRICS_LOCALHOST_ACCESS_ONLY를 설정하면 파드 외부에서 포트 15090과 15020으로의 직접 HTTP 접근이 차단돼요. 이 설정을 적용하기 전에 Prometheus가 mTLS 포트를 통해 스크래핑하도록 구성했는지 확인하세요. [!note] 개별 Deployment를 패치하지 않고 메시 전체에 적용하려면 설치 시 IstioOperator를 사용하세요.

$ cat <<EOF > ./istio-secure-metrics.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    defaultConfig:
      proxyMetadata:
        ENVOY_SECURE_METRICS_PORT: "15091"
        ENVOY_SECURE_MERGED_METRICS_PORT: "15092"
        METRICS_LOCALHOST_ACCESS_ONLY: "true"
  components:
    ingressGateways:
    - name: istio-ingressgateway
      enabled: true
      k8s:
        podAnnotations:
          prometheus.istio.io/secure-port: "15092"
          prometheus.io/path: "/stats/prometheus"
EOF
$ istioctl install -f ./istio-secure-metrics.yaml

이렇게 설치하면 istioctl은 proxyMetadata 값을 컨테이너 환경 변수로 직접 렌더링해서 사이드카와 게이트웨이 모두에서 보안 리스너를 활성화해요. components.ingressGateways.k8s.podAnnotations 블록은 게이트웨이 파드에 Prometheus 발견 어노테이션을 추가해요. 사이드카 워크로드의 경우 prometheus.istio.io/secure-port는 사이드카 주입기에 의해 ENVOY_SECURE_MERGED_METRICS_PORT 값으로 자동 설정되므로, Deployment별 어노테이션이 필요 없어요.

검증 (Verification)

Prometheus로 보안 메트릭 스크래핑 검증하기

구성을 완료한 후 Prometheus가 상호 TLS로 메트릭을 성공적으로 스크래핑하는지 확인하세요.

  1. Prometheus 파드에서 워크로드 인증서로 보안 포트에 curl해서 mTLS 스크래핑이 성공하는지 확인하세요.
$ kubectl exec -n istio-system "$PROM_POD" -c istio-proxy -- \
    curl -s -o /dev/null -w "%{http_code}" --max-time 5 \
    --cacert /etc/istio-certs/root-cert.pem \
    --cert /etc/istio-certs/cert-chain.pem \
    --key /etc/istio-certs/key.pem \
    --insecure \
    https://"$HTTPBIN_IP":15092/stats/prometheus
200

HTTP 200 응답은 Prometheus 파드가 httpbin의 포트 15092와 mTLS 핸드셰이크를 성공적으로 완료하고 메트릭을 가져왔음을 확인해요. --insecure 플래그는 호스트네임 검증만 건너뛰는 거예요. Istio 워크로드 인증서는 IP 주소가 아니라 SPIFFE URI SAN(예: spiffe://cluster.local/ns/default/sa/httpbin)을 사용하므로 curl이 파드 IP를 인증서와 매칭할 수 없기 때문이에요. 상호 TLS 핸드셰이크와 인증서 교환은 여전히 일어나므로 --cacert, --cert, --key가 여전히 필요해요. 이것이 Prometheus 스크래핑 잡이 insecure_skip_verify: true를 사용하는 이유이기도 해요. 2. Prometheus UI에서 스크래핑 대상 확인하기. istioctl dashboard prometheus -n istio-system으로 Prometheus 대시보드를 연 다음 Status → Targets로 이동하세요. istio-secure-merged-metrics와 istio-secure-envoy-metrics 잡이 httpbin 파드를 상태 UP으로 나열하고 엔드포인트가 https://<pod-ip>:15092/stats/prometheus 형식인지 확인하세요. 3. 보안 포트에 평문 HTTP 요청이 거부됨을 확인해서 mTLS가 적용되는지 검증하세요.

$ kubectl exec -n default "$HTTPBIN_POD" -c istio-proxy -- curl -s --max-time 3 http://"$HTTPBIN_IP":15091/stats/prometheus
upstream connect error or disconnect/reset before headers. reset reason: connection termination

연결 종료 오류는 포트가 TLS 연결만 수락함을 확인해요 - 평문 HTTP 요청은 즉시 거부돼요. 이는 Prometheus가 원시 텔레메트리 포트(15020 또는 15090)에 직접 접근하는 대신, 네이티브 보안 포트를 통해 HTTPS over Istio mTLS로 메트릭을 스크래핑함을 확인해요.

정리 (Cleanup)

[Istio APIs]

$ kubectl delete -n istio-system -f @samples/addons/extras/prometheus-secure-metrics.yaml@
$ kubectl delete -f @samples/httpbin/httpbin.yaml@
$ kubectl label namespace default istio-injection-

[Gateway API]

$ kubectl delete -n istio-system -f @samples/addons/extras/prometheus-secure-metrics.yaml@
$ kubectl delete -f @samples/httpbin/httpbin.yaml@
$ kubectl delete gateway istio-ingressgateway -n istio-system
$ kubectl label namespace default istio-injection-

레거시 해결 방법 (Istio < 1.31) (Legacy workaround)

1.31보다 오래된 Istio를 실행 중이라면 네이티브 환경 변수 방식은 사용할 수 없어요. 아래 단계는 Istio CRD를 사용해 보안 메트릭 스크래핑을 달성하는 한 가지 방법을 보여드려요. 15091 포트(Prometheus에 노출)에 보안 TLS 프론트엔드가 생성되고, 내부적으로 15020 포트(병합 메트릭 - Envoy + 애플리케이션 + 에이전트) 또는 15090 포트(Envoy 전용 메트릭)로 라우팅해요. 스크래퍼는 ISTIO_MUTUAL TLS로 15091에 연결하고, ServiceEntry와 VirtualService가 평문 백엔드로의 내부 라우팅을 처리해요.

레거시: 사이드카용 보안 메트릭

  1. httpbin을 배포하고 포트 15091에 보안 인그레스 리스너가 있는 Sidecar 리소스를 만드세요.
$ kubectl label namespace default istio-injection=enabled --overwrite
$ kubectl apply -f @samples/httpbin/httpbin.yaml@
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Sidecar
metadata:
  name: secure-metrics
  namespace: default
spec:
  ingress:
  - port:
      number: 15091
      name: https-metrics
      protocol: HTTP
    defaultEndpoint: 127.0.0.1:15020 # Change to 15090 for Envoy-only metrics
EOF
  1. Prometheus 발견을 위해 워크로드 파드에 어노테이션을 추가하세요.
$ kubectl annotate pod -n default \
  -l app=httpbin \
  prometheus.io/scrape="true" \
  prometheus.io/path="/stats/prometheus" \
  prometheus.istio.io/secure-port="15091" \
  --overwrite

레거시: 게이트웨이용 보안 메트릭

  1. 포트 15091에 보안 HTTPS 리스너가 있는 Gateway를 만드세요.
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: metrics-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 15091
      name: https-metrics
      protocol: HTTPS
    tls:
      mode: ISTIO_MUTUAL
    hosts: ["*"]
EOF
  1. 메시 안에 게이트웨이 텔레메트리 포트를 노출하는 ServiceEntry를 만드세요.
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: gateway-admin
  namespace: istio-system
spec:
  hosts: [gateway-admin.local]
  location: MESH_INTERNAL
  ports:
  - number: 15020  # Change to 15090 for Envoy-only metrics
    name: http-metrics
    protocol: HTTP
  resolution: STATIC
  endpoints:
  - address: 127.0.0.1
EOF
  1. 보안 리스너에서 텔레메트리 포트로 요청을 라우팅하는 VirtualService를 만드세요.
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: gateway-metrics
  namespace: istio-system
spec:
  hosts: ["*"]
  gateways: [metrics-gateway]
  http:
  - match:
    - uri:
        prefix: /stats/prometheus
    route:
    - destination:
        host: gateway-admin.local
        port:
          number: 15020  # Change to 15090 for Envoy-only metrics
EOF
  1. Prometheus 발견을 위해 게이트웨이 파드에 어노테이션을 추가하세요.
$ kubectl annotate pod -n istio-system \
  -l app=istio-ingressgateway \
  prometheus.istio.io/secure-port=15091 \
  --overwrite

레거시 정리

$ kubectl delete sidecar secure-metrics -n default
$ kubectl delete gateway metrics-gateway -n istio-system
$ kubectl delete serviceentry gateway-admin -n istio-system
$ kubectl delete virtualservice gateway-metrics -n istio-system
$ kubectl delete -n istio-system -f @samples/addons/extras/prometheus-secure-metrics.yaml@
$ kubectl delete -f @samples/httpbin/httpbin.yaml@
$ kubectl label namespace default istio-injection-

더 알아보기 (Learn more)

  • Prometheus와 Istio 통합에 대한 자세한 내용은 Prometheus 통합 문서를 참고하세요.
  • 메시 CA와 인증서 프로비저닝에 대해서는 인증서 관리 문서를 참고하세요.