kubernetes the hard way on PC (１０．Control Plane)

#初めに
「[kubernetes the hard way](https://github.com/kelseyhightower/kubernetes-the-hard-way) を PC でやってみる」の１０回目、「Bootstrapping the Kubernetes Control Plane」についてです。（ [目次](https://qiita.com/shimauma_Zzzzz/items/84ecf5c748ceafd05380#%E8%AA%AC%E6%98%8E%E8%A8%98%E4%BA%8B%E7%9B%AE%E6%AC%A1) ）

kubernetes の中心的な機能を果たす control plane のセットアップです。
バイナリ入手はどれかの control plane で実施して、コピーすればよいですが、
それ以降については **※3台の control plane ノードで実施します※**


やることは以下の通りです。

- kubernetes バイナリの入手
- API サーバーの構成
- Controller-manager の構成
- Scheduler の構成
- Controller サービスの起動
- HTTP Health Check
- kubelet 用の RBAC 設定
- The Kubernetes Frontend Load Balancer

図で言うと赤枠の部分です。
ただし、外部ロードバランサーを用意していない関係で、 
HTTP Health Check 並びに The Kubernetes Frontend Load Balancer は割愛しています。

![45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310495/60dbc3a2-f6fc-dbbd-1fd0-68cc8cf71132.png)



※ kubernetes the hard way では google cloud の Network load balancer を構成していますが
オンプレではできないので割愛しています。（他のロードバランサーでやってみようか、とは思いましたがあきらめ…）

また、末尾につまづいたエラーとその調査・対応方法も記載しておきます。

---
# kubernetes バイナリの入手

kubernetes の最新版は git hub 上で確認することができます。
以下の URL アクセス後、右側の列の Releases 欄に最新版が記載されています。
https://github.com/kubernetes/kubernetes

2020/07 時点では、 v1.18.6 でした。
Releases 欄の バージョン部分をクリックすると リリースページに遷移します。　そのページ下部にダウンロードできるリンクがあります。
https://github.com/kubernetes/kubernetes/releases/download/v1.18.6/kubernetes.tar.gz

kubernetes the hard way では、各コンポーネントのバイナリを個別に取得する手順になっていますが、
最近は kubernetes.tar.gz となっており、その中のスクリプトを実行してバイナリを取得する構成のようです。

私が実施した際は、 v1.16.9 で、 URL としては以下の通りでした。今でも なぜ 1.16 でやったのか理解に苦しみます。
https://github.com/kubernetes/kubernetes/releases/download/v1.16.9/kubernetes.tar.gz

これを wget でダウンロードします。

```shell-session
# wget -q --show-progress --https-only --timestamping https://github.com/kubernetes/kubernetes/releases/download/v1.16.9/kubernetes.tar.gz
kubernetes.tar.gz                                        100%[================================================================================================================================>] 445.60K   214KB/s    in 2.1s
```

ダウンロード完了後、 展開し、 kubernetes/cluster まで移動します。

```shell-session
# tar zxf kubernetes.tar.gz
# ls
ca  etcd  kubernetes  kubernetes-client-linux-amd64.tar.gz  kubernetes.tar.gz

# cd kubernetes/
# ls
client  cluster  docs  hack  LICENSES  README.md  server  version

# cd cluster/
# ls
addons  clientbin.sh  gce                   get-kube-local.sh  images  kubeadm.sh  kube-down.sh  kubernetes-anywhere  kube-util.sh  OWNERS        README.md               skeleton     test-network.sh  update-storage-objects.sh
BUILD   common.sh     get-kube-binaries.sh  get-kube.sh        juju    kubectl.sh  kubemark      kube-up.sh           log-dump      pre-existing  restore-from-backup.sh  test-e2e.sh  test-smoke.sh    validate-cluster.sh
```

そこで、`get-kube-binaries.sh` を実行します。途中で、 ダウンロードするかどうか聞かれるので y を押します。

```shell-session
# ./get-kube-binaries.sh
Kubernetes release: v1.16.9
Server: linux/amd64  (to override, set KUBERNETES_SERVER_ARCH)
Client: linux/amd64  (autodetected)  (to override, set KUBERNETES_CLIENT_OS and/or KUBERNETES_CLIENT_ARCH)

Will download kubernetes-server-linux-amd64.tar.gz from https://dl.k8s.io/v1.16.9
Will download and extract kubernetes-client-linux-amd64.tar.gz from https://dl.k8s.io/v1.16.9
Is this ok? [Y]/n
y
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   161  100   161    0     0    473      0 --:--:-- --:--:-- --:--:--   473
100  352M  100  352M    0     0  59.2M      0  0:00:05  0:00:05 --:--:-- 78.4M

md5sum(kubernetes-server-linux-amd64.tar.gz)=f606d0f63fd751f857c66e33454cfcbf
sha1sum(kubernetes-server-linux-amd64.tar.gz)=8b3b190f84463dfba117cf49a82c5a58eeb6e67b

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   161  100   161    0     0   1025      0 --:--:-- --:--:-- --:--:--  1025
100 12.3M  100 12.3M    0     0  9774k      0  0:00:01  0:00:01 --:--:-- 29.9M

md5sum(kubernetes-client-linux-amd64.tar.gz)=e9c4ca5fdb50619d35531b859aadc2bd
sha1sum(kubernetes-client-linux-amd64.tar.gz)=cd0cd90c99a461ae3ff12a569b902e58653a1e9b

Extracting /data/assam/k8s/kubernetes/client/kubernetes-client-linux-amd64.tar.gz into /data/assam/k8s/kubernetes/platforms/linux/amd64
Add '/data/assam/k8s/kubernetes/client/bin' to your PATH to use newly-installed binaries.
```

メッセージでは  kubernetes/client/bin に PATH を通すという指示になっていますが、
kubernetes/client/bin 以下には kubectl がダウンロードされているだけです。
サーバー用のコンポーネントは kubernetes/server 以下にダウンロードされています。
それをさらに展開します。展開した先の kubernetes/server/bin 以下に各種コンポーネントがあります。

```shell-session
# cd ../server
# ls
README  kubernetes-manifests.tar.gz  kubernetes-server-linux-amd64.tar.gz

# tar zxf kubernetes-server-linux-amd64.tar.gz
# ls
README  kubernetes  kubernetes-manifests.tar.gz  kubernetes-server-linux-amd64.tar.gz

# cd kubernetes/server/bin
# ls
apiextensions-apiserver    kube-apiserver.tar                  kube-proxy             kube-scheduler.docker_tag  kubelet
hyperkube                  kube-controller-manager             kube-proxy.docker_tag  kube-scheduler.tar         mounter
kube-apiserver             kube-controller-manager.docker_tag  kube-proxy.tar         kubeadm
kube-apiserver.docker_tag  kube-controller-manager.tar         kube-scheduler         kubectl

```

上記の各コンポーネントのうち、以下のものを 各 control plane ノードの /usr/local/bin 以下にコピーします。
（方法はお任せで。あと念のため実行権限も確認してください。）

- kube-apiserver
- kube-controller-manager
- kube-scheduler
- kubectl


#API サーバーの構成

kubernetes の中心ともいえる API サーバーを構成します。
まず、ディレクトリを作成し、その中に 各種秘密鍵や証明書をコピーします。
証明書は第６回の最後で各ノードの作業ユーザーのホームディレクトリに配置したと思います。

```shell-session
# mkdir -p /var/lib/kubernetes/
# cp ca.pem ca-key.pem kubernetes.pem kubernetes-key.pem service-account.pem service-account-key.pem encryption-config.yaml /var/lib/kubernetes/
# ls -l /var/lib/kubernetes/
total 28
-rw------- 1 root root 1675 May  9 05:34 ca-key.pem
-rw-r--r-- 1 root root 1318 May  9 05:34 ca.pem
-rw-r--r-- 1 root root  240 May  9 05:34 encryption-config.yaml
-rw------- 1 root root 1679 May  9 05:34 kubernetes-key.pem
-rw-r--r-- 1 root root 1728 May  9 05:34 kubernetes.pem
-rw------- 1 root root 1679 May  9 05:34 service-account-key.pem
-rw-r--r-- 1 root root 1440 May  9 05:34 service-account.pem
```

次に、 API サーバーを  systemd 配下のサービスとして稼働させるために、
ユニットファイルを作成します。

長いので、 kubernetes the hard way からまずテキストファイル等にコピー＆ペーストして実施すればよいです。
違いは以下のポイントです

- ここでは root ユーザーで作業しているので、 sudo は不要（と言いつつ後半面倒になって使っています)
- IP アドレスが異なる

