このチュートリアルでは、Alibaba Cloud ECS上でDockerコンテナのリソース制限機能を利用した実践的な体験ができます。
他のコンテナに比例するcpu-shares
このテストでは、CPUシェアは他のコンテナに比例します。デフォルト値の1024は本質的な意味を持ちません。
すべてのコンテナのCPU占有率が4の場合、すべてのコンテナでCPU時間を均等に共有します。
これは、すべてのコンテナがCPUシェア=1024の場合、すべてのコンテナがCPU時間を均等に共有しているのと同じです。
実行します。
docker container run -d --cpu-shares=4 --name mycpu1024a alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=100 | md5sum'
docker container run -d --cpu-shares=4 --name mycpu1024b alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=100 | md5sum'
docker container run -d --cpu-shares=4 --name mycpu1024c alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=100 | md5sum'
ログを調査します。
docker logs mycpu1024a
docker logs mycpu1024b
docker logs mycpu1024c
コンテナをpruneして終了です。
docker container prune -f
彼らはまだすべて同じ時間を実行したことに注意してください。4/1024が遅くなることはありませんでした。
cpu-share:CPU サイクルが制限されている場合のみ有効
cpu-shareは CPU サイクルが制限されている場合にのみ適用されます。
他のコンテナが稼働していない状態で、1つのコンテナのcpu-shareを定義することは無意味です。
docker container run -d --cpu-shares=4 --name mycpu1024a alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=100 | md5sum'
docker logs mycpu1024a
real 0m 12.67s
user 0m 0.00s
sys 0m 12.27s
今すぐ共有を4000に増やして再実行してください - 参照 - ランタイムの違いはありません。
1つのコンテナが利用可能なすべてのCPU時間を使用しています。
docker container run -d --cpu-shares=4000 --name mycpu1024a alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=100 | md5sum'
この1つのコンテナをプルーニングして、これで終わりです。
docker container prune -f
--cpus= コンテナが使用できるCPUリソースの量を定義する
コンテナが利用可能なすべてのCPUリソースのうち、どれだけのCPUリソースを使用できるかを指定します。例えば、ホストマシンに2つのCPUがあり、--cpus="1.5 "と設定した場合、コンテナは最大でもCPUの1/2が保証されます。
以下のコマンドで使用している --CPUs の値の範囲に注意してください。実行してみてください。
docker container run -d --cpus=2 --name mycpu2 alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=50 | md5sum'
docker container run -d --cpus=1 --name mycpu1 alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=50 | md5sum'
docker container run -d --cpus=.5 --name mycpu.5 alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=50 | md5sum'
docker container run -d --cpus=.25 --name mycpu.25 alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=20 | md5sum'
docker container run -d --cpus=.1 --name mycpu.1 alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=10 | md5sum'
dockerの統計を調査します。
docker stats
期待される出力
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
843bea7263fb mycpu2 57.69% 1.258MiB / 985.2MiB 0.13% 578B / 0B 1.33MB / 0B 0
186ba15b8258 mycpu1 55.85% 1.25MiB / 985.2MiB 0.13% 578B / 0B 1.33MB / 0B 0
3bcc26eab1ac mycpu.5 46.60% 1.262MiB / 985.2MiB 0.13% 578B / 0B 1.33MB / 0B 0
79d7d7e3c38c mycpu.25 25.43% 1.262MiB / 985.2MiB 0.13% 508B / 0B 1.33MB / 0B 0
b4ba5503a048 mycpu.1 9.76% 1.328MiB / 985.2MiB 0.13% 508B / 0B 1.33MB / 0B 0
mycpu.1, mycpu.25, mycpu.5は完全に制限が適用されていることを示しています。
しかし、mycpu1とmycpu2は100 + 200 %の追加CPUを持っていません。したがって、これらの設定は無視され、残りのCPU時間を均等に共有します。
--cpus CPUの数
cpus設定はコンテナが使用するCPUの数を定義します。
DockerやLinuxディストロではCPUは次のように定義されます。
CPUs = Threads per core X cores per socket X sockets
CPUは物理的なCPUではありません。
私のサーバーのCPU数を調べてみましょう。
lscpu | head -n 10
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 2
On-line CPU(s) list: 0,1
Thread(s) per core: 1
Core(s) per socket: 2
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
不要な情報を削除しました。
lscpu | head -n 10
CPU(s): 2
On-line CPU(s) list: 0,1
Thread(s) per core: 1
Core(s) per socket: 2
Socket(s): 1
CPUs = Threads per core X cores per socket X sockets
CPU=1×2×1=2CPU
で確認してください。
grep -E 'processor|core id' /proc/cpuinfo
2 core id = 2 cores per socket
2 processors = 2 cpus
processor : 0
core id : 0
processor : 1
core id : 1
OK このサーバーは2つのCPUを持っています。あなたのサーバは異なるでしょうから、以下のテストを行う際にはその点を考慮してください。
cpus設定はコンテナが使用するCPUの数を定義します。
ここでは両方のCPUを使用し、1つだけ、1/2、1/4のCPUを使用し、CPU負荷の高いワークロードのためのランタイムを記録してみましょう。
注意: --cpus=2
docker container run --cpus=2 --name mycpu alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=30 | md5sum'
期待される出力
real 0m 3.61s
user 0m 0.00s
sys 0m 3.50s
比較するものが何もありません。他のテストを実行してみましょう
docker container prune -f
注意:--cpus=1
docker container run --cpus=1 --name mycpu alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=30 | md5sum'
real 0m 3.54s
user 0m 0.00s
sys 0m 3.37s
docker container prune -f
注意: --cpus=.5
docker container run --cpus=.5 --name mycpu alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=30 | md5sum'
real 0m 9.97s
user 0m 0.00s
sys 0m 4.78s
docker container prune -f
注意: --cpus=.25
docker container run --rm --cpus=.25 --name mycpu alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=30 | md5sum'
real 0m 19.55s
user 0m 0.00s
sys 0m 4.69s
--cpus=2 realtime. 3.6秒
--cpus=1 realtime. 3.5秒
--cpus=.5 realtime: 9.9秒
--cpus=.25 realtime: 19.5秒
私たちの単純なベンチマークでは、2つのCPUを同時に使用することは効果的ではありません。
半分のCPUは2倍遅く、4分の1のCPUは4倍遅く動作します。
CPUsの設定は有効です。コンテナ内のアプリケーションがマルチスレッド化できない場合や、1つ以上のCPUを効果的に使用できない場合は、1つのCPUだけを割り当ててください。
--CPU-period と --CPU-quota
廃止されたオプションです。Docker 1.13以降を使用している場合は、代わりに--cpusを使用してください。
--cpu-period CPU CFS(Completely Fair Scheduler)の期間を制限する
--cpu-quota CPU CFS (Completely Fair Scheduler) のクォータを制限します。
上記の演習では、--CPUの設定がいかに簡単にできるかを明確に示しています。
--cpuset-cpus 実行を許可するCPU (0-3, 0,1)
--cpuset-cpus - 実行を許可するCPU (0-3, 0, 1)
残念ながら私のサーバーには2つのCPUしかなく、1つ以上のCPUを使っても効果がないことは先ほど見ました。
サーバーに複数のCPUが搭載されている場合、-cpuset設定のより興味深い組み合わせを実行できますが、これは役に立たないでしょう:この特定のベンチマークは1つのスレッドしか使用しません。
このチュートリアルの後半では、スレッド数を指定できる sysbench (実際のベンチマークツール) を使ったテストがあります。
私の結果は以下の通りです: 2つのCPUを使っても違いはありません。
docker container run --rm --cpuset-cpus=0,1 --name mycpu alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=30 | md5sum'
Expected output :
real 0m 3.44s
user 0m 0.00s
sys 0m 3.35s
docker container run --rm --cpuset-cpus=0 --name mycpu alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=30 | md5sum'
Expected output :
real 0m 4.15s
user 0m 0.00s
sys 0m 4.00s
docker container run --rm --cpuset-cpus=1 --name mycpu alpine:3.8 /bin/sh -c 'time dd if=/dev/urandom bs=1M count=30 | md5sum'
Expected output :
real 0m 3.40s
user 0m 0.00s
sys 0m 3.28s
実際のベンチマークアプリケーションを使用したテストコンテナの限界
上記のテストはすべてクイックハックを使用しています。
コンテナのリソース制限を適切にテストするには、実際の Linux ベンチアプリケーションが必要です。
私はCentOSを使い慣れているので、ベンチコンテナのベースとして使用します。どちらのベンチアプリケーションもDebian / Ubuntuでも利用可能です。yumのインストールをapt-getのインストールに変換すれば、簡単に同じ結果が得られます。
2つのベンチアプリケーションをコンテナにインストールする必要があります。最良の方法は、これらのアプリケーションを含むイメージを構築することです。
そのため、dockerbenchディレクトリを作成します。
mkdir dockerbench
cd dockerbench
nn Dockerfile
FROM centos:7
RUN set -x \
&& yum -y install https://www.percona.com/redir/downloads/percona-release/redhat/0.0-1/percona-release-0.0-1.x86_64.rpm \
&& yum -y install sysbench \
&& curl http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -o epel-release-latest-7.noarch.rpm \
&& rpm -ivh epel-release-latest-7.noarch.rpm \
&& yum -y install stress
最初のインストールでは、sysbench のホームである percona yum repo を追加します。
その後、Yum は sysbench をインストールします。
curlはEPELのyumレポを追加します。
そして、Yum は stress をインストールします。
私たちのベンチのイメージを構築します。インターネットでの yum ダウンロード + yum 依存関係の解決と他の通常の活動に基づいて、約1分かかります。
もしCentOS 7のイメージをダウンロードしていない場合は、さらに1分かかるかもしれません。
docker build --tag centos:bench --file Dockerfile .
これでCentOSのベンチイメージができたので、繰り返し使えるようになりました。
docker run -it --rm centos:bench /bin/sh
--cpus Sysbenchツールでテスト
構文:
sysbench --threads=2 --events=4 --cpu-max-prime=800500 —>verbosity=0 cpu run
- --threads=2 ... 2つのスレッドを実行することで、2つのCPUと1つのCPUを比較することができます。
- --events=4 ... 4回実行
- --cpu-max-prime=800500 ... 800500までの素数を計算します。
- --verbosity=0 ... 詳細な出力を表示しません。
- cpu run ... CPUという名前のテストを実行します。
実験を経て、私は10年前のコンピュータで800500がテストを素早く実行するための良い値であると判断しました。CPUマーク700。0桁が多いと読みにくいので、そこに5を入れてみました。
2つのCPU:
docker run -it --rm --name mybench --cpus 2 centos:bench /bin/sh
sh-4.2# time sysbench --threads=2 --events=4 --cpu-max-prime=800500 --verbosity=0 cpu run
real 0m1.952s
user 0m3.803s
sys 0m0.029s
sh-4.2#
sh-4.2# exit
exit
Realはウォールクロック時間-sysbenchの開始から終了までの時間:1.9秒。
Userはsysbench内のユーザモードコード(カーネル外)で使用したCPU時間です。2台のCPUを使用しています:それぞれ1.9秒のCPU時間を使用していますので、合計のユーザ時間はそれぞれのCPUの時間を足したものです。
経過したウォールクロック時間は1.9秒です。2つのCPUが同時に/同時に作業していたので、それらの合計時間がユーザー時間として表示されます。
Sysはカーネルがシステムコールを行っているCPUの時間です。
1つのCPU:
docker run -it --rm --name mybench --cpus 1 centos:bench /bin/sh
sh-4.2# time sysbench --threads=2 --events=4 --cpu-max-prime=800500 --verbosity=0 cpu run
real 0m4.686s
user 0m4.678s
sys 0m0.026s
sh-4.2#
sh-4.2# exit
exit
これらの比較を実行するより便利な方法は、docker の run line で bench コマンドを実行することです。
この方法で1つのCPUを再実行してみましょう。
docker run -it --rm --name mybench --cpus 1 centos:bench /bin/sh -c 'time sysbench --threads=2 --events=4 --cpu-max-prime=800500 --verbosity=0 cpu run'
real 0m4.659s
user 0m4.649s
sys 0m0.028s
この方法で半分のCPUを実行してみましょう。
docker run -it --rm --name mybench --cpus .5 centos:bench /bin/sh -c 'time sysbench --threads=2 --events=4 --cpu-max-prime=800500 --verbosity=0 cpu run'
real 0m10.506s
user 0m5.221s
sys 0m0.035s
結果は完璧に筋が通っています。
- 2 CPUs : real 0m1.952s
- 1 CPU : real 0m4.659s
- 5 CPUs : real 0m10.506s
私たちのイメージにあるsysbenchを使えば、このようなテストを非常に簡単かつ迅速に行うことができます。ほんの数秒でDockerコンテナのCPU使用量を制限することができます。
率直に言って、.5CPUテストのために10.506秒も待つのは長すぎます。
職場の開発サーバでこれを行った場合、CPU負荷は1分以上の経過で劇的に変化します。開発者は 2 秒間の 2CPU 実行中にコンパイルを行い、サーバは 5 秒間の 1CPU 実行中に CPU が静かになる可能性があり、数値は完全に歪んでしまいます。
このような状況の変化に対して、ある程度ロバストなアプローチが必要です。すべてのテストは、可能な限り迅速に、そして直接次から次へと実行しなければなりません。
それを試してみましょう。最大素数を100倍に減らします。
これら3つの命令をすべてカットアンドペーストして、一度に行って、結果を観察します。
docker run -it --rm --name mybench --cpus 2 centos:bench /bin/sh -c 'time sysbench --threads=2 --events=4 --cpu-max-prime=8005 --verbosity=0 cpu run'
docker run -it --rm --name mybench --cpus .5 centos:bench /bin/sh -c 'time sysbench --threads=2 --events=4 --cpu-max-prime=8005 --verbosity=0 cpu run'
docker run -it --rm --name mybench --cpus 1 centos:bench /bin/sh -c 'time sysbench --threads=2 --events=4 --cpu-max-prime=8005 --verbosity=0 cpu run'
期待される出力
2 CPUs
real 0m0.049s
user 0m0.016s
sys 0m0.021s
1 CPUs
real 0m0.049s
user 0m0.019s
sys 0m0.020s
.5 CPU
real 0m0.051s
user 0m0.020s
sys 0m0.019s
ベンチマーク起動時のオーバーヘッドがウォールクロックの実時間を圧倒しています。テストは絶望的に短すぎます。
3回の個人的な実験の後、元のワークロードを10倍に減らせば完璧だと思います。
docker run -it --rm --name mybench --cpus 2 centos:bench /bin/sh -c 'time sysbench --threads=2 --events=4 --cpu-max-prime=100500 --verbosity=0 cpu run'
docker run -it --rm --name mybench --cpus 1 centos:bench /bin/sh -c 'time sysbench --threads=2 --events=4 --cpu-max-prime=100500 --verbosity=0 cpu run'
docker run -it --rm --name mybench --cpus .5 centos:bench /bin/sh -c 'time sysbench --threads=2 --events=4 --cpu-max-prime=100500 --verbosity=0 cpu run'
(中の5は000000の長い文字列を読みやすくするためのものです)
2 CPUs
real 0m0.152s
user 0m0.225s
sys 0m0.015s
1 CPU
real 0m0.277s
user 0m0.279s
sys 0m0.019s
.5 CPU
real 0m0.615s
user 0m0.290s
sys 0m0.024s
比率は完璧です。全体的な実行時間は 1 秒未満で、開発サーバーの CPU 負荷の変化による影響を最小限に抑えています。
ここで説明されていることを理解するために、お使いのサーバーで数分遊んでみてください。
run コマンドで --rm を使用したことに注意してください。これは /bin/sh でコンテナに渡されたコマンドを終了した後、コンテナを自動的に削除します。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