: O. Yuanying

Openstack で Kubernetes を使う

この記事は Kubernetes Advent Calendar 2016の16日目 と、 OpenStack Advent Calendar 2016の16日目 という、一粒で二つ美味しい(エントリを書く人的に)記事です。

あんまり Kubernetes を使っている人は OpenStack に興味ないかもしれませんが、 OpenStack と Kubernetes の連携を軽く触ってみます。

環境のインストール

  • Devstack のインストール
  • Kubernetes のインストール

Devstack のインストール

以下のサービスが起動している OpenStack を用意してください。

  • Nova
  • Cinder
    • Container Volume 連携に利用。
  • Glance
  • Swift
    • k8s インストール時に k8s や各種パッケージを置いておく為に必要。
  • Heat
    • k8s インストールに必要。
  • Neutron
    • コンテナネットワーク及びそのルーティングに必要。
  • Neutron LBaaS
    • Service, Type LoadBalancer に必要。
  • Octavia
    • Service, Type LoadBalancer に必要。

Kubernetes のインストール

まあ、基本的に、 Getting started with OpenStack の記事にある通りに設定してください。

あと、最新 (v1.5.0 ~) の Kubernetes で試す時に、 いくつかのバグと、設定が面倒なところがあるので以下のパッチを適用してください。 (自分で試した時にうまく動かなかったので pull request 出した。)

Devstack を構築した PC 上で作業するなら以下で行けるはず。 (要 Docker 及び Golang。 cluster/env.sh は環境に合わせて適宜修正。)

$ mkdir -p $GOPATH/src/k8s.io
$ cd $GOPATH/src/k8s.io
$ git clone https://github.com/kubernetes/kubernetes.git
$ cd kubernetes/
$ make clean
$ make quick-release
$ nova keypair-add --pub-key ~/.ssh/id_rsa.pub default
$ cat <<EOF > cluster/env.sh
export OS_REGION_NAME=RegionOne
export OS_IDENTITY_API_VERSION=2.0
export OS_PASSWORD=openstack
export OS_AUTH_URL=http://192.168.11.197:5000/v2.0
export OS_USERNAME=admin
export OS_TENANT_NAME=admin

export KUBERNETES_KEYPAIR_NAME=default
export NUMBER_OF_MINIONS=2
export MAX_NUMBER_OF_MINIONS=2
export MASTER_FLAVOR=m1.small
export MINION_FLAVOR=m1.large
EOF
$ KUBERNETES_PROVIDER=openstack-heat ./cluster/kube-up.sh

ちなみに今回作った cluster は以下のようになった。

$ nova list --fields name,networks
+----------------------------------------------+-------------------------------------------------------------------------+
| Name                                         | Networks                                                                |
+----------------------------------------------+-------------------------------------------------------------------------+
| kube-stack-master                            | kube-stack-fixed_network-ea44dsmf23sh=10.0.0.6, 172.16.12.8             |
| kube-stack-node-olvizdnx                     | kube-stack-fixed_network-ea44dsmf23sh=10.0.0.11, 172.16.12.15           |
| kube-stack-node-uyfpznha                     | kube-stack-fixed_network-ea44dsmf23sh=10.0.0.8, 172.16.12.11            |
+----------------------------------------------+-------------------------------------------------------------------------+

意外とすんなり OpenStack 上に最新の Kubernetes が構築される。

そうか Magnum は、いらない子だったんだ!! まあ、すんなり構築されるとは言っても、シングルマスター構成であったり、 ノード管理などの運用のための仕組みがなかったりで、 どちらかというと開発用途の仕組みであることは注意すべし。 (裏返していうと、Kubernetes に OpenStack 関連の機能を追加したい場合は、 この仕組みが開発環境として利用できる、ということになる。)

OpenStack - Kubernetes 連携を試す

Kubernetes が OpenStack と連携するためには、 事前に Kubernetes のクラスターが利用する Cloud Provider として、 OpenStack を設定しておく必要がある。

上記の方法で Kubernetes インストールを行うことによって以下のような設定がされる。

