Feedforce Developer Blog

フィードフォース開発者ブログ

m3.medium のインスタンスの CPU 負荷が高かったため t2.medium へ移行した

こんにちは、エンジニアの id:tsub511 です。 最近頭痛がするのでヨガを始めましたが、効果が出ているのかよく分かりません。

今回は m3.medium のインスタンスの CPU 負荷が高かったため t2.medium へ移行したら解決した話をします。

m3.medium のインスタンスの CPU 負荷が高かった

年始あたりから、週に数回ほど決まった時間に Mackerel でアラートが出ていました。

f:id:tsub511:20180302143106p:plain

CPU の Steal 値が異常に高く、全体としての使用率が 90 % を超えていました。

ずっと原因が分からず、最初は Meltdown と Spectre のパッチを適用した関係で性能が低下したんじゃないか、などを疑っていました。

しかし、ある時全く別の作業をしていたときに別のロールのインスタンスで同様に CPU 負荷が上がり、どちらも m3.medium というインスタンスタイプが共通していたことからなんとなくググってみたところ、以下の記事に辿り着きました。

toritori0318.hatenadiary.jp

どうやら、m3.medium というインスタンスタイプのみ CPU の Steal が発生しやすいようです。

他にも同様の報告をしている記事をいくつか見つけました。

情報が 2014 年と古いですが、現に同様の事象が発生しているため、当時と変わっていない可能性が高いです。

そのため、インスタンスタイプを変更することを検討しました。

他のインスタンスタイプを検討

m3.medium から別のインスタンスタイプに変更するに辺り、どのインスタンスタイプを選択するか、まずはコスト面で比較しました。

f:id:tsub511:20180302145249p:plain

calculator.s3.amazonaws.com

順当に行けば m4 ファミリーが妥当なところですが、m4 ファミリーは medium サイズは提供していないため、費用がそれなりに増えてしまいます。

この中で、m3.medium よりも安い t2.medium に目を付けました。

t2.mediumm3.medium に比べると、 vCPU が 1 コア増え、メモリも 0.25 GB 増える上に料金が安くなるというかなりお得なインスタンスタイプです。

m3.medium

インスタンスファミリー インスタンスタイプ プロセッサアーキテクチャ vCPU メモリ (GiB) インスタンスストレージ(GB) EBS 最適化利用 ネットワークパフォーマンス
汎用 m3.medium 64 ビット 1 3.75 1 x 4 -

https://aws.amazon.com/jp/ec2/previous-generation/

t2.medium

モデル vCPU CPU クレジット/時 メモリ (GiB) ストレージ
t2.medium 2 24 4 EBS のみ

https://aws.amazon.com/jp/ec2/instance-types/

ただ、ここで安易に t2.medium を選択してはいけません。 t2 ファミリーは「バースト可能パフォーマンスインスタンス」という特別な性質があります。

T2 インスタンスについて

T2 インスタンスについて、今までふわっとした理解しかなかったため、この機会に AWS のドキュメントをちゃんと読んでみました。

docs.aws.amazon.com

結論から言うと、弊社のサービスの性質上、決まった時間に Sidekiq のジョブがまとまって大量に実行されるため、普段は CPU 使用率は低く、ある時間だけ CPU 使用率が高くなるというまさに T2 インスタンスがピッタリなケースでした。

CPU クレジット

T2 インスタンスには CPU クレジットという概念があります。

docs.aws.amazon.com

1 CPU クレジットは 100 % の CPU 使用率を 1 分間稼働させることができます。

t2.medium は CPU クレジットが 1 時間あたり 24 なので、100 % の CPU 使用率を 24 分間、あるいは 50 % の CPU 使用率を 48 分間、40 % の CPU 使用率なら 60 分間稼稼働させることができることになります。(ただし、t2.medium は vCPU が 2 コアなので、実際には 20 % の CPU 使用率で 60 分間の稼働)

実際の CPU 使用率は平均で 20 % 以下に収まっていることが多い (たまにスパイクはする) ので、CPU クレジットが 24 ならまず問題ないです。

f:id:tsub511:20180302150357p:plain

この 40 % (20 %) という値をベースラインパフォーマンスと呼び、これを超えて CPU を使用することを「バースト」と呼びます。

また、ベースラインパフォーマンスよりも CPU 使用率が下回っていた場合、クレジットバランスというものに余分な CPU クレジットが保存されます。 クレジットバランスに保存された CPU クレジットは、CPU 負荷がベースラインパフォーマンスを上回った時に消費されます。

つまり、余分な CPU クレジットは蓄積されて後で使うことができるということになります (ただし t2.medium の最大クレジットバランスは 576)。

注意点としてはインスタンスを停止するとクレジットバランスに貯まった CPU クレジットは破棄されるというところでしょうか。

T2 Unlimited

ただ、T2 インスタンスを使う以上、気にしなければいけないのは CPU クレジットがなくなった場合は CPU のバーストができなくなるということです。

CPU のバーストができないということはつまり、ベースラインパフォーマンス (t2.medium の場合は 20 %) 以上の CPU が使えなくなるということになります。

ただし、去年の Re:Invent にて発表された T2 Unlimited という機能を有効にすることで CPU クレジットがなくなった場合でも自動的に CPU クレジットを追加され、CPU 使用に制限がかからなくなります。

docs.aws.amazon.com

