Kubernetes background and network configuration

Kubernetesとそれに関連するミドルウェア

Kubernetesはdockerなどのコンテナのスケジューリングやロードバランスを担うクラスタリング制御用運用管理ツール。様々なミドルウェアを組み合わせて使うモジュラー型のアーキテクチャになっている。

etcdは分散KVSで、Zookeeperと同じようなもの。コンセンサスアルゴリズムによる合意形成の手段を実装することにより、分散環境におけるKVSのスケーラビリティとリダンダンシーを実現している。 etcdは、Linuxディストリビューションの一つであるCoreOSのアプリケーションとして提供されているが、今回はCentOS上で使用している。

flannelはネットワーク管理用ツールで、VXLANなどのオーバーレイのエンドポイントを作る。etcdとflannelが協調してクラスタ内のネットワークを形成する役目を持つ。

Kubernetesで実際にコンテナを作成するにはkubectl create -f pod.yamlでPodの作成を実行する。PodはKubernetesの概念において一つのノード上に存在するコンテナの集まりを指す。以下は、一つのコンテナのみを作成する最もシンプルなPodの設定ファイル。なお、replica数の指定なども設定ファイルに記述することになる。

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
      - containerPort: 80

なお、kubernetes がどのノードにPodを作成するか、はおまかせになるが、下記のように確認することはできる。

[xxx@master ~]$ kubectl describe pods nginx-pod
Name:           nginx-pod
Namespace:      default
Node:           minion2/10.140.0.4
...

ちなみに、yamlの設定ファイルを部分的に管理しておいて、後でマージして一つの大きな設定ファイルを作る、といったことがSpruceというOSSを使うとできるらしく、インフラのコード化を考えると重宝しそう。以下のスライドを参照のこと。

flanneldのコンフィグとして、etcdのURLエンドポイントとキーを指定する。flanneldが管理する、クラスタとして割り当てるネットワークサブネットは、etcdctlコマンドでetcdに登録する。なお、etcdへの登録や参照にはREST APIが提供されているので、curlを使ってPOSTメソッドで行うこともできる。

