メモリ512MBで動く軽量Kubernetes！k3sをDocker上で試してみた

# 記事を書いたきっかけ

Publickeyの記事を読んで気になったので、動かしてみたくなって、実際に動かしてみた。
「k3sとは何か？」に関しては、記事を読んでもらうのが良いと思う。

[Kubernetesをわずか40MBのシングルバイナリとして軽量かつシンプルにした新ディストリビューション「k3s」登場。Rancher Labsがオープンソースで公開 － Publickey](https://www.publickey1.jp/blog/19/kubernetes40mbk3srancher_labs.html)

とりあえずはk3sが動く環境を整えてみるが目標。

# 概要

* k3sの公式リポジトリ [rancher/k3s](https://github.com/rancher/k3s) (2019/03/02時点)の手順を参考に実践。
* ただし、手元のPCに本家Kubernetesがインストール済みで、環境を汚したくなかったので、Dockerのコンテナの中で動かす方法をお試し。

## マシン環境(ホストマシン)

```bash
# OS
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.2 LTS"

# Docker
$ docker version
Client:
 Version:           18.09.3
 API version:       1.39
 Go version:        go1.10.8
 Git commit:        774a1f4
 Built:             Thu Feb 28 06:53:11 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.3
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.8
  Git commit:       774a1f4
  Built:            Thu Feb 28 05:59:55 2019
  OS/Arch:          linux/amd64
  Experimental:     false
```

# 動かしてみる

まずは公式リポジトリのソースをチェックアウトから...と思ったけど、このリポジトリ、けっこう重い...

なので、今回やりたい手順([Running in Docker (and docker-compose)](https://github.com/rancher/k3s#running-in-docker-and-docker-compose)で必要そうな`docker-compose.yml`だけダウンロードした。

```bash
curl -L "https://raw.githubusercontent.com/rancher/k3s/master/docker-compose.yml" -o docker-compose.yml
```

公式手順の通り、クラスタを立ち上げ。

```bash
$ docker-compose up --scale node=3
...(略)...

# 結構なメッセージ量に紛れてエラーっぽいメッセージがチラホラ...んー？
```

クラスタの状態を確認。

```bash
$ kubectl --kubeconfig kubeconfig.yaml get node
NAME           STATUS   ROLES    AGE    VERSION
164b688fd280   Ready    <none>   118s   v1.13.3-k3s.6
3a7ad8d03bed   Ready    <none>   119s   v1.13.3-k3s.6
5b48d23be7ea   Ready    <none>   118s   v1.13.3-k3s.6
```

動いていそう...？
だけどPodが全然動いて無い...

```bash
$ kubectl --kubeconfig kubeconfig.yaml get pods --all-namespaces
NAMESPACE     NAME                         READY   STATUS             RESTARTS   AGE
kube-system   coredns-7748f7f6df-ps6jb     1/1     Running            0          3m50s
kube-system   helm-install-traefik-lzkgw   0/1     CrashLoopBackOff   4          3m50s
```

失敗だ...これ...

ログの内容を見ると、コンテナ内からカーネルモジュールが読み込めてない〜というようなエラーだったので、`docker-compose.yml`にオプションを追加。

```yaml
version: '3'
services:
  server:
    # ...(略)...

  node:
    # ...(略)...
    volumes:
    - /lib/modules:/lib/modules
    cap_add:
    - ALL

volumes:
  k3s-server: {}
```

これでトライしてみる。

```bash
$ docker-compose up --scale node=3
...(略)...

# 相変わらずエラーっぽいメッセージがチラホラ...んー？
```

ただクラスタの状態的には動いてそう。

```bash
$ kubectl --kubeconfig kubeconfig.yaml get pods --all-namespaces
NAMESPACE     NAME                         READY   STATUS    RESTARTS   AGE
kube-system   coredns-7748f7f6df-rvz5z     1/1     Running   0          3m19s
kube-system   helm-install-traefik-nmr75   1/1     Running   0          3m18s

$ kubectl --kubeconfig kubeconfig.yaml get nodes
NAME           STATUS     ROLES    AGE     VERSION
1a9985426a91   Ready      <none>   71s     v1.13.3-k3s.6
5a3963d09b0c   Ready      <none>   70s     v1.13.3-k3s.6
6ec3ef89103f   Ready      <none>   69s     v1.13.3-k3s.6
```

とりあえずは動いてそう。

ちなみに...本家Kubernetesだと、`get nodes`の結果でMasterも表示されますが、今回は表示されませんね。そういう仕様なのでしょうか...？

```
# コンテナはMasterを含めて4つ起動
$ docker-compose ps
    Name                  Command               State           Ports         
------------------------------------------------------------------------------
k3s_node_1     /bin/k3s agent                   Up                            
k3s_node_2     /bin/k3s agent                   Up                            
k3s_node_3     /bin/k3s agent                   Up                            
k3s_server_1   /bin/k3s server --disable- ...   Up      0.0.0.0:6443->6443/tcp
```

## 念ため動作確認

確認のため、下記のマニフェストを使って、Podを1つ動かしてみる。

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
      - containerPort: 80
```

カレントに`kubeconfig.yaml`が吐かれるので、それを使う。

```bash
$ kubectl --kubeconfig kubeconfig.yaml apply -f sample-pod.yaml 
pod/sample-pod created

$ kubectl --kubeconfig kubeconfig.yaml get pods
NAME         READY   STATUS    RESTARTS   AGE
sample-pod   1/1     Running   0          100s
```

動いてそう！
...まぁ、ロクに機能を動かしていないので、実際は不具合が起こりそうですが、とりあえずお試できる環境は手に入りました。

# 失敗談

k3sの公式リポジトリの教え([Running in Docker (and docker-compose)](https://github.com/rancher/k3s#running-in-docker-and-docker-compose)に従う前に、「シングルバイナリで簡単にインストールできるなら、`ubuntu:18.04`のイメージ使っていけるはず！」と思って、試した時の話。

そもそも、コンテナでの起動オプションとか調べずに突っ走ったことが原因な気もするけど、あれ〜？ってなった。
どなたかトライして成功したらコメント下さい。

## インストール

まずは`ubuntu:18.04`のイメージを起動し、空の環境を用意する。

```bash
$ docker run -it ubuntu:18.04
root@6e9a76a9bbe3:/#
```

curlコマンドを使って、k3sをダウンロードする。

```bash
# curlコマンドが無いので、先にインストール
# (iproute2は後で使う)
root@6e9a76a9bbe3:/# apt-get update && apt-get install -y curl iproute2
...(略)...

# k3sをGitHubからダウンロード
root@6e9a76a9bbe3:/# curl -L "https://github.com/rancher/k3s/releases/download/v0.1.0/k3s" -o /usr/local/bin/k3s
...(略)...

# 実行権限を付与
root@6e9a76a9bbe3:/# chmod +x /usr/local/bin/k3s

# 確認
root@6e9a76a9bbe3:/# k3s --help
NAME:
   k3s - Kubernetes, but small and simple

USAGE:
   k3s [global options] command [command options] [arguments...]

VERSION:
   dev (HEAD)

COMMANDS:
     server   Run management server
     agent    Run node agent
     kubectl  Run kubectl
     crictl   Run crictl
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --debug        Turn on debug logs
   --help, -h     show help
   --version, -v  print the version
```

動いていそう。

せっかくなので、Master-Node構成を取るべく、同じ要領でもう１つコンテナを立ち上げ、k3sをインストールしておく。

## クラスタ立ち上げ

k3s公式リポジトリの[Quick Start](https://github.com/rancher/k3s#quick-start)の内容をそのままを実行してみる。
コンテナの中では、`sudo`が要らないので、そこだけ注意。

```bash
# 以下は失敗(後述)
root@6e9a76a9bbe3:/# k3s server &
[1] 2775
root@6e9a76a9bbe3:/# INFO[0000] Preparing data dir /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5 
INFO[2019-03-02T12:18:36.307868926Z] Starting k3s v0.1.0 (91251aa)                
...(略)...

[1]+  Exit 255                k3s server
```

`kubeadm init`と似た感じでメッセージ出力された...と思ったけど、止まってるー！？

k3sの公式リポジトリに置いてある[docker-compose.yml](https://github.com/rancher/k3s/blob/master/docker-compose.yml)を覗いてみると、

```yaml
version: '3'
services:
  server:
    image: rancher/k3s:v0.1.0
    command: server --disable-agent
    # ...(略)...
```

おっと何やら`--disable-agent`というオプションが必要そう。

```bash
root@6e9a76a9bbe3:/# k3s server --disable-agent &
[1] 2821
root@6e9a76a9bbe3:/# INFO[2019-03-02T12:21:49.330150358Z] Starting k3s v0.1.0 (91251aa)                
...(略)...
INFO[2019-03-02T12:21:49.815062892Z] k3s is up and running
```

今度は動いてそう...？

```
root@6e9a76a9bbe3:/# k3s kubectl get node
No resources found.
```

んー？
まぁ、一旦無視してNodeを追加してみる。

Node追加のコマンドのフォーマットは下記の通り。

```bash
k3s agent --server https://<Master IP>:6443 --token <Token>
```

`<Master IP>`はiproute2パッケージで入れた`ip`コマンドで確認。

```bash
root@6e9a76a9bbe3:/# ip address show eth0
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
```

`<Token>`は`/var/lib/rancher/k3s/server/node-token`に格納されているそう。

```bash
root@6e9a76a9bbe3:/# cat /var/lib/rancher/k3s/server/node-token
K10bf48c01ba01fe012bcb74ada31b6baabb0a82269af8a2d68c9160a1faf95a68a::node:1902f272fd82f0798cf7a99e0ee64e18
```

長いけど、全部かな？
Node用に立ち上げたもう一方のコンテナで、Node追加コマンドを実行。

```bash
# これも失敗(後述)
root@b303f31b9a67:/# k3s agent --server https://172.17.0.2:6443 --token K10bf48c01ba01fe012bcb74ada31b6baabb0a82269af8a2d68c9160a1faf95a68a::node:1902f272fd82f0798cf7a99e0ee64e18
INFO[0000] Preparing data dir /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5 
INFO[2019-03-02T12:41:02.093092358Z] Starting k3s agent v0.1.0 (91251aa)          
INFO[2019-03-02T12:41:02.590974830Z] Logging containerd to /var/lib/rancher/k3s/agent/containerd/containerd.log 
INFO[2019-03-02T12:41:02.591223430Z] Running containerd -c /var/lib/rancher/k3s/agent/etc/containerd/config.toml -a /run/k3s/containerd/containerd.sock --state /run/k3s/containerd --root /var/lib/rancher/k3s/agent/containerd 
INFO[2019-03-02T12:41:02.591501029Z] Waiting for containerd startup: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial unix /run/k3s/containerd/containerd.sock: connect: no such file or directory" 
WARN[2019-03-02T12:41:03.595279617Z] failed to write value 1 at /proc/sys/net/bridge/bridge-nf-call-iptables: open /proc/sys/net/bridge/bridge-nf-call-iptables: no such file or directory 
WARN[2019-03-02T12:41:03.595424541Z] failed to write value 1 at /proc/sys/net/ipv4/ip_forward: open /proc/sys/net/ipv4/ip_forward: read-only file system 
INFO[2019-03-02T12:41:03.600671376Z] Connecting to wss://172.17.0.2:6443/v1-k3s/connect 
INFO[2019-03-02T12:41:03.600777136Z] Connecting to proxy                           url="wss://172.17.0.2:6443/v1-k3s/connect"
WARN[2019-03-02T12:41:03.610703830Z] Disabling CPU quotas due to missing cpu.cfs_period_us 
INFO[2019-03-02T12:41:03.610955979Z] Running kubelet --healthz-bind-address 127.0.0.1 --read-only-port 0 --allow-privileged=true --cluster-domain cluster.local --kubeconfig /var/lib/rancher/k3s/agent/kubeconfig.yaml --eviction-hard imagefs.available<5%,nodefs.available<5% --eviction-minimum-reclaim imagefs.available=10%,nodefs.available=10% --fail-swap-on=false --cgroup-driver cgroupfs --root-dir /var/lib/rancher/k3s/agent/kubelet --cert-dir /var/lib/rancher/k3s/agent/kubelet/pki --seccomp-profile-root /var/lib/rancher/k3s/agent/kubelet/seccomp --cni-conf-dir /var/lib/rancher/k3s/agent/etc/cni/net.d --cni-bin-dir /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin --cluster-dns 10.43.0.10 --container-runtime remote --container-runtime-endpoint unix:///run/k3s/containerd/containerd.sock --address 127.0.0.1 --anonymous-auth=false --client-ca-file /var/lib/rancher/k3s/agent/client-ca.pem --hostname-override b303f31b9a67 --cpu-cfs-quota=false 
Flag --allow-privileged has been deprecated, will be removed in a future version
W0302 12:41:03.611511    2828 server.go:194] WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.
INFO[2019-03-02T12:41:03.625252893Z] waiting for node b303f31b9a67: nodes "b303f31b9a67" not found 
W0302 12:41:03.626363    2828 proxier.go:480] Failed to read file /lib/modules/4.15.0-45-generic/modules.builtin with error open /lib/modules/4.15.0-45-generic/modules.builtin: no such file or directory. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0302 12:41:03.629437    2828 proxier.go:493] Failed to load kernel module ip_vs with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0302 12:41:03.633111    2828 proxier.go:493] Failed to load kernel module ip_vs_rr with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0302 12:41:03.637358    2828 proxier.go:493] Failed to load kernel module ip_vs_wrr with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0302 12:41:03.637778    2828 proxier.go:493] Failed to load kernel module ip_vs_sh with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0302 12:41:03.638181    2828 proxier.go:493] Failed to load kernel module nf_conntrack_ipv4 with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0302 12:41:03.642478    2828 node.go:103] Failed to retrieve node info: nodes "b303f31b9a67" not found
I0302 12:41:03.642508    2828 server_others.go:148] Using iptables Proxier.
F0302 12:41:03.642619    2828 server.go:377] unable to create proxier: can't set sysctl net/ipv4/conf/all/route_localnet: open /proc/sys/net/ipv4/conf/all/route_localnet: read-only file system
```

同じくk3sの公式リポジトリの[docker-compose.yml](https://github.com/rancher/k3s/blob/master/docker-compose.yml)を覗いてみると、

```yaml
version: '3'
services:
  # ...(略)...
  node:
    image: rancher/k3s:v0.1.0
    tmpfs:
    - /run
    - /var/run
    privileged: true
    # ...(略)...
