最初に疑ったのはCPUクレジット

Jenkinsのエージェントに AWS スポットインスタンス(t系)を使っているのですが、起動から1時間ほど経つと処理が突然重くなる現象がありました。

CPUもメモリも余っているのに、なぜか遅い。

最初はCPUクレジットを疑ったんですが、調べていくうちに「あ、ネットワークか」と気づいて、原因が特定できたのでメモしておきます。

t系インスタンスを使っているので、まずCPUクレジットの枯渇を疑いました。unlimited モードで動いているとはいえ、念のため残高を確認しようとしたのがきっかけです。

CLIでCPUクレジット残高を確認する

#!/bin/bash
# IMDSv2でインスタンスIDとリージョンを取得
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id)
REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/placement/region)

# CPUクレジット残高を取得
CREDIT=$(aws cloudwatch get-metric-statistics \
  --region $REGION \
  --namespace AWS/EC2 \
  --metric-name CPUCreditBalance \
  --dimensions Name=InstanceId,Value=$INSTANCE_ID \
  --start-time $(date -u -d '10 minutes ago' +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 300 \
  --statistics Average \
  --query 'sort_by(Datapoints, &Timestamp)[-1].Average' \
  --output text)

echo "Instance: $INSTANCE_ID / CPU Credit Balance: $CREDIT"

macOSで実行する場合は date -u -d '10 minutes ago'date -u -v-10M に変えてください。

CPUSurplusCreditBalance が積み上がっていることに気づく

CloudWatchを見ると、CPUSurplusCreditsCharged はほぼゼロなのに CPUSurplusCreditBalance がじわじわ増えていました。

「これ、課金されてるの?」と一瞬焦ったのですが、整理するとこういう状態でした。

CPUSurplusCreditBalance が上昇中
  → unlimited モードでバーストし続けている「借り」が積み上がっている

CPUSurplusCreditsCharged = 0
  → まだ返済も課金も発生していない

CPUSurplusCreditsCharged が計上されるのは、補充クレジットで「借り」を返し切れなかったとき、もしくはインスタンスが終了したときです。今は「借りたまま走っている」段階で、特に問題ない状態でした。

スポットインスタンスだと終了時に注意

ひとつ気をつけておきたいのは、スポットインスタンスが突然終了したとき。CPUSurplusCreditBalance に残っている借り残高がそのまま CPUSurplusCreditsCharged に計上されます。

ただ、コストへの影響はかなり小さいです。正確な計算式はこうなります。

1 vCPU-hour = 60クレジット = $0.05
→ 1クレジット ≈ $0.00083

ちなみに t3.medium の CPUSurplusCreditBalance 上限は 576クレジット(24クレジット/時間 × 24時間分)。上限まで使い切っても 576 × $0.00083 ≈ 約48セント。「コストが跳ね上がる」ほどの話ではなかったので、CPUクレジットは今回の問題の主因ではなさそうでした。

EBSのI/Oクレジット枯渇を疑ったが…

次に疑ったのはEBSのI/Oクレジット枯渇。gp2はCPUと同様にI/Oにもバーストクレジットがあって、Jenkinsのビルドは依存ライブラリのダウンロードやコンパイルでI/Oを多用するので枯渇しやすいはずです。

ただ、確認したらEBSはgp3を使っていました。gp3はクレジット方式ではなくプロビジョンドIOPS方式なので、I/Oクレジット枯渇は関係ないということでこちらも外れ。

NetworkOut のグラフを見て「あ、これだ」となった

行き詰まったので CloudWatch で NetworkOut のグラフを眺めていたら、起動から約1時間後に急激に下がっているのが見えました。

CloudWatchグラフ: NetworkPacketsOutとNetworkOutが起動後1時間前後で急落している

グラフを見ると NetworkPacketsOut(パケット数)も同じタイミングで落ちています。パケット数まで落ちているということは、帯域クレジットの枯渇でほぼ確定です。

t系インスタンスにはネットワークにもバーストクレジットがある

これ、知らなかったんですが t系のネットワーク帯域もCPUと同じバースト構造になっています。

起動直後
  → ネットワークバーストクレジットが満タン
  → 最大帯域(t3.medium なら最大5Gbps)でフルに使える

1時間前後
  → クレジット枯渇
  → ベースライン帯域に絞られる(t3.medium なら約256Mbps)

Jenkinsのエージェントは起動直後にDockerイメージのプルや依存ライブラリのダウンロードで一気にネットワークを使います。なのでクレジットが早く枯渇して、その後のビルドで帯域が絞られていたわけです。CPUもメモリも余っているのに遅い、という症状と完全に一致します。

CPUクレジットと違って「設定で対策できない」

CPUクレジットには unlimited モードという設定があって、クレジットが枯渇してもバーストし続けられます。「じゃあネットワークも同じように設定できないの?」と思って調べたら、ネットワークバーストクレジットにはunlimitedに相当する設定が存在しません。

CPUクレジット ネットワーククレジット
unlimitedモード ✅ ある ❌ ない
サイズアップで緩和
設定で無効化

AWS の仕様として、インスタンスタイプで決まっているパラメータで、ユーザー側では変更できないとのことです。

現時点で考えている対策

原因は特定できたので、対策はこれから検討します。優先度順にまとめると以下の感じです。

1. 依存ライブラリ・Dockerイメージのキャッシュ整備
起動直後の大量通信を減らしてクレジット枯渇を遅らせる。コスト増なしで試せるので、まずここから手をつけたいと思っています。

2. インスタンスサイズのアップ
サイズが上がればベースライン帯域自体が増えるので、枯渇後も耐えやすくなります。スポット料金との兼ね合いで検討。

3. m系・c系への変更
ネットワークバーストクレジットの概念がなくなるので根本解決。ただしスポット料金は上がります。

Jenkinsエージェントをビルドのたびに使い捨てにしているなら、キャッシュの整備が最もコスパよく効く対策だと思っています。実施したら続きを書きます。

参考:今回調べた AWS 公式ドキュメント

テーマ URL
バーストパフォーマンスインスタンス概要 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances.html
CPUクレジットの基本概念 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-credits-baseline-concepts.html
Unlimitedモードの概念 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-unlimited-mode-concepts.html
スポットインスタンスでのUnlimitedモードの注意事項 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-unlimited-mode.html
EC2のネットワーク帯域幅(バーストクレジットについての記載あり) https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-network-bandwidth.html

参考書籍

AWS のインスタンス運用やインフラの基礎を体系的に学びたい方には、以下の書籍が参考になります。

[📦 商品リンク: moshimo-book-aws-operations]

[📦 商品リンク: moshimo-book-server-infra]

[📦 商品リンク: moshimo-book-jenkins-jissen]