Kubernetes: クラスタ構築が簡単になるkubeadm

Kubernetes 1.4の新機能として`kubeadm`というkubernetesクラスタ構築を大幅に簡略化するための仕組みが入りました。1.4の時点ではalpha機能のため本番環境の構築には今後のバージョンを待つ必要があります。

[マニュアル](http://kubernetes.io/docs/getting-started-guides/kubeadm/)に沿ってGoogle Compute EngineのVMで実際に試してみました。

# 概要

下記の4手順で簡単にKubernetesクラスタを作ることができるようになりました。(Dockerの`swarm join/init`のイメージ)

1. 関連パッケージのインストール
2. `kubeadm init`でマスターを初期化
3. `kubeadm join --token xxxx yyy.yyy.yyy.yyy`でワーカーを追加
4. Podネットワークのデプロイ

これでクラスタの構築が完了します。 :tada:

# 準備

GCPで下記のVMを3台(Master*1, Worker*2)作成しました。

- **マシンタイプ**: n1-standard-1
- **OS**: Ubuntu 16.04 LTS(amd64 xenial image built on 2016-09-30)

まず、下記のコマンドで必要パッケージをインストールします。

```bash
sudo su -
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF > /etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y docker.io kubelet kubeadm kubectl kubernetes-cni
```

# Masterの設定

Masteでやることは`kubeadm init`だけです。

Masterにログインし下記のように`kubeadm init`を実行します。複数のIPアドレスがある場合は`--api-advertise-addresses`で公開するIPアドレスを指定する必要があります。

```bash
sudo kubeadm init
```

1分弱でKubermetesのマスターが初期化されました。worker用のjoinコマンドが表示されるので控えておきます。

```
<master/tokens> generated token: "59fd3a.744950d511c9cd4b"
<master/pki> created keys and certificates in "/etc/kubernetes/pki"
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"
<util/kubeconfig> created "/etc/kubernetes/admin.conf"
<master/apiclient> created API client configuration
<master/apiclient> created API client, waiting for the control plane to become ready
<master/apiclient> all control plane components are healthy after 26.083726 seconds
<master/apiclient> waiting for at least one node to register and become ready
<master/apiclient> first node is ready after 5.002739 seconds
<master/discovery> created essential addon: kube-discovery, waiting for it to become ready
<master/discovery> kube-discovery is ready after 10.002818 seconds
<master/addons> created essential addon: kube-proxy
<master/addons> created essential addon: kube-dns
Kubernetes master initialised successfully!
You can now join any number of machines by running the following on each node:

# worker用のコマンドが表示される
kubeadm join --token 59fd3a.744950d511c9cd4b 10.240.0.5
```

`/etc/kubernetes`以下に下記のようなファイルが生成されています。証明書なども自動で生成されていて素晴らしいです。 :+1: 

```bash
# 証明書
/etc/kubernetes/pki/apiserver-key.pem
/etc/kubernetes/pki/ca.pem
/etc/kubernetes/pki/apiserver-pub.pem
/etc/kubernetes/pki/ca-pub.pem
/etc/kubernetes/pki/tokens.csv
/etc/kubernetes/pki/sa-pub.pem
/etc/kubernetes/pki/apiserver.pem
/etc/kubernetes/pki/sa-key.pem
/etc/kubernetes/pki/ca-key.pem
# Masterコンポーネントの設定
/etc/kubernetes/manifests/kube-scheduler.json
/etc/kubernetes/manifests/kube-controller-manager.json
/etc/kubernetes/manifests/kube-apiserver.json
/etc/kubernetes/manifests/etcd.json
# kubeletの設定
/etc/kubernetes/admin.conf
/etc/kubernetes/kubelet.conf
```

kubeletが下記のように立ち上がり、KubernetesのMasterコンポーネント(scheduler, controller, apiserver, etcdなと)がコンテナで立ち上がりました。

```bash
/usr/bin/kubelet \
  --kubeconfig=/etc/kubernetes/kubelet.conf \
  --require-kubeconfig=true \
  --pod-manifest-path=/etc/kubernetes/manifests \
  --allow-privileged=true \
  --network-plugin=cni \
  --cni-conf-dir=/etc/cni/net.d \
  --cni-bin-dir=/opt/cni/bin \
  --cluster-dns=100.64.0.10 \
  --cluster-domain=cluster.local \
  --v=4
```


# Workerの設定

Workerにログインし、`kubeadm init`で表示されたコマンドでworker用VMで実行します。

```bash
sudo kubeadm join --token 59fd3a.744950d511c9cd4b 10.240.0.5
```

下記のように表示されNodeの追加が一瞬で完了します。とても簡単です。

```
<util/tokens> validating provided token
<node/discovery> created cluster info discovery client, requesting info from "http://10.240.0.5:9898/cluster-info/v1/?token-id=59fd3a"
<node/discovery> cluster info object received, verifying signature using given token
<node/discovery> cluster info signature and contents are valid, will use API endpoints [https://10.240.0.5:443]

<node/csr> created API client to obtain unique certificate for this node, generating keys and certificate signing request
<node/csr> received signed certificate from the API server, generating kubelet configuration
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"

Node join complete:
* Certificate signing request sent to master and response
  received.
* Kubelet informed of new secure connection details.

Run 'kubectl get nodes' on the master to see this machine join.
```

2台目も同じ手順を行います。

生成された`/etc/kubernetes/kubelet.conf`を見ると下記のように、クレデンシャルやAPIサーバーなどの設定が自動的になされています。(クレデンシャルは一部省略)

```yaml:/etc/kubernetes/kubelet.conf
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0...Qo=
    server: https://10.240.0.5:443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubelet-kube-worker2
  name: kubelet-kube-worker2@kubernetes
current-context: kubelet-kube-worker2@kubernetes
kind: Config
preferences: {}
users:
- name: kubelet-kube-worker2
  user:
    client-certificate-data: LS0...o=
    client-key-data: LS0t...o=
```

kubeletが下記のように立ち上がり、コンテナで`kube-proxy`が立ち上がります。

```bash
/usr/bin/kubelet \
  --kubeconfig=/etc/kubernetes/kubelet.conf \
  --require-kubeconfig=true \
  --pod-manifest-path=/etc/kubernetes/manifests \
  --allow-privileged=true \
  --network-plugin=cni \
  --cni-conf-dir=/etc/cni/net.d \
  --cni-bin-dir=/opt/cni/bin \
  --cluster-dns=100.64.0.10 \
  --cluster-domain=cluster.local \
  --v=4
```

# Master側で確認とPodネットワークの設定

Masterにログウインしてノードが追加されているか見てみます。

```
kubectl get nodes
```

下記のようにNodeが追加されていることが確認できます。

```
NAME           STATUS    AGE
kube-master    Ready     5m
kube-worker1   Ready     4m
kube-worker2   Ready     3m
```

最後にPodネットワークのインストールを行います。Podネットワークはいくつか選択でき、ここではマニュアルの通り[Weave Net](https://github.com/weaveworks/weave-kube)を選択しました。現状他にはCanal, Calicoが選択できます(参考: [Networking and Network Policy](http://kubernetes.io/docs/admin/addons/))

```
kubectl apply -f https://raw.githubusercontent.com/weaveworks/weave-kube/master/weave-daemonset.yaml
```

この時点でPodは以下のようになっています。

```
kubectl get pods --all-namespaces

NAMESPACE     NAME                                  READY     STATUS    RESTARTS   AGE
kube-system   etcd-kube-master                      1/1       Running   0          5m
kube-system   kube-apiserver-kube-master            1/1       Running   0          6m
kube-system   kube-controller-manager-kube-master   1/1       Running   0          6m
kube-system   kube-discovery-982812725-vr5or        1/1       Running   0          6m
kube-system   kube-dns-2247936740-qclp0             2/3       Running   0          6m
kube-system   kube-proxy-amd64-a6niu                1/1       Running   0          5m
kube-system   kube-proxy-amd64-cgqz5                1/1       Running   0          4m
kube-system   kube-proxy-amd64-mf3m7                1/1       Running   0          6m
kube-system   kube-scheduler-kube-master            1/1       Running   0          5m
kube-system   weave-net-kkipk                       2/2       Running   0          26s
kube-system   weave-net-xxtgw                       2/2       Running   0          26s
kube-system   weave-net-yyb64                       2/2       Running   0          26s
```

# 動作確認

動作確認をするため[サンプルアプリ](https://github.com/microservices-demo/microservices-demo)をデプロイします。マイクロサービス的にDBを含む10個以上のPodがデプロイされるよくできたサンプルです。

Masterで以下を実行します。

```
git clone https://github.com/microservices-demo/microservices-demo
kubectl apply -f microservices-demo/deploy/kubernetes/manifests
```

Podが全てRunningになっていることを確認します。

```
kubectl get pods

NAME                           READY     STATUS    RESTARTS   AGE
cart-3694116665-ethb2          1/1       Running   0          20m
cart-db-2305146297-k860k       1/1       Running   0          20m
catalogue-11453786-gs9k9       1/1       Running   0          20m
catalogue-db-393939662-bx08x   1/1       Running   0          20m
front-end-3820830240-3pvn7     1/1       Running   0          20m
orders-3498886496-jymtz        1/1       Running   0          20m
orders-db-1775353731-bsfa4     1/1       Running   0          20m
payment-3012088042-e7xn6       1/1       Running   0          20m
queue-master-936560853-omntu   1/1       Running   0          20m
rabbitmq-1897447621-fhb8y      1/1       Running   0          20m
shipping-1232389217-7aqn7      1/1       Running   0          20m
user-3090014237-y64z3          1/1       Running   0          20m
user-db-1338754314-q7mv9       1/1       Running   0          20m
```

NodePortを使ってサンプルアプリにアクセスするためにServiceの情報を見ます。

```
kubectl describe svc front-end

Name:                   front-end
Namespace:              default
Labels:                 name=front-end
Selector:               name=front-end
Type:                   NodePort
IP:                     100.69.60.103
Port:                   <unset> 80/TCP
NodePort:               <unset> 31992/TCP # このPortを使う
Endpoints:              10.38.0.3:8079
Session Affinity:       None
```

NodePortを使ってアクセスします。ポート開放の手間を省くためローカルにSSH PortForwardします。(NodePortは毎回変わり今回は31992)

```
ssh -L 31992:127.0.0.1:31992 <ユーザ名>@<VMのPublic IP>
```

http://localhost:31992/ にアクセスし、下記のようなサンプルウェブアプリが動作することが確認できました:tada:。

![image](https://qiita-image-store.s3.amazonaws.com/0/100377/d0bcc76f-9ca6-1cf0-aea3-3f00774dfac3.png)

# 制限

マニュアルにある通り現状は下記のような制限があります。

- cloud-providerを設定できない
  - Persistent VolumeやLoad Balancerなどの機能が使えない
- 1台のMasterしか作れない
- `kubectl logs`が動かない
- `kubectl`用の`kubeconfig`が簡単に取得できない
  - ひとまずMasterの`/etc/kubernetes/admin.conf`をコピーしてくる方法がある


# 参考情報

- 公式のマニュアル
  -  http://kubernetes.io/docs/getting-started-guides/kubeadm/
- kubeadmのソースコード
  -  https://github.com/kubernetes/kubernetes/tree/master/cmd/kubeadm