Dockerコンテナのパフォーマンス劣化とチューニング
はじめに
本連載の第2回『ベアメタル環境とDockerコンテナ環境の性能比較』で実施したコンテナ環境とベアメタル環境との性能比較では、コンテナ環境は高負荷時に挙動が不安定になるという結果となった。本記事には皆様からTwitterなどのソーシャルメディアを通じて、検証についてのご意見や問題点の解消方法について貴重なご指摘をいただいている。今回はこれらを参考に、発生したパフォーマンス劣化の原因の調査と改善策を模索していきたい。
まず筆者が目をつけたのは、Dockerが利用しているコピーオンライトデバイスの部分がパフォーマンス劣化の原因となっているのではないだろうか? という点である。実際にテストを実施しながら検証していきたい。
ストレージドライバとは
ストレージドライバは、物理サーバ上ではストレージ(HDD)コントローラのチップセットドライバだが、Dockerではコンテナ記憶域に使用されるコピーオンライトのファイルシステムの種類を指す用語だ。
Dockerのv1.5で利用出来るストレージドライバは、以下の5種類である。
ファイルシステム | 概要・特徴 |
---|---|
aufs | Another UnionFS。overlavfs(unionfs)の信頼性や性能を向上させるために開発されたファイルシステム |
btrfs | B-tree file system。Oracleが開発したコピーオンライトのファイルシステム。耐障害性や修復機能などを持つことを目的に開発されている |
device-mapper | 仮想的なブロックデバイスを作成し、それをファイルシステムとして利用する。Linux LVMも本技術を利用し、複数の物理HDDを1つの仮想ディスクに結合している |
overlayfs | unionfsとも呼ばれる。Linuxのunion mountの機能を利用したファイルシステムで、LiveCDなどで起動したOS上にマウントすることで書き込みも可能な状態を実現する |
vfs | Virtual File System。通常のLinuxファイルシステムをマウントして利用する。コピーオンライトはサポートしていない。 |
OSやDockerを標準設定でインストールした場合、筆者の環境ではCentOS 6.6やCentOS 7ではdevice-mapperが選択され、一方Ubuntu 14.04ではaufsが選択された。これはDockerがインストール時に、ホストOSで利用出来るストレージドライバを検出し、適合したストレージドライバは優先順位をつけて利用される仕組みとなっているためだ。
稼働中のDockerが利用しているストレージドライバは、以下のコマンドで確認出来る。
root@centos7:~ # docker info Containers: 0 Images: 0 Storage Driver: devicemapper Pool Name: docker-253:1-33556714-pool Pool Blocksize: 65.54 kB Backing Filesystem: xfs Data file: /dev/loop0 Metadata file: /dev/loop1 Data Space Used: 0 B Data Space Total: 107.4 GB Data Space Available: 28.4 GB Metadata Space Used: 577.5 kB Metadata Space Total: 2.147 GB Metadata Space Available: 2.147 GB Udev Sync Supported: true Data loop file: /var/lib/docker/devicemapper/devicemapper/data Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata Library Version: 1.02.93-RHEL7 (2015-01-28) Execution Driver: native-0.2 Kernel Version: 3.10.0-229.1.2.el7.x86_64 Operating System: CentOS Linux 7 (Core) CPUs: 1 Total Memory: 1.797 GiB Name: tissrv152 ID: 3NO4:WORM:B4X7:MHAT:RWFT:72LT:RIEU:M3CF:FYJB:NMLM:J4YH:KQAH
== ホスト環境準備今回の検証では「CentOS Linux release 7.1.1503」と「Docker version 1.5.0-dev」を利用する。ホストサーバのスペックは、以下の表を参考にして欲しい。
CPU | Intel(R) Core(TM) i7 870 @ 2.93GHz |
---|---|
コア数 | 8コア |
Memory | 4GB |
Swap | 4GB |
HDD1 | Western Digital 1TB(SATA3 7200rpm) |
HDD2 | Western Digital 1TB(SATA3 7200rpm) |
HDD3 | Western Digital 1TB(SATA3 7200rpm) |
NIC1 | 負荷発生端末(JMeter)とのみ接続可能なクローズドなネットワーク |
NIC2 | インターネットへ接続可能なネットワーク |
今回の検証環境は、連載第2回のものとは異なっている。変更点は以下の通りとなる。
- ベースOSをCentOS 6.6からCentOS 7.1に変更
- DockerをVer 1.4.1からVer 1.5.0に更新
変更の理由は、これから利用をしていくCentOS 7系で評価を行うべきとのご指摘に対応をしたものである。さらに2015年4月20日にepelリポジトリのDockerがVer 1.5.0に更新された。Ver 1.5.0はLinuxカーネルのVer 3.0系を推奨としており、システムの安定性も考慮し、上記の変更を行っている。
JMeterサーバは連載第2回から変更は行っていないため、説明は割愛する。
Dockerコンテナ準備
今回の検証も、連載第2回と同じくRedmine+MySQLを処理性能測定用のプロダクトとして利用する。ただし今回は、読者の方にも同じ検証が行えるように、DockerHubにsameersbn氏が公開しているRedmineコンテナ(https://github.com/sameersbn/docker-redmine/tree/2.5-stable)を利用することにした。
このコンテナは最新版ではないのだが、前回行った物理環境がApacheで動いているため、コンテナ環境でもApache上で動くRedmineコンテナを選出したい経緯がある(最新版はnginxで稼働する構成となる)。既存で公開されているDocker imageを使用しているため、最新のRedmineとMySQLを利用していない点はご容赦いただきたい。
利用コンテナ | バージョン |
---|---|
sameersbn/redmine:2.5.0 | 2.5.0 stable |
テスト項目
今回は連載第2回で行ったテストを、様々なチューニングを行ったDockerコンテナに対して実施し、性能改善の有無を検証する。ただし、テスト回数を1000回/60秒から2000回/60秒へと増やしている。これは、CentOS 6.6+Docker 1.4.1の環境上では1000回/60秒の条件で多発したエラーが、今回のCentOS 7.1+Docker 1.5.0環境では全く発生しなかったためだ。ベースOS、Dockerともにバージョンアップが行われたことで、大幅に性能が改善していると考えられる。ただ、2000回/60秒に増やしたところ、やはりベアメタル環境では発生しなかったレスポンス悪化とエラーが再発するようになったため、負荷を上げる形で検証を行うこととした。
JMeter上から実行したオペレーションは以下となる。
1.参照オペレーション
- Redmineのログイン画面へアクセス
- ユーザ名とパスワードを用いてRedmineにログイン
本オペレーションでは、原則、DBやディスクには参照の処理が行われる。CPU、ネットワーク、ディスクに平均的に負荷を発生させることを目的としている。
2.更新オペレーション
- Redmineのプロジェクトに対して新しいチケットを1件作成
- 表題と内容を記載し登録
本オペレーションでは、チケットの登録が行われるため、ディスクの更新が発生する。データ量は小さいがDB、ディスクへの負荷を発生させることを目的としている。
JMeter上から発生させる負荷量は以下となる。
高負荷
- 参照、更新のオペレーションを2000件/60秒の間隔で実行する
- テスト時間は60秒間で行い、それを3回繰り返して平均を取得する
- 負荷のスコアはJMeter上のスコアを利用する。
テスト1:基準となるデータの測定
前回の検証で利用したコンテナはRedmineとMySQLがそれぞれのコンテナに分離していたが、今回のコンテナは1個のコンテナにRedmineとMySQLが統合さている。まず性能評価の基準を作成するために、ダウンロードしたコンテナイメージを変更することなく性能測定を実施する。コンテナが統合されていることにより、コンテナ化のネットワークの負荷が小さくなる点で差異が発生する可能性が考えられる。そのため後述でコンテナを分離し、ネットワークを介したテストも実施するのでご安心いただきたい。
まずはコンテナを立ち上げる。
[root@host_server ~]# docker run --name=redmine -p 8080:80 -d sameersbn/redmine:2.5.0
ここでは、コンテナ内に対してはチューニング等の変更は加えていない。テスト開始の準備として、Redmineが立ち上がりWebブラウザでアクセス出来るようになったら、デフォルト設定をロードし、テストするためのプロジェクトを一つ作成する。
作業 | 1分間のオペレーション | 平均(ミリ秒) | 中央値(ミリ秒) | 90%Line(ミリ秒) | 最小値(ミリ秒) | 最大値(ミリ秒) | Error% | スループット | KB/sec |
---|---|---|---|---|---|---|---|---|---|
アクセス | 2000 | 15490 | 6258 | 62024 | 11 | 118298 | 10.00 | 14.4 | 63.5 |
ログイン実行 | 2000 | 1731 | 1381 | 3676 | 8 | 12528 | 10.00 | 14.4 | 85.2 |
Projectへ遷移 | 2000 | 15321 | 7070 | 37539 | 21 | 106873 | 8.62 | 13.9 | 72.4 |
チケット作成 | 2000 | 1655 | 1308 | 3578 | 9 | 13001 | 8.62 | 13.9 | 71.3 |
平均 | 2000 | 8549.25 | 4004.25 | 26704.25 | 12.25 | 62675 | 9.31 | 14.15 | 73.1 |
テスト中に取得できたiostatの最大値を以下に示す。この値は、テスト中iostatコマンドを用いて3秒おきに出力されるIOデータのWriteに注目し、最大値を抽出したものだ。この後のテストでも、同様のデータ抽出方法を採用している。
テスト1で取得できたiostatの最大値
avg-cpu: %user %nice %system %iowait %steal %idle 43.82 0.00 5.27 7.56 0.00 43.35 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util sda 0.33 1.67 3.00 140.67 102.67 3229.83 46.39 1.04 7.22 10.33 7.15 5.77 82.97 dm-1 0.00 0.00 3.33 110.67 102.67 3229.50 58.46 1.05 9.25 13.10 9.13 7.27 82.87 dm-2 0.00 0.00 1.67 276.33 26.67 3328.00 24.13 1.12 4.03 19.40 3.93 2.81 78.23 dm-3 0.00 0.00 1.33 264.33 5.33 3340.00 25.18 1.25 4.70 9.25 4.67 3.16 83.83
テスト1の結果を見ると、平均の待ち時間(90%Line)が約26秒とかなり遅く、エラー率も10%近い値を出している。またテストを行っている時のDiskIOをウォッチしていると、書き込みの最大値が3229kB/sとなっており、こちらも若干遅いように見える。
ログやコンテナ内のApacheの設定から察するに、「アクセス」の作業で投げられたHTTPリクエストが、ApacheのMaxClientsである150件を超え、60秒でタイムアウトしている様子だ。そのため何度検証を行っても待ち時間(90%Line)が60000ミリ秒を大きく超えることはなかった。この値を本記事の基準とし、次のテストから性能の改善を試みる。