(Proxy環境下)kubeadmでkubernetesシングルクラスタ構築

# 初めに

本記事は、 ``kubeadm`` によるkubernetesのシングルクラスタ（Master1台、Worker1台）の環境構築について記載します。Proxy環境下で、``sudo`` をつけてコマンド実行する方向けです。
シングルクラスタ環境を作るための手順は、大きく4つあります：

  1. 事前準備（Master,　Worker）
  2. kubeadm による kubernetes(v1.14)環境構築（Master、　Worker）
  3. CNI(Container Network Interface)のインストール（Master）
  3. Worker nodeの登録（Worker）

今回のベースとなる環境（Master, Worker）：

```bash:command
lsb_release -a
```
```command:output
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.6 LTS
Release:        16.04
Codename:       xenial
```
* 2cores
* 8GB RAM
* 40GB HDD

# 1. 事前準備(Master, Worker)

1. Proxyの設定

    ```bash:command
export http_proxy='http://your_proxy:password@hogehogeproxy.com'
export https_proxy='http://your_proxy:password@hogehogeproxy.com'
    ```
    * ``/etc/apt/apt.conf.d/01proxy`` を用意しても良いですが、今回は **不要** です：

    ```bash:/etc/apt/apt.conf.d/01proxyを用意するcommand
echo "Acquire::http::Proxy \"http://your_proxy.pass@hogehogeproxy.com\"" | sudo tee /etc/apt/apt.conf.d/01proxy
echo "Acquire::https::Proxy \"http://your_proxy.pass@hogehogeproxy.com\"" | sudo tee -a /etc/apt/apt.conf.d/01proxy
    ```