```

`tmpfs`と`privileged`が必要そう。
Node用のコンテナは作り直し...

```bash
$ docker run -it --mount type=tmpfs,destination=/run --mount type=tmpfs,destination=/var/run --privileged ubuntu:18.04 
root@f0b7efd14ae8:/# apt-get update && apt-get install -y curl iproute2
...(略)...
root@f0b7efd14ae8:/# curl -L "https://github.com/rancher/k3s/releases/download/v0.1.0/k3s" -o /usr/local/bin/k3s
...(略)...
root@f0b7efd14ae8:/# chmod +x /usr/local/bin/k3s
```

気を取り直して、もう一度Node追加をトライ。

```bash
# これも失敗
root@f0b7efd14ae8:/# k3s agent --server https://172.17.0.2:6443 --token K10bf48c01ba01fe012bcb74ada31b6baabb0a82269af8a2d68c9160a1faf95a68a::node:1902f272fd82f0798cf7a99e0ee64e18
...(略)...

...(以下が繰り返し出力)...
E0302 12:55:40.162851    2808 kuberuntime_manager.go:677] createPodSandbox for pod "coredns-7748f7f6df-rk9g5_kube-system(ca04369a-3ce5-11e9-9c1c-0242ac110002)" failed: rpc error: code = Unknown desc = failed to start sandbox container: failed to create containerd task: failed to mount rootfs component &{overlay overlay [workdir=/var/lib/rancher/k3s/agent/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/18/work upperdir=/var/lib/rancher/k3s/agent/containerd/io.containerd.snapshotter.v1.overl
```

ここで断念。