はじめに
前回の投稿でGCPとAWSをVPNで繋ぐところまでやってみました。
その続きで今回は GKE の Pod から VPN を経由して AWS のサービスを利用したかったので、その方法をまとめます。
普通にサブネットと GKE クラスタを作成して、AWS と通信できるか確認
サブネットの準備
AWS と VPN 接続されている VPC aws-internal-connect
に、サブネット aws-internal-connect-subnet-1
を CIDR 10.1.0.0/24
で作成します。
GKEクラスタの作成
クラスタ test-cluster
を、普通に作成してみます。その際、サブネットには上記 aws-internal-connect-subnet-1
を設定します。
$ gcloud beta container clusters create test-cluster \
--region=asia-northeast1 \
--cluster-version=1.9.7-gke.3 \
--machine-type=n1-standard-1 \
--image-type=cos \
--num-nodes=1 \
--network=aws-internal-connect \
--subnetwork=aws-internal-connect-subnet-1 \
--enable-autorepair
デプロイ
作成したクラスタに Pod をいくつかデプロイします。
$ kubectl run <Deployment Name> \
--image=asia.gcr.io/<My Project>/<Image Name>:v1 \
--replicas=1 \
--port=80
deployment.apps "<Deployment Name>" created
クラスタ内のネットワーク
デプロイ後のGKEクラスタのネットワークを調べてみた結果、下図のようなネットワークを構成していることが判りました。
今回作成した GKE クラスタのネットワークは、次の CIDR で構成されていました。
- Node IP (10.1.0.0/24)
- Pod IP (10.32.0.0/24, 10.32.1.0/24, 10.32.2.0/24)
- Service IP (10.35.240.0/20)
Node IP はクラスタ作成時に指定したサブネット aws-internal-connect-subnet-1
から IP が割り当てられており、Pod IP と Service IP は 自動的に割り当てられているようです。
さて、このクラスタ内の Pod から AWS VPC 内のプライベート IP に対して通信することはできるでしょうか?
答えは NO です。
Cloud Router の BGP アドバタイズ(広報)を確認すれば判るのですが、あくまでもアドバタイズされるのは VPC 内に作成したサブネットの CIDR だけですので、Pod IP や Service IP の CIDR が AWS 側にアドバタイズされない為です。(AWSへリクエストは到達するが、AWS側はPodへのルートを知らない為、レスポンスを GCP に返せない)
# Cloud Router の BGP アドバタイズ確認
$ gcloud compute routers get-status <Cloud Router Name> --region asia-northeast1
・・・
bgpPeerStatus:
- advertisedRoutes:
- destRange: 10.1.0.0/24
kind: compute#route
nextHopIp: 169.254.24.222
priority: 100
ipAddress: 169.254.24.222
name: aws-vpn-1-bgp-1
numLearnedRoutes: 6
peerIpAddress: 169.254.24.221
state: Established
status: UP
uptime: 1 weeks, 1 days, 23 hours, 0 minutes, 0 seconds
uptimeSeconds: '774000'
- advertisedRoutes:
- destRange: 10.1.0.0/24
kind: compute#route
nextHopIp: 169.254.27.98
priority: 100
ipAddress: 169.254.27.98
name: aws-vpn-2-bgp-1
numLearnedRoutes: 6
peerIpAddress: 169.254.27.97
state: Established
status: UP
uptime: 2 days, 13 hours, 19 minutes, 0 seconds
uptimeSeconds: '220740'
・・・
Pod から AWS へプライベート IP 通信する方法
GKE クラスタ内の Pod から VPN を介してプライベート IP で通信させるには、 「Pod IP と Service IP を BGP アドバタイズできるように GKE クラスタを構成すればよい」のです。
具体的には、IP エイリアスを利用した『VPCネイティブ クラスタ』を作成すれば、Pod IP と Service IP がアドバタイズされるようになります。
セカンダリ IP の CIDR を指定してサブネットを作成
新しいサブネット aws-internal-connect-subnet-2
を作成します。その際、セカンダリ IP 範囲
を2つ作成します。
セカンダリ IP のひとつは Pod IP の CIDR、もうひとつには Service IP の CIDR を設定します。(下図参照)
セカンダリ IP 範囲を設定する際、注意する点はネットマスク長です。
GKE クラスタでは各 IP 範囲のマスク長に制限が設けられています(下表参照)ので、その範囲を超えないマスク長をセカンダリ IP の CIDR に設定します。
今回はそれぞれ最小マスク長でセカンダリ IP の CIDR を設定しました。
IP 範囲 | デフォルトマスク長 | 最小マスク長 | 最大マスク長 |
---|---|---|---|
Pod IP | /14 | /19 | /11 |
Service IP | /20 | /22 | /18 |
これでプライマリ IP とセカンダリ IP の CIDR (つまり Pod IP と Service IP の CIDR)もアドバタイズされるようになります。
IP エイリアスを使用して VPC ネイティブ クラスタを再作成
VPC ネイティブ(IP エイリアス)を有効化して GKE クラスタ test-cluster
を再作成します。
VPC ネイティブを有効化するには、次のようにオプション --enable-ip-alias
と、前述でサブネットに設定した2つのセカンダリ IP の CIDR を指定します。
#GKEクラスタ作成(VPCネイティブ有効)
$ gcloud beta container clusters create test-cluster \
--region=asia-northeast1 \
--cluster-version=1.9.7-gke.3 \
--machine-type=n1-standard-1 \
--image-type=cos \
--num-nodes=1 \
--network=aws-internal-connect \
--subnetwork=aws-internal-connect-subnet-2 \
--enable-ip-alias \
--cluster-secondary-range-name=subnet-2-pod \
--services-secondary-range-name=subnet-2-svc \
--enable-autorepair
クラスタ作成後は、任意のPodをデプロイします。
クラスタのネットワーク確認
VPC ネイティブ クラスタ作成後のネットワークは、下図のように構成されています。
Cloud Router の BGP アドバタイズを確認すると、Pod IP と Service IP の CIDR がアドバタイズされていることが判ります。
$ gcloud compute routers get-status <Cloud Router Name> --region asia-northeast1
・・・
bgpPeerStatus:
- advertisedRoutes:
- destRange: 10.1.128.0/19
kind: compute#route
nextHopIp: 169.254.24.222
priority: 100
- destRange: 10.1.160.0/22
kind: compute#route
nextHopIp: 169.254.24.222
priority: 100
- destRange: 10.1.0.0/24
kind: compute#route
nextHopIp: 169.254.24.222
priority: 100
- destRange: 10.1.1.0/24
kind: compute#route
nextHopIp: 169.254.24.222
priority: 100
ipAddress: 169.254.24.222
name: aws-vpn-1-bgp-1
numLearnedRoutes: 6
peerIpAddress: 169.254.24.221
state: Established
status: UP
uptime: 1 weeks, 1 days, 23 hours, 0 minutes, 0 seconds
uptimeSeconds: '774000'
- advertisedRoutes:
- destRange: 10.1.128.0/19
kind: compute#route
nextHopIp: 169.254.27.98
priority: 100
- destRange: 10.1.160.0/22
kind: compute#route
nextHopIp: 169.254.27.98
priority: 100
- destRange: 10.1.0.0/24
kind: compute#route
nextHopIp: 169.254.27.98
priority: 100
- destRange: 10.1.1.0/24
kind: compute#route
nextHopIp: 169.254.27.98
priority: 100
ipAddress: 169.254.27.98
name: aws-vpn-2-bgp-1
numLearnedRoutes: 6
peerIpAddress: 169.254.27.97
state: Established
status: UP
uptime: 2 days, 13 hours, 19 minutes, 0 seconds
uptimeSeconds: '220740'
・・・
クラスタ内にある Pod にログイン(kubectl exec
)し、AWS VPC 内にある任意の IPアドレス 宛に疎通テストを行ってみます。
尚、以下では EC2 インスタンス 宛に Ping テストを行いましたが、テストの前に EC2 インスタンス 側のセキュリティグループのルールに、 Pod IP の CIDR を追加(プロトコル = ICMP)する必要があるので注意してください。
# Pod 内から AWS の VPC IP へ疎通テスト
$ ping -c 1 10.0.1.7
PING 10.0.1.7 (10.0.1.7) 56(84) bytes of data.
64 bytes from 10.0.1.7: icmp_seq=1 ttl=62 time=10.0 ms
--- 10.0.1.7 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 10.000/10.000/10.000/0.000 ms
これで GKE クラスタ内の Pod から VPN を介して、AWS の色々なサービスを利用できるようになりました
GKE サービスのエンドポイントについて
Pod が提供するアプリケーションはサービスを作成し、そのサービスを介してネットワーク上に公開しますが、そのサービスのエンドポイントにはデフォルトでは外部(パブリック) IP アドレスが割り当てられます。
折角オンプレミス、AWS、GCP を VPN で繋いでシームレスに連携できる環境が出来たので、社内向けに公開したいサービス等、GKE サービスのエンドポイントにプライベート IP アドレスを割り当てたいと思います。
YAML ファイルの準備
サービスを定義した YAML ファイルを準備します。
今回は例として plot
というサービスを作成することにします。
apiVersion: v1
kind: Service
metadata:
name: plot
namespace: default
annotations:
cloud.google.com/load-balancer-type: "internal"
labels:
run: plot
spec:
type: LoadBalancer
#loadBalancerIP: [IP-ADDRESS]
ports:
- port: 80
protocol: TCP
targetPort: 5000
selector:
run: plot
ポイントは annotations
の部分です。
cloud.google.com/load-balancer-type
を internal
と定義することで、Node IP(≒クラスタに設定したサブネットのプライマリ IP の CIDR)から任意の IP アドレスがエンドポイントに割り当てられます。
ちなみに任意の IP アドレスではなく、割り当てる IP アドレスを指定したい場合は、loadBalancerIP
のコメントを削除して [IP-ADDRESS]
にその IP アドレスを記述します。
サービスの作成
準備した YAML ファイルを元に、サービスを作成します。
$ kubectl create -f service_plot.yaml
サービスの確認
作成されたサービスを確認します。
EXTERNAL-IP
が外部 IP アドレスではなく、Node IP の CIDR から IP アドレスが割り当てられていることが判ります
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
plot LoadBalancer 10.1.163.57 10.1.1.7 80:32141/TCP 6d
参考
https://cloud.google.com/kubernetes-engine/docs/ip-aliases?hl=ja
https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips