はじめに
自宅クラスタで Istio Ambient Mesh を触ってみると、いくつかハマりどころがありました。
表面的には単なる 503 や timeout に見えても、実際には waypoint、HBONE、NetworkPolicy、AuthorizationPolicy、mesh 外 caller など、見るべきレイヤーが分かれていることが多いです。
例えば次のようなケースです。
- waypoint 経由のアクセスだけ 503 になる
- 同じ namespace 内通信なのに AuthorizationPolicy で拒否される
- admission webhook や health probe だけ失敗する
- 特定ノード上の workload だけ急に不安定になる
サイドカー型と比べると、Ambient Mesh は「どの通信が HBONE になっているか」「誰が mesh の外から来ているか」を意識しないと見通しが悪くなりがちです。
この記事では、実際に遭遇したトラブルをもとに、Ambient Mesh でまず最初に見るべきポイントをまとめます。
この記事の前提
この記事の内容は、少なくとも次のような環境を前提にしています。
- Istio Ambient Mesh
-
ztunnel+istio-cni - CNI は Cilium
- waypoint を使う namespace / service がある
-
AuthorizationPolicyとNetworkPolicyを併用している
特に NetworkPolicy や reconcileIptablesOnStartup まわりは、Istioバージョン や CNI の差で見え方が変わることがあります。
そのため「Ambient では常にこうなる」というより、「この前提ではまずここを見ると切り分けやすい」と読んでもらえると安全です。
先に結論
Ambient Mesh の切り分けは、まず次の順で見ると進めやすいです。
- その workload は
HBONEで enrolled されているか - ztunnel ログに
src.identityとdst.hbone_addrが出ているか - waypoint 経由なら NetworkPolicy が
15008/TCPを止めていないか -
kube-apiserverやkubeletのような mesh 外 caller をAuthorizationPolicyが落としていないか - 特定ノードだけ壊れるなら ztunnel / istio-cni / ノード再起動タイミングを疑う
この 5 つを分けて考えるだけで、かなり迷いにくくなります。
まずは HBONE になっているかを見る
最初に見るのは istioctl ztunnel-config workloads です。
istioctl ztunnel-config workloads
見るポイントはシンプルです。
-
PROTOCOL=HBONE: ひとまず ambient mesh への enroll 自体はできている -
PROTOCOL=TCP: ztunnel をバイパスしている可能性が高い
特定 namespace だけ見たいなら、例えばこんな形で絞れます。
istioctl ztunnel-config workloads | awk 'NR==1 || $NF=="TCP"' | \
grep -E "^(longhorn-system|monitoring|oauth2-proxy)\s"
ここで TCP が出ているなら、まずは NetworkPolicy より先に「HBONE 化に失敗している」と考えた方が早いです。
ztunnel ログでは src.identity と dst.hbone_addr が手がかりになる
次に ztunnel ログを見ます。
kubectl logs -n istio-system <ztunnel-pod> --since=5m | grep -E "<workload>|<port>|error|policy|deadline"
ここで見えれば手がかりになるのは次の 2 つです。
src.identity="spiffe://..."dst.hbone_addr=...
正常系では、この 2 つが出てくることがあります。
逆に問題があるときは、
-
src.identityがない -
dst.hbone_addrがない -
direction="inbound"だけが見える
という形になります。
このパターンは「送信側 pod のアウトバウンドが HBONE に変換されず、直接 TCP として ztunnel に入ってきている」ことを示すことが多いです。
つまり、AuthorizationPolicy に見えていても、根本は policy そのものではなく HBONE 化の失敗かもしれません。
PROTOCOL=TCP なら in-pod iptables を疑う
Ambient Mesh では、pod 作成時に istio-cni が pod の netns に iptables を設定し、アウトバウンドを ztunnel に流します。
ここが壊れると、annotation は付いていても実際には HBONE になりません。
よくある兆候は次のとおりです。
-
istioctl ztunnel-config workloadsではTCP - ztunnel ログに
src.identityが出ない -
ambient.istio.io/redirection: enabledは付いている - でも実際の
ISTIO_*ルールが pod netns に存在しない
このケースでは、まず問題 pod を再作成するのが第一候補です。
ノード再起動やアップグレード直後なら、istio-cni と ztunnel の起動競合も疑います。
なお、この文脈でよく出てくる reconcileIptablesOnStartup は、古い ambient の記事だと「明示的に有効化する」前提で書かれていることがありますが、Istio 1.29 では cni.ambient.reconcileIptablesOnStartup が default true に昇格しています。
そのため、バージョンを見ずに一律で「まず有効化」と考えない方が安全です。
HBONE なのに 503 なら NetworkPolicy を早めに疑う
ここは Ambient Mesh でかなりハマりやすいポイントです。
waypoint 経由の通信では、pod への到達に 15008/TCP が関わります。
そのため、Helm chart が自動生成した NetworkPolicy が app の listen port しか許可していないと、Ambient 化した途端に通信が落ちることがあります。
まず見るコマンドはこれです。
kubectl get -n <namespace> networkpolicies
kubectl get -n <namespace> networkpolicy <name> -o yaml
確認したいのは次の点です。
- Helm chart 自動生成の NetworkPolicy がないか
-
15008/TCPが許可されているか - 複数 policy を重ねた結果、想定より厳しくなっていないか
典型的には、
- workload は
HBONE - ztunnel の enroll 状態も正常そう
- それでも waypoint 経由アクセスだけ 503 / timeout
という見え方になります。
このときは ztunnel の enroll や AuthorizationPolicy ではなく、先に NetworkPolicy を疑った方が早いです。
ここで 1 つ設計上の考え方として持っておくとよいのが、15008/TCP は app 固有 port というより、Ambient Mesh の基盤 port に近いということです。
もちろん「クラスタ全体で 15008 を無条件許可する」という意味ではありません。
ambient 対象 workload に対しては、「waypoint や ztunnel から到達するために 15008/TCP が必要になることがある」と意識して、対象 workload や到達元を絞って許可する方が安全です。
実際のアクセス制御は AuthorizationPolicy 側で行い、NetworkPolicy 側ではまず HBONE を物理的に通せるようにする、という役割分担で考えるとハマりにくくなります。
webhook や probe だけ失敗するなら mesh 外 caller を疑う
もう 1 つの落とし穴が、kube-apiserver や kubelet のような mesh 外 caller です。
例えば次のような症状です。
- admission webhook だけタイムアウトする
- readiness / liveness probe だけ失敗する
- app 間通信は通るのに、operator まわりだけ不安定
このとき重要なのは、少なくとも kube-apiserver や kubelet のような caller は ambient mesh の外側にいるということです。
そのため、waypoint 経由の L7 制御だけを前提にすると整理しづらくなります。
整理すると、
-
src.identityを持たない -
from.source.namespacesによる制御に乗らない - namespace ベースの
AuthorizationPolicyだけだと落ちることがある
という整理になります。
少なくとも webhook / probe は L4 入口として考える
少なくとも kube-apiserver / kubelet 由来の通信は source identity が見えにくく、host や path ベースの制御だけでは整理しづらいです。
そのため、webhook や probe については次のように考えるのが分かりやすいです。
- webhook / probe 専用の受け口なら、その workload を ambient mesh 対象外に置く
- mesh 内通信も同じ workload が受けるなら、ambient に残したまま webhook / probe 用 port だけを狭く許可する
後者では、selector と port を絞った AuthorizationPolicy を使うのが基本です。
最小例:
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-webhook-port-9443
namespace: <namespace>
spec:
action: ALLOW
selector:
matchLabels:
app.kubernetes.io/name: <webhook-app>
rules:
- to:
- operation:
ports: ["9443"]
この例は「その workload の 9443 への到達を許可する」ための簡略化した例です。
専用 webhook port 以外には広げず、selector もできるだけ狭く保つ前提で使うのが安全です。
専用受け口として分離できるなら、その workload 自体を ambient mesh 対象外に置く方が分かりやすいこともあります。
「同じ namespace から来るはずだから allow-namespace で十分」と考えると、mesh 外 caller でハマります。
実際に遭遇した例
1. Longhorn で in-pod iptables 不全が起き、結果的に policy rejection に見えた
症状だけ見ると AuthorizationPolicy の問題に見えましたが、実際には src.identity がなく、送信側 pod が HBONE 化できていませんでした。
つまり、真因は policy そのものではなく in-pod iptables 未設定です。
このパターンでは、
PROTOCOL=TCP-
src.identityなし -
dst.hbone_addrなし
が強いシグナルになります。
2. Kiali で HBONE 自体は正常だが NetworkPolicy が 15008 を止めていた
このケースでは workload は ambient mesh に正常に enrolled されていました。
それでも waypoint 経由アクセスだけ 503 になっていて、原因は Kiali Helm chart が自動生成した NetworkPolicy が 15008/TCP を許可していないことでした。
イメージとしては次のような構図です。
つまり、
-
HBONEだから mesh は正常 - それでも落ちるなら NetworkPolicy
という切り分けが効きました。
3. admission webhook は mesh 外 caller を前提に考える必要があった
このリポジトリでは実際に、
- Longhorn manager に対する
9500,9501,9502 - Victoria Metrics Operator の webhook port
9443
を、selector と port を絞った AuthorizationPolicy で許可しています。
これは「mesh 内 workload 間通信を広く許可したい」のではなく、「ambient / waypoint を namespace や Helm app 全体に有効化した結果、その中に含まれる webhook も保護対象になり、kube-apiserver など mesh 外 caller から見て通りにくくなった」ことに対する例外です。
ここで大事なのは、同じ app に対するポリシーでも役割が 2 つあることです。
- 人がブラウザ経由でアクセスする経路: namespace / waypoint にぶら下がる L7 の
AuthorizationPolicyやCUSTOMauthz - admission webhook のような機械アクセス経路: webhook port に対する L4 の
AuthorizationPolicy
この 2 つを同じものとして扱うと整理しづらくなります。
イメージとしては次のような構図です。
ポイントは、app 全体を ambient / waypoint 配下に置くと、その app が内部で持っている webhook port も同じ namespace / policy の文脈に入ることです。
ただし admission webhook の caller は mesh 外の kube-apiserver なので、この部分は waypoint 経由の L7 制御として考えるより、selector + port を使う L4 の入口として分離して考える方が整理しやすくなります。
迷ったときのチェックリスト
-
istioctl ztunnel-config workloadsでHBONE/TCPを見る - ztunnel ログで
src.identityとdst.hbone_addrを確認する -
HBONEなのに落ちるなら NetworkPolicy を見る - webhook / probe だけ落ちるなら mesh 外 caller を疑う
- 特定ノードだけ壊れるなら ztunnel / istio-cni / ノード再起動タイミングを見る
まとめ
Ambient Mesh のトラブルシュートで大事なのは、現象を 1 つのレイヤーで見ないことだと思っています。
特に次の 5 つを分けて考えると、かなり整理しやすくなります。
- その workload は
HBONEで enrolled されているか - ztunnel ログに
src.identityとdst.hbone_addrが出ているか - waypoint 経由なら NetworkPolicy が
15008/TCPを止めていないか -
kube-apiserverやkubeletのような mesh 外 caller をAuthorizationPolicyが落としていないか - 特定ノードだけ壊れるなら ztunnel / istio-cni / ノード側の要因がないか
今回のトラブルシュートを整理してみると、src.identity と dst.hbone_addr は重要な観点でした。
この 2 つが見えるかどうかを意識するだけでも、かなり早い段階で切り分けしやすくなります。