01kb.json が403を返す
まず気づいたのは、ブラウザのコンソールに出た一行だった。
JSONを期待したところにHTMLが返ってきている。まずは配信されているファイル自体を疑い、 静的スクリプト側から一つずつ確かめていった。
片方は通り、片方だけ403。Apacheのエラーログを見ると、原因は一目瞭然だった。
kb.json の権限は所有者(u)のみrw、グループ・その他は権限なし——いわゆる600相当。
旧サーバーではApacheの実行ユーザーとファイルの所有者が同じだったため問題なく読めていたが、
新サーバーではApacheの実行ユーザーをデフォルトの www-data に変更したため、
同じ600のファイルが突然読めなくなっていた。
chmod 644 kb.json で即座に復旧。curlも200へ。
本当の原因はもう一段深いところにあった
ここで一度は「umaskの設定差だろう」と考えたが、実際にkb.jsonを生成しているコードを見直すと、 そもそもumaskとは無関係に600が生まれる作りになっていた。
tempfile.mkstemp() はセキュリティ仕様として、umaskの設定に関わらず常に0600でファイルを作る。
そのtmpファイルをそのままos.replace()で本番パスへ差し替えているだけなので、
サーバーのumaskをどう設定しても、実行ユーザーを誰にしても、生成されるkb.jsonは必ず600になる運命だった。
旧サーバーで動いていたのは「umaskが正しかったから」ではなく、
「たまたまApacheの実行ユーザーとファイル所有者が同じで600でも読めていたから」に過ぎなかった。
os.replace() の直後に os.chmod(kb_path, 0o644) を1行追加。
再生成のたびに600へ戻ってしまう問題を止めた。
02RabbitMQ管理プラグインが応答しない
kb.jsonの復旧作業の途中、別のログにも目が止まった。
localhost が 127.0.0.1 ではなく 127.0.1.1 に解決されている。
/etc/hosts を確認すると、たしかに妙な状態だった。
本来は2行目が 127.0.1.1 <hostname> であるべきところ、両方とも
localhost を指している。一見これが犯人に見えたが、結論を急がず先にプラグイン自体の状態を確認した。
プラグインは未有効化、ポート自体がLISTENしていなかった。
/etc/hosts の重複行は事実として気になる状態ではあるものの、今回の直接原因ではない。
設定ファイルはコピーで引き継げても、プラグインの有効化状態はRabbitMQ自身が持つデータディレクトリ側の状態で、
新規インストールでは引き継がれていなかった。
sudo rabbitmq-plugins enable rabbitmq_management で解決。再確認すると
0.0.0.0:15672 でLISTEN——ついでにufwで外部からブロックされていることも確認し、
将来の保険として rabbitmq.conf に management.tcp.ip = 127.0.0.1 を足す方針にした。
03「移設のせい」ではなかったもの
kb.jsonの修正後も、埋め込みモデルのロードだけは120秒のタイムアウトで失敗を繰り返した。
CDN経由でモデル本体をダウンロードする処理なので、新サーバーのCSPヘッダーなどを疑ったが、 実機(Android Chrome / iPad Safari)でモバイル回線条件を揃えて確認したところ、 旧サーバーでも同じ条件では成功率が半々程度だったことがわかった。数十MBのモデルDLがLTE回線で 120秒に間に合わないのは移設以前からの既知の挙動であり、失敗時はサーバー検索へフォールバックする 設計が、まさに想定どおりに機能していただけだった。
04次の移設への教訓
-
Apacheの実行ユーザーを変えるときは、「サイトが表示されるか」だけでは足りない。
tempfile.mkstemp()のようにumaskを無視して権限を決め打ちする処理がコード内に潜んでいないか、 書き込み系の処理を洗い出して確認する。 - サービスやプラグインの有効化状態は、設定ファイルのコピーだけでは引き継がれないことがある。 「起動しているか」ではなく「ポートが実際にLISTENしているか」まで見て、機能そのものを検証する。
- 動かない原因を全部「移設のせい」だと決めつけない。モバイル回線のような外部要因は、 移設前の同条件と比較して初めて切り分けられる。