kubernetes the hard way と合わせた名前、IP 等にしていれば単純にコピー＆ペーストで
対応可能なのですが、最初に何も考えずにホスト名、IP 等を決めてしまったツケが出ていますね。。
（ただ、そのおかげで注意深く見ながらやることになり、結果として理解が深まったかもしれません）

例によって、cat コマンドでファイルを作るため、事前に IP アドレス等を変数に入れています。
kubernetes the hard way では Google cloud のコマンドで IP を取得していますが、
こちらはオンプレPC ですので、以下のように ip コマンドで取得します。

tmux で３台同時に操作する前提でコマンドを用いて IP を変数に格納していますが、
１台ずつ実施する場合は `INTERNAL_IP=192.168.199.200` 等で全く問題ありません。

```shell-session
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:01:97:f0 brd ff:ff:ff:ff:ff:ff
    inet 192.168.199.200/24 brd 192.168.199.255 scope global enp1s0
       valid_lft forever preferred_lft forever

# ip addr | grep 192.168 | awk '{ print $2 }' | cut -d / -f 1
192.168.199.200

# INTERNAL_IP=$( ip addr | grep 192.168 | awk '{ print $2 }' | cut -d / -f 1 )
# echo ${INTERNAL_IP}
192.168.199.200
```

変数の設定ができれば、 いよいよ API Server のユニットファイルを作成します。

繰り返しになりますが、ここから先は　
**INTERNAL_IP などに注意しながら Control plane のノード３台それぞれで作成します。**

