一覧に戻る
    Jenkins エージェントが1時間後に遅くなる謎、CPUじゃなくてネットワークだった
    開発ラボ
    PRこの記事には広告が含まれています

    Jenkins エージェントが1時間後に遅くなる謎、CPUじゃなくてネットワークだった

    12 分で読める

    最初に疑ったのは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"

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

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

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

    この記事は役に立ちましたか?

    Coffee cup

    この記事が、何かの整理につながったら

    コーヒー1杯分の応援をもらえると嬉しいです。

    ※ これは応援とは別の話ですが、

    同じようなテーマを自分の文脈で整理したい場合は、 (文章だけだと詰まりやすい人向けに) 思考整理の壁打ちという形で対話の時間も取っています。

    対話の時間について