EC2からECS on Fargateへ移行し、GitHub Actionsで更新できるようにした後、稼働状況と費用を見直しました。第3回は、ChatGPT ProとともにCompute Optimizerの推奨や実測値を確認し、縮小・統合・監視改善を進めた記録です。障害復旧のために費用を増やした判断も含めてまとめます。(2026/09/27作成)
janjf93 / Pixabay
AWS CLI参考コードについて:以下は記事の確認内容を再現するための汎用例で、当時の実行ログそのものではありません。AWS CLI v2とBashを想定しています。<…>は自分の環境の値へ置き換え、認証済みのプロファイルと対象APIを利用できる権限を用意してください。同じ記事のコードは、先頭の変数設定を行った同じシェルで実行する想定です。内部のサービス名・ID・認証情報は掲載していません。
参考コード:実行環境の指定
export AWS_PROFILE='<AWS_PROFILE>'
export AWS_DEFAULT_REGION='<AWS_REGION>'
export AWS_PAGER=''
CLUSTER='<ECS_CLUSTER>'
SERVICE='<ECS_SERVICE>'
# 接続先アカウントとロールを確認する
aws sts get-caller-identity
0.移行できたことと、安く運用できることは別に確認する
Fargateへ移行した直後の試算では、当初の追加費用の目標に収まりませんでした。CPUとメモリだけを見ていると、ALB、パブリックIPv4、EFS、定期処理、通信、監視などを見落とします。まず、何に費用が発生しているかを整理しました。
使用したのは、Compute Optimizer、Cost Optimization Hub、Cost Explorer、CloudWatchの情報です。推奨サイズ、削減見積り、請求の内訳、CPU・メモリの時系列を突き合わせ、変更する対象と見送る対象を分けています。
参考コード:Compute Optimizerの推奨を取得する
対象サービスの推奨をJSONで取得し、評価対象の構成、観測期間、更新日時と候補を確認します。Compute Optimizerへの登録と十分なメトリクスが必要です。結果が空でも、最適化済みという意味にはなりません。
SERVICE_ARN=$(aws ecs describe-services \
--cluster "$CLUSTER" --services "$SERVICE" \
--query 'services[0].serviceArn' --output text)
aws compute-optimizer get-ecs-service-recommendations \
--service-arns "$SERVICE_ARN" --output json
公式リファレンス:aws compute-optimizer get-ecs-service-recommendations
1.平均使用率だけでサイズを決めない
平均CPU使用率が低くても、起動、データ復元、定期処理、アクセス集中で一時的に負荷が上がります。そこで、CPUとメモリの平均・最大、観測期間、現在の構成が動き始めた時刻を確認しました。
構成変更前後のデータが同じグラフに入る場合にも注意が必要です。過去の大きなピークが現在の構成で起きたとは限らず、直近の低い値だけでは起動時の余裕を説明できません。推奨が評価したタスク定義と、今動いている定義の違いも確認しました。
検索基盤では段階的な縮小を行い、9月19日に0.5 vCPU・1 GiB、9月26日に0.25 vCPU・512 MiBへ変更しています。データのスナップショット、復元、ヘルスチェック、変更後の定期バックアップを確認し、元の構成へ戻す手順も残しました。
ここで確認できたのは、切替と初期動作の正常性です。長期のピーク負荷や全件再インデックス、同時検索への耐性まで実証したわけではなく、引き続き観測する項目です。
参考コード:CPU・メモリの平均と最大を時系列で確認する
直近の24時間を指定する例です。日時は対象期間のUTCへ置き換えてください。サービス単位の指標なので、個別タスクの瞬間的なピークをすべて捉えるものではありません。5分ごとのMaximumとAverageを並べ、変更時刻と照合します。期間を広げる場合は保持期間と1回1,440データポイントの上限に応じてperiodも調整します。
START_TIME='<START_UTC_ISO8601>'
END_TIME='<END_UTC_ISO8601>'
# 日時形式例:2026-09-25T00:00:00Z
for METRIC in CPUUtilization MemoryUtilization; do
aws cloudwatch get-metric-statistics \
--namespace AWS/ECS --metric-name "$METRIC" \
--dimensions "Name=ClusterName,Value=$CLUSTER" \
"Name=ServiceName,Value=$SERVICE" \
--start-time "$START_TIME" --end-time "$END_TIME" \
--period 300 --statistics Average Maximum \
--query '{metric:Label,points:sort_by(Datapoints,&Timestamp)}'
done
公式リファレンス:aws cloudwatch get-metric-statistics
2.これ以上小さくできない場合は、統合を検討する
すでにFargateの最小構成で動いている小規模Webでは、CPUやメモリをさらに下げることはできません。そこで、一つのWeb機能を既存の共有タスクへ統合しました。
既存の処理やデータを保ち、新しい転送先の正常性を確認してからALBを切り替えています。その後、旧サービスを廃止し、不要になったタスクのパブリックIPv4も解放されることを確認しました。
ただし、統合後は更新や障害の影響を共有します。単独タスクの固定費を減らす代わりに、同じタスク内の競合と影響範囲が増えます。短時間の確認で十分とせず、繁忙時間帯も含めて観測する方針です。
3.不要なものを整理し、必要なものを残す
9月14日には移行済みの旧EC2 2台を終了し、EBS 3本と旧Elastic IPを整理しました。EC2を停止しただけでは残るディスクやIPの費用を、個別に確認した作業です。移行前のスナップショットは復旧用に保持しました。
その後、不要となったメール移行用S3バックアップも整理しています。一方、現在の配信に使う成果物、イメージ、共有ストレージや、本番ALBが使用するIPは残しました。名前や見た目だけでは、削除してよいか判断できません。
ネットワークも比較しましたが、今回の規模ではNAT Gatewayを追加しても節約にならないため見送りました。また、既存のSavings PlansがFargateに適用されていることを確認しています。契約の利用率が100%でも、すべての計算費用を割引で賄えているとは限りません。
4.EFSの帯域不足では、安定性を優先した
9月下旬には、共有EFSを使う複数のWebで起動失敗やALBの異常が発生しました。調査ではEFSのバーストクレジットが枯渇し、利用可能な帯域が大きく低下していることを確認しました。
当初はPHPの警告や設定不足も疑いましたが、共有するストレージの状況と、複数サイトで同時に起きた現象を突き合わせました。EFSをProvisioned 10 MiB/秒へ変更し、同じイメージ・設定のまま再デプロイしたところ復旧しています。警告は復旧後も残っており、それだけを原因と断定できないことも分かりました。
この対応は追加費用を伴います。それでも、必要な帯域を確保して復旧させる判断を優先しました。今後はPHPやキャッシュによるEFSへの読み書きを減らし、安定性を保ちながら適正な帯域を再検討します。
参考コード:EFSの設定とクレジット・帯域を確認する
上で指定した時間範囲を使います。BurstCreditBalanceとPermittedThroughputを同じ時刻で照合します。帯域指標の単位はBytes/Secondで、設定のMiB/秒とは異なります。クレジットの意味や取得可否はスループットモードに依存するため、最初に現在のモードを確認します。
FILE_SYSTEM_ID='<EFS_FILE_SYSTEM_ID>'
aws efs describe-file-systems --file-system-id "$FILE_SYSTEM_ID" \
--query 'FileSystems[].{mode:ThroughputMode,provisionedMiBps:ProvisionedThroughputInMibps,state:LifeCycleState}'
for METRIC in BurstCreditBalance PermittedThroughput; do
aws cloudwatch get-metric-statistics \
--namespace AWS/EFS --metric-name "$METRIC" \
--dimensions "Name=FileSystemId,Value=$FILE_SYSTEM_ID" \
--start-time "$START_TIME" --end-time "$END_TIME" \
--period 300 --statistics Minimum Average \
--query '{metric:Label,points:sort_by(Datapoints,&Timestamp)}'
done
公式リファレンス:aws efs describe-file-systems
5.監視の「赤」と、本当の障害を分ける
CloudWatchのTargetTracking AlarmLowは、今回の構成では低負荷時のスケールイン制御に使われています。ALARMと表示されていることだけを理由に、障害と判断したり、アラームを削除したりしないよう整理しました。
一方、実際の障害を検知する既存アラームには通知先がないものがあり、12件を既存SNSの通知先へ接続しました。設定を保存した後に通知アクションを再確認しています。ただし、受信箱へのメール到達試験は別で、記録時点では未実施です。
以前このブログに書いたCloudWatchの情報をZabbixへ保存する構成から環境は変わりましたが、実際の値を残して判断する姿勢は共通しています。古い記事の手順や保持期間の記述を、そのまま現在の仕様として使わないことも大切です。
参考コード:アラームの役割と通知先を確認する
対象アラームの状態・説明・アクションを読み取ります。AlarmActionsがSNSなのかスケーリングポリシーなのかを確認し、スケールイン制御と障害通知を区別します。このコマンドは通知の送信やメール到達試験を行いません。
ALARM_NAME='<CLOUDWATCH_ALARM_NAME>'
aws cloudwatch describe-alarms --alarm-names "$ALARM_NAME" \
--query 'MetricAlarms[].{name:AlarmName,description:AlarmDescription,state:StateValue,enabled:ActionsEnabled,alarmActions:AlarmActions,okActions:OKActions}'
公式リファレンス:aws cloudwatch describe-alarms
6.今回の成果と、これから確認すること
ChatGPT Proとの伴走で、推奨を読むところから、実測、変更、公開確認、復旧手順の整理まで進めることができました。同時に、障害原因の仮説は観測結果で見直す必要があることも、今回の作業で確かめています。
削減見積りには、異なる時点の構成や割引条件が混在します。古い推奨と新しい推奨を足した数字を、実際に安くなった金額として扱うことはできません。EFSの追加費用も含め、同じ期間・同じ費用指標で請求を比較する必要があります。
9月の取り組みで、移行、自動配信、縮小、統合、監視改善まで実践できました。今後は繁忙時の負荷、バックアップの鮮度、実際の請求を確認し、必要な性能と費用の釣り合いを調整していきます。
参考コード:同じ費用指標でサービス別コストを取得する
Cost Explorerを利用できる権限・設定が必要です(API利用料の対象)。開始日を含み、終了日は含まない期間指定です。AmortizedCostは前払い料金などを期間に配分する指標で、請求書の支払額そのものとは一致しません。同じ長さの比較期間・同じアカウント範囲で取得し、当月のEstimatedとデータ反映の遅延も確認します。
COST_START='<YYYY-MM-DD>'
COST_END='<YYYY-MM-DD>'
aws ce get-cost-and-usage --region us-east-1 \
--time-period "Start=$COST_START,End=$COST_END" \
--granularity DAILY --metrics AmortizedCost \
--group-by Type=DIMENSION,Key=SERVICE \
--query 'ResultsByTime[].{period:TimePeriod,estimated:Estimated,services:Groups}'
公式リファレンス:aws ce get-cost-and-usage
以上
よしまさのブログをもっと見る
購読すると最新の投稿がメールで送信されます。