```shell-session
# cat <<EOF | tee /etc/systemd/system/kube-apiserver.service
> [Unit]
> Description=Kubernetes API Server
> Documentation=https://github.com/kubernetes/kubernetes
>
> [Service]
> ExecStart=/usr/local/bin/kube-apiserver \\
>   --advertise-address=${INTERNAL_IP} \\
>   --allow-privileged=true \\
>   --apiserver-count=3 \\
>   --audit-log-maxage=30 \\
>   --audit-log-maxbackup=3 \\
>   --audit-log-maxsize=100 \\
>   --audit-log-path=/var/log/audit.log \\
>   --authorization-mode=Node,RBAC \\
>   --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
>   --etcd-cafile=/var/lib/kubernetes/ca.pem \\
>   --etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
>   --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
>   --etcd-servers=https://192.168.199.200:2379,https://192.168.199.201:2379,https://192.168.199.202:2379 \\
>   --event-ttl=1h \\
>   --encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
>   --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
>   --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
>   --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\
>   --kubelet-https=true \\
>   --runtime-config=api/all \\
>   --service-account-key-file=/var/lib/kubernetes/service-account.pem \\
[Service]
ExecStart=/usr/local/bin/kube-apiserver \
  --advertise-address=192.168.199.200 \
  --allow-privileged=true \
  --apiserver-count=3 \
  --audit-log-maxage=30 \
  --audit-log-maxbackup=3 \
  --audit-log-maxsize=100 \
  --audit-log-path=/var/log/audit.log \
  --authorization-mode=Node,RBAC \
  --bind-address=0.0.0.0 \
  --client-ca-file=/var/lib/kubernetes/ca.pem \
  --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \
  --etcd-cafile=/var/lib/kubernetes/ca.pem \
  --etcd-certfile=/var/lib/kubernetes/kubernetes.pem \
  --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \
  --etcd-servers=https://192.168.199.200:2379,https://192.168.199.201:2379,https://192.168.199.202:2379 \
  --event-ttl=1h \
  --encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \
  --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \
  --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \
  --kubelet-https=true \
  --runtime-config=api/all \
  --service-account-key-file=/var/lib/kubernetes/service-account.pem \
  --service-cluster-ip-range=10.32.0.0/24 \
  --service-node-port-range=30000-32767 \
  --tls-cert-file=/var/lib/kubernetes/kubernetes.pem \
  --tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
#
```

よく見ると、各種証明書の指定や、 encryption-config.yaml の指定、 クラスターIP レンジやポートレンジの指定が行われていることがわかります。
`--service-cluster-ip-range=10.32.0.0` を指定しているので、 kubernetes 上でサービスを作成すると 10.32.0.0 のNW からアドレスが払い出されます。
また、 NodePort を作成する際のポートは 30000-32767 になります。
API サーバーの各種オプションについては、 [Command Line tools reference の kube-apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) をご覧ください。

API サーバーの準備はここまでです。　systemd のユニットファイルを作成しているので、
`systemctl daemon-reload` の実行と `systemctl list-unit-files | grep xxx` で確認したりする必要がありますが、
他にも同様にユニットファイルを作成するため、最後にまとめて実施します。


#Controller-manager の構成

まず、第７回で作成・配布した kubeconfig ファイルをコピーします。

```shell-session
# cp kube-controller-manager.kubeconfig /var/lib/kubernetes/
```

API サーバー同様、ユニットファイルを作成します。
`--cluster-cidr=10.200.0.0/16` を指定していますね。

```shell-session
# cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
> [Unit]
> Description=Kubernetes Controller Manager
> Documentation=https://github.com/kubernetes/kubernetes
>
> [Service]
> ExecStart=/usr/local/bin/kube-controller-manager \\
>   --address=0.0.0.0 \\
>   --cluster-cidr=10.200.0.0/16 \\
>   --cluster-name=kubernetes \\
>   --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\
>   --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\
>   --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
>   --leader-elect=true \\
>   --root-ca-file=/var/lib/kubernetes/ca.pem \\
>   --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\
>   --service-cluster-ip-range=10.32.0.0/24 \\
>   --use-service-account-credentials=true \\
>   --v=2
> Restart=on-failure
> RestartSec=5
>
> [Install]
> WantedBy=multi-user.target
> EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \
  --address=0.0.0.0 \
  --cluster-cidr=10.200.0.0/16 \
  --cluster-name=kubernetes \
  --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \
  --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \
  --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \
  --leader-elect=true \
  --root-ca-file=/var/lib/kubernetes/ca.pem \
  --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \
  --service-cluster-ip-range=10.32.0.0/24 \
  --use-service-account-credentials=true \
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
#

```

オプションについては API サーバー同様に
[Command Line tools reference の kube-controller-manager](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/) をご覧ください。

#Scheduler の構成

Scheduler についても同様に対応します。

```shell-session
# cp kube-scheduler.kubeconfig /var/lib/kubernetes/
```