[Global]
auth-url=http://192.168.11.197:5000/v2.0
username=admin
password=openstack
region=RegionOne
tenant-name=admin
[LoadBalancer]
lb-version=
subnet-id=2a199f33-a1dc-4525-ae84-56a07345f93b
floating-network-id=06a24f91-cb89-4c4f-ac2b-05f8abd6a5e6
[Route]
router-id=6ce06bce-a359-4ec1-a0d0-1636d0721afb

この設定によって、以下の三つの機能が OpenStack と連携を取れるようになる。

  • Container Network
  • Container Volume
  • Service, Type LoadBalancer

Container Network

Kubernetes というと Flannel やら Calico やら、 OpenStack だと Kuryr とか、 ホストをまたがるコンテナ間の通信を実現するために めんどくさいミドルウェアを導入しなければいけないのが悩みの種でしたが、 Kubernetes 1.5 から Routes provider API for OpenStack が導入されました!

これは何かというと、Flannel の aws-vpcgce バックエンド同等のもの、 というか、テナントネットワークのルーターにコンテナネットワークへのルーティング情報を書き込んでしまうというもの。 No SDN Kubernetes という記事の 「Route tables」に詳しい仕組みが書いてある。 複数のテナントネットワークにまたがった Kubernetes クラスターだったりとか、 コンテナ自体を複数の OpenStack のテナントネットワークに置きたい、 とかいう複雑な構成にしない限りこれで十分なんじゃなかろうか。

実際に、上記のインストールで 2 ノードの Kubernetes クラスターを構築すると、 以下のように Neutron のルーターにルーティング情報が追加されていることが確認できる。


$ neutron router-show 6ce06bce-a359-4ec1-a0d0-1636d0721afb
+-------------------------+---------------------------------------------------------------+
| Field                   | Value                                                         |
+-------------------------+---------------------------------------------------------------+
| admin_state_up          | True                                                          |
| availability_zone_hints |                                                               |
(略)
| routes                  | {"destination": "10.244.1.0/24", "nexthop": "10.0.0.8"}       |
|                         | {"destination": "10.244.0.0/24", "nexthop": "10.0.0.11"}      |
| status                  | ACTIVE                                                        |
| tenant_id               | 478ff154808f410eb587fb6be5079959                              |
| updated_at              | 2016-12-16T03:53:16Z                                          |
+-------------------------+---------------------------------------------------------------+

ここの destination はノード内の Docker ネットワーク、 nexthop はノードの IP アドレスとなる。

Container Volume

Cinder と Kubernetes の一番簡単な使い方は、 volumes に cinder の volumeID を指定する方法。 サンプルは、 MySQL installation with cinder volume plugin で見ることができる。

使い方の流れとしては、

  1. Cinder ボリュームを作成。(cinder create)
  2. 作成したボリュームの ID を Pod の spec に設定する。

Service, Type LoadBalancer

こっちはこっちで、Kubernetes が自動で LB を作って、 メンバーを追加してくれる。使い方に特別なことはなく、 Service のタイプを LoadBalancer にするだけ。

例えばこんな感じ。


$ cat <<EOF > nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer
EOF
$ kubectl create -f nginx-service.yaml
service "frontend" created
$ kubectl describe service frontend
kubectl describe service frontend
Name:           frontend
Namespace:      default
Labels:         <none>
Selector:       app=nginx
Type:           LoadBalancer
IP:         10.246.32.195
LoadBalancer Ingress:   10.0.0.12, 172.16.12.10
Port:           <unset>   80/TCP
NodePort:       <unset>   30703/TCP
Endpoints:      172.17.0.3:80
Session Affinity:   None
Events:
...

実際にこの Service にアクセスするには、最後に describe しているところの LoadBalancer Ingress の値を利用することになる。 で、この LoadBalancer Ingress の値は何者かというと、


