GKE Dataplane V2 の使用

このページでは、Google Kubernetes Engine(GKE)クラスタで GKE Dataplane V2 を有効にしてトラブルシューティングする方法について説明します。

新しい Autopilot クラスタでは、バージョン 1.22.7-gke.1500 以降とバージョン 1.23.4-gke.1500 以降で GKE Dataplane V2 が有効になっています。GKE Dataplane V2 の使用に関する問題が発生した場合は、トラブルシューティングに進んでください。

GKE Dataplane V2 を使用して GKE クラスタを作成する

GKE バージョン 1.20.6-gke.700 以降で新しいクラスタを作成する場合は、gcloud CLI か GKE API を使用して、GKE Dataplane V2 を有効にできます。GKE バージョン 1.17.9 以降を使用した新しいクラスタを作成する場合は、GKE Dataplane V2 をプレビューで有効にすることもできます。

Console

GKE Dataplane V2 を使用して新しいクラスタを作成するための作業は次のとおりです。

  1. Google Cloud コンソールで、[Kubernetes クラスタの作成] ページに移動します。

    [Kubernetes クラスタの作成] に移動

  2. [ネットワーキング] セクションで、[Dataplane V2 を有効にする] チェックボックスをオンにします。GKE Dataplane V2 にはネットワーク ポリシーの適用が組み込まれているため、[Dataplane V2 を有効にする] を選択すると、[Kubernetes ネットワーク ポリシーを有効にする] オプションは無効になります。

  3. [作成] をクリックします。

gcloud

GKE Dataplane V2 を使用して新しいクラスタを作成するには、次のコマンドを使用します。

gcloud container clusters create CLUSTER_NAME \
    --enable-dataplane-v2 \
    --enable-ip-alias \
    --release-channel CHANNEL_NAME \
    --location COMPUTE_LOCATION

次のように置き換えます。

  • CLUSTER_NAME: 新しいクラスタの名前。
  • CHANNEL_NAME: GKE バージョン 1.20.6-gke.700 以降を含むリリース チャンネル。リリース チャンネルを使用しない場合は、--release-channel ではなく --cluster-version フラグを使用してバージョン 1.20.6-gke.700 以降を指定することもできます。
  • COMPUTE_LOCATION: 新しいクラスタの Compute Engine のロケーション

API

GKE Dataplane V2 を使用して新しいクラスタを作成するには、create リクエストnetworkConfig オブジェクトdatapathProvider フィールドを指定します。

次の JSON スニペットは、GKE Dataplane V2 を有効にするために必要な構成を示しています。

"cluster":{
   "initialClusterVersion":"VERSION",
   "ipAllocationPolicy":{
      "useIpAliases":true
   },
   "networkConfig":{
      "datapathProvider":"ADVANCED_DATAPATH"
   },
   "releaseChannel":{
      "channel":"CHANNEL_NAME"
   }
}

次のように置き換えます。

  • VERSION: クラスタのバージョン。GKE 1.20.6-gke.700 以降にする必要があります。
  • CHANNEL_NAME: GKE バージョン 1.20.6-gke.700 以降を含むリリース チャンネル

GKE Dataplane V2 に関する問題のトラブルシューティング

このセクションでは、GKE Dataplane V2 の問題を調査して解決する方法について説明します。

  1. GKE Dataplane V2 が有効になっていることを確認します。

    kubectl -n kube-system get pods -l k8s-app=cilium -o wide
    

    GKE Dataplane V2 が実行中の場合、出力には接頭辞が anetd- の Pod が含まれます。anetd は GKE Dataplane V2 のネットワーク コントローラです。

  2. サービスまたはネットワーク ポリシーの適用に問題がある場合は、anetd の Pod ログを確認します。Cloud Logging で次のログセレクタを使用します。

    resource.type="k8s_container"
    labels."k8s-pod/k8s-app"="cilium"
    resource.labels.cluster_name="CLUSTER_NAME"
    
  3. Pod の作成に失敗した場合は、kubelet のログで手がかりを探します。Cloud Logging で次のログセレクタを使用します。

    resource.type="k8s_node"
    log_name=~".*/logs/kubelet"
    resource.labels.cluster_name="CLUSTER_NAME"
    

    CLUSTER_NAME は、クラスタの名前に置き換えます。または、すべてのクラスタのログを表示するには、完全に削除してください。

  4. anetd Pod が実行されていない場合は、cilium-config ConfigMap に変更がないか確認します。この ConfigMap 内の既存のフィールドを変更しないでください。このような変更によってクラスタが不安定になり、anetd が中断する可能性があるためです。ConfigMap は、新しいフィールドが追加された場合にのみ、デフォルトの状態に戻されます。既存のフィールドに対する変更は元に戻せないため、ConfigMap は変更またはカスタマイズしないことをおすすめします。

