Kubespray で OpenStack 上に Kubernetes クラスタをデプロイする
Terraform と Kubespray (Kargo) を使って OpenStack 環境上に Kubernetes クラスタをデプロイしてみました。 クラスタの初期構築を行い、スケールアウト、 Kubernetes 自体のアップデートを行う手順を社内向けドキュメントとして書いていたので、ブログ用に手直しして紹介したいとおもいます。
Kubespray の概要
Kubespray は Kubernetes クラスタをデプロイするための Ansible Playbooks です。各種クラウド環境にも対応しており、 AWS や GCP 等に対応しています。 元々 Kargo という名前だったようですが、 Kubespray に変わったようです。
repository はここ 👇
今回は kubespray-cli などのツールは使わず、 Terraform で構築したインスタンスに対して Kubespray の Ansible Playbook を流すという方法で試してみました。
手順は以下のサイトを参考にしています。
Kubespray 実行環境の構築
まずは Ansible 2.3 系, Jinja2 2.9 系をインストール。
$ pip install ansible==2.3
$ pip install Jinja2==2.9
Terraform は tfenv を使っていたので、それを使用してインストールしました。
$ tfenv install 0.9.11
$ tfenv use 0.9.11
$ terraform version
Terraform v0.9.11
Kubernetes Cluster の初期構築
プロジェクトディレクトリの準備
適当な作業用ディレクトリをつくり、その中に kubespray のリポジトリを clone する。
参考にしたサイトと同様に terra-spray
ディレクトリを作り、作業ディレクトリとしてみました。
$ mkdir terra-spray
$ cd terra-spray
$ git clone https://github.com/kubernetes-incubator/kubespray.git
Cloning into 'kubespray'...
remote: Counting objects: 14268, done.
remote: Total 14268 (delta 0), reused 0 (delta 0), pack-reused 14268
Receiving objects: 100% (14268/14268), 5.65 MiB | 474.00 KiB/s, done.
Resolving deltas: 100% (7614/7614), done.
OpenStack 上にインスタンスをつくろう
Terraform を実行する前に、 OpenStack の認証情報等を環境変数として持たせておきます。OpenStack RC ファイルを使うのが良いかと思います。
- OS_AUTH_URL
- OS_TENANT_ID
- OS_TENANT_NAME
- OS_PROJECT_NAME
- OS_USERNAME
- OS_PASSWORD
- OS_REGION_NAME
Terraform テンプレートの作成
先程作成したプロジェクトディレクトリ配下に、以下の内容で Terraform の tf ファイルを作成する。
terra-spray
├── variables.tf # 変数達
├── 00-k8s-nodes.tf # VM を作成するためのファイル
├── 01-create-inventory.tf # Ansible Inventory を生成するファイル
└── kubespray # git clone した kubespray
variables.tf
variable "availability_zones" {
default = ["az01", "az02", "az03"]
}
variable "flavor_name" {
default = "tiny"
}
variable "base_image" {
default = "centos72"
}
variable "base_vlan" {
default = "provider-2xxx"
}
00-k8s-nodes.tf
k8s の master, node 及び etcd 用インスタンスを作成します。kube-node
kube-master
etcd
はそれぞれ別の VM にインストールました。2 master, 3 etcd, 3 node の構成です。
(ドキュメントにも書いてありますが、 etcd サーバはフェイルオーバのために少なくとも3台のサーバが必要となります。)
resource "openstack_compute_instance_v2" "k8s-master" {
name = "k8s-master${format("%02d", count.index+1)}"
count = "2"
image_name = "${var.base_image}"
flavor_name = "${var.flavor_name}"
config_drive = false
availability_zone = "${element(var.availability_zones, count.index)}"
security_groups = ["default"]
network {
name = "${var.base_vlan}"
}
}
resource "openstack_compute_instance_v2" "k8s-etcd" {
name = "k8s-etcd${format("%02d", count.index+1)}"
count = "3"
image_name = "${var.base_image}"
flavor_name = "${var.flavor_name}"
config_drive = false
availability_zone = "${element(var.availability_zones, count.index)}"
security_groups = ["default"]
network {
name = "${var.base_vlan}"
}
}
resource "openstack_compute_instance_v2" "k8s-node" {
name = "k8s-node${format("%02d", count.index+1)}"
count = "3"
image_name = "${var.base_image}"
flavor_name = "${var.flavor_name}"
config_drive = false
availability_zone = "${element(var.availability_zones, count.index)}"
security_groups = ["default"]
network {
name = "${var.base_vlan}"
}
}
01-create-inventory.tf
kubespray/inventory/inventoy
に、Ansible の inventory を生成します。inventory に記述する必要がある group は kube-node
kube-master
etcd
の3種類。
resource "null_resource" "ansible-provision" {
depends_on = ["openstack_compute_instance_v2.k8s-master", "openstack_compute_instance_v2.k8s-node", "openstack_compute_instance_v2.k8s-etcd"]
## Create Masters Inventory
provisioner "local-exec" {
command = "echo \"[kube-master]\" >> kubespray/inventory/inventory"
}
provisioner "local-exec" {
command = "echo \"${join("\n",formatlist("%s ansible_ssh_host=%s", openstack_compute_instance_v2.k8s-master.*.name, openstack_compute_instance_v2.k8s-master.*.network.0.fixed_ip_v4))}\" >> kubespray/inventory/inventory"
}
## Create ETCD Inventory
provisioner "local-exec" {
command = "echo \"\n[etcd]\n${join("\n",formatlist("%s ansible_ssh_host=%s", openstack_compute_instance_v2.k8s-etcd.*.name, openstack_compute_instance_v2.k8s-etcd.*.network.0.fixed_ip_v4))}\" >> kubespray/inventory/inventory"
}
## Create Nodes Inventory
provisioner "local-exec" {
command = "echo \"\n[kube-node]\" >> kubespray/inventory/inventory"
}
provisioner "local-exec" {
command = "echo \"${join("\n",formatlist("%s ansible_ssh_host=%s", openstack_compute_instance_v2.k8s-node.*.name, openstack_compute_instance_v2.k8s-node.*.network.0.fixed_ip_v4))}\" >> kubespray/inventory/inventory"
}
provisioner "local-exec" {
command = "echo \"\n[k8s-cluster:children]\nkube-node\nkube-master\" >> kubespray/inventory/inventory"
}
}
terraform apply する
テンプレートが完成したら、terraform apply
で VM を作成する。
apply が完了したら、 kubespray/inventory/inventory
ファイルに正しく inventory が書かれているかどうか確認しておこう。
$ cat kubespray/inventory/inventory
[kube-master]
k8s-master01 ansible_ssh_host=192.0.2.1
k8s-master02 ansible_ssh_host=192.0.2.2
[etcd]
k8s-etcd01 ansible_ssh_host=192.0.2.3
k8s-etcd02 ansible_ssh_host=192.0.2.4
k8s-etcd03 ansible_ssh_host=192.0.2.5
[kube-node]
k8s-node01 ansible_ssh_host=192.0.2.6
k8s-node02 ansible_ssh_host=192.0.2.7
k8s-node03 ansible_ssh_host=192.0.2.8
[k8s-cluster:children]
kube-node
kube-master
Ansible を流して Kubernetes をインストールしよう
インスタンスと inventory ができたら、 Kubespray の Ansible playbook を流して Kubernetes をインストール & クラスタ構築をします。
ansible-playbook を実行する際に、コマンドラインオプションで Kubernetes のバージョンや OS の種類、 network plugin を指定することが可能です。 今回は cloud_provider=openstack
kube_network_plugin=weave
bootstrap_os=centos
を指定してみました。
$ ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i kubespray/inventory/inventory -b kubespray/cluster.yml --extra-vars "cloud_provider=openstack bootstrap_os=centos kube_network_plugin=weave" --forks 50 --timeout 600 -K
SUDO password:
~ 中略 ~
PLAY RECAP ****************************************************************************************************************************
k8s-etcd01 : ok=154 changed=44 unreachable=0 failed=0
k8s-etcd02 : ok=151 changed=44 unreachable=0 failed=0
k8s-etcd03 : ok=151 changed=44 unreachable=0 failed=0
k8s-master01 : ok=325 changed=69 unreachable=0 failed=0
k8s-master02 : ok=321 changed=70 unreachable=0 failed=0
k8s-node01 : ok=319 changed=71 unreachable=0 failed=0
k8s-node02 : ok=292 changed=61 unreachable=0 failed=0
k8s-node03 : ok=291 changed=61 unreachable=0 failed=0
localhost : ok=3 changed=0 unreachable=0 failed=0
初期構築にはおよそ40分かかりました。長い…
k8s-master01
にログインして kubectl
コマンドを叩いてみます。
kubectl get nodes
を実行すると、用意した VM が一覧で表示され、 cluster として認識されている様子が確認できました。
VERSION に coreos の記述が見えますが、これは Kubespray が取得している hyperkube の Repository Tag の表記が見えているようです。
$ kubectl get nodes -o wide
NAME STATUS AGE VERSION EXTERNAL-IP OS-IMAGE KERNEL-VERSION
k8s-master01 Ready,SchedulingDisabled 2h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-master02 Ready,SchedulingDisabled 2h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node01 Ready 2h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node02 Ready 2h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node03 Ready 2h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s 関連のコンポーネントも kube-system namepsace の pod として動作しているようです。
$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system kube-apiserver-k8s-master01 1/1 Running 0 2h
kube-system kube-apiserver-k8s-master02 1/1 Running 0 2h
kube-system kube-controller-manager-k8s-master01 1/1 Running 0 2h
kube-system kube-controller-manager-k8s-master02 1/1 Running 1 2h
kube-system kube-dns-3841192733-qwtwg 3/3 Running 0 2h
kube-system kube-dns-3841192733-t515c 3/3 Running 0 2h
kube-system kube-proxy-k8s-master01 1/1 Running 0 2h
kube-system kube-proxy-k8s-master02 1/1 Running 0 2h
kube-system kube-proxy-k8s-node01 1/1 Running 0 2h
kube-system kube-proxy-k8s-node02 1/1 Running 0 2h
kube-system kube-proxy-k8s-node03 1/1 Running 0 2h
kube-system kube-scheduler-k8s-master01 1/1 Running 0 2h
kube-system kube-scheduler-k8s-master02 1/1 Running 0 2h
kube-system kubedns-autoscaler-1833630871-437zl 1/1 Running 0 2h
kube-system nginx-proxy-k8s-node01 1/1 Running 0 2h
kube-system nginx-proxy-k8s-node02 1/1 Running 0 2h
kube-system nginx-proxy-k8s-node03 1/1 Running 0 2h
kube-system weave-net-1k6dl 2/2 Running 0 2h
kube-system weave-net-3r2mw 2/2 Running 0 2h
kube-system weave-net-d8fkb 2/2 Running 0 2h
kube-system weave-net-dk0kz 2/2 Running 0 2h
kube-system weave-net-dx9qs 2/2 Running 0 2h
master の apiserver へのリクエストは、各 node に存在する nginx-proxy を介して行われます。 nginx-proxy の backend は各 master の apiserver となっており、 apiserver へ接続するためのローカルロードバランサとして動作します。
アプリケーションをデプロイし動作させてみる
試しに、 nginx の pod を立ち上げ、 welcome ページが見られるか確認してみます。
## nginx の pod を立ち上げてみる
$ kubectl run nginx --image=nginx:1.13 --port=80
deployment "nginx" created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-1122307028-hpxxw 0/1 ContainerCreating 0 7s
## service を作って NodePort で叩けるようにする
$ kubectl expose deploy nginx --name=nginx-nodeport --port=80 --target-port=80 --type="NodePort"
service "nginx-nodeport" exposed
$ kubectl get services
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.233.0.1 <none> 443/TCP 2h
nginx-nodeport 10.233.20.109 <nodes> 80:30968/TCP 6s
## curl で叩いてみる
$ curl http://localhost:30968/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
## pod の数を増やしてみる
$ kubectl scale --replicas=6 deployment/nginx
deployment "nginx" scaled
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-1122307028-2f2f4 1/1 Running 0 12s 10.233.88.2 k8s-node02
nginx-1122307028-9thvz 1/1 Running 0 12s 10.233.64.4 k8s-node01
nginx-1122307028-hpxxw 1/1 Running 0 1m 10.233.64.3 k8s-node01
nginx-1122307028-hthb1 1/1 Running 0 12s 10.233.96.3 k8s-node03
nginx-1122307028-pls2s 1/1 Running 0 12s 10.233.88.3 k8s-node02
nginx-1122307028-szc8p 1/1 Running 0 12s 10.233.96.2 k8s-node03
Kubernetes Cluster の scale 方法
ノード追加
k8s の node を増やしたくなったらどうすればよいか。 scale 用の playbook が用意されており、 VM を増やしたあとにそれを適用することで実現できるようでした。
まずは Terraform のコードで VM の count を増やす。んで terraform apply
。
count = "3" -> "6"
inventory に追加した node を追記し、 Ansible を流す。
$ ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i kubespray/inventory/inventory -b kubespray/scale.yml --extra-vars "cloud_provider=openstack bootstrap_os=centos kube_network_plugin=weave" --forks 50 --timeout 600 -K
~ 中略 ~
PLAY RECAP ****************************************************************************************************************************
k8s-etcd01 : ok=1 changed=0 unreachable=0 failed=0
k8s-etcd02 : ok=1 changed=0 unreachable=0 failed=0
k8s-etcd03 : ok=1 changed=0 unreachable=0 failed=0
k8s-master01 : ok=1 changed=0 unreachable=0 failed=0
k8s-master02 : ok=1 changed=0 unreachable=0 failed=0
k8s-node01 : ok=265 changed=6 unreachable=0 failed=0
k8s-node02 : ok=244 changed=3 unreachable=0 failed=0
k8s-node03 : ok=244 changed=3 unreachable=0 failed=0
k8s-node04 : ok=278 changed=66 unreachable=0 failed=0
k8s-node05 : ok=278 changed=66 unreachable=0 failed=0
k8s-node06 : ok=279 changed=66 unreachable=0 failed=0
Ansible 実行にはおよそ 40 分かかった。
kubectl で node の一覧を確認してみると、追加した node0{4..6} 3台が一覧に居ることを確認できた。
$ kubectl get nodes -o wide
NAME STATUS AGE VERSION EXTERNAL-IP OS-IMAGE KERNEL-VERSION
k8s-master01 Ready,SchedulingDisabled 3h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-master02 Ready,SchedulingDisabled 3h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node01 Ready 3h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node02 Ready 3h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node03 Ready 3h v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node04 Ready 11m v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node05 Ready 11m v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
k8s-node06 Ready 11m v1.6.7+coreos.0 <none> CentOS Linux 7 (Core) 3.10.0-327.28.2.el7.x86_64
ノード削除
逆に、node の台数を減らしたくなったらどうすればよいか?
まずは、 drain
で新規 pod の配備の禁止と実行中の pod の停止を行い、 delete
で対象の node をクラスタから外す。
$ kubectl cordon k8s-node04
$ kubectl drain k8s-node04 --force
$ kubectl delete node k8s-node04
その後、 Terraform の count の値を減らして terraform apply
することで、 クラスタを構成する node を減らすことができます。
count = "6" -> "3"
Kubernetes の Upgrade
k8s のバージョンを新しくしたくなった場合は、初期構築と同様に Ansible を流すことでアップデートができます。upgrade-cluster.yml
を適用することで、ノードの cordon, drain, uncording を行う Graceful なアップグレードが行われます。
(バージョンを指定して cluster.yml
を流し直すこともできるらしいが Unsafe な方法)
アップデートは、最新の kubespray リポジトリを取得後、 kube_version
変数でアップデート先の k8s のバージョンを指定し Ansible を流します。
kube_version
のほか、docker_version
や flannel_version
のように、各コンポーネントごとにバージョンを指定してアップデートすることもできるようです。
## 最新の kubespray を取得
$ cd kubespray
$ git fetch origin
$ git checkout origin/master
## プロジェクトディレクトリの root に戻り upgrade-cluster.yml を適用
$ cd ../
$ ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i kubespray/inventory/inventory -b kubespray/upgrade-cluster.yml --extra-vars "cloud_provider=openstack kube_network_plugin=weave bootstrap_os=centos kube_version=v1.7.2" --forks 50 --timeout 600 -K
~ 中略 ~
PLAY RECAP ************************************************************************************************************************************************************************
k8s-etcd01 : ok=133 changed=4 unreachable=0 failed=0
k8s-etcd02 : ok=129 changed=7 unreachable=0 failed=0
k8s-etcd03 : ok=129 changed=7 unreachable=0 failed=0
k8s-master01 : ok=296 changed=17 unreachable=0 failed=0
k8s-master02 : ok=288 changed=16 unreachable=0 failed=0
k8s-node01 : ok=271 changed=13 unreachable=0 failed=0
k8s-node02 : ok=262 changed=11 unreachable=0 failed=0
k8s-node03 : ok=262 changed=11 unreachable=0 failed=0
localhost : ok=3 changed=0 unreachable=0 failed=0
Ansible が完走するのにかかった時間はおよそ1時間。
アップグレード完了後 kubectl version
見ると、 Server Version が上がっていることが確認できます。
$ kubectl version
2017-08-18 17:05:09.424573 I | proto: duplicate proto type registered: google.protobuf.Any
2017-08-18 17:05:09.424715 I | proto: duplicate proto type registered: google.protobuf.Duration
2017-08-18 17:05:09.424748 I | proto: duplicate proto type registered: google.protobuf.Timestamp
Client Version: version.Info{Major:"1", Minor:"7", GitVersion:"v1.7.2+coreos.0", GitCommit:"c6574824e296e68a20d36f00e71fa01a81132b66", GitTreeState:"clean", BuildDate:"2017-07-24T23:28:22Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"7", GitVersion:"v1.7.2+coreos.0", GitCommit:"c6574824e296e68a20d36f00e71fa01a81132b66", GitTreeState:"clean", BuildDate:"2017-07-24T23:28:22Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/amd64"}
所感
- Ansible が流れ終わるのにめちゃ時間がかかる
- Large Cluster の場合は Ansible 高速化の TIPS 類を試したほうが良さそう
- 今回の検証では Service type: Load Balancer は試せていない
- 社内の OpenStack 環境を利用したが、諸々の事情で Neutron LBaaS を導入していないため選択不可
- inventory の自動生成は Ansible の Dynamic Inventory を活用するときれいになりそう
- Kubespray が持っている Ansible コードと自分達で記述した Ansible コードを共存させる仕組みが必要そう
- DOCKER_OPTS の変更や、サービス要件に合わせて一部設定ファイルに手を加えたいケースなど
- リポジトリを切り替えながらアレコレ適用するのは面倒、一発で環境構築したいマン
- ドキュメントのこの内容を見てみようと思う - Kubespray (kargo) in own ansible playbooks repo
余談
検証中、 Weave のインストールで Ansible がコケたので、 PR を送ってみました。
Kubespray のリポジトリは CNCF の Contributor License Agreement に署名する必要があるようで、 Bot が何やら小難しいことを書き込んできたのでビビりました… 😅
Previous Post
Next Post