20年モノSNS移行 後編 ─ DNS切り替えからHTTPS化、移行後トラブル全記録
はじめに
前回の記事では、旧レンタルサーバからUbuntu + Docker Composeへの環境構築とコンテンツ同期までを記録した。今回はその続き、DNS切り替えから移行完了までの後半戦だ。
後半戦はむしろ問題が多かった。「ファイルを並べ替えてrsyncするだけ」のはずが、メール環境の再構築、SSL設定、HTTPS化後のリダイレクトループ、謎のヘッダー画像差し替えなど、次々とトラブルが発生した。それをClaude Codeと一緒に解決していった記録を残す。
メール環境の移行(DNS切り替え前)
DNS切り替え前に気づいた。メール受信環境をまったく移行していなかった。
旧サーバのOpenPNEは、会員登録の確認メールやSNS内の通知メールを処理するために、サーバー側でメールを受信してPHPスクリプトに渡す構成になっていた。具体的には /etc/aliases でメールをパイプしてPHPを呼ぶ仕組みだ。
# 旧サーバ /etc/aliases
rv-navi_op_user: "|php /home/site01/OpenPNE_mysite/bin/mail.php"
この構成をDocker環境に移植するのは一筋縄ではいかない。コンテナ内のPostfixからホスト側Postfixへリレーし、ホスト側からDockerコンテナ内のPHPを呼び出す必要があるからだ。
Claude Codeに「旧サーバの設定を調査して新サーバで同じ環境を再現して」と頼んだところ、以下の構成を設計してくれた。
コンテナ内Postfix(リレー専用):
relayhost = [172.17.0.1] # Dockerホストへ転送
inet_interfaces = loopback-only
mydestination =
ホスト側Postfix:
# /etc/postfix/virtual
@mysite.com mysite_op_user
sns@mysite.com admin@example.com # 管理者転送先
# /etc/aliases
mysite_op_user: "|docker exec -i mysite-web-1 php /var/www/OpenPNE_mysite/bin/mail.php"
コンテナ内でメールを受け取り → ホストへリレー → ホストがDockerコンテナ内のPHPを実行、という流れだ。
このセットアップスクリプトも自動で作成してくれた。setup-postfix.sh として保存しておき、新サーバで sudo bash setup-postfix.sh を実行するだけで完結する。DNS切り替え前に動作確認(テストメール送信)もClaude Code経由で実施できた。
DNS切り替えとSSL設定
メール環境が整ったのでDNSを切り替えた。その直後にSSL証明書取得を実施。
Let’s Encryptの取得はHTTP-01チャレンジを使うため、certbotコンテナから /.well-known/acme-challenge/ への疎通が必要だ。nginx設定を一時的にHTTPのみにしておき、証明書取得後にHTTPSへ切り替えるという2段階手順をClaude Codeが事前に設計してくれていたので、当日の作業はスクリプトを実行するだけだった。
# Phase 1: HTTP onlyでcertbot実行
docker compose run --rm --entrypoint '' certbot certbot certonly \
--webroot -w /var/www/certbot \
-d www.mysite.com -d mysite.com \
--email admin@example.com --agree-tos --no-eff-email
# Phase 2: nginx設定をHTTPS版に差し替えてリロード
cp nginx/conf.d/app-ssl.conf.template nginx/conf.d/app.conf
docker compose exec nginx nginx -s reload
証明書の自動更新はcertbotコンテナが12時間ごとに certbot renew を実行する設定にしてある。
トラブル①:HTTPS化直後にDB接続エラー
DB Error: connect failed
HTTPS化した直後にサイトを開いたら、このエラーが表示された。
原因はrsyncだった。DNS切り替え前にコンテンツを同期した際、config.php が旧サーバの値で上書きされていたのだ。
// 旧サーバの値に戻ってしまった
define('DB_SERVER', 'localhost'); // → 'db' であるべき
define('SESSION_SAVE_PATH', '/home/site01/OpenPNE_mysite/tmp'); // → コンテナ内のパスに変更済みだったはず
Claude Codeがエラーを見た瞬間に「rsyncで config.php が上書きされた可能性がある」と指摘。コンテナ内から直接 sed -i で修正した。
docker exec rv-navi-web-1 sed -i "s/'localhost'/'db'/" /var/www/OpenPNE_mysite/config.php
再発防止: 今後rsyncを実施する場合は --exclude config.php オプションが必須だと教えてもらった。
トラブル②:HTTPSにしたのにhttp://に戻るリダイレクトループ
DB接続が直ったが、今度はログインすると http:// のURLにリダイレクトされてしまう問題が発生。
原因を掘り下げていくと、2箇所の修正が必要だった。
1つ目: config.php の OPENPNE_URL が http:// のまま
define('OPENPNE_URL', 'https://www.mysite.com/'); // httpからhttpsに
2つ目: DBの c_admin_config テーブルにハードコードされた http:// のURL
UPDATE c_admin_config
SET value = 'https://www.mysite.com/?m=portal&a=page_user_top'
WHERE name = 'LOGIN_URL_PC';
OpenPNEの古いバージョンはDB内にURLをハードコードしている箇所がある。これはコードを読まないと気づかない。Claude Codeが「config.phpだけでなくDBにも設定が分散している可能性がある」とアドバイスしてくれて、関連テーブルを調べてくれた。
また、nginxからApacheへのプロキシ時に X-Forwarded-Proto: https を渡しているが、Apache側でそれを認識するための設定も必要だった。
RUN echo 'SetEnvIf X-Forwarded-Proto https HTTPS=on' >> /etc/httpd/conf/httpd.conf
トラブル③:サーバ再起動後にDockerが起動しない
「新サーバが再起動した時、サイトは自動復帰する?」という確認をしたところ、docker-compose.yml の一部サービスに restart: always が抜けていることが判明した。
services:
web:
restart: always # ← 抜けていた
certbot:
restart: always # ← 抜けていた
nginx:
restart: always # ← OK
db:
restart: always # ← OK
Claude Codeが docker-compose.yml を読んで「web と certbot に restart: always がありません」と即座に指摘。修正後、コンテナを再ビルドして対応した。
restart: always が設定されていれば、Dockerデーモン自体は systemd で管理されているため、サーバ再起動後に自動でコンテナが立ち上がる。
トラブル④:ヘッダー画像が旧サーバと違う(一番の謎バグ)
移行完了後に気づいた。旧サーバと新サーバでサイトのヘッダー画像が異なって見える。
最初はファイルが欠けているのかと思い、スキン画像ディレクトリを旧新で比較した。
# 旧サーバ skin/900_openpne_ver28/img/: 58ファイル
# 新サーバ skin/900_openpne_ver28/img/: 58ファイル(同じ)
ファイル数は同じ。サイズも同じ。なぜ違う画像が表示されるのか。
Claude Codeが「OpenPNEはスキン画像を表示する前に is_readable() でファイルの存在確認をしている。このパスが間違っている場合、デフォルトスキンにフォールバックする」と推測。コードを確認すると:
// function.t_img_url_skin.php
$file = sprintf('skin/%s/img/%s.%s', OPENPNE_SKIN_THEME, $p['filename'], $ext);
if (!is_readable(OPENPNE_PUBLIC_HTML_DIR . '/' . $file)) {
// ← ここでデフォルトスキンにフォールバック!
$file = sprintf('skin/default/img/%s.%s', $p['filename'], $ext);
}
OPENPNE_PUBLIC_HTML_DIR を確認すると…
// config.php
define('OPENPNE_PUBLIC_HTML_DIR', '/home/site01/public_html'); // 旧サーバのパス!
コンテナ内の実際のパスは /var/www/html なのに、旧サーバのパスが残ったままになっていた。is_readable() が常に false を返し、カスタムスキン画像の代わりにデフォルトスキンが使われていたのだ。
docker exec rv-navi-web-1 sed -i \
"s|'/home/site01/public_html'|'/var/www/html'|" \
/var/www/OpenPNE_mysite/config.php
修正後、Smartyのテンプレートキャッシュをクリアしてブラウザのハードリフレッシュ(Cmd+Shift+R)をしたところ、カスタムスキンのヘッダー画像が正しく表示されるようになった。
ちなみに「ヘッダーが違う」と気づいてから根本原因にたどり着くまで、コード調査・パス確認・DB確認と複数の仮説を順番に潰していった。この手の「なんか違う」系バグを一人で調べていたら数時間溶けていたと思う。
今回の移行で生じた設定変更まとめ
// config.php に必要な修正(旧サーバ値 → Docker環境値)
define('DB_SERVER', 'db'); // localhost → db
define('SESSION_SAVE_PATH', '/var/www/OpenPNE_mysite/tmp');
define('OPENPNE_URL', 'https://www.mysite.com/');
define('OPENPNE_PUBLIC_HTML_DIR', '/var/www/html'); // 旧パス → コンテナ内パス
-- DB内の設定も変更が必要
UPDATE c_admin_config SET value = 'https://www.mysite.com/?m=portal&a=page_user_top'
WHERE name = 'LOGIN_URL_PC';
これだけの修正点が必要だと、事前には把握できていなかった。rsyncで都度上書きされるリスクもあり、移行後は変更箇所を意識しておく必要がある。
Claude Codeと進める移行作業の実感
前回・今回の作業を通じて感じたことをまとめる。
ファイルを直接読んでくれる強み
「このエラーが出た」と伝えると、関連するコードを自分で読みに行って原因を特定してくれる。「Apacheのログを見せて」→「次はconfig.phpを確認します」→「DBのこのテーブルも確認が必要です」という流れが自然で、人間のエンジニアがデバッグする時と同じ思考をたどってくれる感覚がある。
「次に何が必要か」を先回りして教えてくれる
HTTPS化を進める際、「nginx設定だけでなく、Apache側にX-Forwarded-Protoの処理が必要」「OpenPNEのURLはconfig.phpとDBの2箇所にある」といった、知らないと見落とすポイントを自動で押さえてくれた。
スクリプトとして残してくれる
setup-postfix.sh、init-letsencrypt.sh など、一連の手順をスクリプトとして生成してくれる。手順書を別途書く必要がなく、次回同じ作業が必要になった時にも使い回せる。
おわりに
DNS切り替え〜HTTPS化〜移行後トラブル対応まで、後半戦だけでも4〜5個のトラブルが発生した。それぞれ単独でも調査に時間がかかる内容だったが、Claude Codeと並走することで1日以内に全て解消できた。
特にヘッダー画像の謎バグのように「何かがおかしい」程度の情報しかない状態から、コードの奥深くにある is_readable() のパス問題まで辿り着けたのは、コード全体を把握した状態でデバッグを手伝ってくれるAIの強みが出ていたと思う。
これで一連の移行作業は完了。約20年稼働し続けたSNSサイトが、無事Docker環境へ引っ越しできた。