同様に、と言いつつ　scheduler に関しては yaml の設定ファイルが必要なようです。
この辺りはあまり丁寧に確認していないのですが、
kube-scheduler を systemd 配下のサービスではなく、コンテナで動かす場合
この yaml ファイルにコンテナ関連の設定を行い、
static pod として各ノード上で稼働させるということをやるようです。
ただ、今回はあくまで systemd 配下で稼働させるため、
内容としては kubeconfig ファイルの指定と、 Leader Election を行うかどうかの設定ぐらいのようです。
（※ [static pod](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/) とは kubelet に直接管理される pod で、
乱暴に言うと、あらかじめ指定したディレクトリ内に Pod 用 yaml ファイルを置くと kubelet が起動、管理してくれるというものです）

```shell-session
# cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml
> apiVersion: kubescheduler.config.k8s.io/v1alpha1
> kind: KubeSchedulerConfiguration
> clientConnection:
>   kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
> leaderElection:
>   leaderElect: true
> EOF
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
  leaderElect: true
#
```

次にユニットファイルを作成します。

```shell-session
# cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
> [Unit]
> Description=Kubernetes Scheduler
> Documentation=https://github.com/kubernetes/kubernetes
>
> [Service]
> ExecStart=/usr/local/bin/kube-scheduler \\
>   --config=/etc/kubernetes/config/kube-scheduler.yaml \\
>   --v=2
> Restart=on-failure
> RestartSec=5
>
> [Install]
> WantedBy=multi-user.target
> EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-scheduler \
  --config=/etc/kubernetes/config/kube-scheduler.yaml \
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
#
```

オプションについては API サーバー同様に
[Command Line tools reference の kube-scheduler](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-scheduler/) をご覧ください。


#Controller サービスの起動

ここまで作成してきた ユニットファイルを用いて、 systemd 配下で動かす kubernetes の各種サービスを起動します。
まずは daemon-reload です。

```shell-session
# systemctl daemon-reload
```

