第 6 章で見た経路では、ロードバランサーがノードへ送り、そのノードが Pod へ転送していました。 最初からロードバランサーが Pod に送れれば、この転送を 1 段減らせます。
そのためには、Pod の IP まで通信が届くことと、動いている Pod をロードバランサーに知らせることが必要です。 AWS と Google Cloud では、この 2 つをどう実現しているのかを見ていきます。
GOAL この章のゴール
- ロードバランサーがノードへ送る構成と、Pod へ送る構成を区別できる
- Pod へ直接送るために必要な条件を説明できる
- AWS と Google Cloud で、その条件を満たす仕組みが分かる
ノードを経由する構成の回り道
第 6 章の経路をもう一度見ます。
ELB のターゲットは「全ノードの NodePort」でした。
この構成では、ELB にノード (10.0.1.11 など) を送り先として登録しています。
Pod の IP に直接届く経路や登録の仕組みを用意していないため、ノードを経由して送ります。
GKE のドキュメントは、この構成の問題を次のように列挙しています。
「ロードバランサーから VM の NodePort へ、そこから kube-proxy の経路で Pod IP へ (別の VM に居るかもしれない) という 2 ホップの負荷分散になり、レイテンシが増え経路が複雑になる」、「ロードバランサーは Pod を直接見られないので負荷分散が最適にならない」、「VM や Pod の消失が、2 ホップのせいで一時的な通信の欠落を起こしやすい」。
externalTrafficPolicy: Local (第 6 章) はヘルスチェックで Pod の無いノードを外す対症療法で、LB が見ているのがノードであることは変わらず、Pod 数がノード間で偏れば負荷も偏ります。
LB のターゲットを Pod にする
理想は、LB が Pod の所在を知っていて、ClusterIP も NodePort も介さず Pod に直接送ることです。
これが成り立つ条件は 1 つで、Pod の IP が VPC から直接届くアドレスであること です。 VXLAN で包む構成では、Pod の IP は VPC のルートテーブルに載らないので、LB はそこへ送れません。 第 4 章で見たとおり、CNI プラグインの仕事には「Pod の IP を配る (IPAM)」と「ノード間の疎通を担保する」があり、両方を VPC に任せる CNI プラグインなら、Pod は VPC のアドレスを持ちます。
コンテナネイティブ・ロードバランシング とは、GKE のドキュメントの定義では「Pod のエンドポイントに直接負荷分散すること」で、こうして LB のターゲットをノードから Pod に変えた構成を指します。
公式ドキュメントの Service の説明にも、Pod へ直接送る LB 実装のために NodePort の割り当てを止める allocateLoadBalancerNodePorts: false という設定があります。
AWS での実装
AWS では 2 つの部品で実現します。
- Amazon VPC CNI:第 4 章で見たとおり、ノードの ENI に付いたセカンダリ IP を Pod に渡します。AWS のドキュメントの言い方では「Pod は VPC ネットワーク上と同じ IP アドレスを持つ」ので、VPC から Pod に直接届きます。
- AWS Load Balancer Controller:Ingress から ALB (L7) を、Service から NLB (L4) を、Gateway API からその両方を作るコントローラーです。ターゲットの種類は
instanceとipの 2 つで、ドキュメントは ip モードを「ALB から Kubernetes の Pod に直接届く。CNI が ENI のセカンダリ IP で Pod に直接到達できる必要がある」、instance モードを「ALB から各 Service の NodePort 経由でノードに届く」と説明しています。IP ターゲットでは EndpointSlice から Pod の IP を集めて登録し、Pod の増減に追従します。
ターゲットグループの中身がノードの IP か Pod の IP かを見れば、どちらの構成かすぐ分かります。
ip モードにすると、Pod の readiness gate (elbv2.k8s.aws/pod-readiness-gate-inject) で、ターゲットグループ側で healthy になるまで Pod を Ready にしない仕組みも使えます。
ローリングアップデートで新しい Pod が LB から見て準備できる前に古い Pod が消える問題を防ぐための機能で、ドキュメントは「instance モードではノードが backend なので使えない」と書いています。
GCP での実装
GKE では、クラスタを VPC-native で作ると、サブネットのセカンダリ範囲 (alias IP) が Pod の IP 範囲になります。
GKE のドキュメントによれば、各ノードは /24 の alias IP 範囲を 1 つ持ち、その範囲から Pod に IP を配り、「Pod の IP はクラスタの VPC ネットワーク内でネイティブにルーティングできる」ので、静的ルートに依存しません。
VPC-native は新しいクラスタの既定です。
LB 側は NEG (Network Endpoint Group) を使います。
GKE のドキュメントの定義では、コンテナネイティブ LB は「GCE_VM_IP_PORT 型の NEG を使い、そのエンドポイントは Pod の IP アドレス」です。
Service に cloud.google.com/neg: '{"ingress": true}' の annotation が付くと NEG が作られ、Cloud Load Balancing は NEG をバックエンドにします。
ドキュメントは「コンテナネイティブ LB が無いと、通信はノードのインスタンスグループへ送られ、kube-proxy が設定した iptables ルールで Pod へ転送される。コンテナネイティブ LB では Pod に直接負荷分散され、VM の IP と kube-proxy を経由しない」と説明し、Pod の readiness gate で LB から見た健康状態を判定する点も利点に挙げています。
| 観点 | AWS | GCP |
|---|---|---|
| Pod の IP | VPC CNI が ENI のセカンダリ IP を配る | VPC-native の alias IP 範囲 (ノードごとに /24) から配る |
| LB のターゲット | ターゲットグループ (target-type: ip) | NEG (GCE_VM_IP_PORT) |
| 登録するコントローラー | AWS Load Balancer Controller | GKE 組み込みの NEG controller |
| Pod 数の上限 | インスタンスタイプの ENI 数 × IP 数 | ノードの alias 範囲のサイズ |
オンプレではどうなのか
オンプレでは、Pod の IP を物理ネットワークから直接届くアドレスにする必要があります。 技術的には、第 4 章の BGP で Pod CIDR を物理ルーターに教えれば (Calico や Cilium)、外部の LB のターゲットに Pod IP を登録できます。 ただし物理網の設定変更が要るので、クラウドほど手軽ではありません。
ふりかえり
Q標準の type: LoadBalancer で余計なホップが生じる根本の理由は?
Node ネットワークと Pod ネットワークが分断されているので、LB はノードにしか送れません。受けたノードが Pod の所在を引いて転送するため、Pod の無いノードに届いた分が遠回りします。
Qコンテナネイティブ LB が成り立つための条件は?
LB のターゲットに Pod IP を登録するには、VPC のルーティングでその IP に届く必要があります。VPC CNI の ENI セカンダリ IP や GKE の alias IP がそれを満たします。
QAWS で Pod IP をターゲットグループに登録しているのは?
AWS Load Balancer Controller が EndpointSlice を読み、target-type: ip のターゲットグループに Pod IP を登録し、消えれば外します。cloud-controller-manager の Service controller が作る CLB / NLB はノードをターゲットにする従来方式です。
参考
- GKE ドキュメント「Container-native load balancing」(定義、GCE_VM_IP_PORT、2 ホップの問題、利点) https://cloud.google.com/kubernetes-engine/docs/concepts/container-native-load-balancing
- GKE ドキュメント「VPC-native clusters」(alias IP、ノードごとの /24、VPC 内でネイティブにルーティング可能) https://cloud.google.com/kubernetes-engine/docs/concepts/alias-ips
- AWS Load Balancer Controller「How it works」(instance モードと ip モード) https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/how-it-works/
- AWS Load Balancer Controller「NLB」(target type、ip モードの前提) https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/guide/service/nlb/
- AWS Load Balancer Controller「Pod readiness gate」 https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/deploy/pod_readiness_gate/
- Amazon EKS Best Practices「Amazon VPC CNI」(Pod は VPC 上と同じ IP を持つ) https://docs.aws.amazon.com/eks/latest/best-practices/vpc-cni.html
- Amazon EKS ドキュメント「EKS Auto Mode networking」(LB のプロビジョニングを AWS が管理、既定は IP モード) https://docs.aws.amazon.com/eks/latest/userguide/auto-networking.html
- Kubernetes ドキュメント「Service」(allocateLoadBalancerNodePorts) https://kubernetes.io/docs/concepts/services-networking/service/
- MetalLB ドキュメント「Concepts」(L2 モードと BGP モード) https://metallb.io/concepts/
- Cilium ドキュメント「LoadBalancer IP Address Management」 https://docs.cilium.io/en/stable/network/lb-ipam/