Kata Container を k3s で使えるようにしてみた

## 概要
[k3s](https://k3s.io/) で構築済みの Kubernetes クラスタの Worker に [Kata Container](https://katacontainers.io/) を導入してみたやつです。
Kata Container のドキュメントが結構カオス && 古い(v1)の情報も多かったので、インストールできた方法をまとめてみます。

## TL;DR
- Kata Containerを動かしたいWorkerに、[このテーブル](https://github.com/kata-containers/documentation/blob/da83738c93850b71e22f2097b187b6a613a926c1/install/README.md#supported-distributions)の対応するDistributionのリンク先のスクリプトでKata Conatinerをインストール(本記事では[Ubuntu](https://github.com/kata-containers/documentation/blob/da83738c93850b71e22f2097b187b6a613a926c1/install/ubuntu-installation-guide.md), 20.04向けが用意されていないため、18.04向けにした)
- 下記設定を`/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl`に入れる (同じディレクトリにある `config.toml` をコピーして、下記の設定を追加する)

```toml:config.toml.tmpl
(snip)
[plugins.cri.containerd.runtimes.kata]
  runtime_type = "io.containerd.kata.v2"
[plugins.cri.containerd.runtimes.kata.options]
  ConfigPath = "/usr/share/defaults/kata-containers/configuration.toml"
```
- kataを使うためのRuntimeClassをk8sに作成する
 
```yaml:kata-RuntimeClass.yaml
apiVersion: node.k8s.io/v1beta1
kind: RuntimeClass
metadata:
  name: kata
handler: kata
```
- Kata ContainerをインストールしたWorkerのk3sを再起動する
- `spec.runtimeClassName=kata` なPodを動かす

## 動機とか
業務で触れているCI/CDツールの[Screwdriver.cd](https://screwdriver.cd/)[^1] が[Kata Container でのビルドをサポート](https://blog.screwdriver.cd/post/619378783194071041/kata-containers-in-screwdriver)したため、お試しでご家庭のk8sクラスタに入れてみました(これを書いている時点ではまだScrewdriver.cdでKata Container によるビルドは試せていませんが…)

k3sとKata Containerについて、それぞれが何者なのか詳しくは公式ページや専門に扱った記事におまかせしますが、雑に説明すると

- [k3s](https://k3s.io/): スーパー軽量なKubernetesで、デバイスが非力でも割と動かせる
- [Kata Container](https://katacontainers.io/): OCI準拠のイメージを使ってVMを起動させる。VMなのでホストマシンと色々分離させられてセキュア

ちなみに、Screwdriver.cd では長らく(2017年から[^2]) Kata Containerの前身とも言える[HyperContainer](https://github.com/hyperhq/hyperd) を利用していましたが、公式ページもバイナリもいつの間にか消えるし、不安定だしで大変つらいものでした[^3]。
使われ方はブログ記事にある下記の画像がわかりやすいかと思います。

> [Screwdriver — Kata Containers in Screwdriver](https://blog.screwdriver.cd/post/619378783194071041/kata-containers-in-screwdriver) より
![](https://66.media.tumblr.com/1f90ef88dcadeb8cb2a38e7d7d1aeeff/a74849879cd376a2-8e/s1280x1920/60e5253bf93ccf07d885feebf4513bd013b2de26.png)

## 環境
|Role|Device|OS|k8s version|
|-----|-----|-----|-----|
|Master|Raspberry Pi 3 Model B|Raspbian GNU/Linux 9 (stretch)|v1.17.4+k3s1|
|Worker|NUC10i3FNK |Ubuntu 20.04|v1.17.4+k3s1|
- k3s のインストール時のオプションは特になし (なのでruntimeはデフォルトのcontainerd)
- Worker にのみKata Containerを導入

## 導入にあたってのハマリポイント
### インストールについて
[documentation/README\.md](https://github.com/kata-containers/documentation/blob/da83738c93850b71e22f2097b187b6a613a926c1/install/README.md) にあるとおり、Kata Container のインストール方法はいくつか用意されています。
ここではインストール試したけど途中で諦めた方法や、ワークアラウンドとして行ったことを書き残しておきます。
成功したインストール手順については`TL;DR`を見てもらえると:pray:
また、今回はk3sに導入するため、Kata本体以外はすでにk3sにくっついてインストール済みなので不要です (よくあるk8sを既に動かしている環境ならいらない気がする)

#### Snapでのインストール (:no_good:)
Snapが使える環境ではSnapで入れられるよ！とご丁寧にバナーまでついていますが、自分はこの方法でインストールしたKata Containerをcontainerdから使うことができませんでした。
おそらく下記のようなこと原因なのかなと思っています。インストール方法としては一番楽っぽいので、もし成功した方がいれば教えていただけると幸いです:pray:

- Snapでインストールした場合は他の方法とインストールされるPathが違う
- 実行できるCLIが`kata-runtime` ではなく `kata-containers.runtime` になる
- symlinkが開発者の意図した通りに張られていない気がする

#### apt install (:ok_woman:)
今回成功したのはこれです。
お使いのDistributionに応じたパッケージマネージャでインストールする方法です。これもっと推して欲しいのにわかりにくいところにある気がする。
ちなみに、[kata-manager](https://github.com/kata-containers/documentation/blob/da83738c93850b71e22f2097b187b6a613a926c1/install/installing-with-kata-manager.md) を使ってのインストールもこの方法のラッパーっぽかったです。
1つだけいじった点は、Ubuntu 20.04 向けのパッケージがなかったため、[Install Kata Containers on Ubuntu](https://github.com/kata-containers/documentation/blob/da83738c93850b71e22f2097b187b6a613a926c1/install/ubuntu-installation-guide.md)の `$(lsb_release -rs)` 部分を`18.04`にしてインストールを行いました。

#### ソースから (:no_good:)
Ubuntu 20.04 向けのパッケージがなかったため、[documentation/Developer\-Guide\.md at master · kata\-containers/documentation](https://github.com/kata-containers/documentation/blob/da83738c93850b71e22f2097b187b6a613a926c1/Developer-Guide.md#initial-setup) を参考に、試みました。
buildとinstallまでは特にハマることもなくできましたが、その後 `$ sudo kata-runtime kata-check`を実行した際にエラーの原因となっている、Kata Containerが依存しているパッケージやライブラリ類をインストールしていくのが激しくつらそうだったので諦めました。

### その他
#### k3sのcontainerdに設定を入れる
現在利用しているk8sはk3sで動いているので、「通常のcontainerd」ではなく、「k3sにくっついてくるcontainerd」対して設定を行います。
[Rancher Docs: Advanced Options and Configuration](https://rancher.com/docs/k3s/latest/en/advanced/#using-docker-as-the-container-runtime) によると、`/var/lib/rancher/k3s/agent/etc/containerd/config.toml` に設定ファイルがあり、``/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl` を置いておくとそちらが優先して読み込まれるとのことなので、元の設定ファイルをコピーして追記する形にしました。

```toml:config.toml.tmpl
(snip)
[plugins.cri.containerd.runtimes.kata]
  runtime_type = "io.containerd.kata.v2"
[plugins.cri.containerd.runtimes.kata.options]
  ConfigPath = "/usr/share/defaults/kata-containers/configuration.toml"
```

### `RuntimeClass`をk8s上に作成する
[ブログ記事](https://blog.screwdriver.cd/post/619378783194071041/kata-containers-in-screwdriver)では触れられていません[^4]が、k8s上にKata Containerに対応する`RuntimeClass`を作成する必要があります。
RuntimeClassは下記のように、`metadata.name` と `handler` しかないシンプルな設定です。
containerdをruntimeに利用している場合は、handlerは`[plugins.cri.containerd.runtimes.${HANDLER_NAME}]` の部分、つまり今回は`kata`が正解です。

```yaml:kata-RuntimeClass.yaml
apiVersion: node.k8s.io/v1beta1
kind: RuntimeClass
metadata:
  name: kata
handler: kata
```

## 動かしてみる
ctrで直接コンテナを起動してみるのと、k8sのPodをKata Containerを動かしてみます。

```shell:crt
# runtimeにkataを指定して、k3sのcontainerdを使ってコンテナを起動
tk3fftk@nuc1:~$ sudo k3s ctr run --runtime io.containerd.run.kata.v2 -t --rm docker.io/library/busybox:latest hello sh
/ #

# 別シェルで動いているコンテナを確認。runtimeがkataになっている
tk3fftk@nuc1:~$ sudo k3s ctr c ls | grep hello
hello                                                               docker.io/library/busybox:latest                                                                                 io.containerd.run.kata.v2  
```

```shell:k8s
# 適当にPodのYAMLを吐き出す
$ kubectl run --image nginx nginx --dry-run=client -o yaml > nginx_pod.yaml

# spec.runtimeClassName を追加する。下記は追加後のYAML
$ cat nginx_pod.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
spec:
  runtimeClassName: kata
  containers:
  - image: nginx
    name: nginx
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

# deploy
$ kubectl apply -f nginx_pod.yaml

# worker側で確認。pauseコンテナとnginxコンテナのruntimeがkataになっている
tk3fftk@nuc1:~$ sudo k3s ctr containers list | grep kata
910d6bf06948071677690a9cc482455617c7d17e386a0fe3a02c15762a0a7f88    docker.io/library/nginx@sha256:c870bf53de0357813af37b9500cb1c2ff9fb4c00120d5fe1d75c21591293c34d                  io.containerd.kata.v2
95dbaab3534ba6a87036d2f0057cad600734466293baec222b998817d3b84b40    docker.io/rancher/pause:3.1
                                 io.containerd.kata.v2
```

## 参考
- [Screwdriver — Kata Containers in Screwdriver](https://blog.screwdriver.cd/post/619378783194071041/kata-containers-in-screwdriver)
- [How to use Kata Containers and Containerd](https://github.com/kata-containers/documentation/blob/da83738c93850b71e22f2097b187b6a613a926c1/how-to/containerd-kata.md)
- [Kata Containers installation user guides](https://github.com/kata-containers/documentation/blob/da83738c93850b71e22f2097b187b6a613a926c1/install/README.md)
- [Runtime Class \- Kubernetes](https://kubernetes.io/docs/concepts/containers/runtime-class/)

[^1]: ちなみに、[CDF](https://cd.foundation/projects/)のIncubating Projectになりました:tada:
[^2]: [screwdriver-cd/executor-k8s-vm](https://github.com/screwdriver-cd/executor-k8s-vm/commits/master?after=f8bd60ddc11460ce068993f4c647964bae9b3950+104) の初回commitが2017年
[^3]: まだ使っているので現在進行系でつらいです
[^4]: Installationがただのリンク集で雑すぎるのでは？確かにどれかのリンク先には書いてあったけど…