kubesprayを使ってOpenStack上にKubernetesを構築する

kubernetes-incubator/kubesprayは複数の環境(AWS、GCE、Azure、OpenStack、Baremetal)に対応したKubernetesクラスタのデプロイツールです。kubespray自体はAnsibleやTerraform用のコードで構成されています。今回はkubersprayを使い、OpenStack上にKubernetes環境を構築します。

Terraformでクラスタ用のインスタンスを作成する

まずはクラスタ用のインスタンスを作成します。kubesprayにはTerraformでインスタンスを建てるためのconfigurationファイルが付属しているので、これを使ってみます。なお、Configurationはv1.0以降ではRemovedな機能を使っているので、v0.9.11を使います。

添付されているConfigurationはかなりよくできていて、tfvarsで設定を変えるだけでいろいろな構成のクラスタを構築できます。今回はk8s-masterを3台、k8s-nodeを2台(内1台はFloatingIPを持たない)という構成にするので、terraform.tfvarsは以下のようになりました。

number_of_etcd = "0"
number_of_k8s_masters = "3"
number_of_k8s_masters_no_etcd = "0"
number_of_k8s_masters_no_floating_ip = "0"
number_of_k8s_masters_no_floating_ip_no_etcd = "0"
number_of_k8s_nodes_no_floating_ip = "1"
number_of_k8s_nodes = "2"

network_name = "int-net-name"
floatingip_pool = "ext-net-name"
az = "nova"
image = "ubuntu-16.04-server-cloudimg-amd64"
cluster_name = "alpaca"

terraform apply 前の注意点として、作成されるセキュリティグループに 0.0.0.0/0 からのアクセスを許可するルールが2つあるのでここを修正しておくのがよいかもしれません。リソースを作成し終わると以下のようにインスタンスが作られています。

$ openstack server list --name alpaca-k8s --column Name
+----------------------+
| Name                 |
+----------------------+
| alpaca-k8s-node-nf-1 |
| alpaca-k8s-master-1  |
| alpaca-k8s-master-2  |
| alpaca-k8s-node-2    |
| alpaca-k8s-node-1    |
| alpaca-k8s-master-3  |
+----------------------+

Ansibleを適用し、Kubernetesクラスタを作る

各インスタンスにSSHでアクセスできるかチェックする

インスタンスを作成できたので次はAnsibleを適用します。まず、各インスタンスにSSHでアクセスできるのかどうかを確認します。以下のコマンドで疎通確認が可能です。ansible_python_interpreter を指定しているのは、各インスタンスのOSであるubuntu-16.04には /usr/bin/python がなく、/usr/bin/python3 であるためです。

$ ansible -i contrib/terraform/openstack/hosts -m ping all --private-key=~/.ssh/id_rsa -e 'ansible_python_interpreter=/usr/bin/python3'
alpaca-k8s-master-1 | SUCCESS => {
    "changed": false,
    "failed": false,
    "ping": "pong"
}
alpaca-k8s-node-1 | SUCCESS => {
    "changed": false,
    "failed": false,
    "ping": "pong"
}
alpaca-k8s-master-2 | SUCCESS => {
    "changed": false,
    "failed": false,
    "ping": "pong"
}
alpaca-k8s-master-3 | SUCCESS => {
    "changed": false,
    "failed": false,
    "ping": "pong"
}
alpaca-k8s-node-2 | SUCCESS => {
    "changed": false,
    "failed": false,
    "ping": "pong"
}
alpaca-k8s-node-nf-1 | SUCCESS => {
    "changed": false,
    "failed": false,
    "ping": "pong"
}

ここで、アクセス先のIPアドレスを全く指定していないことに気づくかもしれません。AnsibleにはDynamic Inventoryという機能があり、ファイルやAPIから自動的に適用先の情報を取得することができます。ここでは、Terraformのtfstateファイルから適用先を取得しているというわけです。また、FloatingIPがついていないalpaca-k8s-node-nf-1 へSSHするため、terraform apply 時点で踏み台の設定も生成しています。contrib/terraform/openstack/group_vars/no-floating.yml というファイルがそれで、no-floating グループであればこの設定を用い、踏み台経由でSSHするというわけです。非常によくできているなーと思います。

Ansibleを適用する

各インスタンスにSSHでアクセスできることを確認したので、Ansibleを適用します。

$ ansible-playbook --become -i contrib/terraform/openstack/hosts cluster.yml --private-key=~/.ssh/id_rsa -e '{"ansible_python_interpreter": "/usr/bin/python3", "kubeconfig_localhost": true, "loadbalancer_apiserver": {"address": "XXX.XX.XXX.XXX", "port": 6443}, "apiserver_loadbalancer_domain_name": "kubernetes.default"}'

extra-varsをpretty printしたものは以下の通りです。

{
  "ansible_python_interpreter": "/usr/bin/python3",
  "kubeconfig_localhost": true,
  "loadbalancer_apiserver": {
    "address": "XXX.XX.XXX.XXX",
    "port": 6443
  },
  "apiserver_loadbalancer_domain_name": "kubernetes.default"
}

実行したら30分〜40分ほどかかるので、コーヒーを飲んだりして待ちます。

PLAY RECAP ************************************************************************************************************************************************
alpaca-k8s-master-1        : ok=256  changed=14   unreachable=0    failed=0
alpaca-k8s-master-2        : ok=257  changed=15   unreachable=0    failed=0
alpaca-k8s-master-3        : ok=265  changed=10   unreachable=0    failed=0
alpaca-k8s-node-1          : ok=232  changed=14   unreachable=0    failed=0
alpaca-k8s-node-2          : ok=206  changed=9    unreachable=0    failed=0
alpaca-k8s-node-nf-1       : ok=206  changed=9    unreachable=0    failed=0
localhost                  : ok=2    changed=0    unreachable=0    failed=0

