GKE/Kubernetesネットワークの基礎 ルートベースネットワークとVPC-nativeネットワークの違い

Hiroki Tanaka
google-cloud-jp
Published in
14 min readDec 24, 2019

この記事は Google Cloud Japan Customer Engineer Advent Calendar 2019 の 14日目の記事です。

以前にこちらの記事で VPC-native クラスタの説明と、ルートベースのクラスタについても簡単に触れましたが、今回はもっと具体的な違いと、両者のメリットデメリットについて解説していきたいと思います。

今回解説する内容は次の点です。

・実際に GCP の VPC ネットワークのどの設定がこの両者の違いを実現しているのか?

・クラスタ内の Pod と外部の通信の仕組みついてなにか違いはあるのか?

・両者のメリット、デメリットとそれぞれのユースケースについて

実際に GKE のクラスタを作りながらみていきましょう。

ルートベースのクラスタ

まずはルートベースのクラスタを gcloud コマンドで作成します。ルートベースを指定するオプションは — no-enable-ip-alias になります。

gcloud コマンドでクラスタを構築する詳細な説明はこちらhttps://cloud.google.com/sdk/gcloud/reference/container/clusters/create

$ gcloud container clusters create route-based-cluster \
--zone us-central1-a \
--no-enable-ip-alias

オプションで指定していない設定値はデフォルト値が使われます。例えば Node 数は3です。

なお、VPC-native クラスタが GKE でサポートされるまでは、全ての GKE クラスタはルートベースクラスタとして作成されていました。現在のところルートベースクラスタを VPC-native クラスタに移行する方法はありません。クラスタ自体の再作成が必要になります。

また、GCP の Web コンソールおよびバージョンが v251.0.0–255.0.0のgcloud SDK で作成すると、デフォルトで VPC-native のオプションが有効化されますが、REST API および、gcloud SDK のバージョンがv251.0.0–255.0.0以外ではデフォルトの設定はルートベースになります。環境によってデフォルト値が異なりますので注意してください。一度作成すると設定変更できないので、ルートベースにするか、VPC-nativeにするかのオプションは常に指定してクラスタを作ることをおすすめします。

こちらに記載の一覧表もご確認ください。
https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips

コマンド実行後しばらく待つとクラスタが作成されます。GCP の Web Console から、作成されたクラスタの情報が確認出来ます。たくさん設定値がありますが、コマンドで指定したとおり、VPC-native は無効になっています。またその下の項目の Pod のアドレス範囲が 10.12.0.0/14 と自動で設定されていることがわかります。(アドレス範囲は明示的に指定することも可能なので、必要に応じて変更可能です。)

この10.12.0.0/14 すべてが Pod 用に予約されているわけではなく、/20 のアドレス範囲が k8s のService用に使われて、残りが k8s 内の Pod で利用可能な IP アドレスの範囲になります。

実際には、1 Node あたりの Pod が110と制限があるため、1 Node あたり/24 ( = 256 アドレス)のアドレス範囲が Pod で利用可能なように割り振られています。今回は 3 Node でクラスタを作成したので、 10.12.0.0/24 , 10.12.1.0/24, 10.12.2.0/24の3つが Node ごとに利用される Pod のアドレス空間です。

1 Node あたり110 Pod であれば /25 (=128アドレス)でも問題ないように思えますが、Podを一度にアップデートする際などには一時的に最大で元のPod 数の2倍のアドレスが必要になるので、/24 がここでは割り振られていることになります。

では、この1 Node ごとに割り振られた /24 のアドレス範囲が GCP のネットワーク設定のどこに反映されているかというと、Webコンソールの VPCネットワーク → ルート の画面を開くと確認できます。以下のスクリーンショットのようにルートが3つ追加されていることが確認できます。 (gcloud compute routes listコマンドでも確認できます)

k8s 内のクラスタ内部の1 Node に割り振られた Pod 用のアドレス範囲は、通常の VPC ネットワークから見れば別ネットワークになるので、ルートの追加が必要になります。

試しに以下のデプロイメントを作成して、Pod の IP アドレスを確認してみましょう。コンテナのイメージに指定している echoserver は、HTTP request の情報をそのまま respose として返してくれるwebサーバーをデプロイしています。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: route-based-echoheaders
spec:
replicas: 9
template:
metadata:
labels:
app: echoheaders
spec:
containers:
- name: echoheaders
image: k8s.gcr.io/echoserver:1.10
ports:
- containerPort: 8080

デプロイメント自体はすぐに作成が完了しますので、kubectl get pods -o wideコマンドで、Pod の詳細情報を確認すると、Pod にアサインされたIP アドレスが確認できます。

kubectl get pods -o wideの結果(抜粋)

NAME                                     IP
route-based-echoheaders-74d49b6cc5-w6tz6 10.12.0.8
route-based-echoheaders-74d49b6cc5-stptl 10.12.0.9
route-based-echoheaders-74d49b6cc5-p662t 10.12.1.8
route-based-echoheaders-74d49b6cc5–9c7qb 10.12.1.9
route-based-echoheaders-74d49b6cc5-n5mm8 10.12.1.10
route-based-echoheaders-74d49b6cc5-wsq6d 10.12.1.11
route-based-echoheaders-74d49b6cc5-kdxt8 10.12.2.5
route-based-echoheaders-74d49b6cc5-k6hgm 10.12.2.6
route-based-echoheaders-74d49b6cc5-rpsrh 10.12.2.7

上記の結果から、
10.12.0.0/24
10.12.1.0/24
10.12.2.0/24
と、実際にそれぞれのノートに割り当てられたアドレス範囲の中で Pod が作成されていることがわかります。