# /etc/sysconfig/flanneld
FLANNEL_ETCD="http://10.140.0.2:2379"
FLANNEL_ETCD_KEY="/atomic.io/network"```
# /atomic.io/network/config
{
    "Network": "10.20.0.0/16",
    "SubnetLen": 24,
    "Backend": {
        "Type": "vxlan",
        "VNI": 1
    }
}

VXLANにおける宛先解決

enakai00.hatenablog.com

上記の記事で特におもしろいのは、Linuxカーネルが発行するL3MISSというイベントによってアプリケーションにMACアドレス解決を移譲する、L2MISSによってアプリケーションにIPアドレスの取得を移譲する、というところ。 VXLANでは、どのようにUnknown Unicastの宛先VTEPを解決するかは、VTEPの実装に依存しているようだ。etcdのような仕組みを持たない場合は、単にマルチキャストによって全てのVTEPに解決を促すことになるだろう。 これを見ると、もうARPリクエストのようにマルチキャストやブロードキャストでMACアドレスを解決するという手法は時代遅れなのかもしれない。

なお、flannelを使わずにOpen vSwitchを使うこともできる。こちらの方が、OpenFlowのAPIを使って柔軟にネットワークの制御ができるはず。

http://kubernetes.io/docs/admin/ovs-networking/kubernetes.io

VXLANを使わないネットワーク構築方法

techblog.yahoo.co.jp

VXLANはオーバーレイなので、カプセリングのオーバーヘッドにより、データ転送のスループットが犠牲になる。これを避けるために、ピュアなL3ルーティングによってクラスタ内のネットワーク接続性を担保する方法もある。上記の記事は、flannelではなくCalicoを使った、BGPでのIPルーティングによるネットワーク構築方法になる。

クラスタを一つのASとみなしてプライベートAS番号を割り当てて管理している。BGPピアはメッシュで接続する必要があるため、BGPスピーカが増えた際のAS内のIntra BGP (iBGP)でのフルメッシュによる負荷を削減するために、ルートリフレクタを使ってスター型のピアリングを構成している。

おまけの、トラブル事例もおもしろい。サーバの前段に置いたロードバランサーARP応答させるため、サーバに直接届くARP要求には応えない設定にしておいたために、サーバ内のVMからのARP要求にも応えることが出来なかった、という問題のようだ。

Google Compute Engineでの構築

実際にGCEを使って、KubernetesでのPod構築と、VXLANによる疎通確認を行った。詳細な設定については他のソースに譲るが、外部に公開するサービスとしてのIPアドレスには0.0.0.0を使用する、service_account_key_fileを設定する、といった点には注意したい。sudo ss -untplでListenしているポートの確認を行いながら進めた方が良い(sudoを付けないとサービスの名前が取れないので、サービスでgrepをかけることができなくなる)。あるノードのホスト(10.140.0.4)から、別のノードのコンテナ(10.20.4.2)に対してPingを実施している。flannelデバイス(10.20.52.0)が、オーバーレイネットワークのソースアドレスになっている。10.140.0.0/16がアンダーレイ(物理ネットワーク)、10.20.0.0/16がオーバーレイ(仮想ネットワーク)になる。

受信側でtcpdump(-T vxlan)によってVXLANパケットをキャプチャしている。

[xxx@minion2 ~]$ ping 10.20.4.2
PING 10.20.4.2 (10.20.4.2) 56(84) bytes of data.
64 bytes from 10.20.4.2: icmp_seq=1 ttl=63 time=1.53 ms
64 bytes from 10.20.4.2: icmp_seq=2 ttl=63 time=0.365 ms
64 bytes from 10.20.4.2: icmp_seq=3 ttl=63 time=0.332 ms
64 bytes from 10.20.4.2: icmp_seq=4 ttl=63 time=0.347 ms
64 bytes from 10.20.4.2: icmp_seq=5 ttl=63 time=0.357 ms
64 bytes from 10.20.4.2: icmp_seq=6 ttl=63 time=0.373 ms
64 bytes from 10.20.4.2: icmp_seq=7 ttl=63 time=0.450 ms
64 bytes from 10.20.4.2: icmp_seq=8 ttl=63 time=0.336 ms
^C
--- 10.20.4.2 ping statistics ---
8 packets transmitted, 8 received, 0% packet loss, time 7001ms
rtt min/avg/max/mdev = 0.332/0.511/1.533/0.388 ms

[xxx@minion2 ~]$ ip r
default via 10.140.0.1 dev eth0  proto static  metric 100 
10.20.0.0/16 dev flannel.1  proto kernel  scope link  src 10.20.52.0 
10.20.52.0/24 dev docker0  proto kernel  scope link  src 10.20.52.1 
10.140.0.1 dev eth0  proto dhcp  scope link  metric 100 
10.140.0.4 dev eth0  proto kernel  scope link  src 10.140.0.4  metric 100 
169.254.169.254 via 10.140.0.1 dev eth0  proto dhcp  metric 100 
[xxx@minion1 ~]$ sudo tcpdump -i eth0 -T vxlan host 10.140.0.4
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
16:07:19.808846 IP minion2.c.kubernetes-test-141313.internal.52229 > minion1.c.kubernetes-test-141313.internal.otv:
 VXLAN, flags [I] (0x08), vni 1
IP 10.20.52.0 > 10.20.4.2: ICMP echo request, id 1291, seq 1, length 64
16:07:19.808945 IP minion1.c.kubernetes-test-141313.internal.54591 > minion2.c.kubernetes-test-141313.internal.otv:
 VXLAN, flags [I] (0x08), vni 1
IP 10.20.4.2 > 10.20.52.0: ICMP echo reply, id 1291, seq 1, length 64
16:07:20.809721 IP minion2.c.kubernetes-test-141313.internal.52229 > minion1.c.kubernetes-test-141313.internal.otv:
 VXLAN, flags [I] (0x08), vni 1
IP 10.20.52.0 > 10.20.4.2: ICMP echo request, id 1291, seq 2, length 64
16:07:20.809804 IP minion1.c.kubernetes-test-141313.internal.54591 > minion2.c.kubernetes-test-141313.internal.otv:
 VXLAN, flags [I] (0x08), vni 1
IP 10.20.4.2 > 10.20.52.0: ICMP echo reply, id 1291, seq 2, length 64
16:07:21.809474 IP minion2.c.kubernetes-test-141313.internal.52229 > minion1.c.kubernetes-test-141313.internal.otv:
 VXLAN, flags [I] (0x08), vni 1

その他

ロードバランスやフェイルオーバーなども実際にさわってみたい。