本文へスキップ

Kubernetes ネットワーク図:Pod、Service、CNI、ネットワークポリシー

Kubernetes のネットワークを層ごとに解説します。Pod IP と CNI、Service、CoreDNS、NetworkPolicy、Gateway API を扱います。

· 創業者・開発者

公開日:

2 つのワーカーノードのフレーム。ユーザーは Gateway に到達し、Gateway は worker-1 の web Pod にルーティングします。web Pod は CoreDNS で api Service を引き、その ClusterIP に接続して worker-2 の 2 つの api Pod に届きます。NetworkPolicy がそれらを選択し、VXLAN トンネルが両ノードを結びます。

shop Namespace の Pod が http://api を呼び出し、リクエストがタイムアウトします。Pod は動いていて、Service も存在し、その EndpointSlice には準備完了のバックエンドが 2 つ載っています。原因は DNS、kube-proxy がノードに書いたルール、ノード間のオーバーレイ、先週誰かが適用した NetworkPolicy のどれかにあるかもしれません。それぞれ別の層で、別のコンポーネントが動かしています。

この記事では、図を使ってそれらの層を順に見ていきます。ネットワークモデル、Pod 間トラフィック、CNI プラグイン、Service、DNS、NetworkPolicy、そしてクラスターへの出入りのトラフィックです。

Kubernetes のネットワークモデル

Kubernetes のネットワークモデルには 3 つのルールがあります。各 Pod はクラスター全体で有効な固有の IP アドレスを持ち、その中のコンテナで共有します。各 Pod は、どのノード上にあっても、プロキシや NAT なしで他のすべての Pod に到達できます。そして kubelet のようなノードエージェントは、自分のノード上のすべての Pod に到達できます。

Kubernetes が API を定義し、実際の処理はインストールしたコンポーネントが行います。

層 提供するもの 実装するもの
Pod ネットワーク Pod ごとの IP、Pod 間の到達性 CNI プラグイン
Service Pod の集合に対する安定した仮想 IP と名前 kube-proxy または eBPF による代替
DNS api.shop.svc.cluster.local のような名前 CoreDNS
NetworkPolicy Pod トラフィックの許可ルール CNI プラグイン
Ingress、Gateway API クラスター外からのルーティング 自分でインストールするコントローラー

この分担を知っていると、多くの意外な挙動に説明がつきます。たとえば、API サーバーは受け付けるのに、プラグインが黙って無視する NetworkPolicy などです。

同じノード上の Pod 間通信

コンテナランタイムは Pod を起動するとき、ADD で CNI プラグインを呼び出します。多くのプラグインは veth ペアを作成し、IP を割り当てます。veth ペアは仮想ケーブルで、片端が Pod 内の eth0、もう片端がホスト側にあります。Flannel はホスト側の端を Linux ブリッジ cni0 に接続します。Calico はブリッジを使いません。各 Pod に /32 ルートを持たせ、ホストがそのルーターとして動作します。どちらの場合も、同じノード内のトラフィックにカプセル化も NAT も不要です。

ノードをまたぐ Pod 間通信:オーバーレイとネイティブルーティング

通常、各ノードは Pod 範囲の一部を持ちます。kube-controller-manager の --node-cidr-mask-size が既定の 24 なら、10.244.0.0/16 の範囲は 256 ノードにそれぞれ /24 を与えます。これは kubelet の既定である 110 Pod に対して 256 アドレスということです。ノード間では、プラグインに 2 つの選択肢があります。

オーバーレイ(VXLAN、Geneve、IP-in-IP) ネイティブルーティング(BGP、クラウドのルート、VPC IP)
ノード間のネットワークから見えるもの ノード IP のみ Pod IP
必要なもの ノード間の IP 到達性 Pod 範囲をルーティングするネットワーク、または Pod 用の VPC アドレス
パケットあたりのオーバーヘッド IPv4 の VXLAN で 50 バイト、IP-in-IP で 20 バイト なし
例 Flannel と Cilium の既定、Calico VXLAN BGP を使う Calico、AWS VPC CNI、Azure CNI

このオーバーヘッドは MTU の問題です。Calico の MTU ガイドによると、1,500 バイトのネットワークでは、IPv4 の VXLAN の内側にある Pod の MTU は 1,450 が必要です。ここを誤ると大きなパケットが消え、原因不明のハングのように見えます。

