Herokuを止める前に導線を移す — app.eurekapu.com 静的化の本番切替とUdemyリンク62本の移設

開発eurekapu

朝の時点で、Googleタスクに今日期限のものが4件並んでいた。本命は app.eurekapu.com の静的化の本番切替で、これはDNSを1レコード書き換えれば終わる。手順は前日に確定している。

ただ、順番が引っかかっていた。

まず2件を閉じた

1Password のサービスアカウント化は、調べさせたら未実施だった。記憶では終わっているつもりでいたので逆の結果だったが、急ぐ理由もないし、そもそもやる必要がないと判断してタスクごと閉じた。将来のセッションが計画書を見て再着手しないよう、計画書の冒頭に中止の一行だけ足させた(既存の内容は消さずに残す)。

教材化のほうは、PR 12本がすべてマージ済みで、12講が公開状態になっていることを証拠つきで確認できた。こちらも閉じる。

残ったのが、Udemyのスライドリンクの移設と、app静的化の本番切替。この2つだった。

どちらを先にやるか

Udemyの自講座(会計入門系)の補足リンクは、app.eurekapu.com のページを指している。そのappを今日Cloudflareへ移し、数日後にはHerokuを止める。

先に静的化を終わらせてからリンクを移すのか、逆か。聞いてみたら、スライドからで正しいという答えが返ってきた。理由は単純で、Udemyの導線を先に info-accounting.com へ移してしまえば、あとでDNSを切ってHerokuを止めるとき、受講生への影響が構造的にゼロになる。切替時に何が起きても怖くない状態を先に作る、という順序だ。

移設先はもう出来上がっていた

調査させたら、思っていたより話が小さかった。

info側にはスライドビューアとデータがすでに移植済みで、公開もされていた。画像1,286枚も別ドメインのR2に移してあった。残っていたのは、Udemyが参照している61ページを info側のURLへ対応づけて、リンクを差し替える作業だけだった。

対応表は機械抽出させた。62リンクのうち、直接ひけるのが24本、スライドのグループから導出するのが38本、未解決ゼロ。

読み上げ原稿は自分だけ見えればいい

スライドビューアには、読み上げ原稿のセクションとURLのセクションがある。これは受講生に見せるものではないので、adminアカウントだけに表示を絞ってもらった。

devで両方の状態を確認したところまでは報告が上がってきたが、これは本番で自分の目で見るまで信じない類のものだ。デプロイまでやってもらい、非adminのアクセスで両セクションが消えることを本番で確認させた。

差し替えは通った。導線は通らなかった

Udemyには一括更新のUIがない。ログイン済みのChromeから、リソースを1件追加するときのリクエストを実際にキャプチャさせて、asset作成のAPIルートを特定した。あとは13件ずつバッチで叩くだけになる。

バッチ1が13/13。バッチ2も13/13。

そこで受講生プレビューを開いて、リソースを実クリックさせた。ここで手が止まった。演習系のリンクは、info側に受け皿がない。スライドは移してあったが、演習ページは app に残ったままだった。

演習系38本は一旦appに戻すほかない。差し戻しは、元タイトルと旧URLで作り直してから新しいリンクを消す逆向きの手順で、38/38成功。全数検証で元の状態と一致した。

戻したことで、問題の形ははっきりした。演習ページがそもそもappから移行できていない、というだけの話だ。移植の計画書を作らせて、別セッションへ渡した。

静的化の本番切替

nuxt generate の再実行から。約12分でEXIT=0、HTML 795・4,610ファイル。ログに出た "error" 13件は本物かどうか中身を確認させた。

Cloudflare Pagesに新規プロジェクトを作って、4,610ファイルのデプロイが63秒。プレビューでスライドの描画、仕訳クイズのクリック、主要ページの200を確認してから、カスタムドメインを追加した。DNSレコードはHerokuのホスト名からPages側へ自動で書き換わる。

切替は即時だった。切替直後のプローブでPages配信を確認して(トレイリングスラッシュの308が識別子になる)、全794ルートのHTTPスイープが794/794の200。

エラーが出たらすぐ戻せるように、ドキュメントの一番上にロールバック手順を書かせた。戻すべきDNSの値、直リンク、Pages側のカスタムドメインを先に外す注意。Herokuは止めずに稼働させたままにしてある。

Codex 2並列でUI全面テスト

794ページを367と427に割って、Codexを2本並列で走らせた。

配信面はクリーンだった。794/794が200、参照アセット835も全部200、致命的な配信障害ゼロ。