$ nova list --fields name,networks
+----------------------------------------------+-------------------------------------------------------------------------+
| Name                                         | Networks                                                                |
+----------------------------------------------+-------------------------------------------------------------------------+
| amphora-d75b0c95-0d78-4da5-9850-54d4dc1bea83 | lb-mgmt-net=172.17.1.5; kube-stack-fixed_network-ea44dsmf23sh=10.0.0.14 |
| kube-stack-master                            | kube-stack-fixed_network-ea44dsmf23sh=10.0.0.6, 172.16.12.8             |
| kube-stack-node-olvizdnx                     | kube-stack-fixed_network-ea44dsmf23sh=10.0.0.11, 172.16.12.15           |
| kube-stack-node-uyfpznha                     | kube-stack-fixed_network-ea44dsmf23sh=10.0.0.8, 172.16.12.11            |
+----------------------------------------------+-------------------------------------------------------------------------+

Octavia が作成したロードバランサー用の VM の IP アドレスだ、ってことがわかる。

では、Neutron の Load Balancer はどんな感じで作られているかというと、


$ neutron lbaas-loadbalancer-show a8607812cc34411e6821ffa163e2880a
+---------------------+-------------------------------------------------+
| Field               | Value                                           |
+---------------------+-------------------------------------------------+
| admin_state_up      | True                                            |
| description         | Kubernetes external service                     |
|                     | a8607812cc34411e6821ffa163e2880a                |
| id                  | 3ecee93a-eda7-4f46-b9fc-3a9e41bd5021            |
| listeners           | {"id": "64580346-edfa-47f8-939d-6413c6115f9f"}  |
| name                | a8607812cc34411e6821ffa163e2880a                |
| operating_status    | ONLINE                                          |
| pools               | {"id": "98f17e52-a9f6-482b-b735-7dffc6821824"}  |
| provider            | octavia                                         |
| provisioning_status | ACTIVE                                          |
| tenant_id           | 478ff154808f410eb587fb6be5079959                |
| vip_address         | 10.0.0.12                                       |
| vip_port_id         | f492b016-325b-4e6e-aa7f-a5614a0d0f7b            |
| vip_subnet_id       | 2a199f33-a1dc-4525-ae84-56a07345f93b            |
+---------------------+-------------------------------------------------+
$ neutron lbaas-member-list \
        -c address -c protocol_port -c subnet_id \
        98f17e52-a9f6-482b-b735-7dffc6821824
+-----------+---------------+--------------------------------------+
| address   | protocol_port | subnet_id                            |
+-----------+---------------+--------------------------------------+
| 10.0.0.11 |         30703 | 2a199f33-a1dc-4525-ae84-56a07345f93b |
| 10.0.0.8  |         30703 | 2a199f33-a1dc-4525-ae84-56a07345f93b |
+-----------+---------------+--------------------------------------+

と、こんな感じで、Member として Node のアドレスが追加されている。 また、protocol_port は指定した 80 ではなく、30703 が設定されている。

これはどういうことかというと、 実際に OpenStack Provider の作る type LoadBalancerService は、type NodePortService をロードバランスする LoadBalancer を作っているということで、 直接コンテナに対してリクエストをロードバランスしているわけではないということのよう。

後片付け

使い終わったクラスターは以下のコマンドで削除できる。

$ KUBERNETES_PROVIDER=openstack-heat ./cluster/kube-down.sh

もしくは、

$ heat stack-delete ${STACK_NAME}

kube-down.sh も結局は heat stack-delete してるだけなので、 どっちを使っても構わない。

ただ、多分、今日時点では、普通に heat stack-delete してやっただけだと、 Stack の削除に失敗してリソースを全部削除することはできない。 というのも、今の所二つの原因があり得る。

  1. Service 作成時に作られた LB が残っているため、依存性があるネットワークを削除できないため。
  2. Neutron のルーターにルーティング情報が残っており、そのルーティングが内部のサブネットに依存しているため、 サブネットが削除できないため。

一番目の原因の場合は、作った Service を事前に削除しておく必要がある。 事前に削除しなかった場合は、 neutron lbaas-* コマンドを使って一個一個 LBaaS 関連のリソースを削除してから heat stack-delete を実行してやる必要がある。

$ kubectl delete service frontend

二番目の原因は簡単で、ルーターに設定されたルーティング情報をクリアしてやるだけ。

$ neutron router-update --no-routes 6ce06bce-a359-4ec1-a0d0-1636d0721afb

もちろん、これをやってしまうとネットワークがおかしくなってしまうので注意。