CNI プラグインの比較

プラグイン ノード間トラフィック NetworkPolicy 備考
Flannel 既定は VXLAN(UDP 8472)。共有のレイヤー 2 ネットワークでは host-gw Flannel 自体は適用しない。kube-network-policies か Calico を追加する 小さくてシンプル
Calico BGP、または VXLAN(UDP 4789)か IP-in-IP のオーバーレイ フル対応。加えて Calico の Namespace 単位とグローバルのポリシー iptables、nftables、eBPF、VPP、Windows のデータプレーン
Cilium 既定は VXLAN(UDP 8472)、Geneve、またはネイティブルーティング フル対応。加えてレイヤー 7 と DNS ベースのルール eBPF データパス
AWS VPC CNI オーバーレイなし。Pod は VPC アドレスを取得 v1.14 以降、enableNetworkPolicy を指定 ノードあたりの Pod 数はインスタンスタイプで制限

AWS の制限は早い段階で効いてきます。既定の上限は ENI 数 × (ENI あたりの IP 数 − 1) + 2 なので、10 アドレスのインターフェースを 3 つ持つ m5.large では 3 × 9 + 2 = 29 Pod になります。プレフィックス委任でこの上限を引き上げられます。

Service:入れ替わる Pod の前に置く安定した IP

Pod の IP は、Pod が置き換わるたびに変わります。Service は Pod の集合に、安定した仮想 IP である ClusterIP と DNS 名を与えます。背後にある Pod は EndpointSlice が追跡します。

タイプ 得られるもの
ClusterIP(既定) クラスター内の仮想 IP
NodePort 加えて各ノードのポート。既定は 30000–32767
LoadBalancer 加えてクラウドまたはアドオンのロードバランサー
ExternalName DNS エイリアス(CNAME)。プロキシはしない
Headless(clusterIP: None) 仮想 IP なし。DNS が Pod IP を返す

ClusterIP を所有するマシンは 1 台もありません。各ノードで kube-proxy がカーネルを設定し、10.96.48.20:80 宛てのパケットを、クライアントのノードを出る前に 10.244.2.22:8080 のような Pod アドレスへ書き換えます(宛先 NAT)。Service はサーバーではなく、論理的なステップです。ルールはプロキシモードによって変わります。

モード Kubernetes 1.37 での状況 仕組み
iptables Linux での既定 Service とエンドポイントごとのルール。バックエンドはランダム
nftables 1.33 から GA。将来の既定になる予定 大規模でも更新が速い。カーネル 5.13 以降が必要
ipvs 1.35 から非推奨。1.40 で既定では無効、1.43 で削除 カーネルの IPVS テーブル、より多くの分散アルゴリズム
eBPF(Cilium、Calico) kube-proxy を置き換える eBPF プログラムでロードバランシング

アップグレードで変わらないようにモードを明示的に設定し、IPVS からの移行を計画してください。nftables モードでは、NodePort は既定でノードのプライマリアドレスでのみ待ち受けます。

CoreDNS による DNS

CoreDNS は既定のクラスター DNS で、kubeadm は今も kube-dns という名前の Service の背後で動かします。各 Service には <service>.<namespace>.svc.cluster.local という名前が付き、これは ClusterIP に解決され、Headless Service では Pod IP に解決されます。kubelet は各 Pod の /etc/resolv.conf を書き込みます。kubeadm クラスターの shop では、通常次のようになります。

nameserver 10.96.0.10
search shop.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

落とし穴は ndots:5 です。api.stripe.com のようにドットが 5 つ未満の名前は、まずすべての検索ドメインを付けて試されるため、外部への 1 回の名前解決で失敗するクエリが 3 回発生することがあります。頻繁に使う名前には末尾にドットを付けるか、Pod の dnsConfig で ndots を下げるか、NodeLocal DNSCache を動かしてください。その後、外部の名前は DNS の仕組みで示した経路をたどります。

NetworkPolicy:既定で許可から最小権限へ

既定では、すべての Pod がどこからのトラフィックも受け付けます。いずれかの NetworkPolicy が Pod を ingress(または egress)で選択した時点から、いずれかのポリシーが許可したものだけが通ります。応答は自動的に許可されます。ポリシーは許可ルールを追加するだけなので、順序は関係ありません。各 Namespace はデフォルト拒否から始めます。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: shop
spec:
  podSelector: {}
  policyTypes:
  - Ingress

