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의 독립형 대체품이에요.
- 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 사이드카가 필요하지 않아요.
- 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 샘플을 기반으로 해요.
- 보안 메트릭 포트가 활성화된
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 리스너 포트예요.
- 아래 검증 단계에 사용할 환경 변수를 설정하세요.
$ 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}')
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로 메트릭을 성공적으로 스크래핑하는지 확인하세요.
- 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가 평문 백엔드로의 내부 라우팅을 처리해요.
레거시: 사이드카용 보안 메트릭
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
- 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
레거시: 게이트웨이용 보안 메트릭
- 포트
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
- 메시 안에 게이트웨이 텔레메트리 포트를 노출하는
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
- 보안 리스너에서 텔레메트리 포트로 요청을 라우팅하는
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
- 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와 인증서 프로비저닝에 대해서는 인증서 관리 문서를 참고하세요.