既知の問題

GKE Dataplane V2 を使用すると、次の既知の問題が発生することがあります。

準備ができていない Pod の接続タイムアウト

Pod の準備ができていない場合、関連付けられた Service への接続がタイムアウトすることがあります。これは GKE Dataplane V2 の想定される動作であり、より速く connection refused エラーを返すことがある kube-proxy とは異なります。

Cilium Identity の Identity-Relevant Label フィルタリングが有効にならず、Pod が ContainerCreating 状態で停止する

影響を受けるバージョン: 1.34、1.35

GKE Dataplane V2 クラスタでは、kube-system/cilium-config-emergency-override ConfigMap を介した Identity-Relevant Label フィルタリングの緊急使用が、影響を受けるバージョンで正しく適用されません。

このアプローチでは、Cilium ID の生成に使用される Pod ラベルが制限されます。

Pod から高カーディナリティのラベルキー/値を防止/削除する他のメカニズム(ツールやフレームワークによってラベルが適用される場合など)が使用できない場合は、Identity-Relevant Label フィルタリングを使用して、Cilium Identity の計算からラベルキーを除外できます。これらのルールの構成の詳細については、Cilium ドキュメントのID 関連ラベルをご覧ください。

影響を受ける GKE バージョンでは、オペレータによって作成された Cilium ID に除外されたラベルが引き続き含まれます。

現象

  • Cilium Identity の生成でフィルタリングされるラベルを持つ Pod が起動に失敗し、ContainerCreating 状態のままになることがあります。Pod イベントにタイムアウト エラーが表示されることがあります。

      {"level":"warning", "msg":"Error changing endpoint identity", "error":"unable to resolve identity: timed out waiting for cilium-operator to allocate CiliumIdentity for key ...;, error: exponential backoff cancelled via context: context canceled", "k8sPodName":"...", "subsys":"endpoint"}
    
  • フィルタリングされたラベルに基づいて ID を共有する代わりに、一意のラベル値を持つ Pod は一意の Cilium ID を生成し続けます。これにより、ID が急増し、使用可能な Cilium ID が枯渇(上限は 65,536)して、スケーラビリティの問題が発生する可能性があります。

修正済みのバージョン

この問題を解決するには、次のいずれかの GKE バージョンにクラスタをアップグレードします。

  • 1.34.6-gke.1307000 以降
  • 1.35.2-gke.1962000 以降

回避策

回避策として、ラベル フィルタリング ルールをメインの cilium-config ConfigMap の data.labels フィールドに適用し、cilium-config-emergency-override から削除します。GKE は、cilium-config ConfigMap 内で管理しないフィールドに対するユーザーの変更を保持するため、この状況はアップグレードなどのコントロール プレーン オペレーションでも維持されます。

  1. cilium-config-emergency-override ConfigMap の data セクションから labels キーを削除します(存在する場合)。
  2. data セクションで labels キーを追加または変更して、cilium-config ConfigMap を編集します。たとえば、uuid という名前のラベルが ID の生成に使用されないようにするには:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: cilium-config
      namespace: kube-system
    data:
      # ... other existing keys
      labels: "!uuid"
      # ... other existing keys
    
  3. コントロール プレーンで anet-operator を再起動するには、コントロール プレーンを、実行中のバージョンと同じバージョンにアップグレードします。これにより、オペレーターが強制的に再起動され、構成が再読み込みされます。

    gcloud container clusters upgrade CLUSTER_NAME \
        --location CLUSTER_LOCATION \
        --project PROJECT_ID \
        --cluster-version $(gcloud container clusters describe CLUSTER_NAME --location CLUSTER_LOCATION --project PROJECT_ID --format="value(currentMasterVersion)") \
        --master
    
  4. コントロール プレーンが再起動したら、anetd DaemonSet を再起動して、ノード エージェントも必要な変更を取得するようにします。

    kubectl rollout restart daemonset anetd -n kube-system
    

GKE Dataplane V2 クラスタ内の NodePort 範囲の競合に関連する、接続が断続的になる問題

