IBM Cloud Kubernetes サービスでは、クラスタのノードを次のように配置できます。
- シングルゾーン・クラスタ : 一つのデータセンターに k8sのワーカーノードを配置
- マルチゾーン・クラスタ : 複数のデータセンターに k8sのワーカーノードを配置
それぞれのデータセンターは、完全に独立した設備で運営されており、当然ネットワークのアドレスも、データセンター毎に独立しています。そのため、マルチゾーン・クラスタとした場合、データセンター横断で、ロードバランスできなければなりません。
今回は、マルチゾーン・クラスタのイングレスの動作について、確認していきます。
イングレス (Ingress)
イングレスは、k8sのサービス・オブジェクトを、DNS名で外部へ公開するもので、次の特長があります。
- 利用できるクラスタ: 無料のクラスタでは利用できません。標準クラスタでのみ動作
- プロトコル: HTTP(80), HTTPS(443) または、TCP / UDP でポート番号を指定可能
- 一つのドメインネームで、パス部分に複数のアプリケーション(k8sのサービス)に対応づけて、要求をルーティングする。
イングレスの構成要素は、以下の3つです。
1.Ingress Resource
基本的な設定は、CNCFのAPI仕様に従います。
- CNCF Concept Ingress, https://kubernetes.io/docs/concepts/services-networking/ingress/
- API Reference, https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.12/#ingress-v1beta1-extensions
2. Ingress Container ( IKSでは ALB )
IBMが独自実装する Ingress Controller を ALB (Application LoadBalancer) と表現されており、上記の Ingress Resource で記述されるマニフェストの annotation に、ALB に与える設定を記述します。このドキュメントは以下になります。
- Ingress を使用してアプリを公開する、https://cloud.ibm.com/docs/containers/cs_ingress.html#ingress
3. Multi Zone LoadBalancer (MZLB)
マルチゾーン・クラスタを作成すると、MZLBは自動的に作成されます。この実態は、仮想IPアドレスを持ちリクエストを分配するものでは無く、クラスタを代表するDNSアドレスに各ゾーンのALBのIPアドレスを対応づけます。しかし、DNSのラウンドロビンではありません。この動作には、Cloudflare のマルチゾーン・ロードバランサー(MZLB) が自動作成[1]されるとされていますが、その動作は隠蔽されており、Cloudflareを意識したり、別途注文する必要はありません。
マルチゾーンの MZLB と ALB 動作確認
次のターミナル画面に表示されたマルチゾーンに作成したクラスタがあります。 これは「ワーカー・ゾーン:」にあるように、3つのデータセンター tok02, tok04, tok05 にワーカーノードが配置されています。
$ ibmcloud ks cluster-get iks1
ロケーション: tok04
マスター URL: https://c1.jp-tok.containers.cloud.ibm.com:31963
マスター・ロケーション: Tokyo
マスター状況: Ready (16 hours ago)
入口サブドメイン: iks1.jp-tok.containers.appdomain.cloud
入口の秘密: iks1
ワーカー: 4
ワーカー・ゾーン: tok02, tok05, tok04
バージョン: 1.12.5_1537
所有者: TAKARA@jp.ibm.com
ダッシュボードのモニタリング: https://metrics.ng.bluemix.net/app/***
リソース・グループ ID: 6fcbec624d904ab19a28a1b75c58d467
リソース・グループ名: default
これのクラスタのワーカーをリストすると、次のようになります。(見やすくなるように表示を編集しています)ゾーンはデータセンターと同じレベルで、3つのデータセンターで、ベアメタル(mb2c)を含む4つのノードが存在しています。
$ ibmcloud ks workers iks1
OK
ID プライベート IP マシン・タイプ 状態 状況 ゾーン
kube-tok02-cr41679d0177fa-w18 10.132.253.30 u2c.2x4.encrypted normal Ready tok02
kube-tok04-cr41679d0177fa-w11 10.192.9.68 mb2c.4x32.encrypted normal Ready tok04
kube-tok04-cr41679d0177fa-w16 10.192.9.93 u2c.2x4.encrypted normal Ready tok04
kube-tok05-cr41679d0177fa-w17 10.193.10.50 u2c.2x4.encrypted normal Ready tok05
これらの ALB は、各ゾーンのワーカーノード上に、ポッドとして起動しています。そして、それらの上に マルチゾーン・ロードバランサー(MZLB)が動作しています。この動作をコマンドで参照することはできませんが、このクラスタを代表するDNS名でアドレスをひいてみると、MZLBの動作に一端がわかります。以下に、DNS名のアドレス解決結果を挙げます。この「iks1.jp-tok.containers.appdomain.cloud」は3つのIPアドレスに対応していることがわかります。
> iks1.jp-tok.containers.appdomain.cloud
サーバー: google-public-dns-a.google.com
Address: 8.8.8.8
権限のない回答:
名前: iks1.jp-tok.containers.appdomain.cloud
Addresses: 165.192.71.230
128.168.73.198
169.56.27.94
これの3つのIPアドレスは、旧SoftLayerで開発されたポータブルIPアドレスで、ノードのIPアドレスから独立して確保されたIPアドレスです。各ゾーンのポータブルIPアドレスは、ALBが動作するノードにマップされています。もしも、ノードが停止した場合、ポータブルIPは、同じゾーンの別のワーカーノードに対応づけられ、役割りを継続します。また、ゾーンが動作できなくなった場合は、前述のDNSの割り当て先から削除されリクエストの分配が停止します。
次のコマンド実行結果は、ALBをリストしたものです。 ALB IPに注目してください。先に挙げたドメイン名に対応した IPアドレスと同じことがわかります。
$ ibmcloud ks albs --cluster iks1
OK
ALB ID 有効化 状況 タイプ ALB IP ゾーン ビルド
public-cr41679d0177fa4f92ab10262fa586956c-alb1 true enabled public 128.168.73.198 tok04 ingress:404/ingress-auth:300
public-cr41679d0177fa4f92ab10262fa586956c-alb2 true enabled public 165.192.71.230 tok05 ingress:404/ingress-auth:300
public-cr41679d0177fa4f92ab10262fa586956c-alb3 true enabled public 169.56.27.94 tok02 ingress:404/ingress-auth:300
ここで、ALBの一つを停止させると、DNSから引けるIPアドレスのエントリは削除されます。次のコマンドでALBの一つを停止することができます。
$ bx ks alb-configure --albID public-cr41679d0177fa4f92ab10262fa586956c-alb2 --disable
ALB を構成中...
しばらくしてから、ALBをリストすると、2番目のALBが停止しており、ALB IPの一つが削除されていることがわかります。
$ ibmcloud ks albs --cluster iks1
ALB ID 有効化 状況 タイプ ALB IP ゾーン ビルド
public-cr41679d0177fa4f92ab10262fa586956c-alb1 true enabled public 128.168.73.198 tok04 ingress:404/ingress-auth:300
public-cr41679d0177fa4f92ab10262fa586956c-alb2 false disabled public - tok05 ingress:404/ingress-auth:300
public-cr41679d0177fa4f92ab10262fa586956c-alb3 true enabled public 169.56.27.94 tok02 ingress:404/ingress-auth:300
アドレス解決で、MZLBの動作を確認すると、つぎのように、2番目のIPアドレスが削除されました。このようにMZLBは、DNSと連動してデータセンターレベルの障害に対して、リクエストの分配先を削除することで、サービスの継続を維持します。
> iks1.jp-tok.containers.appdomain.cloud
サーバー: google-public-dns-a.google.com
Address: 8.8.8.8
権限のない回答:
名前: iks1.jp-tok.containers.appdomain.cloud
Addresses: 128.168.73.198
169.56.27.94
ALB と ポッドの対応づけ
クラスタのDNS名とポッドを対応づけるためには、つぎの3つの Kubernetes オブジェクトを作成します。
- Ingress Resource (ingress-mz.yml イングレスの設定)
- Service (deploy-web.yml のサービス部分)
- Controller / Pod (deploy-web.yml のデプロイメント部分)
イングレスとサービスの対応づけは、イングレスのマニフェストに、サービス名を指定します。
そして、サービスとポッドの対応は、ラベルとセレクターによっておこないます。
マルチゾーンクラスタでの動作の様子
GitHub https://github.com/takara9/container-session-check からクローンして利用します。マニフェストのコードは、GitHubを参照ねがいます。ここで適用したマニフェストは、試験用PHPのアプリケーションのポッドを、デプロイメント・コントローラーで起動します。そして、これらのポッドを代表するサービスを起動します。
サービスは、ポッドのクラスタを代表するIPアドレス 172.21.7.146 を獲得しています。このIPアドレスは、サービスオブジェクトが存在している間は変更されること無く、k8sクラスタ内のDNSで名前で、IPアドレスを解決できます。
$ kubectl apply -f deploy-test.yaml
deployment.apps/web created
service/web-svc created
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/web-ddd5df9c7-6wk9m 1/1 Running 0 9s
pod/web-ddd5df9c7-7sf48 1/1 Running 0 9s
pod/web-ddd5df9c7-8px6k 1/1 Running 0 9s
pod/web-ddd5df9c7-9txgh 1/1 Running 0 9s
pod/web-ddd5df9c7-bn86q 1/1 Running 0 9s
pod/web-ddd5df9c7-dkg6f 1/1 Running 0 9s
pod/web-ddd5df9c7-hntc6 1/1 Running 0 9s
pod/web-ddd5df9c7-nvtjp 1/1 Running 0 9s
pod/web-ddd5df9c7-qkcgm 1/1 Running 0 9s
pod/web-ddd5df9c7-wm7tq 1/1 Running 0 9s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 172.21.0.1 <none> 443/TCP 115d
service/web-svc ClusterIP 172.21.7.146 <none> 9080/TCP 9s
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/web 10 10 10 10 9s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-ddd5df9c7 10 10 10 9s
次の結果から読み取れるように、10個のポッドは、4つのサーバー(NODE)にデプロイされて動作していることがわかります。
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
web-ddd5df9c7-6wk9m 1/1 Running 0 15s 172.30.33.207 10.192.9.93
web-ddd5df9c7-7sf48 1/1 Running 0 15s 172.30.119.19 10.192.9.68
web-ddd5df9c7-8px6k 1/1 Running 0 15s 172.30.210.215 10.132.253.30
web-ddd5df9c7-9txgh 1/1 Running 0 15s 172.30.169.96 10.193.10.50
web-ddd5df9c7-bn86q 1/1 Running 0 15s 172.30.210.214 10.132.253.30
web-ddd5df9c7-dkg6f 1/1 Running 0 15s 172.30.119.18 10.192.9.68
web-ddd5df9c7-hntc6 1/1 Running 0 15s 172.30.210.216 10.132.253.30
web-ddd5df9c7-nvtjp 1/1 Running 0 15s 172.30.119.13 10.192.9.68
web-ddd5df9c7-qkcgm 1/1 Running 0 15s 172.30.169.97 10.193.10.50
web-ddd5df9c7-wm7tq 1/1 Running 0 15s 172.30.119.9 10.192.9.68
上記だけでは、ポッドのアプリケーションは、外部へ公開されませんから、イングレスコントローラー(ALB)に、サービス名 web-svc を登録して、ポッドへ転送できるようにします。
$ kubectl apply -f ingress.yml
ingress.extensions/ingress-web created
$ kubectl get ing
NAME HOSTS ADDRESS PORTS AGE
ingress-web iks1.jp-tok.containers.appdomain.cloud 128.168.73.198,165.192.71.230,169.56.27.94 80 3s
$ kubectl describe ing
Name: ingress-web
Namespace: default
Address: 128.168.73.198,165.192.71.230,169.56.27.94
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
iks1.jp-tok.containers.appdomain.cloud
/ web-svc:9080 (<none>)
Annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"ingress-web","namespace":"default"},"spec":{"rules":[{"host":"iks1.jp-tok.containers.appdomain.cloud","http":{"paths":[{"backend":{"serviceName":"web-svc","servicePort":9080},"path":"/"}]}}]}}
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Success 11s public-cr41679d0177fa4f92ab10262fa586956c-alb2-64478db6dc-xswqh Successfully applied ingress resource.
Normal Success 11s public-cr41679d0177fa4f92ab10262fa586956c-alb1-668b6578db-p5pn5 Successfully applied ingress resource.
Normal Success 11s public-cr41679d0177fa4f92ab10262fa586956c-alb3-b9df97448-j9skc Successfully applied ingress resource.
Normal Success 11s public-cr41679d0177fa4f92ab10262fa586956c-alb1-668b6578db-zslwp Successfully applied ingress resource.
前述のように、コマンド「ibmcloud ks albs iks1」によって、ALBのリストを表示すると、各ゾーン(データセンター)ごとにALBが一つIPアドレスを確保して動作いています。ポッドのレベルで確認すると、待機中のALBポッドを含めて、各ノードに一つは動作しています。
$ kubectl get po -n kube-system -o wide |grep public-cr*
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
public-cr41679d0177fa4f92ab10262fa586956c-alb1-668b6578db-p5pn5 4/4 Running 0 14h 172.30.33.205 10.192.9.93 <none>
public-cr41679d0177fa4f92ab10262fa586956c-alb1-668b6578db-zslwp 4/4 Running 0 17h 172.30.119.47 10.192.9.68 <none>
public-cr41679d0177fa4f92ab10262fa586956c-alb2-64478db6dc-kkp52 0/4 Pending 0 17h <none> <none> <none>
public-cr41679d0177fa4f92ab10262fa586956c-alb2-64478db6dc-xswqh 4/4 Running 0 17h 172.30.169.87 10.193.10.50 <none>
public-cr41679d0177fa4f92ab10262fa586956c-alb3-b9df97448-j9skc 4/4 Running 0 17h 172.30.210.209 10.132.253.30 <none>
public-cr41679d0177fa4f92ab10262fa586956c-alb3-b9df97448-jm5rz 0/4 Pending 0 14h <none> <none> <none>
前述のマニフェストでデプロイされるコンテナ maho/session-check:1.2 が返す結果をリストしたものです。
このリストの中で、ALBは、NGINXをベースとするプロキシサーバーのポッドであるため、HTTP_X_FORWARDED_FOR の ALBが動作するノードのIPアドレス、REMOTE_ADDRに、ALBのポッドのIPアドレスがセットされる。
- HTTP_X_FORWARDED_FOR HTTPプロキシサーバまたはロードバランサを経由してウェブサーバに接続するクライアントの送信元IPアドレスを特定する際のデファクトスタンダード
- REMOTE_ADDR クライアントの接続元IPアドレスであるが、ロードバランサやプロキシを間に挟むと、REMOTE_ADDR がロードバランサ等のアドレスになる
$ while true; do curl -s http://iks1.jp-tok.containers.appdomain.cloud;sleep 60; done
Hostname: web-ddd5df9c7-9txgh<br>
1th time access.
<br>
HTTP_CLIENT_IP = <br>
HTTP_X_FORWARDED_FOR = 10.193.10.50<br>
HTTP_X_FORWARDED = <br>
HTTP_X_CLUSTER_CLIENT_IP = <br>
HTTP_FORWARDED_FOR = <br>
HTTP_FORWARDED = <br>
REMOTE_ADDR = 172.30.169.87<br>
Hostname: web-ddd5df9c7-nvtjp<br>
1th time access.
<br>
HTTP_CLIENT_IP = <br>
HTTP_X_FORWARDED_FOR = 10.132.253.30<br>
HTTP_X_FORWARDED = <br>
HTTP_X_CLUSTER_CLIENT_IP = <br>
HTTP_FORWARDED_FOR = <br>
HTTP_FORWARDED = <br>
REMOTE_ADDR = 172.30.210.209<br>
Hostname: web-ddd5df9c7-qkcgm<br>
1th time access.
<br>
HTTP_CLIENT_IP = <br>
HTTP_X_FORWARDED_FOR = 10.193.10.50<br>
HTTP_X_FORWARDED = <br>
HTTP_X_CLUSTER_CLIENT_IP = <br>
HTTP_FORWARDED_FOR = <br>
HTTP_FORWARDED = <br>
REMOTE_ADDR = 172.30.169.87<br>
Hostname: web-ddd5df9c7-nvtjp<br>
1th time access.
<br>
HTTP_CLIENT_IP = <br>
HTTP_X_FORWARDED_FOR = 10.192.9.68<br>
HTTP_X_FORWARDED = <br>
HTTP_X_CLUSTER_CLIENT_IP = <br>
HTTP_FORWARDED_FOR = <br>
HTTP_FORWARDED = <br>
REMOTE_ADDR = 172.30.119.47<br>
Hostname: web-ddd5df9c7-nvtjp<br>
1th time access.
<br>
HTTP_CLIENT_IP = <br>
HTTP_X_FORWARDED_FOR = 10.192.9.68<br>
HTTP_X_FORWARDED = <br>
HTTP_X_CLUSTER_CLIENT_IP = <br>
HTTP_FORWARDED_FOR = <br>
HTTP_FORWARDED = <br>
REMOTE_ADDR = 172.30.33.205<br>
まとめ
IKSのクラスタを複数ゾーンで構成する場合のイングレスについて、ポイントをまとめると以下になりました。
- マルチゾーンにノードを立てると、自動的にMZLBが設定され、別途CISをオーダーしたり設定する必要はない。
- MZLBを利用するためには、イングレス(ALB)を利用しなければならない。
- ALBは、NGINXイングレス・コントローラと同等のポッドベースのため、アプリケーションはクライアントのIPアドレスを知ることができない。
- ALBのIPアドレスは、ポータブルIPのアドレスが利用される。
- MZLBのDNS名は、代表IPアドレスを持つものでは無く、ALBのIPアドレスを返す。
参考資料
[1] Ingress のコンポーネントとアーキテクチャー https://cloud.ibm.com/docs/containers/cs_ingress.html#planning
[2] ウィキペディア X-Forwarded-For https://ja.wikipedia.org/wiki/X-Forwarded-For