0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Istio Ambient Mesh で詰まったときに最初に見るポイント

0
Posted at

はじめに

自宅クラスタで 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 がある
  • AuthorizationPolicyNetworkPolicy を併用している

特に NetworkPolicyreconcileIptablesOnStartup まわりは、Istioバージョン や CNI の差で見え方が変わることがあります。
そのため「Ambient では常にこうなる」というより、「この前提ではまずここを見ると切り分けやすい」と読んでもらえると安全です。

先に結論

Ambient Mesh の切り分けは、まず次の順で見ると進めやすいです。

  1. その workload は HBONE で enrolled されているか
  2. ztunnel ログに src.identitydst.hbone_addr が出ているか
  3. waypoint 経由なら NetworkPolicy が 15008/TCP を止めていないか
  4. kube-apiserverkubelet のような mesh 外 caller を AuthorizationPolicy が落としていないか
  5. 特定ノードだけ壊れるなら 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.identitydst.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-apiserverkubelet のような mesh 外 caller です。

例えば次のような症状です。

  • admission webhook だけタイムアウトする
  • readiness / liveness probe だけ失敗する
  • app 間通信は通るのに、operator まわりだけ不安定

このとき重要なのは、少なくとも kube-apiserverkubelet のような 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 の AuthorizationPolicyCUSTOM authz
  • 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 の入口として分離して考える方が整理しやすくなります。

迷ったときのチェックリスト

  1. istioctl ztunnel-config workloadsHBONE / TCP を見る
  2. ztunnel ログで src.identitydst.hbone_addr を確認する
  3. HBONE なのに落ちるなら NetworkPolicy を見る
  4. webhook / probe だけ落ちるなら mesh 外 caller を疑う
  5. 特定ノードだけ壊れるなら ztunnel / istio-cni / ノード再起動タイミングを見る

まとめ

Ambient Mesh のトラブルシュートで大事なのは、現象を 1 つのレイヤーで見ないことだと思っています。

特に次の 5 つを分けて考えると、かなり整理しやすくなります。

  • その workload は HBONE で enrolled されているか
  • ztunnel ログに src.identitydst.hbone_addr が出ているか
  • waypoint 経由なら NetworkPolicy が 15008/TCP を止めていないか
  • kube-apiserverkubelet のような mesh 外 caller を AuthorizationPolicy が落としていないか
  • 特定ノードだけ壊れるなら ztunnel / istio-cni / ノード側の要因がないか

今回のトラブルシュートを整理してみると、src.identitydst.hbone_addr は重要な観点でした。
この 2 つが見えるかどうかを意識するだけでも、かなり早い段階で切り分けしやすくなります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?