Kubernetes the hard way を初学者目線で解説する ~ #7 Workerノードの設定

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

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

#Workerノードを設定する
ここではWorkerノードに`kubelet`と`kube-proxy`をインストールします。

##kubeletのサーバー/クライアント証明書を生成
下図の通り、TLS証明書をつくります。
![スクリーンショット 2021-01-15 16.03.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/d2489cc2-133a-a64a-685e-cc7dc9296ae4.png)


```worker-1.node
master-1$ cat > openssl-worker-1.cnf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = worker-1
IP.1 = 192.168.5.21
EOF
```
```worker-1.node
openssl genrsa -out worker-1.key 2048
openssl req -new -key worker-1.key -subj "/CN=system:node:worker-1/O=system:nodes" -out worker-1.csr -config openssl-worker-1.cnf
openssl x509 -req -in worker-1.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out worker-1.crt -extensions v3_req -extfile openssl-worker-1.cnf -days 1000
```
##kubelet Configuration ファイルを生成する
ここで、クラスター内外からのkube-apiserverへのリクエストにおける認証について整理しておくと、[こちら](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#authorization-modules)に規定されている通り、`API-server`へリクエストを送るに当たってクライアント認証の方法は4つのモードがあります。

* Node
* ABAC
* RBAC
* Webhook

その中でも、Nodeモードは`kubelet`にのみ許された特権的な権限で、[様々なAPI操作権限](https://kubernetes.io/docs/reference/access-authn-authz/node/#overview)を有します。このNodeモードを使って認証するため、後述の`kubeconfig`ファイルに`system:node:worker-1`を設定する必要があります。

ここから、`kubelet`の設定ファイルを作っていきます。

###LBのIPをshell変数にメモする
```master-1.node
LOADBALANCER_ADDRESS=192.168.5.30
```
###worker-1.kubeconfigファイルを生成する
```master-1.node
{
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.crt \
    --embed-certs=true \
    --server=https://${LOADBALANCER_ADDRESS}:6443 \
    --kubeconfig=worker-1.kubeconfig

  kubectl config set-credentials system:node:worker-1 \
    --client-certificate=worker-1.crt \
    --client-key=worker-1.key \
    --embed-certs=true \
    --kubeconfig=worker-1.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=system:node:worker-1 \
    --kubeconfig=worker-1.kubeconfig

  kubectl config use-context default --kubeconfig=worker-1.kubeconfig
}
```
###作ったconfigファイルをworker-1ノードに移動する
```master-1.node
scp ca.crt worker-1.crt worker-1.key worker-1.kubeconfig worker-1:~/
```

##worker-1ノードの設定
ここから、worker-1ノードの設定をしていきます。
###worker-1ノードにに`kubelet`/`kube-proxy`/`kubekubectl`をインストール
```worker-1.node
wget -q --show-progress --https-only --timestamping \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubectl \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kube-proxy \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubelet
```
###適切なディレクトリを作成
```worker-1.node
sudo mkdir -p \
  /etc/cni/net.d \
  /opt/cni/bin \
  /var/lib/kubelet \
  /var/lib/kube-proxy \
  /var/lib/kubernetes \
  /var/run/kubernetes
```
###実行権限を付与してディレクトリ移動
```worker-1.node
{
  chmod +x kubectl kube-proxy kubelet
  sudo mv kubectl kube-proxy kubelet /usr/local/bin/
}
```
###`kubelet`の設定
####必要なデータを適切なディレクトリに移動する
```worker-1.node
{
  sudo mv ${HOSTNAME}.key ${HOSTNAME}.crt /var/lib/kubelet/
  sudo mv ${HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig
  sudo mv ca.crt /var/lib/kubernetes/
}
```
####`kubelet-config.yaml`をつくる
```worker-1.node
cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication: 
  anonymous: #(1)
    enabled: false
  webhook: #(2)
    enabled: true
  x509: #(3)
    clientCAFile: "/var/lib/kubernetes/ca.crt"
authorization:
  mode: Webhook #(4)
clusterDomain: "cluster.local"
clusterDNS:
  - "10.96.0.10"
resolvConf: "/run/systemd/resolve/resolv.conf" #(5)
runtimeRequestTimeout: "15m" #(6)
EOF
```
[`KubeletConfiguration`の詳細はこちら](https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/)にありますが、上記を解説してみます。

* (1) kubeletへの（podなどからの）リクエストは全て認証を必要とさせます。
>Enables anonymous requests to the Kubelet server. Requests that are not rejected by another authentication method are treated as anonymous requests. Anonymous requests have a username of `system:anonymous`, and a group name of `system:unauthenticated`.

* (2) kubeletの認証モードをwebhookに設定します。
※[Webhookとは](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#authorization-modules)
> Webhook - A WebHook is an HTTP callback: an HTTP POST that occurs when something happens; a simple event-notification via HTTP POST. A web application implementing WebHooks will POST a message to a URL when certain things happen.

* (3) kubeletがサーバーとして働く際に、利用するルート認証局の証明書の場所を指定します。
>File containing x509 Certificate used for serving HTTPS (with intermediate certs, if any, concatenated after server cert).

* (4) kubeletによるリソースへのアクセス制御モードをwebhookにします。
>Authorization mode for Kubelet server. Valid options are `AlwaysAllow` or `Webhook`.

* (5) コンテナのDNS解決の設定ファイルの場所。後述する`kubelet.service`unitが使用してるubuntuネームサーバーの設定ファイルを指定します。
>Resolver configuration file used as the basis for the container DNS resolution configuration.

* (6) コンテナ操作に時間がかかる場合のタイムアウト設定を指定します。
>Timeout of all runtime requests except long running request - `pull`, `logs`, `exec` and `attach`. When timeout exceeded, kubelet will cancel the request, throw out an error and retry later.

####`kubelet.service`ファイルを設定する
このserviceをenableにすることで、ノード起動と共にkubeletが起動します。
workerノードには既にdockerがインストール済で、依存関係に`docker.service`が入っていることに注目します。

```worker-1.node
cat <<EOF | sudo tee /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=docker.service
Requires=docker.service

[Service]
ExecStart=/usr/local/bin/kubelet \\
  --config=/var/lib/kubelet/kubelet-config.yaml \\
  --image-pull-progress-deadline=2m \\
  --kubeconfig=/var/lib/kubelet/kubeconfig \\
  --tls-cert-file=/var/lib/kubelet/${HOSTNAME}.crt \\
  --tls-private-key-file=/var/lib/kubelet/${HOSTNAME}.key \\
  --network-plugin=cni \\
  --register-node=true \\
  --v=2
Restart=on-failure
RestartSec=5

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

###`kube-proxy`の設定
以前に生成した`kube-proxy.kubeconfig`を使って`kube-proxy-config.yaml`をつくります。
####必要なデータを適切なディレクトリに移動する
```worker-1.node
sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig
```
####`kube-proxy-config.yaml`をつくる
```worker-1.node
cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
  kubeconfig: "/var/lib/kube-proxy/kubeconfig"
mode: "iptables" #(1)
clusterCIDR: "192.168.5.0/24"
EOF
```
(1) proxy modeを規定します。各modeにおけるkube-proxyの挙動については[こちらを参照](https://kubernetes.io/ja/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies)ください。
>* Which proxy mode to use: 'userspace' (older) or 'iptables' (faster) or 'ipvs' or 'kernelspace' (windows). If blank, use the best-available proxy (currently iptables). If the iptables proxy is selected, regardless of how, but the system's kernel or iptables versions are insufficient, this always falls back to the userspace proxy.
* デフォルトでは、iptablesモードにおけるkube-proxyはバックエンドPodをランダムで選択します。
* トラフィックのハンドリングのためにiptablesを使用すると、システムのオーバーヘッドが少なくなります。
* kube-proxyがiptablesモードで稼働し、最初に選択されたPodが応答しない場合、そのコネクションは失敗します。

[`KubeProxyConfiguration`の詳細はこちら](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/)を参照ください。
`kube-proxy`はKubernetesクラスタ内で非常に大事な役割を果たしています。下図のようにユーザーおよびPodがノードを意識せず通信できるのは`kube-proxy`とCNIがうまく機能しているからであり、ここがKubernetesクラスタのミソだと思います。
![スクリーンショット 2021-01-15 16.26.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/572098/9dda35d6-2a77-04ec-839d-d83f38c38439.png)

####`kubelet.service`ファイルを設定する
このserviceをenableにすることで、ノード起動と共にkube-proxyが起動します。

```worker-1.node
cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-proxy \\
  --config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=on-failure
RestartSec=5

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

###`kubelet`と`kube-proxy`を起動する
```worker-1.node
{
  sudo systemctl daemon-reload
  sudo systemctl enable kubelet kube-proxy
  sudo systemctl start kubelet kube-proxy
}
```
###動作確認
```master-1.node
kubectl get nodes
```
```
NAME       STATUS     ROLES    AGE   VERSION
worker-1   NotReady   <none>   93s   v1.13.0
```