ベアメタル環境とDockerコンテナ環境の性能比較
コンテナ環境とベアメタル環境の差異
前回は、Docker向け軽量Linux OSの主要3製品の比較を行った。Dockerを利用した環境の構築は、構築済みコンテナなどの利用により、比較的容易に行える。これは便利ではあるが、一方でコンテナ型仮想化環境には既存のベアメタル環境との差異がある。
コンテナ型の仮想化は軽量でリソース消費量が少ないが、コンテナ型仮想化環境にも管理レイヤは存在し、その上でコンテナが稼働している以上、どうしてもベアメタル環境に比べて性能劣化が発生することが予測される(図1)。
今回は、同一スペックおよび同一プロダクトを利用し、構築したDocker環境とベアメタル環境上で負荷テストを実施することで、両者の性能差を比較検証する。処理性能やリソース負荷状況などの観点で比較し、その差異を表やグラフにまとめているので、ご一読いただきたい。
まずは環境をご紹介する。今回は、図2のようなテスト環境を用意した。
検証環境
ベアメタル環境、Docker環境の検証の性能を同一にするため、1台の物理サーバを使用し、起動(ブート)するHDDを選択することで両環境を切り替えるようにした。
用意したサーバのスペックを、表1に示した。
CPU | Intel(R) Core(TM) i7 870 @ 2.93GHz |
---|---|
Memory | 4GB |
Swap | 8GB |
HDD1 | Western Digital 1TB (SATA3 7200rpm) |
HDD2 | Western Digital 1TB (SATA3 7200rpm) |
NIC1 | インターネットへ接続可能なネットワーク |
NIC2 | 負荷発生端末(JMeter)とのみ接続可能なクローズドなネットワーク |
ベアメタル環境、Docker環境の構成も、合わせて表2に示した。
環境 | ベアメタル環境 | Docker環境 |
---|---|---|
OS | CentOS 6.6(64bit) | CentOS 6.6(64bit) |
Docker | - | Docker 1.4.1 |
アプリケーション | Redmine 3.0.0 | Redmine 3.0.0(コンテナで配置) |
RDB | MySQL 5.5.41 | MySQL 5.5.41(コンテナで配置) |
この環境に負荷発生端末上のJMeterから検証環境上のRedmineへのアクセス負荷を発生させ、処理性能と負荷状況を測定した。
テストシナリオ
今回は以下の条件で負荷テストを実施した。
JMeter上から実行したオペレーションは以下となる。
1.参照オペレーション
- Redmineのログイン画面へアクセス
- ユーザ名とパスワードを用いてRedmineにログイン
本オペレーションでは、原則、DBやディスクには参照の処理が行われる。CPU、ネットワーク、ディスクに平均的に負荷を発生させることを目的としている。
2.更新オペレーション
- Redmineのプロジェクトに対して新しいチケットを1件作成
- 表題と内容を記載し登録
本オペレーションでは、チケットの登録が行われるため、ディスクの更新が発生する。データ量は小さいがDB、ディスクへの負荷を発生させることを目的としている。
JMeter上から発生させる負荷量は以下となる。
- 低負荷
参照、更新のオペレーションを100件/60秒の間隔で実行する
テスト時間は60秒間で行い、それを複数回繰り返して平均を取得する - 高負荷
参照、更新のオペレーションを1000件/60秒の間隔で実行する
テスト時間は60秒間で行い、それを複数回繰り返して平均を取得する
検証
上記の負荷テストを実施し、測定された結果は以下のとおりだ。
低負荷時の測定結果
まず低負荷時の状態として、参照、更新のオペレーションを100件/60秒の間隔で実行した結果は以下となる。
作業 | 1分間のオペレーション | 平均 (ミリ秒) | 中央値 (ミリ秒) | 90%Line (ミリ秒) | 最小値 (ミリ秒) | 最大値 (ミリ秒) | Error% | スループット | KB/sec |
---|---|---|---|---|---|---|---|---|---|
アクセス | 100 | 10 | 10 | 11 | 8 | 14 | 0.00% | 1.7/sec | 7.2 |
ログイン実行 | 100 | 93 | 91 | 107 | 67 | 200 | 0.00% | 1.7/sec | 13.7 |
Projectへ遷移 | 100 | 18 | 18 | 20 | 16 | 28 | 0.00% | 1.7/sec | 8.5 |
チケット作成 | 100 | 20 | 19 | 23 | 17 | 32 | 0.00% | 1.7/sec | 9.1 |
平均 | 100 | 35.25 | 34.5 | 40.25 | 27 | 68.5 | 0.00% | 1.7/sec | 9.63 |
作業 | 1分間のオペレーション | 平均 (ミリ秒) | 中央値 (ミリ秒) | 90%Line (ミリ秒) | Min (ミリ秒) | Max (ミリ秒) | Error% | Throughput | KB/sec |
---|---|---|---|---|---|---|---|---|---|
アクセス | 100 | 11 | 11 | 12 | 10 | 20 | 0.00% | 1.7/sec | 7.0 |
ログイン実行 | 100 | 97 | 92 | 110 | 76 | 199 | 0.00% | 1.7/sec | 13.1 |
Projectへ遷移 | 100 | 22 | 22 | 24 | 18 | 34 | 0.00% | 1.7/sec | 8.4 |
チケット作成 | 100 | 18 | 19 | 20 | 18 | 28 | 0.00% | 1.7/sec | 9.0 |
平均 | 100 | 37 | 36 | 41.5 | 30.5 | 70.25 | 0.00% | 1.7/sec | 9.38 |
表3、表4で特に注目すべきは「90%Line」と「KB/sec」の箇所だ。「90%Line」は、発行したリクエストの90%が、何ミリ秒で応答するかを示す値であり、実際にユーザが体験するレスポンスタイムに最も近い。また「KB/sec」は、サーバからJMeter端末への通信速度を表しており、1秒あたりの転送量が何KB(キロバイト)だったのかを表している。両者の結果をグラフ化したものが、図4、5だ。
2つのグラフから明らかなように、ベアメタル環境、Docker環境の差異はそれほど大きくなく、軽い処理であれば、性能面での影響は少ないと考えられる。
高負荷時の測定結果
続いて、高負荷時の状態として参照、更新のオペレーションを1000件/60秒の間隔で実行した結果を、表5、表6に示す。
作業 | 1分間のオペレーション | 平均 (ミリ秒) | 中央値 (ミリ秒) | 90%Line (ミリ秒) | 最小値 (ミリ秒) | 最大値 (ミリ秒) | Error% | スループット | KB/sec |
---|---|---|---|---|---|---|---|---|---|
アクセス | 1000 | 15 | 10 | 20 | 8 | 293 | 0.00% | 16.6/sec | 70.9 |
ログイン実行 | 1000 | 119 | 94 | 191 | 75 | 675 | 0.00% | 16.6/sec | 135.2 |
Projectへ遷移 | 1000 | 28 | 19 | 44 | 15 | 404 | 0.00% | 16.5/sec | 83.5 |
チケット作成 | 1000 | 29 | 20 | 41 | 17 | 438 | 0.00% | 16.5/sec | 89.4 |
平均 | 1000 | 47.75 | 35.75 | 74 | 28.75 | 452.5 | 0.00% | 16.55/sec | 94.75 |
作業 | 1分間のオペレーション | 平均 (ミリ秒) | 中央値 (ミリ秒) | 90%Line (ミリ秒) | 最小値 (ミリ秒) | 最大値 (ミリ秒) | Error% | スループット | KB/sec |
---|---|---|---|---|---|---|---|---|---|
アクセス | 1000 | 580 | 740 | 999 | 1 | 1316 | 26.40% | 16.4/sec | 51.6 |
ログイン実行 | 1000 | 1193 | 1562 | 1988 | 0 | 2366 | 31.50% | 16.1/sec | 90.4 |
Projectへ遷移 | 1000 | 1065 | 1450 | 1883 | 1 | 2295 | 31.60% | 16.2/sec | 56.8 |
チケット作成 | 1000 | 1031 | 1450 | 1875 | 1 | 2298 | 34.40% | 16.2/sec | 59.4 |
平均 | 1000 | 967.25 | 1300.5 | 1686.25 | 0.75 | 2068.75 | 30.98% | 16.23/sec | 64.55 |
高負荷状態では、大きく差が生じる結果となった。レスポンスタイムの指標である「90%Line」で見ると、Docker環境はベアメタル環境と比較してなんと20倍以上の劣化が起きていた。
さらにレスポンスのタイムアウトの発生率(Error%)も、ベアメタル環境が0%であったのに対し、Docker環境は平均30%とかなり高い値を示した。これは、実際に運用していた場合、60秒間にアクセスした1000人のうち300人にアクセスエラーを返しているということになる。
1000件/60秒というアクセス頻度は、通常のWebサーバでCore i7程度のCPU性能があればそれほど高負荷とは言えない状況である。この結果からは、Docker環境を本番環境として利用することは厳しく、何らかの対策が必要だと考えられる。
高負荷時のデータも同様にグラフにしたので、確認していただきたい。
低負荷の状態では、ベアメタルと大きな性能差は発生しないDockerコンテナ環境であるが、高負荷状態となると、著しく性能劣化することが見て取れる。
Diskの負荷を検証する
ここまではJMeterを利用して主にレスポンスやネットワークについて見てきたが、Diskの状況についても測定を行っている。今回は、測定にMuninを用意してDisk負荷状況を監視した。
ベアメタル環境でのDisk負荷状況
図8の青枠で囲まれている部分が低負荷のテストシナリオを、赤枠で囲まれているところが高負荷のテストシナリオを実施した部分だ。
どちらも負荷をかけた時にはRead I/O Wait timeが跳ね上がっている、筆者はこのテストを実施するまではRead I/O Wait timeが性能のボトルネックになり得ると予想していたが、実際にはベアメタル環境であれば1000アクセス+1000書き込み/60秒程度までなら、そこまでの劣化は発生しないようだ。
Docker環境でのDisk負荷状況
図9はDocker環境での結果で、同じく青枠が低負荷、赤枠が高負荷のテストシナリオを実施した結果だ。図8とは縦軸のスケールが異なる点に注意しよう。
測定結果のグラフを見ると、ベアメタルより負荷が少ない状況となっている。その一方でHTTPレスポンスはリクエストの30%がエラーとなっており、「422:Unprocessable Entity」や「502:Bad Gateway」などが発生している。
今回のDiskの負荷状況の測定だが、Dockerを稼働させたベースのCentOS上で測定している。そのため、Disk I/Oの負荷は標準設定時に利用されるDockerコンテナのファイルシステム(CentOS6.6はdevice-mapper)部分がボトルネックとなるであろうと予想したが、実際にはベースOS側には波及することがなかったため図9のように負荷が少なかったと推察される。
まとめ
今回の検証結果から、低負荷時やピーク負荷が予想できる場合であれば、Dockerコンテナを利用したシステムでもベアメタル環境と遜色ないパフォーマンスが発揮されることがわかった。しかし高負荷時になると、やはりベアメタル環境の安定性に分がある。とはいえ、Docker環境でも対策がないわけではない。挙げられる対策といえば、IO負荷が高いファイル等はコンテナの外のファイルを使う手法や、負荷分散のために分散システムとする手法、障害対応力を上げるためのコンテナのクラスタリングなど、できることはたくさんある。また、このようなDockerの弱点をカバーするために、各社こぞってDockerのツールを作成、改善しているところだ。
次回以降では、今回の性能劣化の原因と推察されるDockerのファイルシステムについて検証を行い、原因の分析を行うとともに、性能改善の対策と効果についても検証し、理解を深めていきたい。
追記
CentOS 6.6環境でのDockerがデフォルトで利用するファイルシステムを「AUFS」と記載しておりましたが「device-mapper」の誤りでした。ご指摘くださった皆様ありがとうございました。お詫びして訂正いたします。
次々回の連載では、CentOS 6系でDockerコンテナを標準で利用されるdevice-mapperで発生する問題点とその対策について公開を予定しております。
<編集部より>記事内容に誤りがあったため修正しました。(2015.03.30)