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を使っている場合、これを使うことで外部ネットワークとうまくやりとりできるのではないか、と期待します。