1. dockerのインストール
    * [dockerの公式ページ](https://docs.docker.com/install/linux/docker-ce/ubuntu/)に沿ってインストール
        * あるある： 公式ドキュメント通りにコマンド実行しても、社内Proxy環境下ではうまくいかないケースが多い

    ```bash:command
sudo -E apt remove docker docker-engine docker.io containerd runc -y
sudo -E apt update
sudo -E apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common -y
sudo -E bash -c "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -"
sudo -E apt-key fingerprint 0EBFCD88
sudo -E add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo -E apt update
sudo -E apt install docker-ce docker-ce-cli containerd.io -y
sudo systemctl start docker
sudo mkdir -p /etc/systemd/system/docker.service.d
    ```
    ```
...(snip)...
Found linux image: /boot/vmlinuz-4.4.0-145-generic
Found initrd image: /boot/initrd.img-4.4.0-145-generic
Found linux image: /boot/vmlinuz-4.4.0-143-generic
Found initrd image: /boot/initrd.img-4.4.0-143-generic
Found memtest86+ image: /boot/memtest86+.elf
Found memtest86+ image: /boot/memtest86+.bin
done
    ```
1. DockerのProxy設定
    * [docker公式のProxy設定](https://docs.docker.com/config/daemon/systemd/)に加えて、[CNIで利用するサブネット](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#pod-network)を考慮した``NO_PROXY``設定を施します。

    ```bash:command(Proxyの値は適宜書き換えてください)
sudo bash -c "echo -e \"[Service]\nEnvironment=\"HTTP_PROXY=http://proxy_user:proxy_pass@your_proxy.com\"\nEnvironment=\"HTTPS_PROXY=http://proxy_user:proxy_pass@your_proxy.com\"\nEnvironment=\"NO_PROXY=localhost,127.0.0.1,192.168.122.33,192.168.0.0/16,10.96.0.0/16,10.244.0.0/16,111.111.0.0/16\"\" | tee vi /etc/systemd/system/docker.service.d/http-proxy.conf"
sudo systemctl daemon-reload
sudo systemctl restart docker
systemctl show --property=Environment docker
    ```
    ```
...(snip)...
Environment=HTTP_PROXY=http://proxy_user:proxy_pass@your_proxy.com HTTPS_PROXY=http://proxy_user:proxy_pass@your_proxy.com NO_PROXY=localhost,127.0.0.1,192.168.122.33
    ```
    * **NO_PROXY** には、MasterのIPを指定
    * (追記)ごめんなさい以下の書き方は間違っていました。``no_proxy``にCIDRを指定しても効いていないとのことでした：
        * ``NO_PROXY=localhost,127.0.0.1,192.168.122.33,192.168.0.0/16,10.96.0.0/16,10.244.0.0/16,111.111.0.0/16``
1. kubeadm, kubelet, kubectlのインストール

    ```bash:command
sudo -E apt update; sudo -E apt install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo -E apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo -E apt update
sudo -E apt install -y kubelet kubeadm kubectl
sudo -E apt-mark hold kubelet kubeadm kubectl
    ```
    ```
...(snip)...
Unpacking kubeadm (1.14.0-00) ...
Setting up cri-tools (1.12.0-00) ...
Setting up kubernetes-cni (0.7.5-00) ...
Setting up kubelet (1.14.0-00) ...
Setting up kubectl (1.14.0-00) ...
Setting up kubeadm (1.14.0-00) ...
    ```
1. ``kubelet``の起動時引数に ``--fail-swap-on=false`` を追加後デーモンの再起動＆``kubelet``が起動時に立ち上がる設定

    ```bash:command
sudo sed -i "/^\[Service\]$/a Environment=\"KUBELET_EXTRA_ARGS=--fail-swap-on=false\"" /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
sudo systemctl daemon-reload; sudo systemctl stop kubelet
sudo systemctl enable kubelet; sudo systemctl start kubelet
    ```
    * あるある： ``--fail-swap-on=false`` を付け忘れると ``kubelet``が起動しないことが多い。 [v1.9のとき](https://qiita.com/yushiro_1121/items/ad3d3f4252b0022857d1)もそうでした。
1. この時点で、screenや別ウィンドウを使って、下記のコマンドの出力結果を出して続けておく。
  * この先、コンテナが起動したかどうかの確認が楽になります。

    ```bash:command
watch -d kubectl get pods --all-namespaces -o wide
    ```
    ```a:この時点ではこんな出力ですが、気にせず先に進みます
Every 2.0s: kubectl get pods --all-namespaces -o wide             Sat Apr  6 xx:xx:2019
.
The connection to the server localhost:8080 was refused - did you specify the right host or port?
    ```

# 2. kubernetes の構築(Masterのみ)


1. ``kubeadm init`` を実行して kubernetes環境をデプロイ
    * 本手順書では、 *Canal* をインストールします

    ```bash:command
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --ignore-preflight-errors=swap
    ```
    ```
..(snip)...
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
...(体感3分くらい)...
Your Kubernetes control-plane has initialized successfully!
...(snip)...
    ```
    * ``Your Kubernetes control-plane has initialized successfully!`` があれば成功です
    * 下記のようなエラーになったため、``--ignore-preflight-errors=swap`` オプションをつけて実行しています
    * あとWARNINGも出ていますが ~~面倒なので今回は~~ スルーします

        ```
...(snip)...
[init] Using Kubernetes version: v1.14.0
[preflight] Running pre-flight checks
        [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
error execution phase preflight: [preflight] Some fatal errors occurred:
        [ERROR Swap]: running with swap on is not supported. Please disable swap
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
        ```
1. ``To start using your cluster, you need to run the following as a regular user`` に従って、コマンド実行

    ```bash:command
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
    ```
1. 上記コマンド実行後、 ``watch -d`` を実行していた画面に下記のような状態が出力されることを確認。

    ```
NAMESPACE     NAME                              READY   STATUS    RESTARTS   AGE     IP                NODE      NOMINATED NODE   READINESS GATES
kube-system   coredns-fb8b8dccf-sj9fb           0/1     Pending   0          3m14s   <none>            <none>    <none>           <none>
kube-system   coredns-fb8b8dccf-tx8pn           0/1     Pending   0          3m14s   <none>            <none>    <none>           <none>
kube-system   etcd-compute                      1/1     Running   0          2m27s   192.168.123.236   compute   <none>           <none>
kube-system   kube-apiserver-compute            1/1     Running   0          2m35s   192.168.123.236   compute   <none>           <none>
kube-system   kube-controller-manager-compute   1/1     Running   0          2m11s   192.168.123.236   compute   <none>           <none>
kube-system   kube-proxy-8dvn6                  1/1     Running   0          3m14s   192.168.123.236   compute   <none>           <none>
kube-system   kube-scheduler-compute            1/1     Running   0          2m15s   192.168.123.236   compute   <none>           <none>
    ```
    * ``coredns-なんちゃら`` が ``Pending`` の状態ですが、ここでは放置して次に進みます。

# 3. CNIのインストール(Masterのみ)

### Canalの場合

1. [こちら](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#pod-network)を参考に下記のコマンドを実行

    ```bash:command
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/canal/rbac.yaml
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/canal/canal.yaml
    ```
    ```
clusterrole.rbac.authorization.k8s.io/calico created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/canal-flannel created
clusterrolebinding.rbac.authorization.k8s.io/canal-calico created
configmap/canal-config created
daemonset.extensions/canal created
serviceaccount/canal created
...(snip)...
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
    ```
    * 本コマンド実行後、``canal-なんたら`` というPodの作成が始まります。
    * ``coredns`` と上記Podを含む全てのPodが ``Running``　になることを確認しましょう
        * 体感2分30秒くらい

# 4. Worker nodeの登録(Workerのみ)

1. Worker node にSSHでログイン後、sudoになります（[公式ドキュメント](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#join-nodes)通りに）

    ```bash:command
sudo su -
    ```
1. ``kubeadm init`` の出力結果に表示された ``kubeadm join`` を実行します

    ```bash:command
kubeadm join 192.168.122.33:6443 --token miserarenaiyo --discovery-token-ca-cert-hash sha256:kottimoyapparimiserarenaiyo --ignore-preflight-errors=swap
    ```
    * **あるある**： UbuntuでSwapをオフにする設定をあとで調べようと思っていても、忘れる（揮発性）

    ```
...(snip)...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
-
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
    ```
1. Master上でノード一覧を表示して、Workerが追加されていることを確認します

    ```bash:command
kubectl get nodes
    ```
    ```
NAME       STATUS     ROLES    AGE    VERSION
k-master   Ready      master   157m   v1.14.0
k-worker   Ready      <none>   87s    v1.14.0
    ```
    * ``STATUS`` が ``Ready`` になれば登録成功です。お疲れ様でした。

# トラシュー

実際に遭遇したエラーの対処について記載します。

## ``kubeadm init`` がコケる

* kubeletの状況確認

    ```bash:command
journalctl -f -e -u kubelet.service
    ```
* docker の Proxy設定状況の確認

    ```bash:command
systemctl show --property=Environment docker
    ```
    * ここの設定が抜けていたので、Proxy設定後、docker-daemonをリロード
1. ``kubeadm init`` を再度実行するため、作成したPodを全て削除

    ```bash:command
kubectl delete -f <curl or yaml file>
    ```
1. ``kubeadm reset`` 後、 ``$HOME`` ディレクトリの ``.kube/`` を削除

    ```bash:command
sudo kubeadm reset
rm -rf ~/.kube
    ```
1. 上記実行後、 ``kubeadm init`` を再度実行
    * ``.kube/`` を消さない場合、 ``kubectl`` コマンドが実行できなくなりました（証明書関係のエラーでしたが、ログを取り忘れました...）

## Worker Nodeの ``STATUS`` が一生 ``NotReady``

Worker側の ``kubelet`` がエラーを吐いている可能性があります。私は下記の2つの現象が発生していました：

**(A) Worker Node上の docker-daemonがProxy設定を認知していない**
**(B) CNIの設定ファイル、ディレクトリがWorkerノードに存在しない**

いずれの場合においても、 まずはWorker nodeの登録解除をする必要があります。

1. (Master) Worker nodeの登録を解除

    ```bash:command
kubectl delete node <worker-node-name>
    ```
1. (Worker) joinの状態を解除する(rootで実行)

    ```bash:command
kubeadm reset
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
    ```

### (A) Worker Node上の docker-daemonがProxy設定を認知していない

1. ``kubelet`` の状態を確認

    ```bash:command
journalctl -f -e -u kubelet.service
    ```
    ```
...(snip)...
Apr 01 19:24:56 k-worker kubelet[9131]: E0401 19:24:56.331896 9131 pod_workers.go:190] Error syncing pod
 836c281a-5465-11e9-8b12-5254007d68f0 ("canal-jwfln_kube-system(836c281a-5465-11e9-8b12-5254007d68f0)"),
skipping: failed to "CreatePodSandbox" for "canal-jwfln_kube-system(836c281a-5465-11e9-8b12-5254007d68f0)"
with CreatePodSandboxError: "CreatePodSandbox for pod \"canal-jwfln_kube-system(836c281a-
5465-11e9-8b12-5254007d68f0)\" failed: rpc error: code = Unknown desc = failed pulling image
\"k8s.gcr.io/pause:3.1\": Error response from daemon: Get https://k8s.gcr.io/v2/: dial tcp:
lookup k8s.gcr.io on 192.168.122.1:53: no such host"
    ```
    * CanalのPodを作る際の ``docker pull`` にコケているように見えます
    * このエラーが出ているときは、まずはProxy設定を確認してみましょう
1. Proxyの設定状況を確認（*DockerのProxy設定* 参照）

    ```bash:command
systemctl show --property=Environment docker
    ```
1. Proxy定義ファイル(``/etc/systemd/system/docker.service.d/http-proxy.conf``)はあるのにデーモン側で設定されていない場合、デーモンの再起動忘れの可能性があるため、 ``docker-daemon``を再起動

    ```bash:command
sudo systemctl daemon-reload
sudo systemctl restart docker
    ```
    * その他の見直しポイント
        * http_proxy, https_proxy にはそれぞれ、適切なproxyが設定されているか？
            * ``http`` のproxy しか存在しない状態で ``https`` 、あるいはその逆を指定していないか？
        * no_proxyに下記のアドレスはセットされているか？
            * flannel, calico, canalが使うサブネット
            * Master, Worker のIPアドレス
1. 再度 worker側から ``kubeadm join`` コマンドを実行

### (B) CNIの設定ファイル、ディレクトリがWorkerノードに存在しない

下記のようなエラーメッセージが確認できます

```bash:command
journalctl -f -e -u kubelet.service
```
```
...(snip)...
Apr 01 19:24:56 k-worker kubelet[9131]: W0401 19:24:56.395397  9131 cni.go:213] Unable to update
cni config: No networks found in /etc/cni/net.d
Apr 01 19:24:57 k-worker kubelet[9131]: E0401 19:24:57.187390  9131 kubelet.go:2170] Container runtime
network not ready: NetworkReady=false reason:NetworkPluginNotReady
message:docker: network plugin is not ready: cni config uninitialized
```
``/etc/cni/net.d`` が見つからない、 **NetworkPluginNotReady** だよ、ということで、えいやで試してみたところ ``Ready`` になりました。ここは「えいや」ですので、この状況に陥らないことを祈ります。

1. (Worker) ``kubeadm reset``を実行(rootで実行)

    ```bash:command
kubeadm reset
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
    ```
1. (Worker)下記のディレクトリ(/etc/cni/net.d/)を作成

    ```bash:command
mkdir -p /etc/cni/net.d
    ```
1. (Master)同じフォルダ上(`/etc/cni/net.d/`)にあるファイルをWorkerへコピー(Canalの場合は以下)
    * *10-canal.conflis*
    * *calico-kubeconfig*
1. *10-canal.conflist* は、node情報がベタ書きされているため、Worker node名に変更

    ```diff
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
-     "nodename": "k-master",
+     "nodename": "k-worker",
      "ipam": {
    ```
* 再度 Worker側からjoinコマンドを実行

以上です。ここまで読んでくださってありがとうございました。
次は metalkubeの環境構築を目指しまっす。