Blog

Kubernetes をいじって Hardware LoadBalancer で “type LoadBalancer” を実現してみた @Kubernetes meetup #7


こんにちは。
Central Infrastructure Agency の青山 真也です。

今回は Kubernetes Meetup #7 で発表してきた話をまとめようと思います。
(CIA にみなさんが予想以上に食いついてきてくださり、緊張がほぐれました。)

 

アドテクスタジオのコンテナ基盤

アドテクスタジオでは、数年前からプライベートクラウドとして OpenStack を導入しています。
また、アドテクシステムではネットワークのレイテンシが許容されないシビアなシステムのため、SNAT や多段ロードバランシングが苦手な背景があります。

また、GKE ライクなコンテナ基盤として AKE (Adtech Container Engine)を社内で正式にリリースし、既にプロダクションでも利用されています。
AKE では OpenStack 連携を行った Kubernetes クラスタを自動構築していますが、ネットワークの制約から Hardware LoadBalancer は L2 DSR を使った構成となっています。

今回はその Hardware LoadBalancer をいかにして AKE と統合し、オンプレミス環境でクラウドネイティブな Kubernetes クラスタを構築したかのお話をさせていただきました。

 

OpenStack の LBaaS で実現できないこと

まず、前提として OpenStack にも L3 機能によるものや Octavia など LBaaS 相当のサービスがあります。
Kubernetes の OpenStack CloudProvider もそれを使えば、何もせずとも type LoadBalancer を利用することが出来ます。
しかし、OpenStack の LBaaS では L3 エージェントを使い、VXLAN や OVS などの技術を利用するため、レイテンシが大きくなる傾向にあります。
また、やはりレイテンシのことなどを考えると Hardware LoadBalancer を利用したいという背景があります。

 

type NodePort + Hardware LoadBalancer + (HAProxy) [AKE 1.0]

まず、真っ先に思いつくのが type NodePort の Service を作って、外部のロードバランサーから NodePort 宛に転送する方式です。
オンプレミス環境で Kubernetes を提供しているところは、この方式を使っているところは結構多いんじゃないかと思います。

弊社の環境では、L2 DSR 構成での利用を想定しており、将来的なことを考えると現状の Hardware LoadBalancer で全てのService の SNAT/NAPT  をさせることに不安が残ったため、一旦は HAProxy に SNAT/NAPT をさせている構成となっています。

 

 

HAProxy を挟んで SNAT/NAPT をしているため、経路としては

  1. Hardware LoadBalancer
  2. HAProxy
  3. Kubernetes Node
  4. Pod (場合によっては Kubernetes Node 間転送)

のように 3 段階も LoadBalancing される構成となってしまいます。
また、Hardware LoadBalancer に SNAT/NAPT させる場合でも、LB の CPU 負荷が高くなってしまうと言った問題もあります。

 

DSR 構成で複数 VIP を利用することを考えると、ポート番号を変えられず、LoadBalancer には下記のようなエントリーが埋め込まれます。
また、VM α、VM βには loopback に VIP がつけられていると想定します。

 

NodePort を利用する場合、特定の Interface に対してバインドするのではなく *:80 のようにに Interface 全てでバインドを行われてしまうため、同じ Port 番号で待ち受けることができなくなります。

 

 

externalIP を使えば、サービス毎に VIP を分散することは可能なのですが、これだとユーザの利便性が低下してしまうという問題が有ります。

 

 

type ClusterIP + iptables + Hardware LoadBalancer [AKE 2.0]

そこで、次に考えられるのが iptables を上手く使うことで SNAT/NAPT の層を排除する方法です。

Kubernetes では ClusterIP が作成された際に、”[ClusterIP] > [対象となるコンテナのIP ]” 宛となるように、iptables のチェイン(KUBE-SVC-*)が自動生成されます。

 

下記の例は 12 Replica の Pod に対して ClusterIP: 10.105.154.99 の Service 例です。
KUBE-SVC-6JWLWQN75X2GPEMN が Service 1 つにあたり、KUBE-SEP-LE6FDRM3QUFC336B がコンテナ 1つにあたるようなイメージです。

そこで、LoadBalancer から転送されてくるパケットをそのチェインに流し込むことで、SNAT/NAPT の層を排除することが可能です。
ちなみに、type LoadBalancer もこの方式を利用しています。

 

 

type LoadBalancer + Hardware LoadBalancer [AKE 3.0]

AKE 2.0 では ClusterIP を作り、別の CLI から LoadBalancer と Service の紐付け(52.0.0.1 > svc1)を行なうことで、擬似的に type LoadBalancer 相当のことが出来るようになりました。
しかし、GKE を使っているユーザが AKE に移行する際や、AKE を使っているユーザが GKE に移行する際にはそのまま移行できないという不便な点が残ってしまっていました。

 

やはり type LoadBalancer 作るしかない。。

 

“type LoadBalancer” や外部永続ディスクの動的生成(Persistent Volume Claim からの Persistent Volume 作成)などは、CloudProvider 連携している Kubernetes が利用できる機能となっています。
そのため、type LoadBalancer を実装する場合には、この CloudProvider の部分を改変することで、実現することが可能です。

他にも CloudProvider で制御できるものには色々なものがあります。

  • LoadBalancer (今回はここの話)
  • Routing
  • Host
  • Zone
  • BlockDevice


CloudProvider の Interface は pkg/cloudprovider/cloud.go に定義されており、OpenStack の場合の処理はpkg/cloudprovider/providers/openstack/* に定義されています。

LoadBalancer の CloudProvider を実装する場合には、下記の 4 つの関数を実装します。

 

実際には上記 3 種類の Interface を実装してあげる形です。
service 構造体には Service の定義が、nodes には K8s Node の情報が配列で渡ってきます。

例えば、下記のような形でそれぞれの情報を取得することが可能です。

 

この 4 つの関数の中で外部 LoadBalancer の制御、自動での IP 払い出しの機能を実装することで、CloudProvider を独自実装することが可能です。
外部 LoadBalancer の制御に関しては、AXC を利用しています。

 

 

こうして無事に GKE 互換な AKE 環境を Hardware LoadBalancer を使って実現することが可能になるのです。

沢山オンプレのコンテナ環境の話をしましたが、もちろん adtech studio でも GKE も積極的に利用しています。
パブリッククラウドでは、World Wide な環境に強かったり立ち上げ期のスピード感に対応できるなど、多くのメリットがあります。
オンプレ環境にも同等の環境を用意できたことによって、相互の利点を生かしつつ、GKE > AKE, AKE > GKE のシームレスな移行が可能となり、コンテナ特有のポータピリティを活かすことができると期待しています。

 

 

参考スライド

発表内容

https://youtu.be/kjK-LpvfZFU?t=6834

Author

アバター
masaya