ニフクラ Photon OS / kubeadm で構築するKubernetesクラスタ

[Photon OS](https://vmware.github.io/photon/)はVMWareが開発しているコンテナ向け軽量Linuxです。VMWare環境向けに最適化され、コンテナをホストする前提で作られているそうです。先日、[ニフクラにPhoton OSがリリース](https://blog.pfs.nifcloud.com/20190417_vmware_photonos)されたので、今回はこのニフクラ上でのPhoton OS 3.0に対してkubeadmでKubernetesクラスタを構築してみます。

結論としては、ほぼ[通常のkubeadmによる構築方法](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/)で構築が出来ました。

## 前提

- マスター1台＋ノード2台のシンプルな構成
- Kubernetes 1.14.1
- PhotonOS 3.0
- ニフクラ サーバータイプ c-medium（これも最近出たやつ）
     - 2 vCPU、メモリ２GBなので[kubeadm構成での最低限のスペック](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/)
- ニフクラ プライベートLAN内で構築

## 構築

### kubeadm環境構築

後ほど、Kubernetesのノード名として使われるので、ホスト名を設定しておきます。今回の例では、 `photon001 〜 003` といったホスト名を設定しています。

```
# hostnamectl set-hostname <ホスト名>
```

ニフクラのPhoton OSでは、初期状態でDockerが動いていないので、有効化＆起動させます。

```
# systemctl enable docker
# systemctl start docker
```

kubeadmのインストール手順上、 tarを使うので入れておきます。

```
# tdnf install -y tar
```

kubeadmのインストール方法は[公式のインストール方法](https://kubernetes.io/docs/setup/independent/install-kubeadm/#installing-kubeadm-kubelet-and-kubectl) を参照して、 `Container Linux` に記載の手順のまま実施します。

インストール後、パスを通しておきます。

```
# echo 'export PATH="${PATH}:/opt/bin"' >> ~/.bash_profile
# source ~/.bash_profile
```

一応、ニフクラ上のPhoton OSのCgroup Driverを確認しておきます。cgroupfsなのでとりあえず何もしなくて大丈夫です（cgroupfs以外の場合は設定が必要）。

```
# docker info | grep -i cgroup
Cgroup Driver: cgroupfs
```

ニフクラ上のPhoton OSでは名前解決に自分自身のunboundを見に行くように設定されていますが、このままだと [CoreDNSがCrashLoopBackOffになる](https://coredns.io/plugins/loop/#troubleshooting) ので、別のDNSに書き換えます。今回は簡単に Google Public DNS にします。

```
# sed -i -e "s/DNS=127.0.0.1/DNS=8.8.8.8/g" /etc/systemd/resolved.conf
# systemctl restart systemd-resolved.service
```

### マスター構築

`kubeadm init` します。 `--pod-network-cidr` は後の手順で構築するCNIと合わせる必要があります。flannelやcanalのデフォルトに合わせるなら `10.244.0.0/16` になります。他にも `kubeadm init` のオプションはあるので必要に応じて設定しましょう。

```
# kubeadm init --apiserver-advertise-address '<マスターのプライベートIP>' --pod-network-cidr=10.244.0.0/16
```

`kubeadm init` を実行すると、WARNINGがいくつか出ますが、今回は一旦無視しています（ちゃんと利用する際は対応したほうがよいでしょう）。最後に `kubeadm join 〜 --token xxxxxx --discovery-token-ca-cert-hash xxxxxx` のような joinする用の情報が出力されているので、控えておきます。

また、kubectlで接続するためのconfigが生成されているので、必要に応じて設定します。別のホストに `/etc/kubernetes/admin.conf` の内容をコピーしてもよいでしょう。

```
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
```

ここまでの段階で、kubectlでノードとPodの状態を見てみます。今回のマスターのノード（photon001）のステータスが `NotReady` なことがわかります。また、corednsのPodが `Pending` になっています。

```
# kubectl get nodes
NAME        STATUS     ROLES    AGE   VERSION
photon001   NotReady   master   12m   v1.14.1

# kubectl get pods --all-namespaces
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   coredns-fb8b8dccf-8zl5h    0/1     Pending   0          51s
kube-system   coredns-fb8b8dccf-c2cbm    0/1     Pending   0          51s
kube-system   etcd-photon001             1/1     Running   0          11s
kube-system   kube-apiserver-photon001   1/1     Running   0          9s
kube-system   kube-proxy-6scq8           1/1     Running   0          50s
kube-system   kube-scheduler-photon001   1/1     Running   0          12s
```

上記は、まだCNIが入っていないせいなので、[公式の手順](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#pod-network)を参考にCNIをインストールします。今回は Canal を入れてみました。導入後、少し待つと下記のように正常な状態になります。

```
# kubectl get nodes
NAME        STATUS   ROLES    AGE    VERSION
photon001   Ready    master   2m2s   v1.14.1

# kubectl get pods --all-namespaces
NAMESPACE     NAME                                READY   STATUS    RESTARTS   AGE
kube-system   canal-gfdp2                         3/3     Running   0          10s
kube-system   coredns-fb8b8dccf-8zl5h             1/1     Running   0          106s
kube-system   coredns-fb8b8dccf-c2cbm             1/1     Running   0          106s
kube-system   etcd-photon001                      1/1     Running   0          66s
kube-system   kube-apiserver-photon001            1/1     Running   0          64s
kube-system   kube-controller-manager-photon001   1/1     Running   0          45s
kube-system   kube-proxy-6scq8                    1/1     Running   0          105s
kube-system   kube-scheduler-photon001            1/1     Running   0          67s
```

### ノード構築

ここまではマスターノードの構築でしたが、ノード構築も「kubeadm環境構築」までは同じです。マスターのホストで `kubeadm init` した際に出力されたコマンドをつかって 2台とも `kubeadm join 〜` するだけです。join後、少し待つと下記のような状態になります。

```
# kubectl get nodes
NAME        STATUS   ROLES    AGE     VERSION
photon001   Ready    master   21m     v1.14.1
photon002   Ready    <none>   6m24s   v1.14.1
photon003   Ready    <none>   3m1s    v1.14.1
```

### 動作確認

適当にPodを動かしてみます。

```
# kubectl run hello-world --replicas=3 --image=gcr.io/google-samples/node-hello:1.0  --port=8080
# kubectl expose deployment hello-world --type=NodePort --name=hello-world

# kubectl get pod
NAME                           READY   STATUS    RESTARTS   AGE
hello-world-77b55d5bf8-hgp9k   1/1     Running   0          62s
hello-world-77b55d5bf8-mjh58   1/1     Running   0          62s
hello-world-77b55d5bf8-vrvwp   1/1     Running   0          62s

# kubectl get pod
NAME                           READY   STATUS    RESTARTS   AGE
hello-world-77b55d5bf8-hgp9k   1/1     Running   0          76s
hello-world-77b55d5bf8-mjh58   1/1     Running   0          76s
hello-world-77b55d5bf8-vrvwp   1/1     Running   0          76s

# kubectl get deployments
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
hello-world   3/3     3            3           81s

# kubectl get service
NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
hello-world   NodePort    10.97.91.234   <none>        8080:31669/TCP   78s
kubernetes    ClusterIP   10.96.0.1      <none>        443/TCP          24m

# curl http://<ノードのプライベートIP>:<NodePortのポート>
Hello Kubernetes!
```

正しくKubernetesクラスタとして動作しているようです。

## まとめ

ニフクラのPhoton OSでもほぼ通常のkubeadmによる構築手順で、Kubernetesを構築することが出来ました。実用的にはマスターを冗長構成にするなど細かい設定は必要でしょうが、他のOSと同様の方法で動作させることができそうです。

## 参考文献

- [VMware Photon OS で構築するK8Sクラスタ - /var/log/study](https://yaaamaaaguuu.hatenablog.com/entry/2018/06/25/005948)