GKE Dataplane V2 クラスタでは、マスカレード トラフィックまたはエフェメラル ポートの使用で、接続が頻繁に途切れる問題が生じることがあります。これらの問題は、予約済みの NodePort の範囲とポートの競合が原因となり、通常は次のシナリオで発生します。

  • カスタム ip-masq-agent: カスタム ip-masq-agent(バージョン 2.10 以降)を使用している場合、クラスタに NodePort サービスまたは Load Balancer サービスが存在します。これらサービスが NodePort の範囲と競合することで接続が断続的になることがあります。バージョン 2.10 以降では、ip-masq-agent--random-fully 引数がデフォルトで内部的に実装されています。これを軽減するには、ip-masq-agent 構成の引数で --random-fully=false(バージョン 2.11 以降で使用可能)を明示的に設定します。構成の詳細については、Standard クラスタでの IP マスカレード エージェントの構成をご覧ください。

  • エフェメラル ポート範囲の重複: GKE ノードの net.ipv4.ip_local_port_range で定義されたエフェメラル ポート範囲と NodePort 範囲(30000~32767)との重複によっても、接続の問題が発生することがあります。この問題を回避するには、これらの 2 つの範囲が重複しないようにします。

ip-masq-agent 構成とエフェメラル ポート範囲の設定を確認し、NodePort 範囲と競合していないことを確認します。接続が断続的になる問題が生じている場合は、これらの原因を検討し、構成を調整してください。

GKE Dataplane V2 クラスタでの hostPort に関連する接続の問題

影響を受ける GKE バージョン: 1.29 以降

GKE Dataplane V2 を使用するクラスタでは、トラフィックがターゲットとするノードの IP で、ポートが Pod で定義された hostPort となっている場合に、接続障害が発生することがあります。これらの問題は、主に次の 2 つのシナリオで発生します。

  • ノードの hostPort がパススルー ネットワーク ロードバランサの背後にある:

    hostPort は Pod を特定のノードのポートに結び付けます。また、パススルー ネットワーク ロードバランサはすべてのノード間でトラフィックを分散します。hostPort とパススルー ネットワーク ロードバランサを使用して Pod をインターネットに公開すると、ロードバランサによって Pod が実行されていないノードにトラフィックが送信される可能性があり、接続エラーの原因となります。これは、パススルー ネットワーク ロードバランサのトラフィックは必ずしも hostPort Pod に転送されないという GKE Dataplane V2 の既知の制限によるものです。

    回避策: パススルー ネットワーク ロードバランサを使用してノード上の Pod の hostPort を公開する場合は、Pod の hostIP フィールドにネットワーク ロードバランサの内部 IP アドレスまたは外部 IP アドレスを指定します。

    ports:
    - containerPort: 62000
      hostPort: 62000
      protocol: TCP
      hostIP: 35.232.62.64
    - containerPort: 60000
      hostPort: 60000
      protocol: TCP
      hostIP: 35.232.62.64
      # Assuming 35.232.62.64 is the external IP address of a passthrough Network Load Balancer.
    
  • hostPort が予約済みの NodePort の範囲と競合する:

    Pod の hostPort が予約済みの NodePort 範囲(30000~32767)と競合している場合、Cilium はトラフィックを Pod に転送できないことがあります。この動作はクラスタ バージョン 1.29 以降で確認されています。以前の Portmap メソッドに代わり、Cilium が hostPort 機能を管理するようになったことが原因です。これは Cilium の想定される動作であり、公開されているドキュメントにも記載されています。

これらの制限を以降のバージョンで修正する予定はありません。これらの問題の根本原因は Cilium の動作に関連しており、GKE で直接管理できる範囲にありません。

推奨事項: 信頼性を高めるために、hostPort ではなく NodePort サービスに移行することをおすすめします。NodePort サービスで同様の機能を利用できます。

ネットワーク ポリシーのポート範囲が有効にならない

GKE Dataplane V2 が有効になっているクラスタのネットワーク ポリシーで endPort フィールドを指定しても、有効になりません。

Kubernetes Network Policy API を使用して、ネットワーク ポリシーが適用されるポートの範囲を指定できます。この API は、Calico ネットワーク ポリシーを使用するクラスタでサポートされますが、GKE Dataplane V2 を使用するクラスタではサポートされません。

NetworkPolicy オブジェクトを API サーバーに書き込んだ後、読み返すことで、このオブジェクトの動作を確認できます。オブジェクトに endPort フィールドが含まれている場合、この機能が適用されます。endPort フィールドがない場合、この機能は適用されません。いずれの場合も、API サーバーに格納されているオブジェクトがネットワーク ポリシーの信頼できる情報源になります。

