低レベルランタイム、どう使い分ける?

はじめに

本稿では「コンテナを作る方法は Docker 標準 (containerd + runc) だけじゃないのは分かった、では何を使えば良いのか?」という疑問について、低レベルコンテナランタイムに焦点を絞り、答えを追い求めた結果を書き記します。

この記事は以下の内容で構成されています。

  • 主要なコンテナランタイムの特色のまとめ
  • 過去に行われた、コンテナランタイムの性能比較結果のまとめ
  • 独自に行った、runc と Nabla Containers のアプリケーション動作パフォーマンスの比較


コンテナランタイムの概要については以下を参照してください。
1. コンテナランタイムの概要 - このアプリ、 gVisor 上でも動きますか?

目次

  1. runc
  2. gVisor (runsc)
  3. Kata containers
  4. Nabla containers (runnc)
  5. パフォーマンス比較
    1. コンテナライフサイク
    2. コンテナアプリの動作
      1. Kata Containers vs. gVisor
      2. Nabla Containers は本当にパフォーマンスに優れる?
  6. まとめ

1. runc

概要
Docker がデフォルトで使用するランタイムで、現状最も使われているランタイムです。パフォーマンスは良好ですが、カーネルをホストと共有しているためコンテナ内で動作するアプリの脆弱性を突いた攻撃がホストにまで影響を及ぼす可能性があります。

導入方法
Docker インストール時に、同時にインストールされます。高レベルランタイムに代わりユーザ自身が低レベルランタイムに指示を出すことによって、コンテナのような隔離環境を作成することもできます。

補足 - 他ランタイムのアプローチ :
Docker デフォルトではない多くのランタイムは、カーネルをホストと共有せずより安全にコンテナを利用する方向で、低レベルランタイムのセキュリティ改善を図っています。カーネル非共有を実現する手法は複数存在し、ランタイムによって採用している手法が異なります。実現手法によってオーバーヘッドや Linux カーネルとの互換性の維持などが障壁になっていますが、成熟が進むにつれてこれらの問題は緩和されていくと思われます。

2. gVisor (runsc)

概要
google が開発するランタイムです。runsc とも呼ばれます。ユーザ空間で動作する特殊なカーネル、 user-space kernel によってホストとのカーネル共有から脱却し、より安全な環境でコンテナを実行します。

