上原正吉(EarthLink Network Co., Ltd.)。Claude Codeを開発の主体に据え、20を超えるプロダクトを1人で同時に開発・運用しています。これは、その現場の実測記です。

2026 年 6 月、AWS 請求の見直しを始めました。目的は、今月の請求を削ることではありません。いま数ドルの小さな課金でも、利用が 100 倍になれば固定費として重く効きます。将来の額を先に想像し、運用しながら直していく。これはその実測記録です。
最初に分かったのは、一番コストがかかっているのは本体機能ではない、という事実でした。かかっていたのは、CDK(コードで AWS 構成を定義するツール)が自動で立てた NAT Gateway(プライベート網から外部へ出る中継。立てているだけで課金される)、ビルドだけをしている Amplify(AWS のビルド・ホスティング基盤)、15 分ごとに走る稼働確認の Lambda(イベントで起動する関数実行基盤)、そして誰も見ていない dev(開発)環境に溜まった 82 万件のゴミデータでした。どれも今は小さい、あるいは気づきにくい課金です。ですが放置すると、利用や時間に比例して伸びます。だから「100 倍になったらどうなるか」で見て、順に対処しました。
結論を先に置きます。
natGateways を明示しないと AZ(アベイラビリティゾーン。データセンターの区画)ごとに 2 基立ち、月 $58〜66 かかっていました。「サーバーレスだから固定費ゼロ」は自動では成立しません私のポートフォリオの請求は、ほぼ 1 つのニュースキュレーション基盤に集中していて、他のプロダクト(静的サイトや小さな SaaS(サービスとして提供されるソフトウェア))は月数ドル以下でした。だからこそ「本体以外」を先に対処するのが効きました。以下、何を測り、どう直したかを残します。