次に、ユニットファイルに間違いがないか簡易的に確認します。
(※３列表示ですが、前から　ユニット・ファイル、STATE、VENDOR PRESET の順です。　VENDOR PRESET は一言でいえばデフォルトです。）
STATE欄が bad となっていればユニットファイル見直しです。

```shell-session
# systemctl list-unit-files | grep kube
kube-apiserver.service                 disabled        enabled
kube-controller-manager.service        disabled        enabled
kube-scheduler.service                 disabled        enabled
```

次に、先に自動起動設定をしておきます。

```shell-session
# systemctl enable kube-apiserver kube-controller-manager kube-scheduler

Created symlink /etc/systemd/system/multi-user.target.wants/kube-apiserver.service → /etc/systemd/system/kube-apiserver.service.
Created symlink /etc/systemd/system/multi-user.target.wants/kube-controller-manager.service → /etc/systemd/system/kube-controller-manager.service.
Created symlink /etc/systemd/system/multi-user.target.wants/kube-scheduler.service → /etc/systemd/system/kube-scheduler.service.

# systemctl list-unit-files | grep kube
kube-apiserver.service                 enabled         enabled
kube-controller-manager.service        enabled         enabled
kube-scheduler.service                 enabled         enabled
```

そして、いよいよ起動です。

##API サーバー起動

systemctl コマンドで起動し、 systemctl status でステータスを見ています。 loaded , active になっているので起動しています。
なお、ログ末尾に healthz check failed となっていますが、現時点では気にする必要はありません。
まだクラスターが構成されていないためのようです。

```shell-session
# systemctl start kube-apiserver
# echo $?
0

# systemctl status kube-apiserver
● kube-apiserver.service - Kubernetes API Server
     Loaded: loaded (/etc/systemd/system/kube-apiserver.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2020-05-09 05:46:22 UTC; 11s ago
       Docs: https://github.com/kubernetes/kubernetes
   Main PID: 2625 (kube-apiserver)
      Tasks: 11 (limit: 2282)
     Memory: 273.2M
     CGroup: /system.slice/kube-apiserver.service
             mq2625 /usr/local/bin/kube-apiserver --advertise-address=192.168.199.200 --allow-privileged=true --apiserver-count=3 --audit-log-maxage=30 --audit-log-maxbackup=3 --audit-log-maxsize=100 --audit-log-path=/var/log>

May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/start-apiextensions-informers ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/start-apiextensions-controllers ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/crd-informer-synced ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/bootstrap-controller ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [-]poststarthook/rbac/bootstrap-roles failed: reason withheld
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/scheduling/bootstrap-system-priority-classes ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/ca-registration ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/start-kube-apiserver-admission-initializer ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/start-kube-aggregator-informers ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/apiservice-registration-controller ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/apiservice-status-available-controller ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/kube-apiserver-autoregistration ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]autoregister-completion ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/apiservice-openapi-controller ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: healthz check failed
```

なお、ログをすべて確認する場合は、`journalctl -u kube-apiserver` でご確認ください。
一度ぐらいは少し時間をかけて　わからないながらも見ておいてもよいと思います。

単体レベルで確認したい場合は以下のようなコマンドで確認するようです。(admin.kubeconfig がある場所で実行）

```shell-session
# kubectl get --raw /healthz/poststarthook/rbac/bootstrap-roles --kubeconfig admin.kubeconfig
ok
```

##kube-controller-manager 起動

```shell-session
# systemctl start kube-controller-manager
# echo $?
0

# systemctl status kube-controller-manager
  kube-controller-manager.service - Kubernetes Controller Manager
     Loaded: loaded (/etc/systemd/system/kube-controller-manager.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2020-05-09 05:57:23 UTC; 7s ago
       Docs: https://github.com/kubernetes/kubernetes
   Main PID: 2654 (kube-controller)
      Tasks: 8 (limit: 2282)
     Memory: 21.5M
     CGroup: /system.slice/kube-controller-manager.service
             mq2654 /usr/local/bin/kube-controller-manager --address=0.0.0.0 --cluster-cidr=10.200.0.0/16 --cluster-name=kubernetes --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem --cluster-signing-key-file=/var/lib/ku>

May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.727766    2654 controllermanager.go:519] Starting "cronjob"
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.727830    2654 attach_detach_controller.go:334] Starting attach detach controller
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.727906    2654 shared_informer.go:197] Waiting for caches to sync for attach detach
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.967158    2654 controllermanager.go:534] Started "cronjob"
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.968030    2654 controllermanager.go:519] Starting "csrsigning"
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.967999    2654 cronjob_controller.go:96] Starting CronJob Manager
May 09 05:57:31 k8smaster0 kube-controller-manager[2654]: I0509 05:57:31.118212    2654 controllermanager.go:534] Started "csrsigning"
May 09 05:57:31 k8smaster0 kube-controller-manager[2654]: I0509 05:57:31.118276    2654 controllermanager.go:519] Starting "clusterrole-aggregation"
May 09 05:57:31 k8smaster0 kube-controller-manager[2654]: I0509 05:57:31.118359    2654 certificate_controller.go:113] Starting certificate controller
May 09 05:57:31 k8smaster0 kube-controller-manager[2654]: I0509 05:57:31.118374    2654 shared_informer.go:197] Waiting for caches to sync for certificate
```

Starting "cronjob" などとなっていますね。　controller-manager は cronjob で Reconciliation loop を回しているんでしょうね。

##kube-scheduler 起動

```shell-session
# systemctl start kube-scheduler
# echo $?
0

# systemctl status kube-scheduler
● kube-scheduler.service - Kubernetes Scheduler
     Loaded: loaded (/etc/systemd/system/kube-scheduler.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2020-05-09 05:59:55 UTC; 10s ago
       Docs: https://github.com/kubernetes/kubernetes
   Main PID: 2689 (kube-scheduler)
      Tasks: 10 (limit: 2282)
     Memory: 8.7M
     CGroup: /system.slice/kube-scheduler.service
             mq2689 /usr/local/bin/kube-scheduler --config=/etc/kubernetes/config/kube-scheduler.yaml --v=2

May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.432942    2689 server.go:148] Version: v1.16.9
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.433059    2689 defaults.go:91] TaintNodesByCondition is enabled, PodToleratesNodeTaints predicate is mandatory
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.433080    2689 server.go:167] Starting Kubernetes Scheduler version v1.16.9
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.447008    2689 factory.go:294] Creating scheduler from algorithm provider 'DefaultProvider'
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.447053    2689 factory.go:382] Creating scheduler with fit predicates 'map[CheckNodeUnschedulable:{} CheckVolumeBinding:{} GeneralPredicates:{} MatchInterPodAffi>
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: W0509 05:59:57.450788    2689 authorization.go:47] Authorization is disabled
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: W0509 05:59:57.451153    2689 authentication.go:79] Authentication is disabled
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.451387    2689 deprecated_insecure_serving.go:51] Serving healthz insecurely on 0.0.0.0:10251
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.453170    2689 secure_serving.go:123] Serving securely on 0.0.0.0:10259
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.553817    2689 leaderelection.go:241] attempting to acquire leader lease  kube-system/kube-scheduler...
```

ログの末尾で、 Leader Election が開始されているのがわかります。
（ただし、このログの時点では他のノードの scheduler が起動していないのでうまく Leader にはなれませんが…）

##起動結果確認

各コンポーネントが問題ないことを確認します。
すると、下記のように表示されるはずです。　

```shell-session
# kubectl get componentstatuses --kubeconfig admin.kubeconfig
# kubectl get componentstatus
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-0               Healthy   {"health":"true"}
etcd-1               Healthy   {"health":"true"}
etcd-2               Healthy   {"health":"true"}
```

実際には、 v1.16 では　下記のようになります。

```shell-session
NAME                 AGE
scheduler            <unknown>
controller-manager   <unknown>
etcd-0               <unknown>
etcd-2               <unknown>
etcd-1               <unknown>
```

これは v1.16 のバグで、 v1.17 では修正されています。
もう少し詳しくは末尾のエラーの説明部分に記載します。

#HTTP Health Check

HTTP Health Check ですが、 kubernetes the hard way では google cloud のロードバランサーからの
Health Check 用途で nginx を入れています。
今回オンプレPC でロードバランサーを入れていないため、あまり実施する意味がありません。
そのため今回は割愛いたします。

#kubelet 用の RBAC 設定

API サーバーから各ノードの kubelet にアクセスするための RBAC 権限設定を行います。
また、ここでの作業は control plane のどれか１ノードで実施すればOKです。
これは kubernetes の世界の中の設定なので etcd 内に格納され、どの control plane ノードからも参照できます。
system:kube-apiserver-to-kubelet という ClusterRole 内で
記載の resource に対する全種類の操作を許可しています。

なお、下記のやり方は 標準入力から直接 kubectl apply に内容を渡して設定していますが、
これは個人的には危ないやり方だと思います。
きちんと一度テキストファイルに出力してから apply したほうが良いと思います。

```shell-session
# cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
> apiVersion: rbac.authorization.k8s.io/v1beta1
> kind: ClusterRole
> metadata:
>   annotations:
>     rbac.authorization.kubernetes.io/autoupdate: "true"
>   labels:
>     kubernetes.io/bootstrapping: rbac-defaults
>   name: system:kube-apiserver-to-kubelet
> rules:
>   - apiGroups:
>       - ""
>     resources:
>       - nodes/proxy
>       - nodes/stats
>       - nodes/log
>       - nodes/spec
>       - nodes/metrics
>     verbs:
>       - "*"
> EOF
clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created
#
```

次に ClusterRole を ユーザーに結び付ける ClusterRoleBinding を作成します。
roleRef 欄で 上記 ClusterRole を指定しています。
また、 subjects 欄で kubernetes というユーザーを指定しています。

```shell-session
# cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
> apiVersion: rbac.authorization.k8s.io/v1beta1
> kind: ClusterRoleBinding
> metadata:
>   name: system:kube-apiserver
>   namespace: ""
> roleRef:
>   apiGroup: rbac.authorization.k8s.io
>   kind: ClusterRole
>   name: system:kube-apiserver-to-kubelet
> subjects:
>   - apiGroup: rbac.authorization.k8s.io
>     kind: User
>     name: kubernetes
> EOF
clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created
```

一応作成されたことを見ておきましょう。

```shell-session
root@k8smaster0:/data/assam/k8s/ca# kubectl --kubeconfig admin.kubeconfig get ClusterRole
NAME                                                                   AGE
admin                                                                  30m
cluster-admin                                                          30m
edit                                                                   30m
system:aggregate-to-admin                                              30m
system:aggregate-to-edit                                               30m
system:aggregate-to-view                                               30m
system:auth-delegator                                                  30m
system:basic-user                                                      30m
system:certificates.k8s.io:certificatesigningrequests:nodeclient       30m
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient   30m
system:controller:attachdetach-controller                              30m
system:controller:certificate-controller                               30m
system:controller:clusterrole-aggregation-controller                   30m
system:controller:cronjob-controller                                   30m
system:controller:daemon-set-controller                                30m
system:controller:deployment-controller                                30m
system:controller:disruption-controller                                30m
system:controller:endpoint-controller                                  30m
system:controller:expand-controller                                    30m
system:controller:generic-garbage-collector                            30m
system:controller:horizontal-pod-autoscaler                            30m
system:controller:job-controller                                       30m
system:controller:namespace-controller                                 30m
system:controller:node-controller                                      30m
system:controller:persistent-volume-binder                             30m
system:controller:pod-garbage-collector                                30m
system:controller:pv-protection-controller                             30m
system:controller:pvc-protection-controller                            30m
system:controller:replicaset-controller                                30m
system:controller:replication-controller                               30m
system:controller:resourcequota-controller                             30m
system:controller:route-controller                                     30m
system:controller:service-account-controller                           30m
system:controller:service-controller                                   30m
system:controller:statefulset-controller                               30m
system:controller:ttl-controller                                       30m
system:csi-external-attacher                                           30m
system:csi-external-provisioner                                        30m
system:discovery                                                       30m
system:heapster                                                        30m
system:kube-aggregator                                                 30m
system:kube-apiserver-to-kubelet                                       3m7s
system:kube-controller-manager                                         30m
system:kube-dns                                                        30m
system:kube-scheduler                                                  30m
system:kubelet-api-admin                                               30m
system:node                                                            30m
system:node-bootstrapper                                               30m
system:node-problem-detector                                           30m
system:node-proxier                                                    30m
system:persistent-volume-provisioner                                   30m
system:public-info-viewer                                              30m
system:volume-scheduler                                                30m
view                                                                   30m
root@k8smaster0:/data/assam/k8s/ca#
```

１つだけ 3m7s のものがありますね。　簡単に色が変えられればよいんですが…

また、詳細も見ておきます。

```shell-session
# kubectl --kubeconfig admin.kubeconfig get ClusterRole system:kube-apiserver-to-kubelet -o wide
NAME                               AGE
system:kube-apiserver-to-kubelet   4m9s
```

yaml 形式でも出力させてみましょう。　入力したもの以外にもあれこれついていますが、
内容は同じです。

```shell-session
# kubectl --kubeconfig admin.kubeconfig get ClusterRole system:kube-apiserver-to-kubelet -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"rbac.authorization.k8s.io/v1beta1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"system:kube-apiserver-to-kubelet"},"rules":[{"apiGroups":[""],"resources":["nodes/proxy","nodes/stats","nodes/log","nodes/spec","nodes/metrics"],"verbs":["*"]}]}
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "2020-05-09T06:14:16Z"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
  resourceVersion: "10679"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Akube-apiserver-to-kubelet
  uid: 1b15b2f1-b090-4e4f-98a0-0ee833a49b5e
rules:
- apiGroups:
  - ""
  resources:
  - nodes/proxy
  - nodes/stats
  - nodes/log
  - nodes/spec
  - nodes/metrics
  verbs:
  - '*'
```

同様に、 ClusterRoleBinding も観ておきましょう。

```shell-session
# kubectl --kubeconfig admin.kubeconfig get ClusterRoleBindings system:kube-apiserver
NAME                    AGE
system:kube-apiserver   4m45s


# kubectl --kubeconfig admin.kubeconfig get ClusterRoleBindings system:kube-apiserver  -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"rbac.authorization.k8s.io/v1beta1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"name":"system:kube-apiserver"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"system:kube-apiserver-to-kubelet"},"subjects":[{"apiGroup":"rbac.authorization.k8s.io","kind":"User","name":"kubernetes"}]}
  creationTimestamp: "2020-05-09T06:14:47Z"
  name: system:kube-apiserver
  resourceVersion: "10721"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/system%3Akube-apiserver
  uid: 6f67f771-41b3-4e1d-a356-6e58251dc0e6
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: kubernetes
```

#The Kubernetes Frontend Load Balancer

ここは Google cloud での ロードバランサー設定のため、
今回は利用しません。
そのため、割愛します。
ロードバランサーからアクセスできるように設定できればかっこいいですが…

（一応、外部のサーバーに nginx のロードバランサーを置いてみようかとトライしましたが
　SSL で受けて SSL で後ろに流す、ということができないようであきらめました。。。）


#エラー並びに調査・対応方法

さて、前回から恒例になりつつあるエラー対応です。
kubernetes the hard way をやっている人は多数いると思いますが、
みんなエラー無く対応できているんでしょうか。。。？？

##1. component status が unknown

本文中でも若干触れましたが、 `kubectl get componentstatus` が unknown になる、という事象です。

```shell-session
# kubectl get componentstatuses --kubeconfig admin.kubeconfig
NAME                 AGE
scheduler            <unknown>
controller-manager   <unknown>
etcd-0               <unknown>
etcd-2               <unknown>
etcd-1               <unknown>
```

unknown と言われてもこちらもわかりません、としか言いようがありません。
こういう時はもう少し詳しく聞いてみましょう。
`--v=レベル` を指定すると、詳細ログが出ます　（下記は、 `--v=6` で実施していますが、 4ぐらいからどんどん上げて確認していきました。　なお、 0-4: debug レベル、 5-10: trace レベルだそうです。 9-10 はかなり詳細が出ます。一度ぐらいは眺めてみると理解が深まってよいです。 あ、 curl 使っているのね、とか。）

```shell-session
root@k8smaster01:~# kubectl --v=6 get componentstatuses --kubeconfig admin.kubeconfig
I0504 06:15:06.831312    4248 loader.go:375] Config loaded from file:  admin.kubeconfig
I0504 06:15:06.960862    4248 round_trippers.go:443] GET https://127.0.0.1:6443/api/v1/componentstatuses?limit=500 200 OK in 122 milliseconds
I0504 06:15:06.963204    4248 table_printer.go:44] Unable to decode server response into a Table. Falling back to hardcoded types: attempt to decode non-Table object into a v1beta1.Table
I0504 06:15:06.963309    4248 table_printer.go:44] Unable to decode server response into a Table. Falling back to hardcoded types: attempt to decode non-Table object into a v1beta1.Table
I0504 06:15:06.963362    4248 table_printer.go:44] Unable to decode server response into a Table. Falling back to hardcoded types: attempt to decode non-Table object into a v1beta1.Table
NAME                 AGE
scheduler            <unknown>
controller-manager   <unknown>
etcd-0               <unknown>

```

ログの 2行目 GET https:....  となっているところをよく見ると、以下のように出力されています。
`GET https://127.0.0.1:6443/api/v1/componentstatuses?limit=500 200 OK in 122 milliseconds`

200 OK となっています。
また、`--v=8` から見られる Reponse Body を見ると（見づらいですが）
Type:healthy　とか、 message:ok などのログが見られます。（上記ログでは表示されていません）
また、 table_printer.go というところには、 "Unable to decode server response into a Table..." という記載もあります。
要は、正常なレスポンスが返ってきているが、テーブル形式に変換できていないのではないか、とあたりがつきます。

そこで、形式を変えて表示させます。
丁寧にみると問題なさそうなことが見て取れますね。

```yaml
# kubectl get componentstatuses --kubeconfig admin.kubeconfig  -o yaml

apiVersion: v1
items:
- apiVersion: v1
  conditions:
  - message: ok
    status: "True"
    type: Healthy
  kind: ComponentStatus
  metadata:
    creationTimestamp: null
    name: scheduler
    selfLink: /api/v1/componentstatuses/scheduler
- apiVersion: v1
  conditions:
  - message: ok
    status: "True"
    type: Healthy
  kind: ComponentStatus
  metadata:
    creationTimestamp: null
    name: controller-manager
    selfLink: /api/v1/componentstatuses/controller-manager
- apiVersion: v1
  conditions:
  - message: '{"health":"true"}'
    status: "True"
    type: Healthy
  kind: ComponentStatus
  metadata:
    creationTimestamp: null
    name: etcd-0
    selfLink: /api/v1/componentstatuses/etcd-0
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""
```

その後、改めて Web で調べてみると、下記のような Issue がありました。
`kubectl get componentstatus` fails to write as table #83024
https://github.com/kubernetes/kubernetes/issues/83024
v1.17 で修正済みのようです。

##2. kube-apiserver 起動時エラー（その１）

`systemctl start kube-apiserver` コマンドで API サーバーを起動した際に
ログに以下のようなエラーが出力されました。

```shell-session
# journalctl -u kube-apiserver
（略）
May 03 07:11:39 k8smaster01 kube-apiserver[3133]: Error: error while parsing encryption provider configuration file "/var/lib/kubernetes/encryption-config.yaml": could not obtain secret for named key key1: illegal base64 data
```

はい、これはもう encryption-config.yaml が間違っている以外の解はなさそうですね。
この時確認すると、ファイルが以下のようになっていました。。。

```yaml
# cat encryption-config.yaml
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
```

コピペするときに変数のことを考えなかったようですね。。。
encryption-config.yaml 作成は 第８回で記載しています。
`head -c 32 /dev/urandom | base64` の出力文字列を該当の変数部分に修正入力しました。

##3. kube-controller-manager の Leader election 失敗

kube-controller-manager の起動時のエラーです。
正確には、起動はするがログにエラーが出ている、という状態です。

```shell-session
# journalctl -u kube-controller-manager
（略）
May 03 07:46:49 k8smaster01 kube-controller-manager[3622]: E0503 07:46:49.285704    3622 leaderelection.go:330] error retrieving resource lock kube-system/kube-controller-manager: rpc error: code = Unavailable desc = etcdserve
May 03 07:46:51 k8smaster01 kube-controller-manager[3622]: I0503 07:46:51.727629    3622 leaderelection.go:287] failed to renew lease kube-system/kube-controller-manager: failed to tryAcquireOrRenew context deadline exceeded
May 03 07:46:51 k8smaster01 kube-controller-manager[3622]: F0503 07:46:51.727722    3622 controllermanager.go:279] leaderelection lost
May 03 07:46:51 k8smaster01 systemd[1]: kube-controller-manager.service: Main process exited, code=exited, status=255/EXCEPTION
May 03 07:46:51 k8smaster01 systemd[1]: kube-controller-manager.service: Failed with result 'exit-code'.
May 03 07:46:56 k8smaster01 systemd[1]: kube-controller-manager.service: Service RestartSec=5s expired, scheduling restart.
May 03 07:46:56 k8smaster01 systemd[1]: kube-controller-manager.service: Scheduled restart job, restart counter is at 1.
May 03 07:46:56 k8smaster01 systemd[1]: Stopped Kubernetes Controller Manager.
May 03 07:46:56 k8smaster01 systemd[1]: Started Kubernetes Controller Manager.

```

以下のようなことが見受けられます。

- ログ１行目： etcdserve が Unavailable である
- ログ２行目： failed to tryAcquireOrRenew context deadline exceeded
- ログ３行目： leaderelection lost
- ログ４行目： Exit Code 255 で異常終了

etcd からの応答が返ってこずに、 Leader Election が失敗し、 kube-controller-manager 自体が異常終了しているようです。
etcd 自体は稼働していたのですが、　第９回で少しふれたとおり、 CPU リソースが wait 100% になっており
体感的にも以上に遅く、Disk I/O によるタイムアウトだと判断しました。
対応としては、 一旦 etcd をシングル構成に格下げしたのですが、最終的には SSD を購入して再度最初からやり直しました。
etcd の Disk I/O は重要です。

※ etcd を　1台構成に変更する方法概要

- etcd 停止
- /var/lib/etcd/member ディレクトリ削除 (状態を保持しているようです)
- etcd.services ファイル修正　（#2, #3 エントリーの削除）
- etcd サービスの無効化（ #2, #3 のみ )
- etcd 起動（#1 のみ）
	
※ kube-apiserver.service ファイルから、　etcd #2, #3 のエントリーを削除	


なお、 control plane の各種コンポーネントのログについては
一度ぐらいは丁寧に見ておくことをお勧めいたします。
いろんな設定がデフォルトでされているんだな、ということがよくわかります。


---
今回はここまでとして、次回は Bootstrapping the Kubernetes Worker Nodes の部分を実施します。


← [９．etcd](https://qiita.com/shimauma_Zzzzz/items/3c61ec5aa869b6a333c2)
↑ [目次](https://qiita.com/shimauma_Zzzzz/items/84ecf5c748ceafd05380#%E8%AA%AC%E6%98%8E%E8%A8%98%E4%BA%8B%E7%9B%AE%E6%AC%A1)
→ [１１．Worker Nodes](https://qiita.com/shimauma_Zzzzz/items/725a333ac1c7c9634793)

----