LoginSignup
2
5

More than 3 years have passed since last update.

Dockerコンテナのリソース制限機能を使った実践的な体験談 その2

Posted at

このチュートリアルでは、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ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5