問題はそこに映らないところにあった。200を返すのに本文が空のページが2件。HTTP監視では永久に見つからない類のもので、片方はExcelセミナーの1ページ、もう片方は演習の空スタブだった。

品質面の宿題も出てきた。description欠落204ページ、h1欠落60ページ、alt欠落154画像、汎用titleが48ページ。

40分で直せる分だけ直した

すぐ終わらないのかと聞いたら、すぐ直せる範囲と重い範囲を分けて出してきた。すぐ直す方を選んだ。

  • alt欠落154画像の大半は、共通コンポーネントの1文字のタイポだった。:alt="item.imgAlt" と書いてあり、レイアウト側は元から imageAlt を渡していた。1行直すだけで大量ページのaltが復活した
  • description欠落204ページ — 設定ファイルの既定descriptionを日本語の実文に変えて、全ページのフォールバックとして効かせた
  • 空スタブ1件 — ページを削除して301
  • もともと500だった3ルート — 存在しない章キーでフィルタして0枚になりクラッシュしていた既存バグ。_redirects を新設して301で実在ページへ逃がした

再生成して再デプロイ、本番で301とaltとdescriptionを実測確認。ここまで約40分だった。

残った1ページは直せていない。generateのときだけSSRが静かに失敗して、SPAシェルだけが出力される。devサーバーでは成功する。2ビルド連続で、794ページ中この1ページだけが同じように落ちた。ブラウザではJSが起動すれば正常に表示されるので、実害はSEOと初期描画に限られる。空リンクの <nuxt-link to=""> を疑って除去したが、原因ではなかった。

午後、演習系38リンクをinfoへ

朝渡した計画書を別セッションで実行した。移植元と移植先の理解を5体の並列調査に分担させてから着手している。

決裁フォームの項目を読んでいて思い出したことがあった。appの演習ページは、ログインなしで見える形で公開していた。無料で開けるようにしていたのを、すっかり忘れていた。

だとすれば移植先も同じ扱いでいい。認証は設けず、サイト内の目次・一覧・内部リンクには載せない公開ページを作る。実質、Udemyのリソースリンクを知っている人だけが来るページになる。

37ページ・108設問を生成した。画像は、設問側566枚がすでにR2へ複製済みで、足りなかったのは○×バーの3枚だけ。ただしスライド画像は抜けていて、あとから141枚を追加でアップロードした。

描画は自動で通ったことにせず、タイプごとに実クリックで確かめさせた。クイズの選択から答え合わせ、解答解説の展開、スライドを送ると財務諸表の数字が動く連動(普通預金 1000→700、売上原価 300 計上)まで。37ページ全部の描画を確認してからデプロイした。

Udemy側の差し替えは3バッチ(13/13/12)で38件全成功。全数再検証で、演習系のappURL残存はゼロになった。これで自講座のスライド系62リンクは全部infoに移った。

別リポジトリの積み残しも2件

eurekapu-nuxt4 は、pushとデプロイ(6.5分)と履歴CSVの追記。前日から持ち越したWebKitのE2E 4件フェイルは、webkit固有のバグではなく、devサーバーのコールドスタート由来のフレーキーだと確定した。ウォーム状態では再現せず、ワーカーの終了が5分ハングして強制killされているログが残っていた。前日「単独再実行が0バイトのまま終わった」ように見えたのも、たぶん同じ現象だった。

wordpress-migration のほうは、前日の画像障害の復旧が本番で生きていることを実測で確認した。恒久対応はこちらではなく mdx-playground 側でやると決めて、進捗台帳に一言足してコミットした。

残っていること

  • Herokuの停止 — ダッシュボードのThroughputが数日 0 rps を続けたら止める。不可逆なので自分の手でやる
  • SSRが1ページだけ失敗する件 — 生成の並列度を絞って再生成するか、移植のときに吸収するか
  • Google側の対応 — ドメイン・URL・コンテンツ・HTTPSが全部不変でホスティングだけが変わったので、Search Consoleの再登録もタグの入れ直しも不要と確認済み

学んだこと

順番の判断は結果的に正しかった。逆順でDNSを切ってから「演習系の受け皿がない」と気づいていたら、受講生のリンクが切れた状態で慌てて直すことになっていた。移設は導線が先、インフラが後。

200は「中身がある」を意味しない。 794/794の全部グリーンを見て安心しかけたところに、本文が空のページが2件出てきた。HTTPコードだけを見る監視は、この種の障害を一生報告しない。

量の多い不具合ほど、原因が1箇所にまとまっていることがある。alt欠落154画像の正体は、共通コンポーネントの1文字だった。