86
49

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS: ECSのCPUユニット(CPU Units)について

Last updated at Posted at 2020-02-01

はじめに

自社サービスの実行基盤としてAWS ECS(on EC2)を利用しています。

これまでは4台固定のECSクラスタでトラフィックを捌いていましたが、ピークタイムの負荷状況が怪しくなってきたため、ECSクラスタへAutoScalingを導入する事にしました。

ひとまずはクラスタのCPU負荷をスケールアウトの基準とする事としたのですが、CloudWatchに表示されるCPU使用率(CPUUtilication)が示す値の意味が良く分からない。

こちらのドキュメントによれば、クラスタのCPU使用率は下記の計算式で算出されているとの事ですが...。

                                  (Total CPU units used by tasks in cluster) x 100
Cluster CPU utilization =  --------------------------------------------------------------
                           (Total CPU units registered by container instances in cluster)

重要なのはタスクのCPUユニット(CPU Units)の様ですが、これまではその意味を深く考えた事はありませんでした。
(数十種類のタスクを本番稼働させていますが、全てCPUユニット=10で固定しちゃってます)

という事で、CPUユニットの意味とその動作についての検証を行いました。

「CPUユニットっていくつにすれば良いの?」とお悩みの方に参考になれば幸いです。

CPUユニット(CPU Units)とは

CPUユニットはECSタスク(=コンテナ)が使用できるCPU能力を制御するための、ECSタスク定義パラメータです。

ECSタスク定義の例
{
    "name": "sample-task",
    "image": "alpine",
    "cpu": 1024,
    "memory": 256
}

設定した値はdocker run--cpu-sharesパラメータとして使用されます。

$ docker inspect 9a7c6f9a5ea0 | grep -i cpushares
            "CpuShares": 1024

ECSインスタンスのCPUユニットについて、ドキュメントには以下のような記載があります。

Amazon EC2 Instances 詳細ページのインスタンスタイプに一覧表示されている vCPU 数に 1,024 を乗算して、Amazon EC2 インスタンスタイプごとに使用可能な CPU ユニットの数を判断できます。

これは4コアのインスタンスは4096のCPUユニットを持つという事を意味します。

言い換えれば1024のCPUユニットを持つタスクは、1個のCPUコアを専有出来るという事でもあります。

ローカルマシン上での検証

まずは--cpu-sharesの動作を2コアのローカルマシン上で検証します。

下記の2コンテナを起動するとします。

| タスク | --cpu-shares |
|---|---|---|
|タスク1|512|
|タスク2|2048|

この場合、、2コアのCPU能力を1:4の割合で分け合う事になるため、各コンテナのCPU使用率は以下の様になるはずです。

タスク --cpu-shares CPU使用率
タスク1 512 40%
タスク2 2048 160%

では本当にそうなるのか、実際に試してみます。

まず、無限ループで100%のCPU負荷を発生させるシェルスクリプトを用意します。
このスクリプトはPROC環境変数で指定されたコア数分の負荷を発生させます。

#!/bin/sh

PROC="${PROC:-1}"

for i in $(seq 1 $PROC); do
    /bin/sh -c 'while true; do :; done' &
done

wait

そうしたら、タスク1(CPU=512, PROC=1)とタスク2(CPU=2048, PROC=2)をそれぞれ起動します。

$ docker run --rm -d --name TASK1 --cpu-shares 512  -e PROC=1 -v $PWD/app.sh:/app.sh alpine /bin/sh /app.sh
$ docker run --rm -d --name TASK2 --cpu-shares 2048 -e PROC=2 -v $PWD/app.sh:/app.sh alpine /bin/sh /app.sh

CPU負荷を確認すると、予想通りの比率となっている事が分かります。

$ docker stats --no-stream --format '{{.Name}} {{.CPUPerc}}' | sort
TASK1 41.20%
TASK2 159.76%

ここでタスク2を停止します。

$ docker kill TASK2

CPU負荷を確認すると、タスク1のCPU使用率が100%(=1コアを専有)に上昇しました。

$ docker stats --no-stream --format '{{.Name}} {{.CPUPerc}}' | sort
TASK1 100.29%

以上の結果から分かるのは、--cpu-shares(=CPUユニット)はそのコンテナが必要とするCPU能力を、他コンテナとの相対的な割合で指定するパラメータであるという事です。

