Kubernetesのネットワークプラグインを自作した話

こんにちは、インフラ刷新プロジェクト「Neco」の@ueokandeです。 本日はインフラ刷新プロジェクトで開発したKubernetesネットワークプラグインについてお話します。 Necoでは現在のアーキテクチャを刷新して、機材管理やアプリケーションのデプロイを簡単にするために、オンプレミス環境でKuberneteを運用できる仕組みづくりに取り組んでます。 その成果物の1つである、Kubernetesネットワークプラグイン「Coil」についてご紹介します。

github.com

Kubernetesは管理者がプラグインを選択・作成することで、実行環境や要件に合わせて自由にカスタマイズできます。 ネットワークプラグインもコンテナの実行環境に合わせてカスタマイズ可能です。 サイボウズが自社データセンターでKubernetesを運用するために、Kubernetesネットワークプラグインを作成しました。 この記事では、Coilのアーキテクチャと、なぜ自社開発に至ったかの経緯をお話しします。

Kubernetesのネットワークの仕組み

コンテナを docker run コマンドで起動するとき、DockerはコンテナにDockerのプライベートネットワークからIPアドレスを割り当てます。 このネットワークはノード上に閉じたネットワークで、異なるノード上のコンテナ間は通信できません。 そのためPodが適切なネットワーク設定やIPアドレスを利用できるよう、Kubernetes管理者は環境に応じて適切なネットワークを設定します。

Kubernetesのネットワークを設定する仕組みは、ネットワークプラグインという形で実現されます。 Kubernetes管理者は適切なネットワークプラグインを各ノードにインストールして、kubeletがそれを利用するように設定します。 ネットワークプラグインはContainer Network Interface (CNI)という仕様に基づいており、CNIプラグインとも呼ばれています。

CNIプラグインは実行可能なバイナリです。 環境変数や標準入出力からコンテナの情報を受け取り、結果を標準出力へ返します。 Podがデプロイされてkubeletがコンテナを起動するとき、CNIプラグインを実行します。 CNIプラグインは渡されるコンテナIDやネットワーク名前空間をもとに、適切なネットワーク設定を適用したり、割り当てるIPアドレスを返します。

なぜネットワークプラグインを開発したか

Kubernetesのネットワークは、性能やスケーラビリティを考慮して、各Podが持つIPアドレスはBGPで経路制御することが多いです。 新しいインフラでも、各ノードやPodはすべてBGPによって経路を制御します。 ネットワーク設計やノード側の設定は以下の記事からどうぞ。

BGPが扱えるCNIプラグインはCalicoRomanaなどすでにいくつか存在します。 特にCalicoは多くの企業で採用実績があります。 これらのプラグイン導入も視野に入れてましたが、調査してみて採用に至りませんでした。 これらのプラグインはBGPスピーカーを組み込んでいます。 そしてNecoのネットワークでは、それぞれのノードも自身のIPアドレスを広告してます。 この2つを併用するとき、BGPでは問題が発生します。

それぞれのBGPスピーカーが利用しているBFDという障害検知の仕組みは、隣接するネットワーク機器とピアリングします。 現状それぞれのノードがToRスイッチとピアリングしているため、CNIプラグインがToRスイッチとピアリングできません。 ノード側とCNIプラグインそれぞれの経路を広告するには、片方のBGPスピーカーが両方の経路を広告するような実装が必要です。 これをCalicoまたはRomanaで実装するのは、メンテナンス性などの理由で将来性がないので、最終的にCNIプラグインを開発するという結果に至りました。

Coilの設計方針

CoilがやることはコンテナのIPアドレス管理とネットワークへ経路を適用するのみです。 CNIプラグインは「プラグインチェーン」という仕組みがあり、複数のプラグインを組合せることができます。 ネットワークポリシーの設定などはCoilはやらず、CalicoやCiliumなど他のプラグインを利用する予定です。

Coilの大きな特徴の1つは、BGPスピーカーを組み込まない という点にあります。 Unix哲学には「1つのプログラムは、1つのことをうまくやる」という言葉あります。 Coilはコンテナのアドレスをネットワークに広告せず、別のソフトウェアが経路広告できるように経路情報を外部に出力するだけです。 Necoのネットワークではすでに各ノードにBIRDがインストールされており、ノードのIPアドレスをネットワークに広告しています。 Coilは自身が経路広告せずに、各ノードにインストールされているBIRDがコンテナのアドレスをネットワークに広告できるようにしました。