そのうえで必要なものを開けます。ここでは web から api への TCP 8080 です。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-web
  namespace: shop
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: web
    ports:
    - protocol: TCP
      port: 8080

つまずきやすい点が 4 つあります。

  • ポートは Pod のポートです。Service のポート 80 ではなく 8080 です。
  • デフォルト拒否は web Pod も隔離します。そのため Gateway が web Pod に到達するには専用のルールが必要です。
  • ハイフン 1 つで意味が変わります。 1 つの from エントリに namespaceSelector と podSelector を書くと両方に一致する必要があり、2 つのエントリに分けるとどちらか一方で足ります。
  • egress のデフォルト拒否は DNS もブロックします。先に kube-dns へのポート 53 を許可してください。

NetworkPolicy には拒否ルールも、クラスター全体のスコープも、TLS に関する機能もありません。ワークロード間の相互 TLS については、サービスメッシュのアーキテクチャ図を参照してください。

出入りのトラフィック:Ingress、Gateway API、egress

Ingress は安定していますが凍結されており、Kubernetes プロジェクトは代わりに Gateway API を推奨しています。コミュニティの Ingress NGINX コントローラーは 2026 年 3 月に廃止され、セキュリティ修正を受け取れなくなりましたが、既存の環境は気づかれないまま動き続けています。

Gateway API は役割で仕事を分けます。プラットフォームの担当者がリスナーと証明書を持つ Gateway を運用し、アプリケーションチームが自分の Namespace から HTTPRoute を関連付けます。2023 年 10 月に GA となり、2026 年のリリースで TLSRoute、TCPRoute、UDPRoute も標準になりました。Kubernetes Ingress の図は、この経路をホップごとにたどっています。

egress は見落とされがちな方向です。クラスターから出るトラフィックは通常ノードの IP に変換されます。AWS VPC CNI も VPC 外の宛先に対してそうしています。制限するには、外部の宛先を IP 範囲としてしか扱えない egress ルール、Cilium の toFQDNs のような DNS 対応のルール、パートナーが固定の送信元 IP を必要とする場合の egress gateway を使います。

Kubernetes ネットワーク図

2 つのワーカーノードのフレーム。ユーザーは Gateway に到達し、Gateway は worker-1 の web Pod にルーティングします。web Pod は CoreDNS で api Service を引き、その ClusterIP に接続して worker-2 の 2 つの api Pod に届きます。NetworkPolicy がそれらを選択し、VXLAN トンネルが両ノードを結びます。

15 個のコンポーネント · 9 個の接続

Archboard で開く
ダイアグラムの内容
  • worker-1 · pods 10.244.1.0/24
  • worker-2 · pods 10.244.2.0/24
  • Users
  • public-gw
  • web · 10.244.1.12
  • coredns · 10.244.1.5
  • api · 10.244.2.21
  • api · 10.244.2.22
  • api · 10.96.48.20
  • api-allow-web
  • HTTPS 443
  • HTTPRoute
  • DNS: api.shop.svc.cluster.local via kube-dns 10.96.0.10
  • TCP 10.96.48.20:80
  • DNAT 10.244.2.21:8080
  • DNAT 10.244.2.22:8080
  • VXLAN overlay over UDP · node IP to node IP
2 ノードのクラスターを通る 1 つのリクエストです。Gateway から入り、DNS で名前を引き、kube-proxy が Pod IP に書き換える ClusterIP に接続し、VXLAN で別のノードへ渡り、NetworkPolicy が接続できる相手を決めます。各層はそれぞれ別の壊れ方をします。

図の読み方は次のとおりです。

  • 2 つのワーカーノードには、それぞれ /24 の Pod 範囲がラベルとして付いています。
  • 青は南北方向です: ユーザーは HTTPS 443 で public-gw に到達し、HTTPRoute が web Pod に送ります。
  • アンバーは DNS です: web は 10.96.0.10 の kube-dns 経由で CoreDNS に api.shop.svc.cluster.local を問い合わせ、10.96.48.20 を得ます。
  • ティールは Service の経路です: web は 10.96.48.20:80 に接続し、worker-1 の kube-proxy のルールがそれをポート 8080 の api Pod に書き換えます。
  • グレーはオーバーレイです: パケットはノード IP 間で VXLAN の中に入って worker-2 へ渡ります。
  • 赤はポリシーです: api-allow-web は app=web の Pod だけが api Pod に到達できるようにします。
  • Markdown ブロックには、数値の背後にある既定値が載っています。

