株式会社ユーザベースの技術チームブログです。kubernetesTweetこんにちは。SPEEDA開発チームの鈴木です。これまでマルチホストでのContainer間通信について、Dockerのネットワークの基礎(前々回)マルチホストでのContainer間通信を実現する手段の一つとしてのOverlayNetwork(前回)といった話をしてきましたが、3回目となる今回はこれまでの内容を踏まえた上でKubernetesのネットワークについてお話します。内容としては大きく次の2つになります。やってマルチホストでのContainer間通信を実現しているかService名でPodと通信できるようするための仕組みでは早速1つ目の話をはじめましょう。Kubernetesを利用する場合、基本的には複数のノード上にKubernetesクラスタを構築することになります。(minikubeを使って単一ノードからなるKubernetesクラスタを構築するような例外はあります)つまり何らかの方法でホストをまたいでContainer間通信を行うわけですが、そのための方法は3つあります。Interface)を使うkubenetを使うそれではこれから上記3つについて少し詳しく説明していきます。ようなツールを用いてOverlayNetworkを構築する方法です。Linuxに改名される前の話です)。ちなみにCoreOSの場合はflannelを用います(flannelはCoreOS社が開発しています)。この場合、CoreOSのcloud-configにflannelの設定とflannelに合わせたDockerの設定が必要になります。すべてのノードに対して設定が必要になるのでなかなか面倒です。Interfaceを構成するためのプラグインを開発するための仕様とライブラリなどから構成されています。Toolが共通して持っている考えのため、重複を避けるために定義されました。Foundationは、Kubernetesの開発主体です。Hat、Google、IBM、Mesosphere、シスコ、インテルなどが主導して2015年7月に発足しました。KubernetesはこのCNIプラグインを利用することができます。ちなみにDocker自体もCNIとは別にネットワークプラグインを利用できる仕組みを持っています。Runtimeは以下のようなものがあるようです。Kubernetesだけではなく、MesosなんかもCNIを利用できるみたいですね。Plugins以下はCNIの仕様に則って作成されたCNIプラグインです(2017年9月現在)。注意点として、上述したようにCNIはKubernetesのためだけのインタフェースではないので、Kubernetes用ではないプラグインもあることを挙げておきます。Kubernetesのマルチプラグインとして動作。複数のプラグインのグループ化(他のプラグインをdelegateして呼び出すらしい)。Kubernetesのマルチプラグインとして動作。一つのContainerに複数のネットワークインタフェースを割り当て、インタフェースごとにCNIプラグインを割り当てられる。eth0はCalico、eth1はFlannel、eth2はWeaveなど。Containerが複数のネットワークにまたがることができる。Foundryのためのプラグイン。flannelに触発されたらしい。vSwitchでOverlayNetworkを構築するプラグインこれらを見ると、CalicoやWeaveといった単体でContainerの仮想ネットワークを構築できるツールがCNIプラグインとしても提供されていることがわかります。プラグインの要件を満たすように既存の機能をうまく使っているのでしょうね。Linenというように「生地」に関連した名前のものが多いですね。Contivといった仮想ネットワークのためのCNIプラグインになります。続いてCNIプラグインはどう動作するのか、具体的にどんなものなのかをご理解いただくために、少々ソースコードを読みながら解説します。CNIプラグインのソースコードCNIプロジェクトは、CNIプラグインを簡単に開発できるようにスケルトンを用意しています。プラグインの動作は、このスケルトンと実際にスケルトンを使って作られたプラグインの両方を見ると理解できると思います。順番に見ていきましょう。軽く眺めて次にいきましょう。コードすべてではありません。これは今現在のバージョンのコードです。nil}はい、続いてはスケルトンを利用した実際のプラグインです。これはCNIプロジェクトがサンプルとして用意している単純なプラグインです。nil}CNIプラグインのソースコード解説それではいよいよ解説に入ります。このサンプルのmain関数を見てください。CmdArgsを引数にとる関数2つを、スケルトンのPluginMain関数に渡しています。version.All)}このcmdAddとcmdDelはContainerが追加・削除されたタイミングで(CNIプラグインを利用するプログラム)から呼び出される関数です。引数のCmdArgsは、スケルトンを見ると次のようになっています。標準入力の値}つまり、プラグインはContainerの追加・削除を契機として、上記構造体の情報を元に任意の処理を行います。ちなみにこのサンプルはContainerの追加時にループバックインタフェースをContainerのネットワーク名前空間内に作成し、Containerの削除時にループバックインタフェースをネットワーク名前空間から削除しています。CmdArgsについて補足main関数ではcmdAdd,cmdDelの両方を渡しているが、addとdelのどちらを使うかどう決定されるのでしょう。これはスケルトンのコードを見ればわかるのですが、CNI_COMMANDという環境変数の値により決定されます。値がADDの場合cmdAddが呼び出され、値がDELの場合cmdDelが呼び出されます。cmdAdd,cmdDelに渡ってくる`CmdArgsの値はどのようにセットされているのでしょうか。これも環境変数が使われていて、単純に以下の環境変数の値が構造体の対応する変数にセットされています。PathつまりCNIプラグインの処理を呼び出すプログラムは、予めこれらの環境変数をセットしておく必要があります。ここまで簡単にコードを見てきましたが、どうでしょう。シンプルな仕組みではないでしょうか。/etc/cni/net.d)の指定が必要になります。CNIプラグイン自体は--cni-bin-dir(defaultは/opt/cni/bin)に存在する必要があります。CNIプラグインとして、Weaveを使用する場合、このような感じになります。weave-plugin-1.9.7*CNIの仕様について更に詳しくCNIの仕様について更に詳しく知りたい場合は、公式のこちらをご覧ください。CNIの設定ファイルについての説明なども書いてあります。3.kubenetを使うこれはGKEでKubernetesクラスタを構築した場合に利用されている方法です。kubeletなどとしてkubeletコマンドの引数を見ることで分かります。--experimental-allocatable-ignore-eviction--network-plugin=kubenetという引数が与えられていますね。ちなみにCNIを使う場合は--network-plugin=cniという指定でした。kubenetを使用する場合、cbr0という名前のブリッジが作成され、cbr0に接続されたpod用のvethペアも作成されます。言うとkubenetを使用する場合、実はCNIプラグインが使用されているようです(恐らくブリッジへの接続などで)。GKEの場合、クラスタの/opt/cni/bin配下を見てみると、以下のようなプラグインが配置されています。ptpまとめこれまでマルチホストでのContainer間通信を実現するための3つの方法を説明してきたわけですが、自前でやるにしろ、CNIプラグインやkubenetを利用するにせよ、Kubernetes本体の機能はほとんど使われていないことがわかったかと思います。Networkを構築するだけでは、Containerが起動するホストが動的に変わるようなケースには対応できません。」というようなことを書きました。これはどういうことを言っているのか説明しましょう。KubernetesのようなオーケストレーションツールでContainerを起動する場合、基本的にはDeployするホストをユーザーが直接指定することはありません。Kubernetesがホストの状況(負荷とか空き容量とか)を見て、適切なホストにDeployしてくれるからです。ContainerがどこにDeployされるかは予めわからないのです。Deploy後であれば、DeployされたホストのIPを知ることができます。Container間で連携を行う場合、Deployされるまで連携するContainerのIPが分からないのは不便きわまりないです。この問題は、当然、ホストをまたいでContainer同士が通信できても解決しません。この問題を解決するには、どのホストにContainerがDeployされてもContainerにアクセスする側が気にしなくていいような仕組みが別に必要になります。KubernetesではServiceがそのための仕組みを提供しています。ServiceKubernetesをお使いの方はご存知の通り、Serviceはいわゆるロードバランサーで、名前をつけてPod(Kubernetesのリソースの一つでContainerを管理する)と紐付けておくことで、Service経由でPodとやりとりができるようになります。Cluster上のPodからのみ名前解決できる)。具体的に説明しましょう。Serviceを生成するとServiceには自動的にIPが割り当てられます。このIP(とServiceで指定したPort)を介してServiceに紐づくPodとやりとりすることができます。そのため、どのホストにPodがDeployされているか気にする必要がなくなります。ちなみにこのIPは仮想的なIPで、紐づく物理的あるいは仮想的なNICはどこにも存在しません。Cluster上のPodからのみやりとりできます。Podを直接名前解決できるようにせず、Serviceを挟んでいるのでしょうか。Podのスケールアウトに対応するためでしょうか。これはDNSラウンドロビンで対応できそうですね。仮にPodを直接名前解決できたとしたらどうでしょう。その名前解決の結果がキャッシュされ、キャッシュが有効なうちに何らかの理由でPodがフェールオーバーされて、これまでとは別のホストで起動されたらどうなるでしょうか。困ったことになりますね。このあたりのことは公式のここで言及されています。Serviceの機能について説明するだけでは面白くないので、今回は最後にもう少し突っ込んだ話をしたいと思います。Serviceの機能はどのように実現されているか今回は機能を次の2つに絞って「誰」が「どのように」実現しているかを説明します。名前解決ルーティングルーティングというのは、ServiceのIP(とPort)にアクセスされたら対応するPodのIP(とPort)にフォワードするという意味で書きました。担当者は誰だ?まず、こちらをご覧ください。これはKubernetesのアーキテクチャ図で、これを見るとKubernetesが様々な登場人物(Component)から成るツールだということがわかります。「誰」がという表現をしたのはこのためです。今回の話でいうと、名前解決の部分を担当しているのがkube-dns(上図にはいませんが)で、ルーティングの部分を担当しているのがkube-proxyです。「どのように」それを実現しているのでしょうか。彼らは一体何をやっているのか?彼らがやっていることを端的に表すと「ServiceやEndpointリソースを監視して、リソースの状況に合わせたアクションを起こす」ということをやっています(今回はEndpointリソースには言及しません)。具体的に説明しましょう。Kubernetesのkube-apiserverは、自身が管理している各種リソース(ServiceやNode、Pod、Secretなど)について、問い合わせる口を持っています。いったリソースの変化を検知しイベント通知を行うためのライブラリが用意されています。イベント通知を受けた際に処理を行うイベントハンドラを簡単に設定できるようになっています。kube-dnsやkube-proxyはサーバとして起動され、常駐しながら上記の仕組みを利用してServiceリソースを監視し、状況に応じた処理を行っています。実際のソースコードを見てみましょう。次はkube-dnsのパッケージのコードです。}}ServiceInformerというのが、Serviceリソースの状態を監視し、通知を行ってくれる構造体になります。このInformerにハンドラを登録している部分が、AddFuncやUpdateFuncなどと書かれている部分です。続いてkube-proxyのパッケージのコードを見てみましょう。こちらも同じような感じで、Informerにハンドラが登録されています。}}kube-dnsのイベントハンドラは何をしているかまずkube-dnsのserverの起動部分を見ると分かるのですが、SkyDNSというDNSサービスを起動しています。nil))}SkyDNSのサーバーには、問い合わせを受けたときに呼び出される「DNSレコードを返す処理」を渡せるようになっており、その部分のみkube-dnsが実装しています。そのレコードの登録処理が、Serviceの変更の通知を受け取ったときに呼び出される処理なのです。Cluster内で名前解決が可能になります。kube-proxyのイベントハンドラは何をしているかまず先に言ってしまうと、ServiceからPodへのルーティングは、iptablesによって行われています。iptablesの内容を表示させてみると次のようになっています(説明に必要なものだけ抜粋しています)。フォワードされていることが分かると思います。--replicas=3このようになります。KUBE-SEP-3もう分かったかと思いますが、kube-proxyのイベントハンドラがやっているのは、このiptablesの動的な更新です。これは上記のkube-proxyのコードを追っていくと分かります(興味がありましたらproxier.goのsyncProxyRulesのあたりをご覧ください)。まとめkube-dnsとkube-proxyがServiceを監視しており、Serviceが作成されたとき、kube-dnsによりDNSレコードが登録され名前解決が可能になり、そしてkube-proxyによりiptablesを使ったルーティングが設定されることが分かったかと思います。図にするとこのような流れになるでしょうか。長くなりましたがKubernetesのネットワーク話はいかがだったでしょうか。様々な仕組みは用意しつつ、巧みに自前での実装を回避しているという印象を私は受けました。Kubernetesは調べていくと感心させられることが多いですね。SPEEDA開発チームの鈴木です。Dockerネットワークの基礎こんにちは。SPEEDA開発チームの鈴木です。»プロフィール『経済情報で、世界を変える』をミッションに、企業・業界情報プラットフォーム「SPEEDA」やソーシャル経済ニュース「NewsPicks」、ABMを強力にサポートする「FORCAS」、スタートアップの情報収集を圧倒的効率化「entrepedia」などを提供する株式会社ユーザベースの技術チームブログです。多国通貨」をハンズオンしたら快適で楽しかったGo言語でオブジェクト指向プログラミングの基本(型編)月別アーカイブ検索@Uzabase_Techさんのツイートスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。