Raspberry Pi 4に64ビット版UbuntuでKubernetes環境を構築する

## 1. はじめに

「Raspberry Pi 3/4に64ビット版Ubuntuをインストールする」、「Raspberry Piを使って勉強用のKubernetes（以下、k8s）環境を構築する」といった記事は多く見つかりますが、それらを組み合わせた記事が見当たらなかったので、メモを兼ねて書いてみました。

## 2. 構成

今回の構成は以下の通りです。
なお、OSイメージのダウンロード、microSDカードへの書き込みなどはmacOS 10.14.6（Mojave）環境で行っています。

* ハードウェア: Raspberry Pi 4 4GB 4台
* OS: Ubuntu Server 19.10 ARM64（AArch64）版
* ノード構成:
    * マスターノード: 1台
    * ワーカーノード: 3台

## 3. 作業の流れ

k8s環境を構築する大まかな手順は以下の通りです。

1. Ubuntu Serverをインストールする
2. Dockerをインストールする
3. k8sをインストールする
4. マスターノードのみ: クラスタを初期化する
5. ワーカーノードのみ: クラスタに参加する

## 4. Ubuntu Serverをインストールする

Raspberry PiにOSとして「Ubuntu Server」をインストールします。手順は以下の通りです。

1. Ubuntu Serverイメージをダウンロードする
2. Ubuntu ServerイメージをmicroSDカードへ書き込む（`cgroup`の設定変更も含む）
3. Ubuntu Serverをブートしてパスワードを変更する
4. Ubuntu ServerへSSHで接続する
5. 初期設定を実施する

### 4.1. Ubuntu Serverイメージをダウンロードする