また、CPU能力の上限を決められる物では無いという事も分かります。

ECS上での検証

では、ECSのCPUユニットでも同様の事が言えるのか、c5.xlargeのインスタンス1台で構成されたECSクラスタで検証します。

c5.xlargeは4コアのインスタンスなので、クラスタ全体で利用可能なCPUユニットは4096となります。
ECSクラスタ

先ほどのシェルスクリプトをDockerイメージ化した上で、5個のECSサービスを作成します。
ECSサービス一覧

個々の内訳は以下の様になっています。

サービス名 CPUユニット PROC変数(プロセス数)
512CPU-1PROC 512 1
512CPU-2PROC 512 2
1024CPU-1PROC 1024 1
1024CPU-2PROC 1024 2
3072CPU-3PROC 3072 3

512CPU-1PROC = 1タスク

512CPU-1PROCを1タスク起動してから、クラスタとサービスのCPU使用率をCloudWatchで確認します。
512CPU-1PROC = 1タスク

サービスのCPU使用率が200%となっていますが、これはCPUユニット=512のタスクが実際はその2倍となる1024ユニットを消費しているためです。

また、1024ユニットを消費しているという事は1コアを専有しているのと同じであるため、クラスタCPU使用率は25%となります。

1024CPU-1PROC = 1タスク

続いて、1024CPU-1PROCを1タスク起動します。
1024CPU-1PROC = 1タスク

CPUユニット=1024のタスクが1024ユニットを消費しているため、サービスのCPU使用率はきっかり100%となります。

クラスタのCPU使用率は変わらず25%(1コア)です。

ちなみに、タスク数を2にしたとしてもサービスのCPU使用率は100%のままに見えますが、メトリクスのタイプを合計に変えるとちゃんと200%になります。(クラスタのCPU使用率は50%になります)
1024CPU-1PROC = 2タスク

512CPU-2PROC = 1タスク

512CPU-2PROCを1タスク起動します。
512CPU-2PROC = 1タスク

CPUユニット=512のタスク内で2個のプロセスがフル稼働しているため、実際は4倍の2048ユニットを消費している事になり、サービスのCPU使用率は400%となります。

また、2コアを専有している事でもあるのでクラスタのCPU使用率は50%となります。

1024CPU-2PROC = 1タスク & 3072CPU-3PROC = 1タスク

最後に1024CPU-2PROCを1タスク3072CPU-3PROCを1タスク、同時に稼働させるとどうなるか検証してみます。

まずは1024CPU-2PROCのタスクを起動します。
1024CPU-2PROC = 1タスク

2個のプロセスが稼働しているため、サービスのCPU使用率は200%となります。

また、インスタンス上でプロセス状況を確認すると、各プロセスはCPUを100%使用出来ている事が分かります。
1024CPU-2PROC = 1タスク & 3072CPU-3PROC - topコマンド01

次に、3072CPU-3PROCのタスクを起動します。
1024CPU-2PROC = 1タスク & 3072CPU-3PROC = 1タスク

4096ユニットのECSクラスタ上で1024ユニットのタスク3072ユニットのタスクが全力で稼働しているため、それぞれのCPU使用率はきっちり100%となります。

また、先ほどはCPUを100%使用出来ていた1024CPU-2PROCタスクのプロセスですが、現在は50%しか使用出来ていません。
1024CPU-2PROC = 1タスク & 3072CPU-3PROC - topコマンド02

--cpu-sharesと同様に、CPUユニットに基づいたタスクの相対的なCPU能力の設定が上手く機能している事が良く分かります。

まとめ

  • ECSインスタンスは1コアあたり1024のCPUユニットを持つ
  • CPUユニットはそのタスクが最低限必要とするCPU能力を他タスクとの相対値で指定するためのパラメータである
  • クラスタの負荷状況に余裕がある時は、タスクは与えられたCPUユニット以上にCPUを利用できる
    • その場合、CloudWatchのサービスCPU使用率は100%を超える
  • クラスタの負荷が高まって来た場合、タスクは処理能力を押さえつけられる場合がある
    • 当然、CPUユニットで与えた分は保証される
    • とあるタスクのCPU使用率が100%で張り付いている場合、そのタスクのCPUユニットの追加を検討するべきである
86
49
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
86
49

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?