kubernetes用コンテナランタイムcri-oを試す


http://cri-o.io/

https://github.com/kubernetes-incubator/cri-o

`cri-o`とは、`kubernetes`専用のコンテナランタイムとして主にRed Hatが開発を進めているツールです。
`kubernetes(kubelet)`が`cri-o`を操作し、`cri-o`がコンテナランタイムを操作するという、`kubelet`とコンテナランタイムの橋渡し的な位置付けになっています。

## 開発の背景

`kubernetes`は、コンテナを操作するインターフェースとして[CRI](https://github.com/kubernetes/community/blob/master/contributors/devel/container-runtime-interface.md)と呼ばれる仕様を策定しており、`kubelet`はこのインターフェースを使ってコンテナを操作することになっています。
CRIに準拠したツールであれば、どんなコンテナランタイムでも`kubernetes`と結合して使えるというわけです。

現在、`kubernetes`を使っている人は大抵コンテナランタイムとしてdockerを利用されているかと思いますが、これもkubelet組み込みの[CRI-dockerアダプタ](https://github.com/kubernetes/kubernetes/tree/release-1.8/pkg/kubelet/dockershim)で操作されています。
`kubernetes`はdockerだけでなくrktも利用できますが、こちらは https://github.com/kubernetes/kubernetes/tree/release-1.8/pkg/kubelet/rktshim というアダプタがありました。

ただ、dockerやrktなど、ランタイムごとにアダプタを用意するとなるとめんどくさいので、[OCI](https://www.opencontainers.org/)という標準に準拠したランタイムであれば何でも使える汎用的なアダプタが欲しいということで開発されているのが、この `cri-o`です。
また、開発の背景には、dockerの開発速度が早い中、それに依存するkubernetesが不安定になっており、より小さく単純な、専用のランタイムを用意することでkubernetesの安定化に繋げたいという意図があったようです。

READMEによると、`cri-o`のスコープは以下となっています。

* 既存のDockerイメージを含む、複数のイメージフォーマットをサポート
* イメージの検証を含む複数のイメージダウンロード方法をサポート
* コンテナイメージの管理（イメージレイヤー、オーバーレイファイルシステムその他）
* コンテナプロセスのライフサイクルを管理
* CRIに準拠するために必要なモニタリングとロギング
* CRIに準拠するために必要なリソースの隔離

そして、以下はスコープ外とされているリストです。

* イメージをビルドしたり署名したり、さまざまなレジストリにpushしたりする機能
* `cri-o`と直接やり取りするcliツール群。このプロジェクトでビルドされるツール群はcri-oをテストするために作られているもので、後方互換性は保証されていない。

さて、先日、その`cri-o`がstableリリースされました。

https://medium.com/cri-o/cri-o-1-0-is-here-d06b97b92a98

今回は、その`cri-o`に簡単に触れてみたいと思います。

## cri-oでコンテナを動かしてみる

cri-oのチュートリアルがあるので、これを参考に`cri-o`だけで`redis`のコンテナを動かしてみます。

https://github.com/kubernetes-incubator/cri-o/blob/master/tutorial.md

また、プロジェクトを見てみるとDockerfileが用意されています。
試すだけであれば、このDockerfileを使用してdockerで実行してみるのが簡単そうです。

### dockerイメージのビルド
`cri-o`プロジェクトをcloneし、以下でイメージをビルドします。
なお、以下はすべてMac上のDocker for Macで動かしています。

```
git clone https://github.com/kubernetes-incubator/cri-o.git
cd cri-o
docker build -t crio_dev:master
```

または、イメージをビルドするためのターゲットが`Makefile`に定義されているので、`make`を使ってもビルドできます。

```
make crioimage
```

### dockerイメージの実行
以下でコンテナを実行し、中に入ります。

```
docker run -it --rm --privileged crio_dev:master /bin/bash
```

### cri-oのビルド

cri-oは、makeでビルド、インストールできます。

```
make install.tools
make
make install
```

また、cri-oは`/etc/crio/crio.conf`に設定ファイルを配置します。以下コマンドで、設定ファイルをインストールできます。

```
make install.config
```

### 設定ファイルの修正
インストールしたままの `crio.conf`だとDocker環境でうまく動かないので、以下のように修正します(vimが入ってないので、私はapt-getで入れてしまいました)。

```
15 # storage_driver = ""
16 storage_driver = "vfs"

...

46 # runtime = "/usr/local/bin/runc"
47 runtime = "/usr/bin/runc"
```

### CNIの設定
チュートリアルにあるとおり、以下でCNIの設定を行います。

```
mkdir -p /etc/cni/net.d

sh -c 'cat >/etc/cni/net.d/10-mynet.conf <<-EOF
{
    "cniVersion": "0.2.0",
    "name": "mynet",
    "type": "bridge",
    "bridge": "cni0",
    "isGateway": true,
    "ipMasq": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.88.0.0/16",
        "routes": [
            { "dst": "0.0.0.0/0"  }
        ]
    }
}
EOF'

sh -c 'cat >/etc/cni/net.d/99-loopback.conf <<-EOF
{
    "cniVersion": "0.2.0",
    "type": "loopback"
}
EOF'
```

### cri-oデーモンの実行

バックグラウンドで`cri-o`を起動します。

```
crio --log-level debug &
```

`crioctl` コマンドで、cri-oが起動していることを確認できます。

```
crioctl runtimeversion
# VersionResponse: Version: 0.1.0, RuntimeName: cri-o, RuntimeVersion: 1.8.0-dev, RuntimeApiVersion: v1alpha1
```

### Pod sandboxの作成

Pod sandboxを作成します。
Podの概念は`kubernetes`に触れている方は馴染み深いと思います。Pod sandboxは複数のコンテナをまとめて格納できる隔離環境を示すCRIの用語です。(「Pod and container lifecycle management
」参照 http://blog.kubernetes.io/2016/12/container-runtime-interface-cri-in-kubernetes.html)

```
POD_ID=$(crioctl pod run --config test/testdata/sandbox_config.json)
```

以下で作成したsandboxの状態を確認できます。

```
crioctl pod status --id $POD_ID
```

### Pod内でコンテナを起動する

上記で作成したPod sandbox内にコンテナを作成します。

```
# コンテナイメージのダウンロード
crioctl image pull redis:alpine

# イメージからコンテナをpod内に作成する
CONTAINER_ID=$(crioctl ctr create --pod $POD_ID --config test/testdata/container_redis.json)
```

作成できたら、以下コマンドでコンテナを立ち上げましょう。

```
crioctl ctr start --id $CONTAINER_ID

# コンテナの状態を確認
crioctl ctr status --id $CONTAINER_ID
```

これで、作成したPod内でRedisコンテナが起動しました。


### コンテナの動作確認

telnetで起動したコンテナのredisにつないでみます。

`crioctl pod status --id $POD_ID`で確認できたPodのIPを指定して以下コマンドを実行します。

```
# telnetのインストール
apt-get install telnet

# podのipを指定して、redisコンテナに接続
telnet 10.88.0.2 6379
```

redisに繋いだら、redisのINFOやMONITORコマンドを実行してみましょう。

```
Trying 10.88.0.2...
Connected to 10.88.0.2.
Escape character is '^]'.

INFO
# Server
redis_version:4.0.2
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:7f502971648182f2
....


MONITOR
+OK
```

このように、pod内に起動したredisコンテナが動作していることを確認できました。

## まとめ

この記事では、簡単に`cri-o`を紹介した上で、`cri-o`を使用してコンテナを起動するまでの動作を見てみました。

`cri-o`のようなシンプルなCRI実装でこのように動作を確認してみると、`kubernetes`がどのようにPodやコンテナを扱っているか内側をのぞいてみることができて大変面白いですね。