コードは書いた瞬間に動きはじめ、インフラ設定は書いた瞬間から腐りはじめる。
複数のCIが一斉に落ちた日に学んだ7つのパターン
朝、CIのダッシュボードが赤一色だった。昨日まで緑だったプロジェクトが、手元で一行も書き換えていないのに、いくつも同時に落ちている。
最初は自分のミスを疑った。でも犯人は一人もコードを触っていなかった。全部「インフラ設定の時間劣化」だった。トークンは期限切れ、シークレットは乖離、休眠アプリはリソースを食い続けていた。
一日かけて手で潰していったら、原因がきれいに7つのパターンへ分類できた。次に同じ赤を見た時、原因の当たりを最短で付けられるように、現場のまま残しておく。
パターン1: デプロイトークンが期限切れで無音のまま失敗する
ホスティングプラットフォームへのデプロイに使うトークンには有効期限がある。期限切れになっても、CIのエラーログには「authentication failed」としか出ない。どのトークンが期限切れかは自明ではなく、プラットフォームのダッシュボードで確認しに行く必要がある。
教訓: デプロイトークンは発行日をどこかに記録しておく。ローテーション頻度は使用頻度に合わせて設定する(3〜6ヶ月が目安)。
パターン2: アプリ側のシークレットとCI側のシークレットが別管理で乖離する
アプリが実行時に読む環境変数(Fly.ioのsecrets、Vercelのenv等)と、GitHub Actionsが読むRepository Secretsは完全に別の保管場所だ。
片方だけ更新してもう片方を忘れると、ローカルでもアプリ本番でも動くのにCIだけ落ちる、という状態になる。
特に.env.exampleに書いてあるキー名と、アプリが実際にprocess.env.XXXで読む変数名が一致していないと気づきにくい。
教訓: シークレットの追加・ローテーション時は「アプリ側」「CI側」の両方を更新するチェックリストを持つ。.env.exampleのキー名は実際のenv読み取り名と完全一致させる。
パターン3: 休眠アプリが組織全体のリソース上限を食いつぶす
ホスティングプラットフォームには組織単位のマシン(インスタンス)上限がある。
停止中(stopped)のアプリも上限にカウントされる。長期間放置した休眠アプリが増えると、アクティブなアプリの新規デプロイはもちろん、既存アプリのin-place updateも「上限超過」で失敗するようになる。
教訓: 定期的に全アプリのインスタンス数を棚卸しする。「停止済みだから大丈夫」は正しくない。不要なアプリはインスタンスをdestroyするか、アプリごと削除する。
パターン4: コードフォーマッタのCIチェックが長い行で引っかかる
コードフォーマッタをCIに組み込むと、ローカルでパスしていても行長制限や自動補完で落ちることがある。
特にテンプレートリテラルの中に長い文字列を書いた時、ローカルのエディタ設定とCIのフォーマッタ設定が微妙にズレていると再現が難しい。
教訓: コミット前にpnpm lint --write(または対応するフォーマットコマンド)を必ず実行する。pre-commitフックに仕込んでおくのが最も確実。
パターン5: 内部ネットワーク上のDBにCIランナーからアクセスできない
本番環境で使うデータベースが内部ネットワーク(VPC内、プラットフォームの内部アドレス)に置かれている場合、GitHubのCIランナーからは物理的に到達できない。
本番DBを使うマイグレーションテストやクーロンジョブのCIスケジュール実行は、この制約で静かに失敗し続ける。
教訓: CIからアクセスできるエンドポイントかどうかを確認してからスケジュールを組む。到達できないDBを使うジョブはCIでスケジュール実行しない(手動トリガーかアプリ本番のクーロンに任せる)。
パターン6: サードパーティAPIの認証情報が失効する
外部サービスのAPIトークン(SNS投稿自動化、データ取得等)は突然無効化されることがある。理由はアプリのセキュリティポリシー変更、スコープの変更、長期未使用による自動失効、API利用規約の更新など。
教訓: 外部APIの認証情報は定期的に動作確認する。自動化ジョブが止まっていても気づかないことが多いので、失敗時に通知が来る仕組みを入れる。
パターン7: 無料枠のメール送信APIがクレジット上限に達する
開発初期に無料プランで使い始めたメール送信サービスが、アプリがある程度の規模になると送信数の上限に引っかかる。エラーは401 Maximum credits exceededのような形で出る。
教訓: メール送信は早い段階から本番グレードのサービスに切り替えておく。無料枠は検証用と割り切り、自動化ジョブには使わない。
まとめ: 「インフラの時間劣化」を定期的に点検する
上の7つに共通するのは「コードを変えていないのに壊れた」という点だ。
コードはレビューもテストも走るのに、インフラ設定には賞味期限を見張る仕組みがない。だから静かに腐る。トークンは期限切れになり、シークレットは乖離し、休眠アプリはリソースを食い続ける。
防ぐ一番の方法は、派手な仕組みではなく「定期点検」だと実感した。月曜の朝、コーヒーを淹れる10分で回せるくらいの軽さでいい。
月次でこれだけ眺める:
- デプロイトークンの発行日 — 3ヶ月以上前のものはローテーション候補
- 直近1ヶ月でシークレットを足した/回したプロジェクト — CI側とアプリ側、両方更新したか
- 全アプリのインスタンス数を棚卸し — 停止中の休眠アプリが上限を食っていないか
- 自動化ジョブのログ — 「成功した記録」が今月もちゃんと残っているか(無音の失敗が一番怖い)
この点検こそ、相棒のAIに渡すと一番効く仕事だ。トークンの発行日や全アプリのマシン数を一覧で集めてくるのは人間がやると退屈だが、AIには得意な往復作業で、人は出てきた赤だけを見て判断すればいい。点検を仕組みに変えてしまえば、「全部同時に落ちる日」はもう来ない。
「全部同時に落ちた」は、結局「全部同時に放置していた」の鏡像だった。