GKEでIPを固定した内部Ingressを作成する
2021/02/19 更新
内部Ingressでもサポートされるようになった
TL;DL;
Kubernetes Ingressでは内部Ingreesの固定IPはサポートしていないのでnginx-ingressを利用する
背景
GKEで内部Ingressを作成する際に、LBのIPを固定させたかった
ドキュメントを読むとKubernetes Ingressでは対応していなかった。
対策を調べてたらnginx-ingressを使うと解決できるというのを見た。
やったのでまとめ
限定公開のGKEを作成する
パブリック エンドポイントへ無制限にアクセス可能な限定公開クラスタの作成をします。
GCPコンソールから作成
- GKEのメニューから
クラスタの作成
を選択 - 名前は
private-cluster-2
, ロケーションタイプはゾーン
でasia-northeast1-c
を選択 - 左のネットワークキングを開く
-
限定公開クラスタ
にチェック -
マスター IP 範囲
に172.16.0.32/28
を入力 - ネットワークとノードのサブネットを
default
に設定 -
マスター承認済みネットワークを有効にする
のチェックボックスをオフのままにしておく(これオンにすると、追加設定しないとcloud shellからkubectlでアクセスできない) - 左のセキュリティを開く
-
クライアント証明書を発行する
チェックボックスがオフのままにしておく - 作成
接続確認
クラスタが作成されたらCLOUD SHELLを開き、以下を実行する
$ gcloud container clusters get-credentials private-cluster-2 --zone asia-northeast1-c --project ${GOOGLE_CLOUD_PROJECT}
さらに以下を実行して、結果が取得できればOK
$ kubectl get no
NAME STATUS ROLES AGE VERSION
gke-private-cluster-2-default-pool-24553f8b-0fpw Ready <none> 96s v1.17.12-gke.1504
gke-private-cluster-2-default-pool-24553f8b-38vk Ready <none> 95s v1.17.12-gke.1504
gke-private-cluster-2-default-pool-24553f8b-nd4r Ready <none> 96s v1.17.12-gke.1504
nginx-ingressをデプロイ
CLOUD SHELLでさらに作業を進めます。
まずnginx-ingressのマニフェストを取得します。
$ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud/deploy.yaml
デフォルトだと外部LBを作成するので、内部LBかつIPを固定して起動するように変更します。
vi deploy.yml
# Source: ingress-nginx/templates/controller-service.yaml
apiVersion: v1
kind: Service
metadata:
+ annotations:
+ cloud.google.com/load-balancer-type: "Internal"
labels:
helm.sh/chart: ingress-nginx-3.6.0
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 0.40.2
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
type: LoadBalancer
+ loadBalancerIP: 10.146.0.101
externalTrafficPolicy: Local
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
- name: https
port: 443
protocol: TCP
targetPort: https
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
固定IPについては、GKEのクラスタから疎通可能なIPである必要があります。とりあえずGKEと同一のsubnet内のIPを指定することにします。asis-northeast1のdefaultサブネットで作っているなら、10.146.0.0/20
が利用できます。(ここでは、10.146.0.101
を指定してみました。
変更したら、applyします。
$ kubectl apply -f deploy.yml
以下のコマンドで、正常にLBが作成されているか確認します。EXTERNAL-IP
と書かれてますが、実際はここが内部IPになります。指定した内部IPアドレスが使われていることが確認できます。
$ kubectl get svc ingress-nginx-controller -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.0.26.117 10.146.0.101 80:31379/TCP,443:32372/TCP 51s
最後に疎通確認を行います。
内部LBにCLOUD SHELLからはアクセスできないので, GKEのnodeから疎通確認をしてみます。
GCEの画面をGCPコンソールで開いて、NodeインスタンスのどれかのSSH
を開きます。
SSH画面が開いた、以下のように内部LBへ疎通ができるか確認します。nginxの応答が帰って来ればOKです。
$ curl 10.146.0.101
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
バックエンドをデプロイ
nginx-ingressを利用するバックエンドをデプロイします
ここからまたCLOUD SHELLで作業をします。以下をapp.ymlというファイル名で作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-app
spec:
selector:
matchLabels:
app: hello
replicas: 3
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: "gcr.io/google-samples/hello-app:2.0"
---
apiVersion: v1
kind: Service
metadata:
name: hello-service
labels:
app: hello
spec:
type: NodePort
selector:
app: hello
ports:
- name: hello-port
port: 80
targetPort: 8080
protocol: TCP
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: hello-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host:
http:
paths:
- path: /
backend:
serviceName: hello-service
servicePort: hello-port
これをこのままデプロイすると下記のようなエラーが出ると思います
$ kubectl apply -f app.yml
deployment.apps/hello-app unchanged
service/hello-service unchanged
Error from server (InternalError): error when creating "app.yml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": Post https://ingress-nginx-controller-admi
ssion.ingress-nginx.svc:443/networking/v1beta1/ingresses?timeout=10s: context deadline exceeded
これの対策としてコントロールプレーンからノードへのTCP8443ポート接続を許可させる必要があります
GCPのドキュメントを見ながら作業します。
以下のコマンドで、GKEのノードが使っているTARGET_TAGSを確認します。
$ gcloud compute firewall-rules list \
--filter 'name~^gke-private-cluster-2' \
--format 'table(
name,
network,
direction,
sourceRanges.list():label=SRC_RANGES,
allowed[].map().firewall_rule().list():label=ALLOW,
targetTags.list():label=TARGET_TAGS
)'
NAME NETWORK DIRECTION SRC_RANGES ALLOW TARGET_TAGS
gke-private-cluster-2-4ffa8a93-all default INGRESS 10.4.0.0/14 tcp,udp,icmp,esp,ah,sctp gke-private-cluster-2-4ffa8a93-node
gke-private-cluster-2-4ffa8a93-master default INGRESS 172.16.0.32/28 tcp:10250,tcp:443 gke-private-cluster-2-4ffa8a93-node
gke-private-cluster-2-4ffa8a93-vms default INGRESS 10.128.0.0/9 icmp,tcp:1-65535,udp:1-65535 gke-private-cluster-2-4ffa8a93-node
gke-private-cluster-2-4ffa8a93-node
というTARGET_TAGSでした
これを確認したら以下でファイアウォールルールを追加します
gcloud compute firewall-rules create gke-private-cluster-2-allow-8443 \
--action ALLOW \
--direction INGRESS \
--source-ranges 172.16.0.32/28 \
--rules tcp:8443 \
--target-tags gke-private-cluster-2-4ffa8a93-node
--target-tags
に1つ前のコマンドで確認したタグを指定してください
ここまで完了したら、app.ymlをデプロイしてください
$ kubectl apply -f app.yml
deployment.apps/hello-app created
service/hello-service created
ingress.extensions/hello-ingress created
デプロイが完了したら、また内部LBへ疎通可能なGKEのノードにSSHします。
先ほどと同じコマンドを打って以下のような結果が帰ってこればOKです。
$ curl 10.146.0.101
Hello, world!
Version: 2.0.0
Hostname: hello-app-7f46745f74-8zxfs
Discussion