最初に見つかった大きな固定費は、稼働監視 SaaS のスタックにありました。ブラウザ監視を ECS Fargate(サーバー管理不要のコンテナ実行基盤)で動かす構成で、CDK に VPC(仮想プライベートネットワーク)を明示していなかったため、暗黙 VPC が自動生成され、AZ ごとに NAT Gateway が計 2 基立っていました。これが月 $58〜66。dev 専用ワークロードの Lambda と DynamoDB を全部足した額より、この固定費の方が高いという逆転が起きていました。
IP allowlist(接続元 IP を限定する運用)もしておらず、NAT の固定出口 IP に価値はありませんでした。そこで VPC を明示し、NAT をゼロにして、public subnet(外部から直接到達できるネットワーク区画)からの直接 egress(外向き通信)へ変えました。
// 暗黙 VPC に NAT を勝手に立てさせない。ゼロを明示する
const vpc = new ec2.Vpc(this, "MonitorVpc", {
natGateways: 0,
subnetConfiguration: [
{ name: "public", subnetType: ec2.SubnetType.PUBLIC },
],
});
// Fargate タスクは public subnet + パブリック IP で外に出る
new ecs.FargateService(this, "BrowserWorker", {
cluster,
assignPublicIp: true,
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
});
これで月 $58〜66 が消えました。金額は小さく見えますが、NAT は立っているだけで課金される固定費で、AZ やプロダクトを増やすほど基数が増えて比例して伸びます。今 $64 でも、構成が 100 倍に育てば重い固定費になります。だから小さいうちに、明示的にゼロへ倒しました。サーバーレスだから固定費ゼロは、自動では成立しません。IaC のデフォルトが何を立てるかまで見ないと、掲げた設計原則が裏で破られます。
次はデプロイ基盤です。CI/CD(変更のたびに自動でテストしデプロイする仕組み)の自作ポータルでコストを可視化し、Amplify の請求を Cost Explorer(AWS の費用内訳を分析するツール)で分解したところ、月次請求の 87〜89% が BuildDuration、つまりビルドに費やした時間でした。実際のランタイム(SSR(サーバー側で HTML を生成する方式)ホスティング)はわずか月 $3〜7。6 月は一時 $60 台に跳ねています。
当初の計画は SSR を別基盤へ移設することでしたが、それはコストの 11% 側を触る話でした。触るべきは 89% 側、すなわちビルドそのものを止めることです。git 連携の自動ビルドを停止し、自前の CI ランナーでビルドした成果物だけをデプロイする「build once, promote many(1 回作って各環境へ昇格させる)」に切り替えました。成果物を dev→staging→prod へ昇格させるので、Amplify 上のビルドは基本ゼロになります。ビルドは開発が活発になるほど回数が増えるため、今の額が小さくても将来は比例して伸びます。だから移設ではなく、丸ごと止めました。
ここで 1 つ、プラットフォーム固有の制約に当たりました。git 連携済みの Amplify アプリは、成果物を直接送り込む create-deployment API(成果物を直接アップロードしてデプロイする API)を拒否します。既存アプリの設定変更では回避できず、環境ごとに repo 未連携の「手動デプロイ専用アプリ」を新設して対処しました。
3 つ目が、15 分ごとの monitor(監視)です。キュレーション基盤の DynamoDB 読み取りコストが月 $47 に達していて、内訳を追うと本体機能ではなく、パイプラインの健康状態を監視する Lambda が 15 分ごとに 1.22GB の GSI(グローバルセカンダリインデックス。検索用の副次索引)を全ステータス分 COUNT(全件を数える走査)していたことが原因でした。しかもその大半は、一度終端に落ちたら変化しない除外系ステータス(パースエラーや anti-bot(ボット除け)による除外)まで毎回数え直していました。監視そのものがコスト源になっていた形です。
応急処置は、ほぼ変化しない終端ステータスの集計頻度を落とすことでした。
# 全ステータスを 15 分毎に COUNT していたのをやめ、
# 変化しうるものだけ高頻度、終端系は 2 時間毎に間引く
ACTIVE = {"RAW", "CRAWLED", "ANALYZING"} # 動くもの
TERMINAL = {"EXCLUDED_PARSE_ERROR", "EXCLUDED_ANTIBOT", "PUBLISHED"}
def collect_counts(now):
statuses = ACTIVE.copy()
if now.minute < 15 and now.hour % 2 == 0: # 終端系は 2h に 1 回だけ
statuses |= TERMINAL
return {s: count_by_status(s) for s in statuses}
これで読み取りコストは約 87% 減(月 $47 → $5〜6 相当)になりました。ただしこれは応急処置です。この設計は、記事が増えるほど COUNT の対象も増え、コストがデータ量に比例して伸びます。今 $47 でも、扱う記事が 100 倍になれば読み取りもおよそ 100 倍。だから恒久解として、ステータス集計を毎回 GSI で数えるのをやめ、DynamoDB Streams(テーブルの変更を逐次流すしくみ)で件数を差分更新するカウンタテーブルにして、集計の読み取りを O(1)(件数に関係なく一定の処理量)にしました。これは翌月に実装しています。
戦線を重ねて見えてきたのは、コスト源がどれも「本体以外」に集まることでした。バックアップエンジンが毎日 dev テーブルをフルスキャンしていて、そこに溜まった 82 万件超のクロールジョブ痕跡・記事ゴミが、スキャン量とバックアップ量を押し上げていました。dev を運用系の 665 件までパージして解消しました。稼働監視 SaaS 側でも、dev の 22 モニターを 5 分間隔から日次に落として月 $18→$4 に、prod の詳細コンテナメトリクスを無効化して CloudWatch(AWS の監視・メトリクス基盤)費を削りました。取り込み前トリアージで分析対象を約 94% 減らし、1 日あたりの分析コストを $0.13 に抑えたのも同じ流れです。
横断で効いたのが、複数プロバイダのコストを 1 画面に集めるダッシュボードでした。ここで 1 つバグに当たりました。ある日 Anthropic のコストだけ異様に大きく表示され、調べると Admin API(Anthropic の管理用 API)はコストをセント建て(最小単位)で返すのに、スクリプトがドルとして合算していました。API の生値 "58.3135" は $0.58 が正しく、100 倍に膨れていたのです。÷100 の 2 行修正で直りましたが、3 プロバイダで通貨単位が全部違う(一方はセント、一方はドル、請求は円)という点をメモに残しました。将来の額を想像する前提は、まず今の数字が正しく見えていることです。可視化が 100 倍間違っていれば、コスト判断はまるごと狂います。

やってみて分かったのは、コストの出やすい場所には共通構造があることです。本体機能はリクエストが来たときだけ課金される (弾力的) のに対し、NAT・ビルド・周期監視・バックアップは「動いていなくても・変化がなくても走り続ける」定常コストです。定常コストは、規模や時間、データ量に比例して静かに伸びます。しかも IaC のデフォルトや「とりあえず 15 分間隔」のような無意識の設定として紛れ込むため、機能開発の視界に入りません。
だから「今いくらか」ではなく「100 倍になったらいくらか」で見ます。そのうえで「測ってから直す・直したら測る」を運用に固定し、コスト変化を請求ではなくダッシュボードで日次に見るようにしました。請求書は 1 か月遅れて届くので、それだけを見ていると手遅れになります。適切なインフラとは、機能が動くことではなく、この定常コストが将来も破綻しない形に整えることでした。
natGateways を明示しないと NAT が勝手に立ちます。「サーバーレス=固定費ゼロ」は自動では成立しません上原正吉。EarthLink Network Co., Ltd. でAI開発をしています。2025年からClaude Codeを開発の主体に据え、今は20を超えるプロダクトを1人で同時に開発・運用しています。この連載では、その現場で実際に起きたこと(うまくいったことも、失敗も)を、数字と一緒に書いていきます。
また、AIで業務や開発を組み替えたい会社・チーム向けに、AI活用のコンサルティングも受け付けています。ご相談は www.eln.ne.jp からどうぞ。