VPC-nativeのクラスタ

では今度は VPC-Native のクラスタを作成してみます。 — enable-ip-aliasのオプションを指定すると VPC-native が有効になったクラスタが作成されます。(ルートベースクラスタから VPC-native クラスタへの変更は出来ません。)

gcloud container clusters create vpc-native-cluster \
--zone us-central1-a \
--enable-ip-alias

詳細はこちらもご確認ください。
https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips?hl=ja

コマンド実行後しばらく待つと、同じようにクラスタが作成されます。VPC-native が有効になっており、Pod のアドレス範囲も設定されています。ルートベースクラスタの場合は、この値から各 Node ごとに切り出された /24 が VPC ネットワークのルートとして追加されていましたが、VPC-native の場合はどんな設定でしょうか?

GCP の Web コンソールの設定画面を確認すると、VPC のルートに設定されいるのではなく、VPC のサブネットの設定に直接Podのアドレス範囲が登録されています。これはつまり GKE のクラスタ内ネットワークが独立しているわけではなく、例えば GKE 内で Pod が新しく作成されるたびに VPC内の IP アドレスを消費することになります。(k8sクラスタがネイティブにVPC を利用している状況)

このようなネットワークの設定の違いはありますが、運用上実際にどんな影響があるか、それぞれのメリット・デメリットを考えながらみていきましょう。

メリット・デメリット

前述のように、ルートベースのクラスタではPod のアドレス空間ごとにルートが作成されるので、ルート数の上限値に達しないように設計をする必要があります。

VPC Native ネットワークを構築する場合には、k8s のクラスタ内部で使用される Pod のアドレス空間と既存の VPC の Alias IP と重複が発生しないように、注意してネットワークの設計を行う必要があります。

また VPC Native クラスタの大きなメリットは Network Endpoint Group(NEG)が使えるようになることです。

NEG の説明をする前に、ルートベースクラスタで負荷分散を行う仕組みについて考えてみましょう。例えば、GKE Ingress を使ってサービスを外部に公開することを考えます。(GKE Ingress は GCP では HTTP(S) ロードバランサーが使用されます)

ルートベースクラスタの場合これまでの説明のように VPC と k8s 内のネットワークが独立しているので、GKE Ingress が認識できる IP アドレスは GKE のNode レベルまでです。そのため GKE Ingree からNodeまでと、Node から Pod までの2段階で負荷分散が行われます。これにより2回分のオーバーヘッドもかかりますし、トラフィックが Node まで来た段階で、その Node には Pod が存在しない状況だった場合には、別のNodeにルーティングされるので、余計な Hop が発生します。

一方 VPC-native クラスタであれば GKE Ingress がPodレベルのIP アドレスまで認識することが可能です。NEGはその特性を利用し、GKE Ingress からPodにダイレクトに負荷分散を実現する機能です。これにより負荷分散の回数は1回のみになりますし、Pod のヘルスチェックも GKE Ingress 側から行うことができるので、Pod が存在しないNodeにトラフィックがルーティングされることも減らすことができます。

右側がNEGを使った負荷分散の仕組み

アドテク(AdExchangeの仕組みを使ってリアルタイムで広告を売買するサービス)などのネットワークレイテンシーが厳しいサービスを提供する場合には、この NEG は必須の機能とも言えます。

Podレベルで直接負荷分散をするという、NEG の特性上 VPC -nativa クラスタのみでしか利用することができません。

実際にNEGを構築する方法はこちらを参考にしてください。
https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing?hl=ja

終わりに

VPC-nativeとルートベースのクラスタの違いについて簡単に説明してきましたが、NEG のように今後はコンテナがネイティブに VPC のアドレスを利用していないと提供することができない機能もリリースも増えてくるかもしれませんので、現在ルートベースクラスタをご利用の方は、この機会にクラスタの再構築も含めてご検討してみてください。

Appendix

PodとGCEインスタンス間通信で、インスタンス側でtcpdumpを実施する方法をおまけで記載します。興味があるかたは実際に試してみてください。

alpineもしくはbusyboxのPodを作る

kubectl run mypod --tty -i --image=alpine:edge sh
kubectl run mypod --tty -i --image=busybox sh
$kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESmypod-785d4f67cc-phkpv 1/1 Running 1 151m 10.12.2.14 gke-route-based-cluster-default-pool-64847d08–3hl5 <none> <none>

一度作ったら、kubectl exec -it pod名でPod内の環境に入れる

これでPodの中に入れるので、PodからCluster外へのPingを試す。

Pod IP (10.12.2.14)
Cluster外 IP (10.128.0.55)

$ping instance-1 -c 3
PING instance-1 (10.128.0.55): 56 data bytes
64 bytes from 10.128.0.55: seq=0 ttl=63 time=1.274 ms
64 bytes from 10.128.0.55: seq=1 ttl=63 time=0.270 ms
64 bytes from 10.128.0.55: seq=2 ttl=63 time=0.290 ms

instance-1からtcpdumpを実行してみる

sudo tcpdump -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
03:45:14.629747 IP 10.12.2.14 > instance-1.c.[poject_id].internal: ICMP echo request, id 14848, seq 0, length 64
03:45:14.629785 IP instance-1.c.[project_id].internal > 10.12.2.14: ICMP echo reply, id 14848, seq 0, length 64

Pod IP のまま、Instance-1に届いていることがわかる。

--

--

Hiroki Tanaka
google-cloud-jp

Customer Engineer at @GoogleCloud. Disclaimer. Opinions are my own, NOT the views of my employer. All posts here are my personal opinion.