Raspberry Pi用の「Ubuntu Server」のイメージ（`img`ファイル）を「[Install Ubuntu Server on a Raspberry Pi 2, 3 or 4 | Ubuntu](https://ubuntu.com/download/raspberry-pi)」からダウンロードし、展開します。
今回は以下のイメージを使用しました。

```text
mac$ ls -l ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img.xz
-rw-r--r--@ 1 yuya  staff  662616972 Feb  5 18:44 ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img.xz

mac$ shasum ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img.xz
a6bd53bd211bfb08886dcfda412e5cc38356d0cf  ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img.xz

mac$ unxz ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img.xz
mac$ ls -l ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img
-rw-r--r--  1 yuya  staff  3115115520 Feb  5 18:44 ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img

mac$ shasum ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img
8ffe93a887337abd4c1e21098c20a04ee69b6339  ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img
```

***参考: `unxz`のインストール***

`xz`ファイルを展開するための`unxz`コマンドは、[Homebrew](https://brew.sh/)でインストールできます。

```text
mac$ brew install xz
```

### 4.2. Ubuntu ServerイメージをmicroSDカードへ書き込む

ダウンロードしたイメージを`dd`コマンドを使ってmicroSDカードに書き込みます。
**書き込み先デバイスは慎重に確認しましょう。システムを破壊してしまう恐れがあります。**
なお、筆者の環境では2分半ほど掛かりました。

イメージの書き込み後、`cgroup`の設定を`/Volumes/system-boot/nobtcmd.txt`に追加します。
これがないと`kubeadm init`に失敗してしまいます。（`[ERROR SystemVerification]: missing cgroups: memory`と表示されます）

```text
# microSDカードのデバイスを確認する。（この例では/dev/disk2）
mac$ diskutil list
...
/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *32.0 GB    disk2
   1:             Windows_FAT_32 boot                    45.3 MB    disk2s1
   2:                      Linux                         31.9 GB    disk2s2

# microSDカードのデバイスのマウントを解除する。
mac$ diskutil unmountDisk /dev/disk2
Unmount of all volumes on disk2 was successful

# イメージをmicroSDカードに書き込む。書き込み先デバイスに注意！
mac$ sudo dd if=ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img of=/dev/rdisk2 bs=1m
2970+1 records in
2970+1 records out
3115115520 bytes transferred in 161.780375 secs (19255213 bytes/sec)

# cgroupの設定を追加する。
mac$ sed -i -e 's/$/ cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1/' /Volumes/system-boot/nobtcmd.txt
mac$ cat /Volumes/system-boot/nobtcmd.txt
net.ifnames=0 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1

# microSDカードを取り出せるようにする。
mac$ diskutil eject /dev/disk2
Disk /dev/disk2 ejected
```

### 4.3. Ubuntu Serverをブートしてパスワードを変更する

イメージを書き込んだmicroSDカードをRaspberry Piに挿入し、ブートします。初回のブートには時間がかかるので、ログイン画面が表示されるまでしばらく待ちます。（Enterキーを押さないと、ログイン画面が表示されないかもしれません）

デフォルトのユーザ名は`ubuntu`、パスワードも同じく`ubuntu`です。
初回ログイン時にはパスワードの変更を求められますので、新しいパスワードを設定します。

### 4.4. Ubuntu ServerへSSHで接続する

Raspbianとは異なり、Ubuntu ServerではSSHがデフォルトで有効になっています。
ただし、Raspbianとは異なり、mDNS（`ホスト名.local`によるアクセス）はデフォルトでは無効なのでご注意ください。

```text
mac$ ssh ubuntu@IPアドレス
```

***参考: 環境***

今回の具体的な環境は以下の通りです。

```text
ubuntu@ubuntu:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="19.10 (Eoan Ermine)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 19.10"
VERSION_ID="19.10"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=eoan
UBUNTU_CODENAME=eoan

ubuntu@ubuntu:~$ uname -a
Linux ubuntu 5.3.0-1014-raspi2 #16-Ubuntu SMP Tue Nov 26 11:18:23 UTC 2019 aarch64 aarch64 aarch64 GNU/Linux

ubuntu@ubuntu:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            1.9G     0  1.9G   0% /dev
tmpfs           380M  3.6M  376M   1% /run
/dev/mmcblk0p2   57G  2.3G   52G   5% /
tmpfs           1.9G     0  1.9G   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           1.9G     0  1.9G   0% /sys/fs/cgroup
/dev/mmcblk0p1  253M  127M  126M  51% /boot/firmware
/dev/loop0       79M   79M     0 100% /snap/core/8042
/dev/loop1       49M   49M     0 100% /snap/lxd/12634
/dev/loop2       81M   81M     0 100% /snap/core/8596
/dev/loop3       60M   60M     0 100% /snap/lxd/13302
tmpfs           380M     0  380M   0% /run/user/1000

ubuntu@ubuntu:~$ cat /proc/cpuinfo
processor	: 0
BogoMIPS	: 108.00
Features	: fp asimd evtstrm crc32 cpuid
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x0
CPU part	: 0xd08
CPU revision	: 3

processor	: 1
BogoMIPS	: 108.00
Features	: fp asimd evtstrm crc32 cpuid
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x0
CPU part	: 0xd08
CPU revision	: 3

processor	: 2
BogoMIPS	: 108.00
Features	: fp asimd evtstrm crc32 cpuid
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x0
CPU part	: 0xd08
CPU revision	: 3

processor	: 3
BogoMIPS	: 108.00
Features	: fp asimd evtstrm crc32 cpuid
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x0
CPU part	: 0xd08
CPU revision	: 3

Hardware	: BCM2835
Revision	: c03112
Serial		: 10000000794dd63d
Model		: Raspberry Pi 4 Model B Rev 1.2

ubuntu@ubuntu:~$ cat /proc/meminfo
MemTotal:        3882408 kB
MemFree:         1965400 kB
MemAvailable:    3563220 kB
Buffers:          117064 kB
Cached:          1362624 kB
SwapCached:            0 kB
Active:          1064036 kB
Inactive:         467572 kB
Active(anon):      61696 kB
Inactive(anon):       76 kB
Active(file):    1002340 kB
Inactive(file):   467496 kB
Unevictable:       18792 kB
Mlocked:           18792 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 8 kB
Writeback:             0 kB
AnonPages:         70724 kB
Mapped:            54144 kB
Shmem:              3680 kB
KReclaimable:     178140 kB
Slab:             247168 kB
SReclaimable:     178140 kB
SUnreclaim:        69028 kB
KernelStack:        2624 kB
PageTables:         1556 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     1941204 kB
Committed_AS:     388532 kB
VmallocTotal:   135290159040 kB
VmallocUsed:        9888 kB
VmallocChunk:          0 kB
Percpu:             3728 kB
CmaTotal:          65536 kB
CmaFree:           31464 kB

ubuntu@ubuntu:~$ cat /proc/cmdline
coherent_pool=1M 8250.nr_uarts=1 cma=64M bcm2708_fb.fbwidth=0 bcm2708_fb.fbheight=0 bcm2708_fb.fbswap=1 smsc95xx.macaddr=DC:A6:32:64:9E:63 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  net.ifnames=0 dwc_otg.lpm_enable=0 console=ttyS0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1

ubuntu@ubuntu:~$ ip address
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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:64:9e:63 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.211/24 brd 192.168.1.255 scope global dynamic eth0
       valid_lft 84799sec preferred_lft 84799sec
    inet6 fe80::dea6:32ff:fe64:9e63/64 scope link
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether dc:a6:32:64:9e:64 brd ff:ff:ff:ff:ff:ff
```

### 4.5. 初期設定を実施する

Raspbianとは異なり、環境設定用の`raspi-config`コマンドは用意されていません。
Ubuntuの流儀に従って設定していきましょう。

```text
# システムをアップデートする。
ubuntu@ubuntu:~$ sudo apt update && sudo apt upgrade --yes

# NTPサーバを「NICT」に変更する。
ubuntu@ubuntu:~$ sudo sed -i 's/^#NTP=/NTP=ntp.nict.jp/' /etc/systemd/timesyncd.conf && sudo systemctl restart systemd-timesyncd

# タイムゾーンを「東京」に変更する。
ubuntu@ubuntu:~$ sudo timedatectl set-timezone Asia/Tokyo

# ホスト名を変更する。
ubuntu@ubuntu:~$ sudo hostnamectl set-hostname k8s-m1

# mDNSを有効化する。
ubuntu@ubuntu:~$ sudo apt install --yes avahi-daemon

# 再起動する。
ubuntu@ubuntu:~$ sudo reboot
```

再起動後、mDNSでアクセスできることを確認します。
また、`ssh-copy-id`コマンドを使って公開鍵をRaspberry Piにコピーし、公開鍵認証できるようにします。

```text
mac$ ssh-copy-id ubuntu@k8s-m1.local
mac$ ssh ubuntu@k8s-m1.local
```

## 5. Dockerをインストールする

Ubuntu 19.10（Eoan Ermine）向けのDocker CEはまだstableでないため、Ubuntu 19.04（Disco Dingo）を使用する必要があります。
そのため、`get.docker.com`を使ったインストールは行えず、[公式ページ](https://docs.docker.com/install/linux/docker-ce/ubuntu/)の手順を一部改変してインストールします。

```text
ubuntu@k8s-m1:~$ sudo apt update && sudo apt install --yes \
  apt-transport-https \
  ca-certificates \
  curl \
  gnupg-agent \
  software-properties-common

ubuntu@k8s-m1:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
OK

ubuntu@ubuntu:~$ echo "deb [arch=arm64] https://download.docker.com/linux/ubuntu disco stable" | sudo tee /etc/apt/sources.list.d/docker.list
ubuntu@k8s-m1:~$ sudo apt update && sudo apt install --yes --no-install-recommends docker-ce
ubuntu@k8s-m1:~$ sudo usermod -aG docker ${USER}
ubuntu@k8s-m1:~$ sudo docker version
Client: Docker Engine - Community
 Version:           19.03.6
 API version:       1.40
 Go version:        go1.12.16
 Git commit:        369ce74
 Built:             Thu Feb 13 01:28:30 2020
 OS/Arch:           linux/arm64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.6
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.16
  Git commit:       369ce74
  Built:            Thu Feb 13 01:27:02 2020
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```

## 6. k8sをインストールする

続いてk8s環境を構築するツールである`kubeadm`などをインストールします。手順は以下の通りです。

1. スワップメモリが無効なことを確認する
2. 自ホストのIPアドレスを解決できるようにする
3. `iptables`を切り替える
4. `kubeadm`などをインストールする

### 6.1. スワップメモリが無効なことを確認する

`kubelet`はスワップメモリが有効な環境では動作しません。今回の環境は、そもそもスワップメモリが有効になっていませんでした。

```text
ubuntu@k8s-m1:~$ free
              total        used        free      shared  buff/cache   available
Mem:         927952      297732       36612        3716      593608      606912
Swap:             0           0           0
```

### 6.2. 自ホストのIPアドレスを解決できるようにする

自身のホスト名でメインのネットワークデバイスのIPアドレスが解決されるように`/etc/hosts`を修正します。

```text
ubuntu@k8s-m1:~$ ip address show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:64:9e:63 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.211/24 brd 192.168.1.255 scope global dynamic eth0
       valid_lft 85525sec preferred_lft 85525sec
    inet6 fe80::dea6:32ff:fe64:9e63/64 scope link
       valid_lft forever preferred_lft forever

ubuntu@k8s-m1:~$ sudo vim /etc/hosts
ubuntu@k8s-m1:~$ cat /etc/hosts
127.0.0.1 localhost
192.168.1.211 k8s-m1 k8s-m1.local

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
```

### 6.3. `iptables`を切り替える

公式サイトに従い、`iptables`を古いものに切り替えます。公式サイトの記載では`arptables`、`ebtables`も切り替えていますが、こちらは`*-legacy`が見当たりませんでした。

```text
ubuntu@k8s-m1:~$ update-alternatives --list iptables
/usr/sbin/iptables-legacy
/usr/sbin/iptables-nft

ubuntu@k8s-m1:~$ update-alternatives --list ip6tables
/usr/sbin/ip6tables-legacy
/usr/sbin/ip6tables-nft

ubuntu@k8s-m1:~$ update-alternatives --list arptables
/usr/sbin/arptables-nft

ubuntu@k8s-m1:~$ update-alternatives --list ebtables
/usr/sbin/ebtables-nft

ubuntu@k8s-m1:~$ sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
ubuntu@k8s-m1:~$ sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
```

参考: 【公式】[iptablesがnftablesバックエンドを使用しないようにする](https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#iptables%E3%81%8Cnftables%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%AA%E3%81%84%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B)

### 6.4. `kubeadm`などをインストールする

パッケージを使って、`kubeadm`、`kubelet`などをインストールします。

```text
ubuntu@k8s-m1:~$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
OK

ubuntu@k8s-m1:~$ echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
ubuntu@k8s-m1:~$ sudo apt update && sudo apt install --yes kubelet kubeadm kubectl kubernetes-cni
ubuntu@k8s-m1:~$ sudo apt-mark hold kubelet kubeadm kubectl kubernetes-cni

ubuntu@k8s-m1:~$ kubelet --version
Kubernetes v1.17.3

ubuntu@k8s-m1:~$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-11T18:12:12Z", GoVersion:"go1.13.6", Compiler:"gc", Platform:"linux/arm64"}

ubuntu@k8s-m1:~$ kubectl version
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-11T18:14:22Z", GoVersion:"go1.13.6", Compiler:"gc", Platform:"linux/arm64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?

ubuntu@k8s-m1:~$ sudo reboot
```

## 7. マスターノードのみ: クラスタを初期化する

`kubeadm`を使ってk8sのクラスタを初期化します。

### 7.1. クラスタを初期化する

`kubeadm init`を実行して、クラスタを初期化します。様々な環境チェックが行われるので、エラーが出たらそれらに対処します。

```text
ubuntu@ubuntu:~$ sudo kubeadm init \
  --apiserver-advertise-address 192.168.1.211 \
  --pod-network-cidr 10.244.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 192.168.1.211:6443 --token a6yh26.z1w8zex5lxtfow80 \
    --discovery-token-ca-cert-hash sha256:80610764ab850e312404b9bc7475cf9f4711ba6d945f71a621a75226bc526e04
```

`kubeadm init`の完了後、出力されたメッセージに従って設定ファイルをコピーします。
設定ファイルをコピーすると、`kubectl`でクラスタの状態を確認できるようになります。

```text
# 設定ファイルをコピーする。
ubuntu@k8s-m1:~$ mkdir -p $HOME/.kube
ubuntu@k8s-m1:~$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
ubuntu@k8s-m1:~$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

ubuntu@k8s-m1:~$ kubectl version
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-11T18:14:22Z", GoVersion:"go1.13.6", Compiler:"gc", Platform:"linux/arm64"}
Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-11T18:07:13Z", GoVersion:"go1.13.6", Compiler:"gc", Platform:"linux/arm64"}

ubuntu@k8s-m1:~$ kubectl get nodes --output wide
NAME     STATUS     ROLES    AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION      CONTAINER-RUNTIME
k8s-m1   NotReady   master   5m19s   v1.17.3   192.168.1.211   <none>        Ubuntu 19.10   5.3.0-1017-raspi2   docker://19.3.6

ubuntu@k8s-m1:~$ kubectl get pods --all-namespaces --output wide
NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE     IP              NODE     NOMINATED NODE   READINESS GATES
kube-system   coredns-6955765f44-jz5pv         0/1     Pending   0          5m17s   <none>          <none>   <none>           <none>
kube-system   coredns-6955765f44-whxsx         0/1     Pending   0          5m17s   <none>          <none>   <none>           <none>
kube-system   etcd-k8s-m1                      1/1     Running   0          5m7s    192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-apiserver-k8s-m1            1/1     Running   0          5m7s    192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-controller-manager-k8s-m1   1/1     Running   0          5m7s    192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-proxy-tngmj                 1/1     Running   0          5m17s   192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-scheduler-k8s-m1            1/1     Running   0          5m7s    192.168.1.211   k8s-m1   <none>           <none>
```

### 7.2. Podネットワークアドオンのインストール

Podネットワークアドオンとして「Flannel」をインストールします。さまざまなアドオン（CNIプラグイン）が存在しますが、ARMv7とARM64の両方のCPUアーキテクチャをサポートしているFlannelを選択しました。

クラスタ初期化直後ではマスターノードの状態が「NotReady」でしたが、Flannelのインストールが完了すると「Ready」になります。

```text
ubuntu@k8s-m1:~$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml

ubuntu@k8s-m1:~$ kubectl get nodes --output wide
NAME     STATUS   ROLES    AGE    VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION      CONTAINER-RUNTIME
k8s-m1   Ready    master   8m1s   v1.17.3   192.168.1.211   <none>        Ubuntu 19.10   5.3.0-1017-raspi2   docker://19.3.6

ubuntu@k8s-m1:~$ kubectl get pods --all-namespaces --output wide
NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE     IP              NODE     NOMINATED NODE   READINESS GATES
kube-system   coredns-6955765f44-jz5pv         1/1     Running   0          7m59s   10.244.0.3      k8s-m1   <none>           <none>
kube-system   coredns-6955765f44-whxsx         1/1     Running   0          7m59s   10.244.0.2      k8s-m1   <none>           <none>
kube-system   etcd-k8s-m1                      1/1     Running   0          7m49s   192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-apiserver-k8s-m1            1/1     Running   0          7m49s   192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-controller-manager-k8s-m1   1/1     Running   0          7m49s   192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-flannel-ds-arm64-58rgk      1/1     Running   0          2m26s   192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-proxy-tngmj                 1/1     Running   0          7m59s   192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-scheduler-k8s-m1            1/1     Running   0          7m49s   192.168.1.211   k8s-m1   <none>           <none>
```

参考: 【公式】[Podネットワークアドオンのインストール](https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#pod-network)

## 8. ワーカーノードのみ: クラスタに参加する

マスターノードの構築後、ワーカーノード側で`kubeadm join`を実行するだけで、ノードをクラスタに追加することができます。

```text
ubuntu@k8s-n1:~$ sudo kubeadm join 192.168.1.211:6443 \
  --token a6yh26.z1w8zex5lxtfow80 \
  --discovery-token-ca-cert-hash sha256:80610764ab850e312404b9bc7475cf9f4711ba6d945f71a621a75226bc526e04
```

```text
ubuntu@k8s-m1:~$ kubectl get nodes --output wide
NAME     STATUS   ROLES    AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION      CONTAINER-RUNTIME
k8s-m1   Ready    master   26m     v1.17.3   192.168.1.211   <none>        Ubuntu 19.10   5.3.0-1017-raspi2   docker://19.3.6
k8s-n1   Ready    <none>   17m     v1.17.3   192.168.1.212   <none>        Ubuntu 19.10   5.3.0-1017-raspi2   docker://19.3.6
k8s-n2   Ready    <none>   2m44s   v1.17.3   192.168.1.213   <none>        Ubuntu 19.10   5.3.0-1017-raspi2   docker://19.3.6
k8s-n3   Ready    <none>   2m39s   v1.17.3   192.168.1.214   <none>        Ubuntu 19.10   5.3.0-1017-raspi2   docker://19.3.6

ubuntu@k8s-m1:~$ kubectl get pods --all-namespaces --output wide
NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE     IP              NODE     NOMINATED NODE   READINESS GATES
kube-system   coredns-6955765f44-jz5pv         1/1     Running   0          26m     10.244.0.3      k8s-m1   <none>           <none>
kube-system   coredns-6955765f44-whxsx         1/1     Running   0          26m     10.244.0.2      k8s-m1   <none>           <none>
kube-system   etcd-k8s-m1                      1/1     Running   0          26m     192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-apiserver-k8s-m1            1/1     Running   0          26m     192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-controller-manager-k8s-m1   1/1     Running   0          26m     192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-flannel-ds-arm64-58rgk      1/1     Running   0          21m     192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-flannel-ds-arm64-7gcj5      1/1     Running   0          17m     192.168.1.212   k8s-n1   <none>           <none>
kube-system   kube-flannel-ds-arm64-b4lc9      1/1     Running   0          3m4s    192.168.1.213   k8s-n2   <none>           <none>
kube-system   kube-flannel-ds-arm64-skgjw      1/1     Running   0          2m59s   192.168.1.214   k8s-n3   <none>           <none>
kube-system   kube-proxy-bh8c5                 1/1     Running   0          2m59s   192.168.1.214   k8s-n3   <none>           <none>
kube-system   kube-proxy-shjkv                 1/1     Running   0          17m     192.168.1.212   k8s-n1   <none>           <none>
kube-system   kube-proxy-tngmj                 1/1     Running   0          26m     192.168.1.211   k8s-m1   <none>           <none>
kube-system   kube-proxy-ttx28                 1/1     Running   0          3m4s    192.168.1.213   k8s-n2   <none>           <none>
kube-system   kube-scheduler-k8s-m1            1/1     Running   0          26m     192.168.1.211   k8s-m1   <none>           <none>
```

以上でk8s環境の構築は完了です。