具体的には、T2 Unlimited を有効にすると、CPU クレジット及びクレジットバランスがなくなった場合、余剰クレジットというものから消費されるようになります。

最初の余剰クレジットは 24 時間で獲得できるクレジットの合計値となります。

例えば t2.medium の場合、1 時間辺りの獲得クレジットは 24 なので 24 時間で 576 のクレジットが余剰クレジットになります。

この 24 時間分の余剰クレジットは前借りのようなもので、消費した分だけ次のクレジット獲得時に余剰クレジットの支払いに使用されます。

24 時間分の余剰クレジットまで全て使い切ってしまった場合でも、その後に消費した余剰クレジット分は追加で課金され、CPU のバーストは継続することが可能です。

つまり、T2 Unlimited を有効にすれば T2 インスタンス特有の CPU クレジットの枯渇による CPU 使用制限の問題が解決されることになります。

ただし、常にバーストし続けて追加でお金が発生し続けるような場合は、T2 インスタンスでなく普通にインスタンスタイプを利用したほうが懸命ですね。

CPU クレジットの監視

T2 Standard (非 T2 Unlimited) であっても、T2 Unlimited であっても、普段からどの程度 CPU がバーストしているかは監視しておいたほうが良いです。

そのために、CloudWatch で CPUCreditUsage, CPUCreditBalance, CPUSurplusCreditBalance, CPUSurplusCreditsCharged という 4 つのメトリクスが提供されています。

個人的には T2 Unlimited の場合、基本的には CPUSurplusCreditBalanceCPUSurplusCreditsCharged を監視しておけば良いと思います。

  • CPUSurplusCreditBalance は消費された 24 時間分の余剰クレジット数
  • CPUSurplusCreditsCharged は 24 時間分の余剰クレジットを使い切った後で更に消費される余剰クレジット数

実際の監視には Datadog を利用しました (現在監視ツールを Datadog へ移行途中なため)。

f:id:tsub511:20180302153003p:plain

閾値はまだ感覚を掴めていないため、とりあえず厳しめにしてあります。

m3.medium から t2.medium へインスタンスタイプを変更する

弊社のサービスのインフラでは、Blue Green Deployment が可能な体制が整っているため、インスタンスタイプの変更は非常に簡単です。

新しい環境のインスタンスは t2.medium で作成し、ELB からコネクションが流れるようになったら、古い環境のインスタンスを削除するだけです。

ただ、EC2 の Launch Configuration + Auto Scaling Group を使っていたため、少し工夫が必要でした。

T2 Unlimited の有効化は Launch Configuration ではサポートされていませんでした。

Auto Scaling グループで T2 インスタンスを無制限に設定して起動するには起動テンプレートを使用する必要があります。起動設定では、T2 インスタンスを無制限として起動することがサポートされていません。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/t2-unlimited.html#t2-auto-scaling-grp

Launch Template ならサポートされているものの、今から移行するのも大変ですし、何より Terraform がまだ Launch Template をサポートしていませんでした (2018/03/02 時点)。

github.com

どうしようと困っていたところ、以下の記事に助けられました。

dev.classmethod.jp

EC2 User Data を使って、インスタンス起動時に自身に対して T2 Unlimited を有効化する、という方法です。

自分では全く思いつきませんでしたが、User Data も Terraform を使って管理できるのでかなりシンプルに実現できました。

実際には以下の User Data を利用しました (CentOS を使っているため $ yum install aws-cli ができない)。

#!/bin/bash

set -x

# Install aws-cli

curl -L https://bootstrap.pypa.io/get-pip.py | python
pip install awscli --upgrade

# Enable T2 Unlimited

INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//')

aws --region "${REGION}" ec2 describe-instance-credit-specifications --instance-id "${INSTANCE_ID}"
aws --region "${REGION}" ec2 modify-instance-credit-specification --instance-credit-specification InstanceId="${INSTANCE_ID}",CpuCredits=unlimited
aws --region "${REGION}" ec2 describe-instance-credit-specifications --instance-id "${INSTANCE_ID}"

これで、Auto Scaling Group によって起動したインスタンスに対して自動的に T2 Unlimited が有効になりました。

インスタンスタイプを t2.medium に変更した結果

実際に t2.medium のインスタンスを稼働させた結果、同程度の負荷がかかった際の CPU の Steal 値はほぼ 0 になりました。

f:id:tsub511:20180302152028p:plain

ちなみに t2.medium は vCPU が 2 つあるため、グラフの最大値は 200 % になっています。

user 値が 90 % 程度なので、実質 CPU 使用率は 45 % 程度で、m3.medium の頃とほとんど性能は変わっていません。

また、その他にも 5 分間のロードアベレージも全体的に下がっていました。

f:id:tsub511:20180302153619p:plain

まとめ

  • m3.medium のインスタンスを使っていて CPU 負荷に悩まされている場合はインスタンスタイプを変更すると解決するかも
  • T2 インスタンスは適材適所で使えば費用を安く抑えられて非常に良い
  • T2 Unlimited によって CPU クレジットがなくなる問題が解決されて安心して T2 インスタンスを使用できるようになった
  • T2 Unlimited を Launch Configuration で有効化したい場合は User Data を使うと良い

m3.medium が原因だったようで、解決して良かったです。日々のアラートに悩まされなくて良くなりました。