メモリ、CPU、GPU に対する実行時オプション

デフォルトにおいてコンテナには、リソースの利用に関して制限がありません。 したがってホスト・カーネルのスケジューラが割り振るリソースを、その分だけ利用できます。 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)もあり、所定の条件下でない限りは、コンテナが求めるメモリ使用を認めることができます。 所定の条件とはたとえば、ホスト上のメモリ不足やリソース・コンフリクト発生をカーネルが検出したような場合です。 制限を指定するオプションを利用する場合には、単独で利用するか複数組み合わせて利用するかによって、その効果はさまざまです。

この制約オプションのほとんどは、正の整数を指定して、バイト、キロバイト、メガバイト、ギガバイトを表わす bkmg を後ろにつけます。

オプション 内容説明
-m または --memory=
コンテナに割り当てるメモリ最大使用量。このオプションを
利用する場合、指定できる最小値は 4m (4 メガバイト) です。
--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-swap0 にすると、この設定は無視され、設定されていないものとして扱われます。
  • --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 スケジューラ です。 Docker 1.13 またはそれ以降では リアルタイム・スケジューラ を利用することもできます。

デフォルト CFS スケジューラの設定

CFS は Linux 上の普通のプロセスに対して用いられる Linux カーネル CPU スケジューラです。 コンテナが利用する CPU リソースのアクセス量を設定するために、いくつかの実行時フラグが用意されています。 この設定を行うと、Docker はホスト・マシン上にあるコンテナの cgroup 設定を修正します。

オプション 内容説明
--cpus=<値>
コンテナが CPU リソースをどれだけ利用可能かを指定
します。たとえばホスト・マシンに CPU が 2 つあり
--cpus="1.5" という設定を行った場合、コンテナ
に対して CPU 最大 1.5 個分が保証されます。これは
--cpu-period="100000"--cpu-quota="150000"
を設定することと同じです。
Docker 1.13 またはそれ以降において利用可能です。
--cpu-period=<値>
CFS スケジューラ間隔を指定します。
これは --cpu-quota とともに指定されます。
デフォルトは 100 マイクロ秒です。たいていの場合、
このデフォルト値を変更することはしません。
Docker 1.13 またはそれ以降の場合は、これではなく
--cpus を使ってください。
--cpu-quota=<値>
コンテナに対して CFS クォータを設定します。
--cpu-period ごとのマイクロ秒単位の時間であり、
スロットリングされる前にこの時間に制限されます。
有効しきい値として動作します。Docker 1.13 または
それ以降の場合は、これではなく --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 1.13 またはそれ以降の場合

docker run -it --cpus=".5" ubuntu /bin/bash

Docker 1.12 またはそれ以前

$ docker run -it --cpu-period=100000 --cpu-quota=50000 ubuntu /bin/bash

リアルタイム・スケジューラの設定

Docker 1.13 またはそれ以降では、コンテナにおいてリアルタイム・スケジューラを利用するように設定することができます。 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 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 ページを参照してください。

参考

Runtime options with Memory, CPUs, and GPUs
https://docs.docker.com/config/containers/resource_constraints/