詳細については、KEP-2079: Network Policy to Port Ranges をご覧ください。

解決済みのバージョン

この問題を解決するには、GKE バージョン 1.32 以降にクラスタをアップグレードします。

接続トラッキング ルックアップが正しくないため、ネットワーク ポリシーによって接続が切断される

クライアント Pod が Service または内部パススルー ネットワーク ロードバランサの仮想 IP アドレスを介して自身に接続する場合、データプレーンでの conntrack ルックアップが正しく行われず、応答パケットが既存の接続の一部として識別されません。つまり、Pod の上り(内向き)トラフィックを制限するネットワーク ポリシーが、誤ってパケットに適用されます。

この問題の影響は、Service に構成された Pod の数によって異なります。たとえば、Service にバックエンド Pod が 1 つある場合、接続は常に失敗します。Service にバックエンド Pod が 2 つある場合、接続は 50% の確率で失敗します。

解決済みのバージョン

この問題を解決するには、次のいずれかの GKE バージョンにクラスタをアップグレードします。

  • 1.28.3-gke.1090000 以降

回避策

この問題は、Service マニフェストの portcontainerPort を同じ値に構成すると軽減できます。

ヘアピン接続フローのパケット ドロップ

Pod が Service を使用して自身との TCP 接続を作成し、Pod が接続の送信元と宛先の両方である場合、GKE Dataplane V2 eBPF 接続トラッキングは接続状態を誤って追跡し、conntrack エントリがリークします。

接続タプル(プロトコル、送信元 / 宛先 IP、送信元 / 宛先ポート)がリークすると、同じ接続タプルを使用する新しい接続で、戻りパケットがドロップされる可能性があります。

解決済みのバージョン

この問題を解決するには、次のいずれかの GKE バージョンにクラスタをアップグレードします。

  • 1.28.3-gke.1090000 以降
  • 1.27.11-gke.1097000 以降

回避策

以下のいずれかの回避策を使用します。

  • Pod 内で実行され、Service を使用して自身と通信する可能性があるアプリケーションに対して TCP 再利用(キープアライブ)を有効にします。これにより、TCP FIN フラグが発行されなくなり、conntrack エントリのリークを回避できます。

  • 短時間の接続を使用する場合は、Gateway などのプロキシ ロードバランサを使用して Pod を公開して、Service を公開します。これにより、接続リクエストの宛先がロードバランサの IP アドレスに設定され、GKE Dataplane V2 がループバック IP アドレスに対して SNAT を実行できなくなります。

GKE コントロール プレーンのアップグレードにより anetd Pod がデッドロックする

GKE Dataplane V2(高度なデータパス)が有効になっている GKE クラスタを、バージョン 1.27 から 1.28 にアップグレードすると、デッドロックが発生する可能性があります。古い Pod を終了できない、または anetd などの必要なコンポーネントをスケジュールできないために、ワークロードが中断する場合があります。

原因

クラスタのアップグレード プロセスでは、GKE Dataplane V2 コンポーネントに必要なリソースが増加します。この増加によりリソース競合が発生し、Cilium Container Network Interface(CNI)プラグインと Cilium デーモン間の通信が中断される可能性があります。

現象

次のような症状が生じる可能性があります。

  • anetd Pod が Pending 状態のままになる。
  • ワークロード Pod が Terminating 状態で停止する。
  • failed to connect to Cilium daemon などの Cilium 通信障害を示すエラーの発生。
  • Pod サンドボックス用ネットワーク リソース クリーンアップ中のエラーの発生。例:

    1rpc error: code = Unknown desc = failed to destroy network for sandbox "[sandbox_id]": plugin type="cilium-cni" failed (delete): unable to connect to Cilium daemon... connection refused
    

回避策

Standard クラスタ: この問題を解決して anetd Pod をスケジュールできるようにするには、影響を受けるノードへの割り当て可能なリソースを一時的に増やします。

  1. 影響を受けるノードを特定し、割り当て可能な CPU とメモリを確認するには、次のコマンドを実行します。

    kubectl get nodes $NODE_NAME -o json | jq '.status.allocatable | {cpu, memory}'
    
  2. 割り当て可能な CPU とメモリを一時的に増やすには、次のコマンドを実行します。

    kubectl patch node $NODE_NAME -p '{"status":{"allocatable":{"cpu":CPU_VALUE, "memory":MEMORY_VALUE}}}'
    