Coilのもう1つの大きな方針は、アドレス単位の経路広告ではなく、サブネット単位の経路広告という点です。 これはRomanaの設計からインスパイアされました。 CalicoはPodのアドレス広告を/32のアドレスとして広告します。 つまりPodの数だけ経路情報がネットワークに広告されます。 Necoでは将来数十万のPodをデプロイすることを想定してるため、パフォーマンス面で心配がありました。 一方Coilは各Podが持つアドレス単位ではなく、ひとかたまりのサブネット(/27など)として経路広告します。 この単位をCoilではブロックと呼んでいます。 経路広告をブロック単位にすることで、ネットワーク全体の経路数が大きく減ります。 たとえばブロックのサブネットマスクが/27とすると、アドレス単位の経路広告と比べて広告する経路数は1/32に抑えることができます。

ブロックの確保はノードに対して割り当てられ、Coilはノードに割り当てられたブロックからIPアドレスをPodに割り当てます。 Coilはブロックの経路広告と、ノード内のPodの2つの経路を設定します。 前者の経路はネットワークに広告され、どのPodのアドレス(のサブネット)をどのノードが持ってるかを知ることができます。 後者はノード内からPodに到達するための経路です。 この2つの経路を設定するとこで、異なるノードにデプロイされたコンテナの経路を知ることができます。

Coilのアーキテクチャ

BIRDがどの経路を広告すべきかを見れるよう、Coilは割り当てられたブロックを指定されたLinuxのルーティングテーブルに出力します。 Linuxカーネルはシステムが利用するルーティングテーブルのほかに、複数のルーティングテーブルを持っています。 これはLinuxカーネルがネットワーク経路に使用しないので、通常のアプリケーションは影響を受けません。 BIRDはこのルーティングテーブルから経路情報を参照し、ネットワーク全体に広告します。 つまりBIRDに限らずこのルーティングテーブルを参照できるなら、quaggaなどの他のソフトウェアと組合せて利用できるようになっています。

Coilのアーキテクチャの図が以下のようになります。ブロックやIPアドレスの割り当て情報はetcdに保存します。 各ノードはCNIプラグインの coil と、coilのデーモン coild がデプロイされます。 CNIプラグイン coil は、kubeletから呼び出して coild に問い合わせるのみです。 ブロックやアドレスの割り当てをするのは coildです。 coild はetcdを参照して、新しいIPアドレスをコンテナに割り当てたり、ブロックが必要なら新たにブロックをノードに割り当てます。 新しいブロックを割り当てたら、それをBGPによって経路広告するためにルーティングテーブルに出力します。

f:id:cybozuinsideout:20181218164058p:plain
Coilのアーキテクチャ

各Podに経路を割り当てるまでのフローは以下の通りです。

  1. Podがデプロイされると、KubeletがCNIプラグインのcoilを呼び出します。
  2. coilcoildにPodの名前空間を渡して、新しいIPアドレスを要求します。
  3. coildは現在のブロック割り当て情報から、利用可能なIPアドレスを探します。 もし割り当てたブロック内で利用可能なアドレスがなければ、新たなブロックを割り当てます。
    • 新たなブロックを割り当てたら、そのブロックをルーティングテーブルに登録します。
    • BIRDは新しいブロックがルーティングテーブルに追加されたら、ネットワークに経路広告します
  4. 割り当て可能なIPアドレスがあると coildcoil に結果を返します。
  5. coil はコンテナのIPアドレスとノード内の経路を設定します。

Coilはアドレスプールと、Podが属するKubernetes名前空間を紐づけます。 Kubernetesの名前空間の認可を設定することで、特定の権限があるユーザのみに、特定のIPアドレスを割り当てるよう設定できます。 たとえばサイボウズでは利用者全員がグローバルIPを利用できないよう、プライベートIPとグローバルIPを別の名前空間に割り当てます。 そして特定の権限があるユーザーのみ、グローバルIPアドレスを持つPodをデプロイできます。

まとめ

Coilの開発状況は、機能は一通り実装が完了しており、これから本番環境に投入予定です。 ネットワークプラグインの開発は新しいチャレンジでしたが、すでにチーム内ではBGPネットワークの知見が溜まってたのでスムーズに開発が進みました。 またCoilの責務を最小限にして、多くの機能を実装しなかったのがカギです。 おかげでBIRD以外のルーティングソフトウェアからも利用できる設計になりました。

サイボウズでは自社でKubernetesを運用して、そのうえでサイボウズのプロダクトを運用できる環境を開発しています。 他にもまだまだ紹介してないソフトウェアがあるのですが、それはまた別の機会に紹介したいと思います。