Kubernetes the hard way を初学者目線で解説する ~ #6 Control Plane コンポーネントを設定する

#はじめに
30代未経験からエンジニアを目指して勉強中のYNと申します。
インフラ初学者の私ですが、`Kubernetes the hard way`を進めるにあたって、インフラに関する基本的な知識を体系的に学ぶことができました。
そこで、初学者目線での学びなどを本記事にまとめておきたいと思います。

#目次
[こちらをご覧ください](https://qiita.com/theFirstPenguin/items/1f702af92f4eee83f398#%E7%9B%AE%E6%AC%A1)

#Control Planeコンポーネントの設定
それぞれのmastetrノードに`API Server`、`Scheduler`および`Controller Manager`をインストールします。

##インストール
`kube-apiserve`、`kube-controller-manager`、`kube-scheduler`、`kubectl`バイナリをインストールして展開します。
 
```master-1.node/master-2.node
sudo mkdir -p /etc/kubernetes/config
```
```master-1.node/master-2.node
wget -q --show-progress --https-only --timestamping \
  "https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kube-apiserver" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kube-controller-manager" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kube-scheduler" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubectl"
```
実行権限を付与してバイナルフォルダに移動します↓

```master-1.node/master-2.node
{
  chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
  sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
}
```

##etcdの暗号化の設定を行う
Kubernetesクラスタの情報は全てetcdデータベースに保存されていますが、[公式](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#encrypting-your-data)でも述べられているとおり、クラスター内で保持されるデータの暗号化を行う必要があります。

> etcdデータベースには、Kubernetes APIを介してアクセス可能なあらゆる情報が含まれています。このため、暗号化ソリューションを活用してetcdのデータのバックアップを暗号化することや、EncryptionConfigurationを利用してデータを暗号化することが可能です。
[参照](https://qiita.com/mamomamo/items/1651b8f9e44938c0de15#etcd%E3%81%AB%E6%A0%BC%E7%B4%8D%E3%81%95%E3%82%8C%E3%81%9F%E7%A7%98%E5%AF%86%E6%83%85%E5%A0%B1%E3%82%92%E6%9A%97%E5%8F%B7%E5%8C%96%E3%81%97%E3%82%88%E3%81%86)

###暗号化のための適当なkeyを発行する
次のようにしてランダムなkeyを生成します。

```master-1.node
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
```
[`head -c 32`はファイルの頭から32バイトの情報](https://www.atmarkit.co.jp/ait/articles/1603/07/news023.html)を取得します。
[`/dev`は擬似デバイスファイルで、`/dev/udrandom`は乱数を永遠に吐き続ける](https://oplern.hatenablog.com/entry/2017/05/29/221313)ファイルです。
つまり、32バイトの乱数を取り出してbase64にエンコードしたものをkeyに設定します。

###暗号化設定ファイルを生成する
ファイルを生成してmasterノードで共有します。
`encryption-config.yaml`はこの後の`API-server`の設定で使います。

```master-1.node
cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
EOF
```
```master-1.node
for instance in master-1 master-2; do
  scp encryption-config.yaml ${instance}:~/
done
```

##APIサーバーの設定
etcdサーバーのときと基本的に同じです。設定の全体像を示します。
![スクリーンショット 2021-01-15 11.10.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/5a800d79-5267-84b6-bfae-88e164009bf7.png)


###事前準備
```master-1.node/master-2.node
{
  sudo mkdir -p /var/lib/kubernetes/

  sudo cp ca.crt ca.key kube-apiserver.crt kube-apiserver.key \
    service-account.key service-account.crt \
    etcd-server.key etcd-server.crt \
    encryption-config.yaml /var/lib/kubernetes/
}
```
masterノードのinternal IPを取得（してshell変数に格納）します↓

```master-1.node/master-2.node
INTERNAL_IP=$(ip addr show enp0s8 | grep "inet " | awk '{print $2}' | cut -d / -f 1)
```

###`kube-apiserver.service`ファイルを設定する
systemdのserviceをファイルを設定します。

```master-1.node/master-2.node
cat <<EOF | sudo 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 \\
  --bind-address=0.0.0.0 \\
  --client-ca-file=/var/lib/kubernetes/ca.crt \\
  --enable-admission-plugins=NodeRestriction,ServiceAccount \\
  --enable-swagger-ui=true \\
  --enable-bootstrap-token-auth=true \\
  --etcd-cafile=/var/lib/kubernetes/ca.crt \\
  --etcd-certfile=/var/lib/kubernetes/etcd-server.crt \\
  --etcd-keyfile=/var/lib/kubernetes/etcd-server.key \\
  --etcd-servers=https://192.168.5.11:2379,https://192.168.5.12:2379 \\
  --event-ttl=1h \\
  --encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.crt \\
  --kubelet-client-certificate=/var/lib/kubernetes/kube-apiserver.crt \\
  --kubelet-client-key=/var/lib/kubernetes/kube-apiserver.key \\
  --kubelet-https=true \\
  --runtime-config=api/all \\
  --service-account-key-file=/var/lib/kubernetes/service-account.crt \\
  --service-cluster-ip-range=10.96.0.0/24 \\
  --service-node-port-range=30000-32767 \\
  --tls-cert-file=/var/lib/kubernetes/kube-apiserver.crt \\
  --tls-private-key-file=/var/lib/kubernetes/kube-apiserver.key \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```
この設定は、全体像における`API-server`とその他のコンポーネントの関係についてを記述していることが見て取れます。その他特記すべき部分としては下記が挙げられます。

* `--etcd-servers=https://192.168.5.11:2379,https://192.168.5.12:2379`

etcdサーバーの冗長化について設定しています

* `--enable-admission-plugins=NodeRestriction,ServiceAccount`：

API-serverに届くリクエストのvaidation方法について設定しています。
>Admission controllers limit requests to create, delete, modify or connect to (proxy). They do not support read requests.
The admission control process proceeds in two phases. In the first phase, mutating admission controllers are run. In the second phase, validating admission controllers are run. Note again that some of the controllers are both.
If any of the controllers in either phase reject the request, the entire request is rejected immediately and an error is returned to the end-user.
[参考](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/)

* `--authorization-mode=Node,RBAC `

APi-serverがKubernetesコンポーネントにどういった権限を認めるかを設定します。[参考](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#authorization-modules)


##Controller Managerの設定
API-serverと大体一緒です。
![スクリーンショット 2021-01-15 11.43.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/9f362fc2-4bb6-33bd-913b-d6d1dc7d0f17.png)

###事前準備
```master-1.node/master-2.node
sudo cp kube-controller-manager.kubeconfig /var/lib/kubernetes/
```
###`kube-controller-manager.service`ファイルを設定する
```master-1.node/master-2.node
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=192.168.5.0/24 \\
  --cluster-name=kubernetes \\
  --cluster-signing-cert-file=/var/lib/kubernetes/ca.crt \\
  --cluster-signing-key-file=/var/lib/kubernetes/ca.key \\
  --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
  --leader-elect=true \\
  --root-ca-file=/var/lib/kubernetes/ca.crt \\
  --service-account-private-key-file=/var/lib/kubernetes/service-account.key \\
  --service-cluster-ip-range=10.96.0.0/24 \\
  --use-service-account-credentials=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```
この設定を見ても、全体像における`controller-manager`とその他のコンポーネントの関係についてほどんと記述していることが分かります。
また、`--leader-elect=true`の部分に着目すると、master-1もしくはmaster-2ノードのどちらかでLeaderが起動していることが分かります。 

##Schedulerの設定
これも上記とだいたい一緒です。
![スクリーンショット 2021-01-15 11.49.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/e960731d-7ded-9daa-17d8-5dca8d7f8c85.png)

###事前準備
```master-1.node/master-2.node
sudo cp kube-scheduler.kubeconfig /var/lib/kubernetes/
```
###`kube-scheduler.service`ファイルを設定する
```master-1.node/master-2.node
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 \\
  --kubeconfig=/var/lib/kubernetes/kube-scheduler.kubeconfig \\
  --address=127.0.0.1 \\
  --leader-elect=true \\
  --v=2
Restart=on-failure
RestartSec=5

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

##control planeの起動
###起動
```master-1.node/master-2.node
{
  sudo systemctl daemon-reload
  sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
  sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler
}
```
###動作確認
```master-1.node/master-2.node
kubectl get componentstatuses --kubeconfig admin.kubeconfig
#もしくは kubectl get componentstatuses
```
```master-1.node/master-2.node
NAME                 STATUS    MESSAGE              ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-0               Healthy   {"health": "true"}
etcd-1               Healthy   {"health": "true"}
```

##Frontend Load Balancerを設定する
外部からAPIサーバーへのアクセスに対するLBを設置します。
![スクリーンショット 2021-01-15 12.40.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/feb785d5-cd99-d0be-cad9-f4b830172683.png)


####Network Load Balancerを配置する
```local.pc
vagrant ssh loadbalancer
```
```loadbalancer.node
#Install HAProxy
sudo apt-get update && sudo apt-get install -y haproxy
```
```loadbalancer.node
cat <<EOF | sudo tee /etc/haproxy/haproxy.cfg 
frontend kubernetes
    bind 192.168.5.30:6443
    option tcplog
    mode tcp
    default_backend kubernetes-master-nodes

backend kubernetes-master-nodes
    mode tcp
    balance roundrobin
    option tcp-check
    server master-1 192.168.5.11:6443 check fall 3 rise 2
    server master-2 192.168.5.12:6443 check fall 3 rise 2
EOF
```
HAproxyの設定については[公式](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/load_balancer_administration/ch-haproxy-setup-vsa)を参照ください。

* frontend => `192.168.5.30:6443`をlisten.後述の`kubernetes-master-nodes`バックエンドにセッションを接続させる
* backend => `kubernetes-master-nodes`バックエンドについて規定する。master-1/master-2にラウンドロビンでスケジューリング。ヘルスチェックは３回連続OKで健全、２回連続失敗でfailと判定。

```loadbalancer.node
sudo service haproxy restart
```
###動作確認
```local.pc
# vagrantクラスタ外のターミナルからアクセス
curl  https://192.168.5.30:6443/version -k
```
```
{
  "major": "1",
  "minor": "13",
  "gitVersion": "v1.13.0",
  "gitCommit": "ddf47ac13c1a9483ea035a79cd7c10005ff21a6d",
  "gitTreeState": "clean",
  "buildDate": "2018-12-03T20:56:12Z",
  "goVersion": "go1.11.2",
  "compiler": "gc",
  "platform": "linux/amd64"
}
```