Kubernetes ネットワーク図をわかりやすく描く

  • ノードには Pod 範囲、Pod には IP をラベルとして付けます。ログで検索する情報だからです。
  • Service は論理的なステップとして描き、書き換えが起きる場所を注記します。
  • エッジにはアドレス、ポート、プロトコルを付け、Service の後は Pod のポートに切り替えます。
  • 南北と東西の色を分け、オーバーレイはノード間に 1 回だけ描きます。
  • 各ポリシーは選択する Pod に接続し、許可する送信元を示します。
  • 省くもの: kubelet、API サーバー、追加のレプリカ。コントロールプレーンは Kubernetes アーキテクチャ図で扱っています。
  • よくある間違い: パケット経路に kube-proxy を描く(kube-proxy はルールを書くだけで、転送するのはカーネルです)、DNS がない、ポリシーを無視するプラグインにポリシーを描く。

Archboard で作る

  1. Google または GitHub でサインインし、ダイアグラムを作成します。無料の Kubernetes YAML をダイアグラムに変換ツールを使うと、既存のマニフェストから最初の下書きを作れます。
  2. 基本パックから フレームノードを 2 つ追加し、worker-1 と worker-2 として Pod 範囲のラベルを付けます。
  3. Kubernetes パックから、IP を付けた Pod ノードを 4 つ追加します。worker-1 に web と coredns、worker-2 に api Pod を 2 つです。
  4. フレームの間に Service(ClusterIP、ポート 80 から 8080)を追加し、web からと 2 つの api Pod へのエッジにラベルを付けます。
  5. app=api を選択する NetworkPolicy を追加し、api Pod へ点線のエッジで接続します。
  6. ネットワークパックからインターネットノードを、HTTPS 443 のリスナーを持つ Gateway を追加し、web Pod までつなぎます。
  7. web から coredns へ破線の DNS エッジを、フレーム間に太い双方向の VXLAN エッジを描きます。
  8. 基本パックから、既定値を書いた Markdown ブロックと、短いテキストのメモを追加します。

Kubernetes のユースケースページには、さらに多くのオブジェクトが載っています。この例で使うダイアグラムノードは 11 個で、Free プランの範囲に収まります。

よくある質問

Kubernetes でノードをまたいで Pod はどう通信しますか? CNI プラグインを通じて行います。オーバーレイが Pod のパケットをノード IP 間で包むか、ネイティブルーティングが Pod のアドレスをそのまま運びます。どちらの場合も、Pod は互いの実際の IP を見ます。

Pod IP と ClusterIP の違いは何ですか? Pod IP は 1 つの Pod に属し、Pod が置き換わると変わります。ClusterIP は Service の安定した仮想 IP で、kube-proxy または eBPF による代替が、現在の Pod IP に書き換えます。

どの kube-proxy モードを使うべきですか? Kubernetes 1.37 では iptables が既定で、nftables は 1.33 から GA であり今後の既定になる予定で、IPVS は非推奨です。カーネル 5.13 以降であれば、長期的には nftables が適切な選択です。

NetworkPolicy はすべての CNI プラグインで動きますか? いいえ。API はどこでも受け付けますが、実際に適用するのは NetworkPolicy を実装したプラグインだけです。Calico、Cilium、ネットワークポリシーを有効にした AWS VPC CNI は適用します。Flannel 単体では適用しません。

Ingress と Gateway API のどちらを使うべきですか? 新しく作るなら Gateway API です。Kubernetes が推奨しており、Ingress は凍結されています。既存の Ingress リソースは動き続けますが、セキュリティ修正を受け取れなくなった Ingress NGINX からの移行を計画してください。

無料で始める — 1 シート、クレジットカード不要。

関連記事

← ブログに戻る

1分でキャンバスを開く

Google または GitHub でサインイン。1シートは無料で、チームが加わったらアップグレード。