コンテナ低位ランタイムの解説&パフォーマンス測定


[Docker Advent Calendar](https://qiita.com/advent-calendar/2018/docker) 2018 3日目です。
こんにちは。入社一年目でインフラ周り触ってるエンジニアです。
至らぬところがあると思いますが、よろしくおねがいします。

## TL;DR
* 低位ランタイムはアプリケーションの隔離を行う
* トレンドは隔離度を上げること
* コンテナライフサイクルのパフォーマンスは `runc` ≒ `gVisor(runsc)` > `kata-runtime`
* コンテナ上のアプリケーションのパフォーマンスは `runc` > `gVisor(runsc)` ≒ `kata-runtime`
* `gVisor(runsc)`は現時点ではまだ不安定

## コンテナ技術とは
軽量でイミュータブルに隔離したアプリケーションを動作させることができる仕組みです
有名どころだと、Docker、Kubernetesがあります

隔離したアプリケーションを動かす仕組みをランタイムと呼びますが、これには高位ランタイムと低位ランタイムがあります

高位ランタイムは主に低位ランタイムの操作、ネットワーキング、イメージの管理などを担います

本記事では低位ランタイムを取り扱います

## 低位ランタイムとは
実際にアプリケーションの隔離を行うランタイムです
NamespaceやCgroupでの隔離が有名ですが、最近ではvmで隔離する低位ランタイムも登場しています
低位ランタイムの仕様には[OCI Runtime Specification](https://github.com/opencontainers/runtime-spec)と[OCI Runtime Command Line Interface](https://github.com/opencontainers/runtime-tools/blob/master/docs/command-line-interface.md)があります

話がそれますが、Amazonが先日発表した超軽量のvmを使う[Firecracker](https://github.com/firecracker-microvm/firecracker)は現状上記仕様を満たしていないので、今回の記事では測定しません（できない）
ただ、上記仕様に準拠する声明を出しているのでDockerやKubernetesの低位ランタイムとしてそのうち使えるようになるはずです

### OCI Runtime Specification
主にコンテナのライフサイクル、構成ファイル（config.json）を定義しています
NamespaceやCgroup、セキュリティ関連の仕様を定義しています
コンテナのライフサイクルは[仕様](https://github.com/opencontainers/runtime-spec/blob/master/runtime.md#lifecycle)にもあるように`Create` → `Start` → `Kill` → `Delete`のように進んでいきます
Killは能動的な操作で、自然にアプリケーションが終了した場合は呼ばれません
![lifecycle.png](https://qiita-image-store.s3.amazonaws.com/0/132463/3e8c26cc-deab-3c97-8ab0-7c776b077731.png)
構成ファイルにはほぼ全ての情報が含まれている（コンテナの生死の判定など除く）ので困ったときに見ると問題を解決できるかもしれません

### OCI Runtime Command Line Interface
低位ランタイムのCLIを定義しています
定義されているコマンドは`create`、`start`、`state`、`kill`、`delete`の5個です
普段使っている人が多い、`run`、`exec`は意外にも定義されていません


### 低位ランタイム紹介

有名なものを紹介

#### runc
言わずと知れた低位ランタイム
元はDocker社、現在はOCIの持ち物
NamespaceやCgroupでアプリケーションの隔離を行います。現状一番使われている低位ランタイム

#### gVisor
開発：Google社
危険なシステムコールをgVisorがフックし、ホストカーネルに渡す低位ランタイム
MeltdownやSpectreなどの脆弱性があるとruncでは処理がとられてしまうが、gVisorでは隔離度が高いため攻撃が成立しません

#### kata-runtime
開発：Openstack Foundation
超軽量のカーネルを動作させることで隔離度を上げる低位ランタイム
VMの隔離性を持ちつつ、コンテナの軽量さを併せ持っています

## パフォーマンス測定

続いてパフォーマンスの測定を行います

以下2つの視点からパフォーマンスを測定します
* コンテナライフサイクル
* コンテナ上のアプリケーション

### 測定環境

訳あってVM環境で動かしているので、Baremetal環境でやると違う値が出ると思いますので、参考程度にどうぞ

* CentOS7.5 x86_64
* Kernel Version: 4.19.0-1.el7.elrepo.x86_64
* vCPU: 4
* Memory: 8GiB

### コンテナライフサイクルのパフォーマンス測定
特徴的な値を拾っていきます

* 基本のプログラム（hello-world）

hello-worldを実行した際のグラフが以下です
runcとgVisorはほぼ同じ値ですが、kata-runtimeに関してはvmを起動する処理がどうしても入ってしまうので、ほかの低位ランタイムよりも起動に時間がかかっています
![hello-world.png](https://qiita-image-store.s3.amazonaws.com/0/132463/4321c1f8-0e90-dcb1-f964-1ad595524f33.png)

* 低負荷なプログラム（google/pause）

google/pauseに関しても同様にkata-runtimeだけが実行時間が長いです
![google-pause.png](https://qiita-image-store.s3.amazonaws.com/0/132463/03ec8a55-7bb1-2cda-b962-f356ccca8eac.png)


* 高負荷なプログラム（rogeriopradoj/yes）

一方で高負荷なプログラムではkata-runtimeの方が実行時間が短かったです
PID1特別問題だと思い、dumb-initを試しましたが、結果は変わらずkata-runtimeが一番実行時間が短かったです
ホストカーネルの1プロセスとして動作するrunc、gVisorはハング時にシグナルを受け取りにくい？
知っている方いたら教えてください
![yes.png](https://qiita-image-store.s3.amazonaws.com/0/132463/d6ce849c-37f3-d975-9535-1b5b2656cb48.png)

### コンテナ上のアプリケーションのパフォーマンス測定
前章までは低位ランタイムの各ライフサイクルでかかる時間を測定しました
しかし、システムコールのエミュレーションなどがアプリケーションに及ぼす影響は調べられていないです
そこで、本章では低位ランタイム上でunixbenchを動かすことで網羅的にパフォーマンスを測定します

1cpuと4cpuで試します

#### runc

インデックスは1cpuで`672.6`、4cpuで`1737.8`
![unix-runc.png](https://qiita-image-store.s3.amazonaws.com/0/132463/0a4d9079-ee6d-13dd-d58c-bdb42e2e33ed.png)


#### gVisor

4cpuではクラッシュしてしまったので、1cpuの結果のみ
インデックスは`70`
（さすがにこんなに低くならないと思うので、設定ミス？）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132463/36657b40-7ebf-62d0-1390-41c9f228fd6f.png)

#### kata-runtime

インデックスは1cpuで`124.2`、4cpuで`181`
![unix-kata.png](https://qiita-image-store.s3.amazonaws.com/0/132463/aa312009-dc75-c183-632a-86de35c90c14.png)

## まとめ

低位ランタイムはいろいろな実装が出てきているが、環境を選ばないと著しい性能劣化が起こる可能性があります
gVisorのようにクラッシュする場面も出てくるかもしれないので、ご利用は計画的に