kubeadm を使って k8s クラスタを構築する

以下の記事を参考に AWS の EC2 上で `kubeadm` を使ってシングルスタークラスタ(コントロールプレーン用 EC2 1台、ワーカー用の EC2 1台)を構築した時のメモ（EKS を使わず、kubeadm を使ってクラスターを構築した理由は勉強のため）

[kubeadminでAWS EC2環境にkuenetesクラスタを作ってみた - 各ノード共通の設定 (1/2)](https://simple-minds-think-alike.hatenablog.com/entry/kubernetes-kubeadm-part1)

[ kubeadminでAWS環境にkuenetesクラスタを作ってみた - マスターノード・ワーカーノードの構築 (2/2) ](https://simple-minds-think-alike.hatenablog.com/entry/kubernetes-kubeadm-part2)

なお、k8s 公式ドキュメントという意味では以下辺り

[Installing kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/)

[Creating a cluster with kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/)

# Installing kubeadm

## 前提

+ AMI は同じ「ami-0eeb679d57500a06c」を利用
+ インスタンスタイプも同じく「t2.medium」を利用

## 前準備

インスタンスを PublicSubnet に起動する
SecurityGroup については以下のようにする

+ Inbound:SSH の許可と自分自身の SecurityGroup からの通信を許可
+ Outbound:すべて OK

SSH できることを確認し、その後、SWAP をオフにしておく

```
$ sudo swapoff -a
```

## 始める前に確認すること

ホスト名、MACアドレス、product_uuidがユニークかを確認する。

```
# ホスト名の確認
$ hostname -s

# product_uuidの確認
$ sudo cat /sys/class/dmi/id/product_uuid

# MACアドレスの確認
$  /bin/ip link show eth0
```

複数ネットワークアダプタがないかも見てみる

```
$ ifconfig
```

ブログにないが、k8s 公式ドキュメントの以下もやっておく

```
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system
```

## Docker(CE)のインストール

コントロールプレーン用、ワーカーノード用 EC2 でブログ手順通りやって Docker が起動するまで確認した

[kubeadminでAWS EC2環境にkuenetesクラスタを作ってみた - 各ノード共通の設定 (1/2)](https://simple-minds-think-alike.hatenablog.com/entry/kubernetes-kubeadm-part1)


## kubeadm, kubelet, kubectlのインストール

k8s のドキュメントの通りにやる

[Installing kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/)

```
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
```

OK
`kubeadm` コマンドが通る事も確認

```
kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.2", GitCommit:"f5743093fd1c663cb0cbc89748f730662345d44d", GitTreeState:"clean", BuildDate:"2020-09-16T13:38:53Z", GoVersion:"go1.15", Compiler:"gc", Platform:"linux/amd64"}
```

## cgroup driverの設定

k8s のドキュメントを見ると以下のように書いてある。

[Configure cgroup driver used by kubelet on control-plane node](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#configure-cgroup-driver-used-by-kubelet-on-control-plane-node)

>When using Docker, kubeadm will automatically detect the cgroup driver for the kubelet and set it in the /var/lib/kubelet/config.yaml file during runtime.

今回使っているのは Docker なので特に何もしない

補足:

上記の通り、何もしなくても良かったのだが、このタイミングで何もしないと `kubelet` は起動しない。

```
# exitCode 255 で終了
sudo systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset:
  Drop-In: /etc/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: activating (auto-restart) (Result: exit-code) since Thu 2020-10-15 02
     Docs: https://kubernetes.io/docs/home/
  Process: 22721 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CO
 Main PID: 22721 (code=exited, status=255)

# journalctl でログを見ると設定ファイルがないことでエラーになっている
journalctl -u kubelet |less
・・・
-- Logs begin at Thu 2020-10-15 01:44:52 UTC, end at Thu 2020-10-15 02:09:41 UTC
. --
Oct 15 02:07:33 ip-172-31-9-65 systemd[1]: Started kubelet: The Kubernetes Node
Agent.
Oct 15 02:07:33 ip-172-31-9-65 systemd[1]: kubelet.service: Current command vani
shed from the unit file, execution of the command list won't be resumed.
Oct 15 02:07:33 ip-172-31-9-65 kubelet[22242]: I1015 02:07:33.739623   22242 ser
ver.go:411] Version: v1.19.3
Oct 15 02:07:33 ip-172-31-9-65 kubelet[22242]: W1015 02:07:33.740375   22242 ser
ver.go:553] standalone mode, no API client
Oct 15 02:07:33 ip-172-31-9-65 systemd[1]: Stopping kubelet: The Kubernetes Node
 Agent...
Oct 15 02:07:38 ip-172-31-9-65 kubelet[22242]: W1015 02:07:38.751027   22242 nvi
dia.go:61] NVIDIA GPU metrics will not be available: no NVIDIA devices found
Oct 15 02:07:38 ip-172-31-9-65 kubelet[22242]: W1015 02:07:38.764634   22242 ser
ver.go:468] No api server defined - no events will be sent to API server.
Oct 15 02:07:38 ip-172-31-9-65 kubelet[22242]: I1015 02:07:38.764661   22242 ser
ver.go:640] --cgroups-per-qos enabled, but --cgroup-root was not specified.  def
aulting to /
Oct 15 02:07:38 ip-172-31-9-65 kubelet[22242]: I1015 02:07:38.765015   22242 con
tainer_manager_linux.go:276] container manager verified user specified cgroup-ro
ot exists: []
```

ただ、手動で設定してもエラーとなった。
とはいえドキュメント上設定不要ということでこのままの状態で次にステップに進めたら成功した。
`kubeadm init` によって設定ファイルを生成してから `kubelet`の再起動をしているのかもしれない。

エラーが出た時は以下のドキュメントにも書いてあるが `journalctl -u kubelet` などを使って状況を確認した。

[Troubleshooting kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/troubleshooting-kubeadm/)

# Creating a cluster with kubeadm

[Creating a cluster with kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/)

## コントロールプレーン

[Initializing your control-plane node ](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#initializing-your-control-plane-node)

`kubeadm init` コマンドを使う。
`--pod-network-cider` は Calico を使うので「10.240.0.0/16」を指定する。

```
sudo kubeadm init --pod-network-cidr=10.240.0.0/16

・・・・
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

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

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 172.31.9.65:6443 --token xxxx \
    --discovery-token-ca-cert-hash sha256:xxxx

```

成功した。
`kubeadm join` はあとで使うのでメモしておく

起動に失敗していた `kubelet` を確認。

```
# 起動している
sudo systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset:
  Drop-In: /etc/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: active (running) since Thu 2020-10-15 02:18:33 UTC; 30s ago
     Docs: https://kubernetes.io/docs/home/
 Main PID: 25661 (kubelet)
    Tasks: 14 (limit: 4703)
   CGroup: /system.slice/kubelet.service
           └─25661 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/boots
# 設定ファイルは自動的に作成され、たしかに自動検出されていた
 sudo cat /var/lib/kubelet/config.yaml |grep cgroup
cgroupDriver: systemd
```

良さそうそうなのでこのまま進む
`kubectl` の設定を行う。

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

```

確認

```
kubectl get pods --all-namespaces
NAMESPACE     NAME                                     READY   STATUS    RESTARTS   AGE
kube-system   coredns-f9fd979d6-nqs6s                  0/1     Pending   0          2m50s
kube-system   coredns-f9fd979d6-s8pkg                  0/1     Pending   0          2m50s
kube-system   etcd-ip-172-31-20-3                      1/1     Running   0          3m1s
kube-system   kube-apiserver-ip-172-31-20-3            1/1     Running   0          3m
kube-system   kube-controller-manager-ip-172-31-20-3   1/1     Running   0          3m
kube-system   kube-proxy-sl6pk                         1/1     Running   0          2m50s
kube-system   kube-scheduler-ip-172-31-20-3            1/1     Running   0          3m
```

coredns は Pending.
Pod network addonがないということなので Calico を入れて少し待つ

```
# Calico を適用
kubectl apply -f https://docs.projectcalico.org/v3.8/manifests/calico.yaml

# 暫し待つと以下のように coredns も起動する
kubectl get po -A
NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-bcc6f659f-6cw22   1/1     Running   0          41s
kube-system   calico-node-c5zql                         1/1     Running   0          41s
kube-system   coredns-f9fd979d6-nqs6s                   1/1     Running   0          6m22s
kube-system   coredns-f9fd979d6-s8pkg                   1/1     Running   0          6m22s
kube-system   etcd-ip-172-31-20-3                       1/1     Running   0          6m33s
kube-system   kube-apiserver-ip-172-31-20-3             1/1     Running   0          6m32s
kube-system   kube-controller-manager-ip-172-31-20-3    1/1     Running   0          6m32s
kube-system   kube-proxy-sl6pk                          1/1     Running   0          6m22s
kube-system   kube-scheduler-ip-172-31-20-3             1/1     Running   0          6m32s
```

OK

マスターノードとして登録されている事を確認

```
kubectl get nodes
NAME             STATUS   ROLES    AGE     VERSION
ip-172-31-20-3   Ready    master   7m33s   v1.19.2
```

OK

## ワーカーノード

次にワーカーノードを参加させる

さきほどメモした `kubeadm join` をワーカーノードで実行する


```
sudo kubeadm join 172.31.20.3:6443 --token xxxxxx     --discovery-token-ca-cert-hash sha256:xxxxxxx

[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

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.
```

成功した

マスターで確認
OK

```
kubectl get nodes
NAME             STATUS   ROLES    AGE   VERSION
ip-172-31-13-2   Ready    <none>   46s   v1.19.3
ip-172-31-9-65   Ready    master   13m   v1.19.3
```

少しハマったが、無事出来た