Autopilot クラスタ: Autopilot クラスタのデッドロックの問題を解決するには、影響のある Pod を強制的に削除してリソースを解放します。

kubectl delete pod POD_NAME -n NAMESPACE --grace-period=0 --force

次のように置き換えます。

  • POD_NAME: Pod の名前。
  • NAMESPACE: Pod の Namespace

ノードに割り当て可能なリソースを増やし、GKE バージョンの 1.27 から 1.28 へのアップグレードが完了すると、anetd Pod は新しいバージョンで実行されます。

containerID エラーが原因でノードが NodeNotReady 状態になる

クラスタが GKE バージョン 1.35.1-gke.1616000 以降にアップグレードされると、GKE Dataplane V2 と Cloud Service Mesh の両方が有効になっている場合、ノードがすぐに NodeNotReady 状態になることがあります。

原因

GKE バージョン 1.35.1-gke.1616000 以降、GKE Dataplane V2 クラスタは CNI 構成ファイルで CNI バージョン 1.1.0 を使用します。この変更では、Google Managed Istio などのダウンストリーム CNI プラグインも CNI バージョン 1.1.0 をサポートする必要があります。Managed Istio のロールアウトの遅延により、一部のクラスタでは互換性のあるバージョン(1.23)がまだ受信されておらず、初期化に失敗しています。

現象

影響を受けるノードはすぐに NodeNotReady と表示されます。containerd ログに次のエラー メッセージが表示されます。

NetworkPluginNotReady message:Network plugin returns error: missing containerID

回避策

この問題を軽減するには、影響を受けるクラスタを 1.35.1-gke.1616000 より前の GKE バージョンにダウングレードします。

カスタム eBPF プログラムの干渉

GKE は、eBPF プログラムを使用して GKE Dataplane V2 のネットワーキングを管理します。GKE マネージド ノード ネットワーク インターフェースにカスタム eBPF プログラムをデプロイすると、これらのプログラムが GKE マネージド eBPF プログラムに干渉し、ネットワーキングの問題が発生する可能性があります。

GKE は、次のネットワーク インターフェースに接続されたカスタム eBPF プログラムをサポートしていません。

  • eth*
  • ens4
  • lo
  • cilium*
  • gke*
  • veth*

これらのインターフェースにカスタム eBPF プログラムが存在すると、GKE Dataplane V2 anetd エージェントがインストールしたプログラムに干渉し、クラスタ ネットワーキングが中断される可能性があります。このようなプログラムを挿入するカスタム eBPF プログラムまたはワークロードは、クラスタから削除することをおすすめします。

カスタム eBPF プログラムを検出する

クラスタノードで実行されているカスタム eBPF プログラムを検出するには、hostNetwork: true 設定で構成された DaemonSet を作成します。この DaemonSet は、bpftool を使用して、このような eBPF プログラムをクエリします。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: bpftool-logger
  labels:
    app: bpftool-logger
spec:
  selector:
    matchLabels:
      app: bpftool-logger
  template:
    metadata:
      labels:
        app: bpftool-logger
    spec:
      hostPID: true
      hostNetwork: true
      containers:
      - name: bpftool
        image: ubuntu:22.04
        securityContext:
          privileged: true
        env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        command:
        - /bin/bash
        - -c
        - |
          echo "Installing dependencies..."
          apt-get update -y > /dev/null 2>&1
          apt-get install -y curl tar > /dev/null 2>&1

          echo "Downloading and setting up bpftool..."
          curl -sL https://github.com/libbpf/bpftool/releases/download/v7.7.0/bpftool-v7.7.0-amd64.tar.gz | tar xz
          chmod +x bpftool
          mv bpftool /usr/local/bin/

          echo "========== $(date) | Node: ${NODE_NAME} =========="
          bpftool net | grep -E '^(eth|ens4|lo|cilium|gke|veth)' | grep -v ' cil_'
          sleep infinity
  1. マニフェストを ebpf-discovery.yaml として保存し、DaemonSet を適用します。

    kubectl apply -f ebpf-discovery.yaml
    
  2. Pod が実行されるまで待ちます。

    kubectl rollout status ds/bpftool-logger
    
  3. Pod のログを確認して、eBPF プログラムを検出します。

    kubectl logs -l app=bpftool-logger
    
  4. 完了したら、DaemonSet を削除します。

    kubectl delete -f ebpf-discovery.yaml
    

次のステップ