メモリ、CPU、GPU に対する実行時オプション
読む時間の目安: 5 分
デフォルトにおいてコンテナーには、リソースの利用に関して制限がありません。
したがってホストカーネルのスケジューラーが割り振るリソースを、その分だけ利用できます。
Docker には、コンテナーが利用するメモリや CPU をどれくらいにするかを制御する方法があります。
docker run
コマンドにおいて実行時フラグを設定する方法です。
この節では、どのようなときにそういった制約を行うのか、そして制約によってどのような影響があるのかを説明します。
制約に関する機能を利用するには、カーネルがケーパビリティーをサポートしている必要があります。
サポートしているかどうかは、docker info
コマンドを実行すればわかります。
利用しているカーネルにおいてケーパビリティーが無効になっていると、このコマンドの出力の最後に、以下のような出力が行われます。
WARNING: No swap limit support
これを有効にする方法は、各オペレーティングシステムのドキュメントを参照してください。 さらに詳しくはここで説明しています。
メモリ
メモリ不足時のリスクへの理解
コンテナーがホストマシンのメモリを必要以上に消費することは避けなければなりません。
Linux ホストにおいて、重要なシステム関数を実行するだけの十分なメモリがないことをカーネルが検出した場合、OOME
例外、つまりOut Of Memory Exception
がスローされます。
そしてプロセスの停止を行いメモリを開放します。
Docker であろうが重要なアプリケーションであろうが、あらゆるプロセスが強制的に停止させられます。
停止させてはならないプロセスが停止してしまうと、システム全体を停止させる事態にもなりかねません。
Docker においては、デーモンに対しての OOM プライオリティ調整機能があります。
これによりメモリ不足のリスクを軽減し Docker デーモンが他のプロセスに比べて停止しにくいようにしています。
この OOM プライオリティの調整機能は、コンテナーにはありません。
したがって Docker デーモンや他のシステムプロセスが停止することよりも、単一のコンテナーが停止する可能性の方が高いことになります。
これは Docker が採用する安全策なので、無理に回避する方法を取らないでください。
Docker デーモンに対して、手動で--oom-score-adj
に極端な負数を指定したり、コンテナーに対して--oom-kill-disable
を指定したりするようなことはやめてください。
Linux カーネルの OOM 管理については Out of Memory Management を参照してください。
OOME に起因する不安定リスクを回避するには、以下の対応があります。
- アプリケーションの本番環境への移行前に、アプリケーションがどのようにメモリを必要とするかをテストして理解すること。
- アプリケーションが、一定のリソースがあればホスト上だけで動作することを確認すること。
- これ以降に示すような、コンテナーのメモリ使用量を制限すること。
- Docker ホスト上のスワップの設定に十分注意すること。 スワップはメモリに比べて、処理速度が遅く性能が劣ります。 ただしシステムメモリの不足を補うためのバッファを利用します。
- コンテナーを サービス に変更する検討をすること。 そしてサービスレベルでの制約やノードラベルを利用することで、十分なメモリを有するホスト上でのみアプリケーションが動作するように検討すること。
コンテナーに対するメモリアクセスの制限
Docker では、ハードリミット(hard limit)により厳しくメモリを制限することができます。 コンテナーが利用するユーザーメモリ、あるいはシステムメモリを指定量以下に抑えます。 また緩い制限であるソフトリミット(soft limit)もあり、所定の条件下でない限りは、コンテナーが求めるメモリ使用を認めることができます。 所定の条件とはたとえば、ホスト上のメモリ不足やリソースコンフリクト発生をカーネルが検出したような場合です。 制限を指定するオプションを利用する場合には、単独で利用するか複数組み合わせて利用するかによって、その効果はさまざまです。
この制約オプションのほとんどは、正の整数を指定して、バイト、キロバイト、メガバイト、ギガバイトを表わすb
、k
、m
、g
を後ろにつけます。
オプション | 内容説明 |
---|---|
-m または--memory= |
コンテナーに割り当てるメモリ最大使用量。このオプションを利用する場合、指定できる最小値は6m (6 メガバイト) です。つまり最低でも 6 メガバイトに設定することが必要です。That is, you must set the value to at least 6 megabytes. |
--memory-swap * |
コンテナーにおいてディスクへのスワップを許容するメモリ容量。--memory-swap の詳細 を参照してください。 |
--memory-swappiness |
デフォルトにおいては、コンテナーによって利用されている匿名ページを一定の割合でスワップアウトすることができます。--memory-swappiness の設定では 0 から 100 までの設定を行って、その割合を調整します。--memory-swappiness の詳細 を参照してください。 |
--memory-reservation |
--memory に比べてソフトリミットとして小さな値を設定します。Docker がホストマシン上のコンフリクトやメモリ不足を検出したときに採用されます。この--memory-reservation を指定する際には、これが優先的に採用されるように --memory よりも小さな値を設定します。これはソフトリミットであり、この設定値を越えない保証はないからです。 |
--kernel-memory |
コンテナーに割り当てるカーネルメモリの最大使用量。指定できる最小値は4m です。カーネルメモリはスワップされるものではないため、カーネルメモリ不足となったコンテナーは、ホストマシンのリソースに影響を及ぼすことになります。これはホストマシンにとっても、また他のコンテナーにとっても副作用を引き起こします。--kernel-memory の詳細を参照してください。 |
--oom-kill-disable |
out-of-memory (OOM) エラーが発生すると、デフォルトでカーネルはコンテナー内のプロセスを停止させます。この動作を変更するには--oom-kill-disable オプションを指定します。これによってコンテナー上での OOM キラープロセスが無効になりますが、それは-m/--memory オプションを同時に指定しているコンテナーに限定されます。-m フラグを設定していなかった場合は、ホストがメモリ不足となり、ホストシステムの他のプロセスを停止させてメモリ確保を行うことになります。 |
cgroups とメモリに関する全般的な情報は、メモリリソースコントローラー に関するドキュメントを参照してください。
--memory-swap
の詳細
--memory-swap
は、--memory
が同時に設定されている場合のみ、その意味をなす修正フラグです。
スワップを利用すれば、コンテナーにおいて要求されたメモリが超過して、利用可能な RAM を使い果たしたとしても、それをディスクに書き出すことになります。
ただしメモリのスワップが頻発すると、アプリケーションの性能は劣化します。
これを設定したときの結果は複雑です。
-
--memory-swap
に正の整数が指定する場合は、--memory
と--memory-swap
を同時に指定する必要があります。--memory-swap
は、利用可能なメモリとスワップの総量を表わします。 また--memory
はスワップを含めず、利用されるメモリの総量を制御します。 したがってたとえば--memory="300m"
と--memory-swap="1g"
を指定した場合、そのコンテナーが利用できるのは 300m のメモリと 700m (1g - 300m
) のスワップとなります。 -
--memory-swap
を0
にすると、この設定は無視され、設定されていないものとして扱われます。 -
--memory-swap
に設定された値が--memory
と同じ値である場合で、かつ--memory
に正の整数が設定されている場合、コンテナーはスワップへアクセスしません。 コンテナーにおけるスワップ利用の防止 を参照してください。 -
--memory-swap
が設定されていない場合で、かつ--memory
が設定されている場合、コンテナーは--memory
に設定されている値をスワップ容量とします。 当然このときは、ホストコンテナーがスワップメモリを持つものとして設定されている場合に限ります。 たとえば--memory="300m"
と設定され、--memory-swap
が設定されていない場合、そのコンテナーはメモリとスワップの総量として 600m を利用することになります。 -
--memory-swap
を明示的に-1
とした場合、コンテナーが利用できるスワップは、ホストシステムでの利用可能なスワップ範囲内で無制限となります。 -
コンテナーの内部から
free
などのツールを実行すると、ホスト上で利用可能なスワップ容量が表示されます。 コンテナー内において利用可能な量を示すわけではありません。free
や同等のツールを利用する際には、出力結果からスワップ容量を判断できないことに注意してください。
コンテナーにおけるスワップ利用の防止
--memory
と--memory-swap
に同じ値を設定した場合、コンテナーがスワップを利用しないようになります。
--memory-swap
は、利用可能なメモリとスワップを合わせた総量を表わすものであり、--memory
は利用可能なメモリ使用量を意味するからです。
--memory-swappiness
の詳細
- 0 を指定すると、匿名ページのスワップを無効にします。
- 100 を指定すると、匿名ページのすべてをスワップ可能とします。
--memory-swappiness
を設定しなかった場合、デフォルトでは、ホストマシンからその値を受け継ぎます。
--kernel-memory
の詳細
カーネルメモリに対する制約は、コンテナーに割り当てられるメモリ全体に関わります。 以下の状況が考えられます。
- メモリ制限なし、カーネルメモリ制限なし: これがデフォルトの動作です。
- メモリ制限なし、カーネルメモリ制限あり: この設定が適当な状況とは、ホストマシン上の実際のメモリ容量よりも、cgroup が必要とするメモリの総量が上回っている場合です。 カーネルメモリは、ホストマシン上での利用可能量を越えないように、またそれ以上に必要としているコンテナーは、利用可能になるまで待つような設定とすることができます。
- メモリ制限あり、カーネルメモリ制限なし: メモリ全体が制限されますが、カーネルメモリは制限されません。
- メモリ制限あり、カーネルメモリ制限あり: ユーザーメモリとカーネルメモリをともに制限するのは、メモリに関する障害をデバッグする際に利用できます。 コンテナーがこのいずれかのメモリを予想以上に消費している場合、メモリ不足となっても、他のコンテナーやホストには影響を及ぼしません。 この設定において、カーネルメモリの制限値がユーザーメモリの制限値より小さい場合は、メモリ不足によってコンテナー内に OOM エラーが発生することになります。 カーネルメモリの制限値の方が大きい場合は、コンテナー内に OOM エラーが発生することはありません。
カーネルメモリに制限を設けた場合、ホストマシンはプロセスごとに「最高水位標」(high water mark)の統計をとります。
そこからどのプロセスが(今の場合、どのコンテナーが)過剰にメモリを消費しているかを知ることができます。
具体的にはホストマシン内の/proc/<PID>/status
を見ることで、プロセスごとの状況がわかります。
CPU
各コンテナーがホストマシンの CPU サイクルにアクセスすることは、デフォルトでは制限がありません。 ホストマシンの CPU サイクルにアクセスするコンテナーに制限を加える方法はいろいろとあります。 よく利用されるのは デフォルト CFS スケジューラー です。 リアルタイムスケジューラー を利用することもできます。
デフォルト CFS スケジューラーの設定
CFS は Linux 上の普通のプロセスに対して用いられる Linux カーネル CPU スケジューラーです。 コンテナーが利用する CPU リソースのアクセス量を設定するために、いくつかの実行時フラグが用意されています。 この設定を行うと、Docker はホストマシン上にあるコンテナーの cgroup 設定を修正します。
オプション | 内容説明 |
---|---|
--cpus=<値> |
コンテナーが CPU リソースをどれだけ利用可能かを指定します。たとえばホストマシンに CPU が 2 つあり--cpus="1.5" という設定を行った場合、コンテナーに対して CPU 最大 1.5 個分が保証されます。これは--cpu-period="100000" と--cpu-quota="150000" を設定することと同じです。 |
--cpu-period=<値> |
CFS スケジューラー間隔を指定します。これは--cpu-quota とともに指定されます。デフォルトは 100000 マイクロ秒(100 ミリ秒)です。たいていの場合、このデフォルト値を変更することはしません。たいていの場合、より便利なのは--cpus です。 |
--cpu-quota=<値> |
コンテナーに対して CFS クォータを設定します。--cpu-period ごとのマイクロ秒単位の時間であり、スロットリングされる前にこの時間に制限されます。有効しきい値として動作します。たいていの場合、より便利なのは--cpus です。 |
--cpuset-cpus |
コンテナーが利用する CPU またはコアを特定します。CPU が複数あれば、カンマ区切りあるいはハイフン区切りのリストで CPU の利用範囲を指定します。1 つめの CPU を 0 とします。指定例としては以下です。0-3 (1 つめから 4 つめまでの CPU を利用する場合)、1,3 (2 つめと 4 つめの CPU を利用する場合) |
--cpu-shares |
コンテナーへの配分を定めるもので、デフォルト値は 1024 です。本フラグを利用する場合は、デフォルト値より大きければ配分を増やし、小さければ減らします。そしてホストマシンの CPU サイクルへのアクセスを高比率、低比率で行います。これは CPU サイクルが制限されている場合に限って動作します。CPU サイクルが豊富に利用可能であるとき、すべてのコンテナーは必要な分だけ CPU を利用します。こういうことから、これはソフトリミットと言えます。--cpu-shares は Swarm モード内においてコンテナーがスケジュールされることを妨げません。コンテナーの CPU リソースは、これによって利用可能な CPU サイクルが優先的に割り当てられます。ただし CPU アクセスを保証したり予約するものではありません。 |
CPU が 1 つである場合に、以下のコマンドはコンテナーに対し、毎秒 CPU の最大 50 % を保証します。
$ docker run -it --cpus=".5" ubuntu /bin/bash
手動で--cpu-period
と--cpu-quota
を指定しても同じです。
$ docker run -it --cpu-period=100000 --cpu-quota=50000 ubuntu /bin/bash
リアルタイムスケジューラーの設定
コンテナーにおいてリアルタイムスケジューラーを利用するように設定することができます。 CFS スケジューラーが利用できないタスクに対して用います。 初めに ホストマシンのカーネルが正しく設定されていること を確認した上で、Docker デーモンの設定 を行うか、各コンテナーの個別設定 を行ってください。
警告
CPU スケジュールや優先処理は、高度なカーネルレベルの機能です。 たいていの場合、その機能設定をデフォルトから変更する必要はありません。 設定を誤ると、ホストシステムが不安定または利用不能になることがあります。
ホストマシンカーネルの設定
Linux カーネルにおいてCONFIG_RT_GROUP_SCHED
が有効になっていることを確認します。
これにはzcat /proc/config.gz | grep CONFIG_RT_GROUP_SCHED
を実行するか、あるいはファイル/sys/fs/cgroup/cpu.rt_runtime_us
が存在するかどうかで確認します。
カーネルのリアルタイムスケジューラーの設定方法については、各オペレーティングシステムのドキュメントを参照してください。
Docker デーモンの設定
リアルタイムスケジューラーを利用するコンテナーを起動するには、Docker デーモンに--cpu-rt-runtime
フラグをつけて起動します。
設定値には、リアルタイムタスクに対して、実行時間ごとに割り当てられる最大の時間をマイクロ秒単位で指定します。
たとえばデフォルトの実行時間である 1000000 マイクロ秒に対して、--cpu-rt-runtime=950000
と設定すると、このリアルタイムスケジューラーを利用するコンテナーは、各 1000000 マイクロ秒ごとに 950000 マイクロ秒ずつ稼動するようになります。
残りの 50000 マイクロ秒は、リアルタイムスレッド以外のタスクに利用されます。
systemd
を利用するシステム上で、これを恒常的な設定とするには systemd を用いた Docker の管理と設定 を参照してください。
個々のコンテナーに対する設定
コンテナーの CPU 優先順位づけ(priority)を制御するフラグがいくつかあります。
docker run
を実行する際に、これを指定します。
適切な値設定に関しては、オペレーティングシステムのドキュメントやulimit
コマンドを参照してください。
オプション | 内容説明 |
---|---|
--cap-add=sys_nice |
コンテナーがCAP_SYS_NICE ケーパビリティーを利用できるようにします。これによってコンテナーにおけるプロセスのnice 値の加算、リアルタイムスケジューラーポリシーの設定、CPU アフィニティの設定、その他が行えるようになります。 |
--cpu-rt-runtime=<値> |
Docker デーモンにおいて、リアルタイムスケジューラー実行時間内のリアルタイム優先順位づけによる最大実行時間をマイクロ秒で指定します。同時に--cap-add=sys_nice フラグの指定も必要です。 |
--ulimit rtprio=<値> |
コンテナーに対して許容するリアルタイム優先順位づけの最大数。同時に--cap-add=sys_nice フラグの指定も必要です。 |
以下に示すコマンドは、debian:jessie
コンテナーに対して 3 つのフラグを設定する例です。
$ docker run -it \
--cpu-rt-runtime=950000 \
--ulimit rtprio=99 \
--cap-add=sys_nice \
debian:jessie
カーネルまたは Docker デーモンが正しく設定できていない場合には、エラーが発生します。
GPU
NVIDIA GPU へのアクセス
前提条件
NVIDIA ドライバーページ にアクセスして、適切なドライバーをダウンロード、インストールしてください。 これを行ったらシステムを再起動してください。
GPU が起動中でありアクセス可能であることを確認してください。
nvidia-container-runtime のインストール
(https://nvidia.github.io/nvidia-container-runtime/) にある手順に従い、次に以下のコマンドを実行してください。
$ apt-get install nvidia-container-runtime
$PATH
上からnvidia-container-runtime-hook
がアクセスできることを確認します。
$ which nvidia-container-runtime-hook
Docker デーモンを再起動します。
GPU の有効化
コンテナーの起動時に--gpus
フラグをつけると、GPU リソースにアクセスすることができます。
このとき GPU をどれだけ利用するかを指定します。
たとえば以下のとおりです。
$ docker run -it --rm --gpus all ubuntu nvidia-smi
利用可能な GPU をすべて有効にした場合、以下のような出力結果となります。
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 384.130 Driver Version: 384.130 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 GRID K520 Off | 00000000:00:03.0 Off | N/A |
| N/A 36C P0 39W / 125W | 0MiB / 4036MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+
device
オプションを使って GPU を指定します。
たとえば以下です。
$ docker run -it --rm --gpus device=GPU-3a23c669-1f69-c64e-cf85-44e9b07e7a2a ubuntu nvidia-smi
これにより指定した GPU が有効になります。
$ docker run -it --rm --gpus '"device=0,2"' ubuntu nvidia-smi
これは 1 つめと 3 つめの GPU が有効になります。
メモ
NVIDIA GPU は、単一の Engine が起動するシステムからのみアクセスすることができます。
NVIDIA ケーパビリティーの設定
ケーパビリティーは手動で設定します。 たとえば Ubuntu では以下のコマンドを実行します。
$ docker run --gpus 'all,capabilities=utility' --rm ubuntu nvidia-smi
上を行うとutility
ドライバーケーパビリティーによってnvidia-smi
ツールが追加され、コンテナーにより利用可能となります。
ケーパビリティーも他の設定も、環境変数を利用してイメージに設定することができます。 利用可能な環境変数の詳細は nvidia-container-runtime GitHub ページを参照してください。 この環境変数は Dockerfile 内に指定することもできます。
その環境変数を自動的に設定する CUDA イメージを利用することもできます。 詳細は CUDA イメージ GitHub ページを参照してください。
docker, daemon, configuration, runtime