概要
Istioを使ってサイドカーを利用している場合、ヘルスチェックの仕組みが通常のKubernetesとは異なります。
一見するとよく分からない仕組みになっているので図を用いながら説明します。
環境
- Kubernetes 1.31.7
- Istio 1.20.8
ヘルスチェックフロー
以下のようなマニフェストだとして説明します。
apiVersion: apps/v1 kind: Deployment metadata: name: sample-app spec: template: metadata: annotations: sidecar.istio.io/rewriteAppHTTPProbers: "true" # here spec: containers: - name: app image: nginx:latest ports: - containerPort: 80 livenessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 80 initialDelaySeconds: 5 periodSeconds: 10 - name: istio-proxy image: docker.io/istio/proxyv2:1.20.8
sidecar.istio.io/rewriteAppHTTPProbers: "true"の場合
デフォルトこちらになります。Istio-Sidecar(Envoy)は経由せず、pilot-agentが直接アプリケーションコンテナに対してヘルスチェックを行います。

Podを見ると次のように上書きされています。
$ kubectl get pod "$LIVENESS_POD" -o json | jq '.spec.containers[0].livenessProbe.httpGet'
{
"path": "/app-health/app/livez",
"port": 15020,
"scheme": "HTTP"
}
ただし中では次のようなマッピングがされています。
$ kubectl get pod "$LIVENESS_POD" -o=jsonpath="{.spec.containers[0].env[?(@.name=='ISTIO_KUBE_APP_PROBERS')]}"
{"name":"ISTIO_KUBE_APP_PROBERS","value":"{\"/app-health/app/livez\":{\"httpGet\":{\"path\":\"/health\",\"port\":80,\"scheme\":\"HTTP\"},\"timeoutSeconds\":5},\"/app-health/app/readyz\":{\"httpGet\":{\"path\":\"/ready\",\"port\":80,\"scheme\":\"HTTP\"},\"timeoutSeconds\":5}
sidecar.istio.io/rewriteAppHTTPProbers: "false"の場合
Envoyを意図的に経由させたい場合はこの設定を入れます。

この場合はkubeletからアプリコンテナへprobeを送ろうとしますが、iptablesによってEnvoyを経由させられてからアプリに届きます。
なぜsidecar.istio.io/rewriteAppHTTPProbersが生まれたか?
この設定が生まれた背景には、IstioでmTLSを有効にしている場合、ヘルスチェックが失敗する問題が発生したためです。
アプリケーションコンテナへのアクセスには適切な証明書が必要ですが、kubelet は Istio 発行証明書を持たない平文 HTTP クライアント なので、TLS ハンドシェイクの段階で Envoy が接続を切断してしまうのです。

その他
Envoy自体へのヘルスチェックは?
アプリコンテナだけでなく、Envoyコンテナに対しても当然ヘルスチェックがあります。

実はこれもpilot-agentへと転送され、Podがdrain中でないかなど判断してヘルスチェックを行います。
Istio周りのポートの使い分けの全体像が知りたい
こちらの図が非常に分かりやすかったので紹介します。

まとめ
Istioを使った場合のヘルスチェックについてまとめました。
パラメータによって挙動が異なったりする点を図示し、そのパラメータ自体がなぜ生まれたかなどを解説しました。