KubenetesのPodを外部公開する時のそれぞれの流れについて図ありで解説します。
ClusterIP
- Serviceのタイプの1つです
- ClusterIPはPodへの接続を実現するためのKubernetesのリソースです。
- とあるアプリケーションが10個のPodで動作している場合に、その中のどれか1つにアクセスするときのVIPとしてClusterIPが存在します。
- 通信の流れ
- Client→ClusterIP→Pod
これによりユーザは常にClusterIPを指定すれば対象のアプリケーションのどこかのPodにアクセスできるようになるわけです。
ただしこのIPはKubernetesクラスタの外からは通信ができないIPアドレスのため、外部からアプリにアクセスさせる処理はClusterIPのみでは実現できません。
ClusteIPの振り分けについて
ClusteIPで指定されたIPにアクセスすると、対応するPodに通信が分散されます。このときの機能はほとんどの場合はkube-proxyによって管理されるiptablesやipvsによって制御されます。(ClusterIPで指定したIPを見つけると、iptables上のルールがPodのIPにDNATする)
しかし最近はこのNATの処理をkube-proxyにさせるのではなく、CNIプラグインにてL2ロードバランシングをさせるというアイデアが出てきています。(kubernetes nodeにまたがった論理ルータ/スイッチを構築し分散するSDN的な考え)
NodePort
- Serviceのタイプの1つです
- アプリケーションを外部に公開するためのServiceです。KubernetesクラスタのすべてのNodeの指定したポート番号にてListenします。
- NodePortのServiceが作成されると、裏でClusterIPのServiceも作成されます。
- 通信の流れ
- Client→NodePort→ClusterIP→Pod
例えば、以下の通りです。
- アプリA:ポート30001
- アプリB:ポート30002
- アプリC:ポート30003
External IP
- external IPは特定のIPに対して通信がくると、それに応じてClusterIPに転送される仕組みです
- Serviceの一種です
- 通信の流れ
- Client→External IP→ClusterIP→Pod
ただしClientからそのIP(external IP)に対して疎通ができなければならないので、仮想IPを用いてARPでうまいこと解決する必要があります。ユースケースとしては結構限られたものになるかなと思います。後述するLoadBalancer(オンプレ)のMetalLBでは external IPの仕組みをうまく使って通信を実現しています
LoadBalancer
クラウドの場合
EKSやGKEの場合はロードバランサーに対してどのように対象のPodを見せるかで変わります。
- 1つ目はNodePortを経由します
- 通信の流れ: Client→LoadBalancer→NodePort→ClusterIP→Pod
- 2つ目は直接PodのEndpointに行きます
- 通信の流れ: Client→LoadBalancer→Pod
NodePortを経由するとClusterIPのところでiptablesによる分散をもう一度行うことになるため無駄が発生しますが、IPターゲットすべてのPodに対してバランシングを行うことができます。(externalTrafficPolicy: Local
を使えば回避することは可能)
またNodeがダウンした際にPodのEvictionに時間がかかり一定時間疎通が取れないPodにも通信が飛んで行ってしまう課題があるのですが、NLBによって各Podが直接ヘルスチェックを受信・応答することでNode障害(kubelet障害)を気にする必要がなくなります。
Node障害の詳細はこちら。
オンプレの場合
MetalLBを使う選択肢しかいまのところないのですが、その場合は上記のようになります。実質LoadBalancerというよりはアクセス可能な仮想IP(VIP)をexternalIPとして払い出すだけです。
仮想IPに対して外部からアクセスできるように、MetalLBはBGPやARPを使ってアクセス情報を広報します。(よってARPのときは同一NWにいるClientからしかアクセスできません)
通信の流れ: Client→External IP→ClusterIP→Pod
MetalLBについてはこちらの記事で詳しく解説しているのでご覧ください。
ちなみにオンプレとは言え、自分でtype: LoadBalancer
が使用されたらそれをトリガーにOpenStackや自分のところのLoadBalancerのAPIを叩くような仕組みを作ることは可能です。(そういう意味では↑のオンプレはMetalLBの例であってオンプレ全体がこうというわけではない)
CyberAgentではまさにそれを実装されています。
Ingress
- Serviceではありません
- Serviceに対する外部からのアクセス(主にHTTP)を管理するAPIオブジェクトです
- IngressはServiceに対して、外部疎通できるURL、負荷分散トラフィック、SSL/TLS終端の機能や、名前ベースの仮想ホスティングを提供します
- [注意] オンプレミス or クラウドでやってることが違います
クラウドの場合
※GKEやEKSの場合
クラウドの場合は、IngressContollerが初めから用意されておりユーザがIngressを作成するとAWSやGCP上にL7のロードバランサーが作成されます。そしてそのロードバランサー経由でPodに通信が飛んでくるようになります。
ただしLoadBalancerと同じようにコンテナに直接くる機能もありますので、要件に応じて使い分けてみてください。
- 通信の流れ
- 1つ目はNodePortを経由します
- 通信の流れ: Client→LoadBalancer→NodePort→ClusterIP→Pod
- 2つ目は直接PodのEndpointに行きます
- 通信の流れ: Client→LoadBalancer→Pod
- ※GKEではNetwork endpoint groups(NEG)を経由しています
- Ingress によるコンテナ ネイティブの負荷分散 | Kubernetes Engine ドキュメント | Google Cloud
- 1つ目はNodePortを経由します
オンプレの場合
オンプレミスの場合はIngressの実体がKubernetesクラスタの中に存在します。よって一度NodePort経由で入ってきた通信をIngress Podが制御し、対象のアプリケーション向けに通信をねじ曲げたりすることになります。
そのためIngressだけだと外部公開の仕組みは実現できないということを覚えておいてください。
Ingressを実現するOSSが以下の通りです。
- Nginx Ingress Controller
Istio など
通信の流れ: Client→NodePort→ClusterIP→Ingress Pod→ClusterIP→Pod
Router(Openshift)
- RouterはOpenshift/OKDのみで使用されるIngressに似た役割のオブジェクトです
- NodePortを保有した状態で起動し、受け付けた通信をL7レベルでバランシングします
- またHAProxyによって分散されるため、接続先PodのClusterIPは使用せず直接PodのIP(Endpointリソース)にリクエストを転送します
- 通信の流れ: Client→NodePort→ClusterIP→Router Pod(HA Proxy)→Pod