はじめに ─ なぜ今この記事を書くか
Amazon Linux 2 の標準サポート終了日(2026 年 6 月 30 日)が目前に迫っています。本番で動き続けてきた WordPress 環境ほど、PHP・MySQL・Apache のバージョンが古いまま放置されているケースが多いはず。
本記事は、ある本番サーバー(14 サイト・28 個の WordPress が同居)を Amazon Linux 2 → 2023、PHP 5.6 → 8.5、MySQL 5.7 → MariaDB 10.11、Apache → nginx に移行した一連の作業の記録です。AI アシスタントを駆使しつつ進めて 所要時間は実働おおよそ 1 日。とはいえ、AI 任せにできない地雷だらけだった部分も含めて全て公開します。
同じ状況に置かれている方が、(1) 自前で移行する、(2) 移行を外部に委託する、(3) サーバー運用そのものから離れる ── のいずれを選ぶにせよ、判断材料となれば幸いです。
1. 移行前後の構成比較
旧サーバーは 2019 年ビルドのまま延命してきた構成で、PHP 5.6 と MySQL 5.7 で WordPress 26 DB ・ 28 インストールを抱えていました。
| 項目 | 旧サーバー (AL2) | 新サーバー (AL2023) |
|---|---|---|
| OS | Amazon Linux 2 (kernel 4.14, 2019年ビルド) | Amazon Linux 2023 (kernel 6.1) |
| Web | Apache 2.4 + mod_pagespeed | nginx 1.28 |
| PHP | 5.6.40 | 8.5.4 |
| DB | MySQL 5.7.28 | MariaDB 10.11.15 |
| ストレージ | 100GB gp2(78GB 使用) | 30GB gp3 暗号化(24GB 使用) |
| WordPress | 28 インストール / 26 DB / コア 3.x〜6.x 混在 | 全て WP 6.9.4 へ更新 |
サーバー上には WordPress に加え、独自 PHP コード、Welcart EC、BuddyPress、bbpress、Luxeritas テーマなどのレガシー資産が乗っており、PHP のバージョン跳躍が最大の難所になることは事前に予想できていました。
2. 移行戦略 ─ 並行運用方式
旧サーバーをそのまま残し、新しい AL2023 のインスタンスを並走で構築 → 検証 → DNS 切替の順に進める「並行運用方式」を採りました。in-place アップグレードは Amazon Linux 2023 では不可能で、また OS だけでなく PHP メジャー 4 段ジャンプを伴うため、本番を生かしたまま検証できる構成は必須です。
# AWS リソース作成(プロファイル指定) aws ec2 create-security-group --group-name <prefix>-al2023 ... aws iam create-role --role-name <prefix>-al2023-role ... aws ec2 run-instances --image-id ami-XXXXXXXX --instance-type t3.medium ... aws ec2 allocate-address --domain vpc ...
選択した構成は t3.medium(RI 適用)・30GB gp3 暗号化・IAM Role で S3 書き込み権限。旧サーバーから 100GB → 30GB に絞り込めた経緯は次のセクションのとおりです。
3. データ移行 ─ rsyncで63GB→23GBに圧縮
旧サーバーの公開ディレクトリは 63GB ありましたが、明確に不要な以下を除外することで 23GB まで削減し、新サーバーは 30GB の gp3 で運用できる見込みになりました。
- 各種バックアップ系(
ai1wm-backups/、backwpup-*/、*.wpress) - メールディレクトリ(別途扱い)
- 使われていない
dl/配下の旧 .exe / .db ファイル(約 30GB) - 長らく休眠状態のサブドメインの中身
転送は VPC 内のプライベート IP 経由で実行し、約 5 分で完了しました。
rsync -aHA --numeric-ids \ --exclude="ai1wm-backups/" \ --exclude="backwpup-*/" \ --exclude="*.wpress" \ --exclude="/<domain>/public_html/dl/" \ --exclude="/<old-subdomain>/" \ ec2-user@10.0.1.176:/srv/sites/ /srv/sites/
MySQL は mysqldump --add-drop-database --routines --triggers --events で 26 DB を一括ダンプし、新サーバーの MariaDB へインポートしました。MySQL 5.7 → MariaDB 10.11 はおおよそ素直ですが、DEFINER 句や utf8mb3/utf8mb4 のデフォルト変更には個別注意が必要です。
4. PHP 5.6 → 8.5 互換性、地獄の全パターン
メジャー 4 段ジャンプ(5.6 → 7.0 → 7.4 → 8.0 → 8.5)で、大量の非互換に遭遇しました。WordPress コアは wp-cli core update で 6.9.4 まで一括更新できたものの、長く更新が止まったテーマ・プラグインの非互換は手作業の連続です。
| パターン | PHP の挙動 | 修正内容 | 影響 |
|---|---|---|---|
| $str{0}(中括弧での文字列オフセット) | PHP 7.4 で deprecated / 8.0 で削除 | $str[0] に置換 | 数十ファイル |
| =& new ClassName()(参照代入) | PHP 5.3 deprecated / 7.0 削除 | = new ClassName() に変更 | 52 ファイル |
| create_function('', 'code') | PHP 7.2 deprecated / 8.0 削除 | クロージャ function() { code } に変換 | 253+ ファイル |
| Walker クラス子クラスのシグネチャ不一致 | PHP 8.0 で strict | 親と互換になるようデフォルト値を追加 | 多数 |
| 静的呼び出しされる非静的メソッド | PHP 8.0 で fatal | static function に変更 | プラグイン単位 |
| スマートクォート(' ' " " )を文字列リテラル中で使用 | PHP 8.0 で fatal | ASCII クォートに置換(テーマ全体一括) | テーマ全体 |
| class Match(PHP 8 予約語)との衝突 | PHP 8.0 で予約語化 | class SR_Match などへリネーム | 1プラグイン |
| ksort($wp_filter[$tag]) (WP_Hookオブジェクト) | PHP 8.0 で TypeError | ksort($wp_filter[$tag]->callbacks) に書き換え | bbpress / BuddyPress |
| break 2(ループ階層誤り) | PHP 8.0 で fatal | break; に修正 | 1プラグイン |
| Null プロパティアクセス | PHP 8.0 で warning → fatal | null チェックを追加 | テーマ多数 |
| 動的プロパティ非推奨 | PHP 8.2 で deprecated | #[\AllowDynamicProperties] アトリビュート付与 | コア・多数 |
個別事例 ① ─ BuddyPress の $wp_admin_nav 配列問題
PHP 7 までは未初期化の変数を配列として push してもエラーにならず動いていましたが、PHP 8 では型不一致で TypeError になります。
// BEFORE — PHP 7 では空文字 "" を array push しても続行
$wp_admin_nav[] = array('parent' => ...);
// AFTER — PHP 8 は TypeError、明示的初期化が必要
if (!is_array($wp_admin_nav)) $wp_admin_nav = array();
$wp_admin_nav[] = array('parent' => ...);個別事例 ② ─ create_function() を 253 ファイルで除去
本件で最も件数が多かった非互換が、PHP 8.0 で削除された create_function() の置換です。引数を解析しながらクロージャに置き換える sed/perl パターンを書き、機械的に置換 → 動作確認の手順を踏みました。
個別事例 ③ ─ WordPress 3.x 系のサイトは mysql_* で起動せず
一部に WP 3.x のまま放置されていたサイトがあり、PHP 8 では mysql_* 関数が存在しないため WP 自体が起動しません。wp-cli core download --force でコアを最新化してから整合性を取りに行く順番が必須です。
PHP 8 はとにかく strict。PHP 7 で警告レベルだったものが軒並み致命的エラーに昇格しています。古い WP プラグインは作者が長らく更新していないものが多く、自前でパッチを当てる / 別プラグインに置換する / そもそも無効化する、の三択を一つずつ判断していく作業になります。
5. nginx vhost設計 ─ Apache mod_rewriteからの変換
旧 Apache では DocumentRoot /wp + フォールバック rewrite で /public_html/cart 等のサブ WP に振り分けていました。これを nginx で完全再現するのは難しいため、既存ファイルシステム上の symlink 構造を活かす形に変更しています。
# ドメインA(DocumentRoot は /wp、symlink でサブ WP に到達)
server {
server_name <domain-a.example> www.<domain-a.example> pro.<domain-a.example>;
root /srv/sites/<domain-a.example>/public_html/wp;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
# ... fastcgi_params
}
}親ドメインとサブドメインで Web フォントを共有していたため、フォントだけ CORS ヘッダーを付与しています。
location ~* \.(woff2?|ttf|eot|otf)$ {
add_header Access-Control-Allow-Origin "*";
}6. EBS最小化と運用基盤の設計 ─ AMIバックアップ + AWSコスト試算
今回の移行で重視したのは、EBS をできるだけ小さく保ち、月々の運用コストを最小化することです。旧サーバーで 78GB 使用していた状態から、新サーバーでは 24GB まで削減できました。バックアップは AWS の AMI スナップショットに任せ、アクセスログや DB ダンプは S3 へ即時退避することで、EBS にデータが堆積し続ける構造から脱却しています。
設計方針
- バックアップは AMI スナップショットに一本化:WordPress プラグイン型のバックアップ(All-in-One WP Migration、BackWPup、UpdraftPlus 等)は全削除し、OS ごと AWS AMI で取得する方式へ。
- アクセスログは S3 へ即時退避:logrotate 内で S3 へコピーし、EBS 上のローカル保持は 7 日のみ。
- DB ダンプは毎日 S3 へ:mysqldump → S3、ローカル保持は最小限。
- 過去のバックアップ堆積を一掃:
.wpress/.tar/.htaccess_yyyymmdd系の遺産ファイルを全削除(セクション 8 参照)。
logrotate → S3
/var/log/nginx/sites/*.log {
daily
rotate 7
lastaction
DATE_DIR=$(date -d yesterday +%Y/%m/%d)
for f in /var/log/nginx/sites/*.log.1.gz; do
aws s3 cp "$f" \
"s3://<bucket>/logs/nginx/sites/$DATE_DIR/$(basename $f .log.1.gz).log.gz"
done
endscript
}S3 ライフサイクルで logs/ プレフィックス配下を 90 日後に expire させ、保管コストも一定以下に抑えています。
DB バックアップ → S3(毎日 5:00)
# /etc/cron.d/db-backup 0 5 * * * root /usr/local/bin/db-backup.sh
26 DB を毎日アップロードしても合計約 35MB に収まる規模。S3 の Glacier Instant Retrieval や IA への移行をライフサイクルで組み合わせれば、長期保管コストはさらに下げられます。
cronie(cron デーモン)が標準同梱されていません。本構成ではあえて cronie を導入していますが、systemd timer で書き換えるのが AL2023 の流儀に合致します。AMI バックアップに集約する意義
プラグイン型のバックアップは便利な反面、wp-content 配下に圧縮ファイル(.wpress / .zip / .tar)が無限に積み上がり、本来必要のない容量を EBS に抱え込ませる原因になります。複数サイトを1台に同居させているサーバーでは特に深刻で、本件でもバックアップ系プラグインの遺産だけで数十 GB 単位の堆積がありました。
新サーバーでは、バックアップは AWS の AMI スナップショット(増分課金)に一本化。WordPress 側のバックアップ系プラグインは全削除し、復旧はインスタンス丸ごとの巻き戻しで対応する設計にしています。
AWS コスト試算 ─ AMI スナップショット 10 世代保持の場合
AMI スナップショットを 10 世代保持する想定で、旧構成と新構成の EBS 関連コストを比較します(東京リージョンの代表的な単価で計算)。
| 項目 | 旧サーバー(AL2) | 新サーバー(AL2023) | 差分 |
|---|---|---|---|
| EBS ボリューム ($0.12/GB gp2 / $0.096/GB gp3) | 100GB gp2 = $12.00 / 月 | 30GB gp3 = $2.88 / 月 | −$9.12 |
| AMI スナップショット ×10 世代 ($0.05/GB、増分課金。 初回 ×約 1.2 倍で見積) | 78GB × 1.2 ≈ 94GB = $4.68 / 月 | 24GB × 1.2 ≈ 29GB = $1.44 / 月 | −$3.24 |
| 合計 | 約 $16.68 / 月 | 約 $4.32 / 月 | −$12.36 / 月 (約 74% 減) |
月々 約 $12 / ¥1,800 前後(年額にすると約 ¥22,000、為替 ¥150/USD 換算)の削減。額そのものは大きくありませんが、複数台のサーバーを抱えているケースでは効いてきますし、EBS が小さいほど AMI 取得・復元時間も短くなるという運用上のメリットも合わせて手に入ります。
- 東京リージョン(ap-northeast-1)の代表的な単価で計算。実価格はリージョン・時期により変動します。
- EBS スナップショットは増分課金。10 世代の合計を「初回フル容量 × 約 1.2 倍」で概算(ログ・DB を S3 に出している前提でデータ変化率は限定的)。実環境では業種・更新頻度により変動します。
- gp3 のベースライン 3,000 IOPS / 125 MB/s は無料枠内のため、追加 IOPS 課金は加算していません。
- EC2 インスタンス本体・データ転送・S3 ストレージは別計算です。
7. セキュリティハードニング
旧サーバーは長年の運用で穴が増えていたため、移行のタイミングで一気に締め直しました。監査前の状態では world-writable のディレクトリ 12 個 / ファイル 404 個 が残っており、全て除去しています。
| カテゴリ | 対策 |
|---|---|
| 機密ファイル | wp-config.php、xmlrpc.php、install.php、readme.html を nginx で 403 |
| アップロード経由PHP実行禁止 | location ~* /wp-content/uploads/.*\.(php|phtml)$ { deny all; } |
| ユーザー列挙対策 | ?author=N を 403、/wp-json/wp/v2/users を 403 |
| HTTPセキュリティヘッダー | X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy, HSTS |
| バージョン情報非表示 | server_tokens off, expose_php = Off |
| ファイル権限統一 | ディレクトリ 755 / ファイル 644 / wp-config.php 640 |
| WP管理画面からのファイル編集禁止 | define('DISALLOW_FILE_EDIT', true) を全 26 wp-config に投入 |
8. クリーンアップ ─ 690MB削減
移行のついでに、長年の堆積物を削除しています。
| カテゴリ | 削除件数 |
|---|---|
| 未使用プラグイン(旧サーバー時代の堆積) | 98 |
| 未使用テーマ(twentyXX系の古いものなど) | 86 |
| バックアップ系プラグイン(All-in-One WP Migration、BackWPup、UpdraftPlus) | 14 |
| バックアップファイル(.wpress、.tar、.htaccess_2019xxxx 等) | 約 400 ファイル |
| 全体削減量 | 約 690MB |
9. メール送信 ─ Postfix + SES SMTPリレー
旧サーバーは Postfix で直接送信(relayhost 未設定)していました。新サーバーでは Postfix を維持しつつ、SMTP リレーを AWS SES に向ける方針に切り替えます。
[WordPress 26サイト] → PHP mail() → Postfix → SES SMTP → 受信者
- WP Mail SMTP プラグインを 26 個に入れる必要なし
- 既存の Contact Form 7 / mw-wp-form 等の修正不要
- SPF / DKIM 自動署名で配信率向上
- $0.10 / 1000 通の低コスト
10. クライアント検証中の修正対応
検証期間中は、レイアウト崩れや UI 細部の修正リクエストが多数発生しました。代表的なものは:
- 停止していた外部ウィジェット(
ws-fe.amazon-adsystem.com、Pocket 等)の置換・削除 - WordPress 6.7+ で
sizes="auto"によりサムネイルが意図せず巨大表示される問題 → mu-plugin で無効化 - テーマの記事一覧レイアウト(画像左 / テキスト右)を CSS Grid で再構築
<meta name="generator">から WordPress バージョンが漏れる箇所の対応- カスタム JS の
null.idエラーを null 安全化
11. 残課題
- ALB + ACM 化(本番 HTTPS):クライアント検証 OK 後に切替
- DNS 切替(旧 → 新):同上
- AWS SES の DKIM など DNS 設定:クライアント側 DNS 操作待ち
- Security Group の SSH/HTTPS 許可元 IP 絞り込み:本番リリース時
12. 工数比較 ─ このコストをどう考えるか
本件は AI アシスタントを併用しつつ、実働で約 1 日(8 〜 10 時間)で 28 サイトの移行作業を完了しました。AI を用いずに同じ規模をこなした場合は、PHP 8 互換修正だけで数日 〜 1 週間かかってもおかしくない物量です。
| 選択肢 | 初期コスト | 毎月の運用コスト | 運用工数 |
|---|---|---|---|
| 自前で AL2023 へ移行 | 数日 〜 数週間の工数 (ノウハウ次第) | EC2 / EBS / バックアップ料金 + 自社運用コスト | パッチ適用 / 監視 / 障害対応 すべて自社 |
| MOOBON に AL2023 移行を依頼 | ¥100,000〜 / 1サイト目安 (規模により別途見積もり) | EC2 / EBS / バックアップ料金 | 運用は自社のまま (運用契約は別オプション) |
| Kinsta(マネージドWP)に移行 | 移行作業:Kinsta 側無料移行 or MOOBON 支援 | 月額 $35〜 (プラン次第) | パッチ・監視・バックアップ・CDN すべて Kinsta 側 |
「Amazon Linux 2 EOL を機に、サーバー運用そのものから降りる」という選択肢は、特に WordPress を業務の一部として持っている企業にとっては合理的です。MOOBON は日本第 1 号の Kinsta 公認パートナーとして、移行支援から運用までワンストップで対応しています。
13. これから移行する人へのアドバイス
- 古い WordPress / プラグインの PHP 8 互換性を事前に静的解析で見積もる。Rector、PHPCS の PHPCompatibility ルールセットを CI に通せば、影響範囲が初日に分かります。
- rsync の除外設計を移行前に決め切る。実行中に EBS 満杯で停止すると面倒です。本件で 63GB → 23GB に減らせたのは、除外戦略の設計に時間をかけたから。
- 大量の WP インストール環境は WP-CLI を回す自動化スクリプトで処理する。手動で 28 個の
wp-config.phpを触るのはミスの温床です。 - メール送信は最初から SES リレーにする。後付けで Postfix 設定を直すより、新サーバー構築時に組み込んだ方が圧倒的に楽です。
- クライアント検証期間を別途確保する。技術的にはほぼ完璧でも、見た目の細部で必ず指摘が来ます。
- そもそも移行せず、マネージドホスティング(Kinsta 等)に逃げる選択肢を真剣に検討する。28 サイトの個別事情(プラグイン互換、SSL、メール、cron)を 1 個ずつ潰すコストと、月額 + 移行費を比較する価値はあります。