終わったら artifacts/admin.conf にkubectl用のコンフィグが生成されているので、~/.kube/config へ配置します。

$ cp artifacts/admin.conf ~/.kube/config

また、admin.conf内のアクセス先がhttps://kubernetes.default:6443となっているはずです。 /etc/hosts に設定して、アクセスできるようにします。

XXX.XX.XXX.XXX kubernetes.default

さて、いよいよノード一覧を見てみましょう。6台のノードが全てReadyになっているはずです。

PMC02V437VHV2R% kubectl get nodes
NAME                   STATUS    ROLES         AGE       VERSION
alpaca-k8s-master-1    Ready     master,node   27m       v1.8.1+coreos.0
alpaca-k8s-master-2    Ready     master,node   27m       v1.8.1+coreos.0
alpaca-k8s-master-3    Ready     master,node   27m       v1.8.1+coreos.0
alpaca-k8s-node-1      Ready     node          18m       v1.8.1+coreos.0
alpaca-k8s-node-2      Ready     node          18m       v1.8.1+coreos.0
alpaca-k8s-node-nf-1   Ready     node          18m       v1.8.1+coreos.0

サービスを動かしてみる

Kubernetes環境ができたので、試しにサービスを動かしてみます。今回はmicroservices-demo/microservices-demo にします。まず、ネームスペースを作成します。

$ kubectl create namespace sock-shop

その後は各種サービスを一気に作成します。

$ kubectl create -f ~/src/github.com/microservices-demo/microservices-demo/deploy/kubernetes/complete-demo.yaml
deployment "carts-db" created
service "carts-db" created
deployment "carts" created
service "carts" created
deployment "catalogue-db" created
service "catalogue-db" created
deployment "catalogue" created
service "catalogue" created
deployment "front-end" created
service "front-end" created
deployment "orders-db" created
service "orders-db" created
deployment "orders" created
service "orders" created
deployment "payment" created
service "payment" created
deployment "queue-master" created
service "queue-master" created
deployment "rabbitmq" created
service "rabbitmq" created
deployment "shipping" created
service "shipping" created
deployment "user-db" created
service "user-db" created
deployment "user" created
service "user" created

ポッド一覧。

$ kubectl --namespace sock-shop get pods
NAME                            READY     STATUS    RESTARTS   AGE
carts-794f6cc876-96gn2          1/1       Running   0          1m
carts-db-787f4b7896-kdlpw       1/1       Running   0          1m
catalogue-845484987d-s68f6      1/1       Running   0          1m
catalogue-db-67dffd76dc-r26kq   1/1       Running   0          1m
front-end-6786c5b4b9-69fhm      1/1       Running   0          1m
orders-5568884f5-kzvt6          1/1       Running   0          1m
orders-db-66f56c7d6d-t8zfm      1/1       Running   0          1m
payment-9f56ff4d-hm7rm          1/1       Running   0          1m
queue-master-75987c9876-br6bv   1/1       Running   0          1m
rabbitmq-55998c84cd-s96pl       1/1       Running   0          1m
shipping-6b7599657-swqkl        1/1       Running   0          1m
user-7dd55b89b6-s66kk           1/1       Running   0          1m
user-db-57689444f9-rzcq5        1/1       Running   0          1m

サービス一覧。

$ kubectl --namespace sock-shop get service
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
carts          ClusterIP   10.233.56.72    <none>        80/TCP         1m
carts-db       ClusterIP   10.233.29.40    <none>        27017/TCP      1m
catalogue      ClusterIP   10.233.22.32    <none>        80/TCP         1m
catalogue-db   ClusterIP   10.233.22.88    <none>        3306/TCP       1m
front-end      NodePort    10.233.23.23    <none>        80:30001/TCP   1m
orders         ClusterIP   10.233.62.13    <none>        80/TCP         1m
orders-db      ClusterIP   10.233.31.36    <none>        27017/TCP      1m
payment        ClusterIP   10.233.13.237   <none>        80/TCP         1m
queue-master   ClusterIP   10.233.4.189    <none>        80/TCP         1m
rabbitmq       ClusterIP   10.233.36.199   <none>        5672/TCP       1m
shipping       ClusterIP   10.233.28.86    <none>        80/TCP         1m
user           ClusterIP   10.233.44.29    <none>        80/TCP         1m
user-db        ClusterIP   10.233.63.41    <none>        27017/TCP      1m

front-endサービスからアクセスするのだけど、これはデフォルトではNodePortとなっています。ノードにアタッチされているFIPの30001番ポートにアクセスするとsock-shopを利用できるはずです。

$ kubectl --namespace sock-shop describe pod front-end-6786c5b4b9-f6kf4 | grep "Node: "
Node:           alpaca-k8s-master-2/XX.XX.XXX.XXX

front-endポッドはalpaca-k8s-master-2 にいるので、そのFIPにアクセスして、HTMLが帰ってくればOKです。

$ curl http://XXX.XX.XXX.XXX:30001

まとめ

複数の環境に対応したKubernetesクラスタのデプロイツールであるkubersprayを用いて、OpenStack上にKubernetesクラスタを構築しました。また、その上でmicroservices-demo/microservices-demo を動かし、NodePort経由でアクセスできることを確認しました。

Kubernetes v1.9では、OpenStackのOctaviaがサポートされたようです。LBaaSとしてOctaviaを使っている場合、これを使うことで外部ネットワークとうまくやりとりできるのではないか、と期待します。

Leave a Reply

Your email address will not be published. Required fields are marked *