主な制約

  • 一部システムコールが未実装なため、動作しないアプリが存在する。
    (簡易的な判定方法: このアプリ、gVisor上でも動きますか?
  • システムコールを多用するアプリケーションで多くのオーバーヘッドが発生。

動作条件
Linux 4.14.77+

CentOS 8, または Ubuntu 18.04 未満のバージョンの OSだと Linux カーネルのアップデート作業が必要になります。

導入手順
(1) Install directly に記されるコマンドを実行するとバイナリを /usr/local/bin に設置します。
https://gvisor.dev/docs/user_guide/install/#install-directly

(2) /etc/docker/daemon.json を書き換えたのちに docker run のオプションを増やせば gVisor でコンテナを作れます。
https://gvisor.dev/docs/user_guide/quick_start/docker/

3. Kata containers

概要
Openstack Foundation で開発が進められているランタイムです。gVisor と同様、ホストとのカーネル分離を志向しています。実現手法に違いがあり、Kata containers の場合は仮想マシンを作成し、その中でコンテナを実行します。仮想マシン作成によって起動に時間がかかる課題を、起動済みの仮想マシンをキャッシュして使いまわすなどの工夫により、runc と同等の性能を実現しています。

主な制約

  • 仮想マシンをホストにすると仮想化がネストするため、パフォーマンスが大きく劣化する。
  • 適切な設定を行わなかった場合コンテナライフサイクル(作成、開始、停止、削除の4動作)のパフォーマンスが大きく低下し、コンテナの諸操作にかかる時間が他ランタイムの倍程度になる。

動作条件
インストール後、kata-runtime kata-check コマンドでハードウェア要件を満たしている環境下どうか確認可能です。
https://github.com/kata-containers/runtime/blob/master/README.md#hardware-requirements

導入手順
各クラウドプラットフォーム、およびディストリビューションへのインストール方法は以下です。
https://github.com/kata-containers/documentation/tree/master/install

4. Nabla containers (runnc)

概要
IBM が開発するランタイムです。runnc とも呼ばれます。Unikernel によってホストとのカーネル分離を実現しています。Unikernel とは、カーネルの機能の中からアプリケーションの動作に必要な最小限の機能だけを備え、アプリケーションと1:1で紐づくカーネルのことです。 ソースコードの絶対量が少ないため、バグが発生しにくく、コンテナライフサイクルも高速である特長があります。

主な制約

  • 通常の docker image を使用できない。Unikernel でコンパイルされたアプリケーションしか動作させることが出来ないため。nabla ベースのイメージを元にした専用イメージを作成する必要がある。
  • docker run -it や docker exec が使えないため、コンテナ作成後に任意のコマンドを実行させることが難しい。
  • ライブラリの動的ロードに未対応なため、Python モジュールが使用できない。
  • bind-Mount の対応が進行中。
  • /tmp 以外に書き込み不可。書き込み可能なファイルシステムを開発中。

導入手順
前提として Golang が必要。
以下のコマンドで Golang version 1.14.2 をインストール。

wget https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.14.2.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

Getting started with the go repo! を実行後、Build with container か Build locally のどちらかを実行するとインストールが完了。
https://github.com/nabla-containers/runnc#getting-started-with-the-go-repo

5. パフォーマンス比較

これまでの章では、主要なコンテナランタイムとそれぞれの特色について紹介しました。これらのコンテナランタイムの性能比較を、大きく分けて以下の2つの観点から行います。

  • コンテナライフサイクル
  • コンテナアプリの動作

5-1. コンテナライフサイクル

先達の方々によって、コンテナライフサイクルの性能比較が行われています。


Makoto Hasegawa 氏の JAPAN CONTAINER DAYS v18.04 での発表資料によると、コンテナの作成、実行、停止、削除の4動作にかかる各ランタイムの動作時間は以下の図の通りとなっています。

上記資料の 49 ページ によると、最もコンテナライフサイクルのパフォーマンスに優れるのは Nabla Container (runnc) です。イメージに関する制約が特に大きい他、まだ機能面で充実していない部分もありますが、その点さえ目を瞑れれば最良の選択となる可能性があります。

Kata containers のパフォーマンスが大きく劣るように見えますが、ここで Xu Wang 氏とFupan Li 氏による、 KubeCon + CloudNativeCon North America 2018 での発表資料中の、コンテナ起動時間の計測結果に着目します。

[出典] Xu Wang, Fupan Li (2018) Kata Containers and gVisor a Quantitative Comparison p.18

異なる設定が施された Kata Containers と、 gVisor, runc とのコンテナ起動時間の比較があります。 黒い線で示されている cli_en-factory が runc とほぼ横並びのパフォーマンスを記録しており、Kata Containers は適切な設定の下で運用されることで他のコンテナランタイムと遜色のないパフォーマンスを発揮できることが示唆されています。

5-2. コンテナアプリの動作

5-2-1. gVisor vs. Kata Containers

さて、Kata Containers の最大の弱点と思われていたコンテナ起動時間がここまで短縮されると、類似点の多い gVisor とKata Containers に甲乙付け難くなります。上記の Kata Containers and gVisor a Quantitative Comparison ではコンテナライフサイクルだけではなく、ディスクIOやネットワーク、実際にいくつかのアプリを動作させたケースなど多角的な比較を行っています。それぞれの項目においてどちらが優位な結果だったか、以下の表に書き起こしました。

gVisorKata Containers
メモリフットプリントの比較
CPU/メモリパフォーマンステスト
dd コマンドによるIOパフォーマンステスト〇(passthru fs)
dd コマンドによるIOパフォーマンステスト(9p, 128k)
dd コマンドによるIOパフォーマンステスト(9p, 4k)
ネットワークパフォーマンステスト
Nginx ベンチマーク
Redis ベンチマーク
Tensorflow ベンチマーク
[参考] Xu Wang, Fupan Li (2018) Kata Containers and gVisor a Quantitative Comparison p.15-26 〇: 優位

特に顕著に差が開いたのはネットワークパフォーマンスと Nginx ベンチマークです。22 ページの Networking Performance によると、host, runc, Kata Containers, gVisor のスループットは以下の通りです。

[出典] Xu Wang, Fupan Li (2018) Kata Containers and gVisor a Quantitative Comparison p.22

前提条件については不明ですが、host, runc,kata に比べ gVisor が大きく落ち込んでいます。また、23 ページの Real-life case: Nginx によると、Apache Bench による性能比較結果は以下の通りです。

[出典] Xu Wang, Fupan Li (2018) Kata Containers and gVisor a Quantitative Comparison p.23

runc と Kata が 3.45 秒前後でテストを完了している一方で、gVisor は 161.338 秒でテストを終えています。Nginx は I/O 多重化のためにシステムコールを利用しており、 gVisor の苦手とする部分であったことが一因と考えられます。

仮想化ネストを避け、適切に運用することで Kata Container は gVisor 以上のパフォーマンスが発揮できそうです。いずれのランタイムを使用するにせよ、ファイルシステムとの噛み合いやネットワークパフォーマンスについては一度見ておいた方が良いかもしれません。

5-2-2. Nabla Containers は本当にパフォーマンスに優れる?

コンテナライフサイクルの性能では頭一つ出た Nabla Containers でしたが、実用する上で気になるのはアプリケーションを動作させた場合まで含めたパフォーマンスです。Redis-benchmark と Apache benchmark を使って、runc とのパフォーマンス比較を独自に行いました。

環境

  • Docker ホスト
    • Hyper-V 仮想マシン
    • 仮想プロセッサ: 4 コア
    • メモリ: 8GB
    • OS: Ubuntu 18.04
  • Hyper-V 物理ホスト
    • CPU: Intel(R) Xeon(R) CPU E5-1620 0 @ 3.60GHz
    • メモリ: 32GB PC3-12800 ECC DDR3 SDRAM
    • ストレージ: SSD 240GB
  • コンテナランタイム
    • runc: version 1.0.0-rc10
    • runnc: v0.3
  • コンテナアプリ
    • Redis: 3.0.6
    • nginx: 1.8.0
  • ホスト – コンテナ間の接続方法
    • Docker ホストの80番ポートから、bridge ネットワークに接続されたコンテナの80番ポートへポートフォワーディングを設定。
Redis-benchmark

測定方法

  • Docker ホスト上で以下のコマンドを実行。
    redis-benchmark -p 80

デフォルトでは 100000 リクエスト、並列クライアント数 50 で測定が行われます。(参考:How fast is Redis?)

実行結果
runc が全項目においてパフォーマンスが上回る結果となりました。

Apache Benchmark

測定方法

  • Docker ホスト上で以下のコマンドを実行。
    ab -n 100000 -c 100 http://localhost/

実行結果
runc の方が時間当たりのリクエスト処理数が多く、優位なパフォーマンスを示す結果となりました。パフォーマンスに関する部分を太字で協調しています。

runcNabla Containers
Concurrency Level100100
Time taken for tests9.451 seconds10.876 seconds
Complete requests5000050000
Failed requests00
Write errors00
Total transferred22150000 bytes22150000 bytes
HTML transferred10600000 bytes10600000 bytes
Requests per second
(mean)
5290.38 #/sec 4597.36 #/sec
Time per request
(mean)
18.902 ms21.752 ms
Time per request
(mean, across all concurrent requests)
0.189 ms0.218 ms
Transfer rate2288.71 [Kbytes/sec] received1918.90 [Kbytes/sec] received

コンテナライフサイクルは Nabla Containers が優れる一方で、アプリケーションの動作では runc に軍配があがる結果となりました。Nabla Containers の運用上の制約の大きさのことを考慮すると、現在は runc の方が使いやすい時期にあると言えそうです。

6. まとめ

  • 基本は runc を使用。

  • セキュリティ重視な場合は以下を検討する。
    • Kata Containers: ホストが仮想マシンだと性能が劣化。
    • gVisor: 動作しないアプリがある。
    • Nabla Containers: 専用のイメージ作成が必要。
  • コンテナサイクルの性能重視な場合は以下を検討する。
    • Nabla Containers: 専用のイメージ作成が必要。

参考文献

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です