新しいVPSへサーバーを移行する手順
新しいVPSへの移行を安全な切り替えにする手順です。インベントリ作成、再構築、データベースのダンプ、DNS TTLの短縮、検証、切り替えまでを順に説明します。
新しい VPS へのサーバー移行を本番切り替えのリハーサルとして実施する
新しい VPS へサーバーを移行する場合は、単純なコピーではなく、本番切り替えのリハーサルとして進めます。新しいサーバーを最初から構築し、データを 2 回同期します。DNS に触れる前に、新しいサーバーが自身の IP アドレスだけで動作することを確認します。その後、レコードを切り替え、確実に問題がないと判断するまで旧サーバーを稼働させます。バイト列をコピーすること自体は簡単です。作業手順の順序によって、移行が問題なく終わるか、高いコストを伴うかが決まります。
このガイドでは、Web アプリケーション、データベース、TLS(transport layer security)証明書を実行する 1 台の Linux サーバーを扱います。一般的な単一サーバー構成の大半をカバーします。2 台のホストを使用するため、各例ではコメントで実行先のホストを示します。アドレスにはドキュメント用の範囲を使用します。198.51.100.10 は旧サーバー、203.0.113.20 は新しいサーバーです。
開始前に、手順書全体を読んでください。最初の手順である DNS TTL の短縮は、実際に切り替える手順の数日前に実施する必要があります。
構築を始める前にインベントリを作成する
記録していないサーバーは再構築できません。古いサーバーの役割を 1 時間かけて書き出してください。移行後に壊れるのは、必ず誰も覚えていなかったものです。cron ジョブ、ファイアウォールの例外、アプリケーションディレクトリの外に置かれた環境ファイルなどが該当します。
古いサーバーで次のコマンドを実行し、新しいサーバーから読める場所に出力を保存してください。
# old server: packages you asked for, not the dependencies they pulled in
apt-mark showmanual > ~/inv-packages.txt
# old server: what runs now, and what starts at boot
systemctl list-units --type=service --state=running --no-pager > ~/inv-services.txt
systemctl list-unit-files --state=enabled --no-pager >> ~/inv-services.txt
systemctl list-timers --all --no-pager > ~/inv-timers.txt
# old server: what is listening, on which address and port
sudo ss -tulpn > ~/inv-ports.txt
# old server: human accounts, skipping the system ones
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $6, $7}' /etc/passwdapt-mark showmanual は、依存関係として追加されたものをすべて除外できるため、作成する価値のある一覧です。5 年使用したサーバーで完全な dpkg --get-selections を実行すると、2,000 行もの出力になり、導入理由は何も分かりません。
定期実行の処理は 2 か所に隠れているため、両方を確認してください。月に 1 回しか実行されないジョブは、移行から 6 週間後に初めて見つかるものです。
# old server: per-user crontabs, then the system drop-ins
for u in $(cut -d: -f1 /etc/passwd); do sudo crontab -lu "$u" 2>/dev/null | sed "s/^/$u: /"; done
sudo ls -la /etc/cron.d /etc/cron.daily /etc/cron.hourly次に、通常のファイルではないものを確認します。ファイアウォールルール、証明書、データベース、そして実際に移行するデータ量です。
# old server
sudo ufw status verbose # or: sudo nft list ruleset
sudo certbot certificates
sudo -u postgres psql -c '\l' # or: sudo mysql -e 'SHOW DATABASES;'
sudo du -xh --max-depth=1 / | sort -hcertbot certificates は、各証明書の名前、対象ドメイン、有効期限、ディスク上のファイルパスを出力します。この出力が TLS の確認項目です。du -x は 1 つのファイルシステム内にとどまるため、マウントされたバックアップボリュームまで走査して、実際の 10 倍の値を報告することがありません。
サーバーの外部にあるため、毎回忘れられる項目が 2 つあります。1 つ目は、サーバーの IP アドレスを許可リストに登録している外部サービスです。決済ゲートウェイ、マネージドデータベース、SMTP リレー、パートナー API などが該当します。新しいサーバーには新しいアドレスが割り当てられるため、切り替え後ではなく、切り替え前に新しい IP を許可リストへ追加する必要があります。2 つ目は、自分で作成していない DNS レコードです。たとえば、MX レコードや、テキスト内で古い IP を指定している SPF レコードなどです。
古い root ファイルシステムを複製せず再構築する理由
root ファイルシステム全体を新しい VPS に複製すると、より速く見えます。実際、問題が発生するまでは速く進みます。何年も本番環境で使われてきた root ファイルシステムには、文書化されていない手作業の設定、すでに存在しないリポジトリ由来のパッケージ、旧プラットフォームの仮想ハードウェア向けに構築されたブート設定が含まれています。それらをすべて取り込むことになり、移行する原因まで持ち込むことになります。
再構築は初日には時間がかかりますが、その後の運用コストを下げられます。現行リリースをインストールし、基本的なセキュリティ強化を適用してから、データだけをコピーします。対象は、アプリケーションディレクトリ、サイト設定、データベースダンプ、証明書、ユーザーのアップロードファイルです。説明できないものは持ち込みません。新しいサーバーでは、新しい VPS で最初の 10 分に行う作業に従って、通常のサーバーと同じように初期設定を行います。その後、インベントリにあるサービスを 1 つずつ追加し、次のサービスを追加する前に各サービスを確認します。
イメージまたはスナップショットの復元が適切な場合
再構築が適切でない例もあります。古いサーバーが起動しない場合や、アプリケーションのソースから再ビルドできなくなっている場合は、プロバイダーのイメージまたはスナップショットを復元するのが現実的です。ただし、制約があります。復元先は通常、同じプロバイダー内に限られ、同じプラン系列内に限定されることもあります。復元したディスクが、そのプラットフォーム固有の仮想デバイスとネットワーク名を前提とするためです。
稼働中のサーバーから取得したスナップショットにも、稼働中のデータベースをファイル単位でコピーする場合と同じ整合性の問題があります。イメージ復元は移行計画ではなく、復旧手段として扱ってください。計画の基盤にする前に、スナップショットがバックアップと同じものではない理由を確認してください。
ファイルの移動方法: SSH 経由の rsync
古いサーバーから rsync を実行し、新しいサーバーへプッシュします。通常はプッシュのほうが簡単です。古いサーバーにはすでにデータがあり、sudo 配下のすべてを読み取れるためです。
# old server: dry run first, and read what it says it will do
sudo rsync -aHAX --dry-run --itemize-changes \
-e 'ssh -i /root/.ssh/id_ed25519' \
/srv/app/ deploy@203.0.113.20:/srv/app/
# old server: the real bulk pass
sudo rsync -aHAX --info=progress2 \
-e 'ssh -i /root/.ssh/id_ed25519' \
/srv/app/ deploy@203.0.113.20:/srv/app/フラグが重要です。-a は、パーミッション、タイムスタンプ、シンボリックリンク、所有者を保持します。-H は、ハードリンクを個別のコピーに展開せず、ハードリンクのまま保持します。-A は POSIX ACL (アクセス制御リスト) をコピーし、-X は拡張属性をコピーします。この最後の 2 つがないと、同一に見えるファイルでも動作が異なる場合があります。SELinux ラベルと ACL は拡張属性に保存され、それ以外には記録されないためです。
ここでは、2 つの点が主な失敗原因になります。
末尾のスラッシュによって、データの配置先が決まります。 /srv/app/ は、そのディレクトリの内容を意味します。/srv/app は、ディレクトリ自体を意味します。間違えると、新しいサーバー上に /srv/app/app ができてしまいます。その結果、アプリケーションは起動した後にファイルがないと報告します。設定されていたパスが 1 階層浅くなるためです。
sudo では、チルダは root のホームディレクトリを表します。 sudo rsync の中に -e 'ssh -i ~/.ssh/id_ed25519' と記述すると、自分のホームディレクトリではなく /root/.ssh で鍵を探します。そこに鍵がない場合、SSH は Permission denied (publickey) を出力し、rsync は rsync: connection unexpectedly closed を出力して、0 以外の終了ステータスで終了します。鍵のパスは完全なパスで記述してください。パスを修正してもこの認証メッセージが表示され続ける場合は、publickey 失敗の原因は限られています。次に、新しいサーバー上のディレクトリパーミッションを確認してください。
所有権については、1 つ判断が必要です。root として実行すると、rsync はデフォルトで所有者とグループを名前で対応付けます。そのため、古いサーバーで www-data が所有するファイルは、数値 UID (ユーザー ID) が異なっていても、新しいサーバーでは www-data が所有するファイルになります。再構築では、この動作が適しています。対象側にアカウントが存在しないファイルシステムをコピーする場合だけ --numeric-ids を追加してください。その後、ls -ln で結果を確認してください。一致するアカウントがない UID の所有ファイルは数字だけで表示され、読み取ろうとするすべてのサービスがアクセスを拒否されるためです。
古いサーバーがまだネットワークトラフィックを処理している間に、数日前から一括コピーを実行しておきます。何度でも繰り返せます。rsync は変更されたデータだけを送信するため、2 回目以降は数時間ではなく数分で完了します。切り替え時間帯に実行する最終コピーでは --delete を追加します。これにより、古いサーバーで削除されたファイルも新しいサーバーから削除されます。
# old server: final pass, inside the window, after the app has stopped writing
sudo rsync -aHAX --delete --info=progress2 \
-e 'ssh -i /root/.ssh/id_ed25519' \
/srv/app/ deploy@203.0.113.20:/srv/app/--delete は、ソースに存在しないファイルを宛先から削除します。そのため、ソースパスを間違えた状態で --delete を指定すると、宛先ディレクトリが空になります。毎回必ず、最初に --dry-run を付けて実行してください。また、ノートパソコンの SSH セッションが切断されると、長時間の転送も停止します。そのため、古いサーバー上で tmux または screen の内部から実行してください。古いサーバーがユーザーへのサービスを継続している間にコピーでリンク帯域を使い切る場合は、--bwlimit=20M を追加します。
データベースの移行方法: ネイティブダンプ
データベースは、ファイルの集合に見えても単なるファイルのディレクトリではありません。データベースは、ファイル、メモリ上の状態、write-ahead log の組み合わせです。整合性が保たれるのは、データベース自身が定義した時点だけです。データベース専用のツールを使用してください。
PostgreSQL では、2 種類のダンプが必要です。ロールはクラスタ全体に適用され、pg_dump には含まれないためです。
# old server
sudo -u postgres pg_dumpall --globals-only -f /var/backups/globals.sql
sudo -u postgres pg_dump -Fc -f /var/backups/appdb.dump appdb# new server: restore in this order
sudo -u postgres psql -f /var/backups/globals.sql
sudo -u postgres createdb -O appuser appdb
sudo -u postgres pg_restore -d appdb --no-owner /var/backups/appdb.dumpglobals.sql を省略すると、すべてのテーブルは復元されても、それらを読み取れるアプリケーションロールが存在しない状態になります。GRANT ステートメントが、存在しないユーザーを参照するためです。-Fc は custom archive 形式で書き出します。この形式を読み取れるのは pg_restore だけで、後から選択したテーブルだけを復元できます。同じメジャーバージョンか、それより新しいバージョンへ復元してください。17 から 16 へ移行する場合など、旧バージョンへ戻すことはサポートされていません。pg_restore は、何も書き込む前にファイルヘッダーの unsupported-version エラーでアーカイブを拒否します。
MySQL と MariaDB では、デフォルトでは有効になっていない4つのオプションを指定して、1つのコマンドを使用します。
# old server
sudo mysqldump --single-transaction --routines --triggers --events \
--databases appdb > /var/backups/appdb.sql# new server
sudo mysql < /var/backups/appdb.sql--single-transaction は、書き込みをブロックせずに整合性のあるスナップショットを取得します。ただし、対象は InnoDB テーブルだけです。同じデータベース内の MyISAM テーブルは、この保証なしでコピーされます。そのため、ダンプを信頼する前にストレージエンジンを確認してください。--routines、--triggers、--events はデフォルトで無効です。つまり、通常のダンプではデータは復元されても、ストアドプロシージャとスケジュールされたイベントが何も通知されずに残されます。データベースユーザーとその権限は mysql システムデータベースに保存されます。--databases appdb のダンプではこのデータベースにアクセスしないため、新しいサーバーで CREATE USER と GRANT を使用して再作成してください。MariaDB 11 では、同じツールを mariadb-dump として提供し、mysqldump をシンボリックリンクとして維持しています。そのため、2026年8月時点ではどちらの名前も使用できます。
SQLite は単一ファイルで構成されます。アプリケーションの書き込み中にコピーすると、破損したファイルになります。SQLite には、安全にコピーするための独自の方法があります。
# old server
sqlite3 /var/lib/app/app.db ".backup '/var/backups/app.db'"データベースエンジンに関係なく、ダンプを信頼する前に内容を確認してください。ディスク容量が不足して途中で停止したダンプでも、切り詰められた位置まではエラーなしで復元されます。
# old server: a complete mysqldump ends with a line reading "-- Dump completed on ..."
tail -n 3 /var/backups/appdb.sql
# new server: after restore, count rows in a table whose size you know
sudo mysql -e 'SELECT COUNT(*) FROM appdb.orders;'稼働中のデータベースを rsync できない理由
rsync はファイル単位でコピーします。稼働中のデータベースは複数のファイルに同時に書き込むため、rsync が最後のファイルに到達する頃には、最初のファイルがすでに古くなっています。コピーには異なる時点のページが混在します。これは、データベースが実際には持ったことのない状態です。結果として、起動を拒否するサーバーになるか、さらに悪い場合は、起動して 1 週間にわたって正しい応答を返した後、クエリが破損したページに到達した時点で停止するサーバーになります。その間に警告はありません。
ファイル自体を安全に移行する方法は 2 つあります。データベースを停止し、コピーして、再び起動します。正確で簡単ですが、コピーにかかる時間だけ停止時間が発生します。もう 1 つは、稼働中のサーバーを物理的にコピーするために用意されたツールを使う方法です。PostgreSQL では pg_basebackup が該当します。このツールはサーバーと連携し、コピーの一貫性を保ちます。
# new server: pull a physical copy from the old one
pg_basebackup -h 198.51.100.10 -U replicator -D /var/lib/postgresql/16/main -X stream -Pこの方法には REPLICATION 属性を持つロールと、旧サーバー上の対応する pg_hba.conf エントリが必要です。そのため、dump よりも準備が多くなります。データベースが十分に大きく、dump と restore をメンテナンス時間内に完了できない場合には有効です。通常の単一サーバー移行では、dump の方が適しています。
切り替え前に証明書を再構築し、切り替え後に行わない
TLS 証明書は IP アドレスではなくドメイン名に紐付いているため、証明書ファイル自体は問題なく移行できます。問題なく移行できないのは更新処理です。Certbot のデフォルトの HTTP-01 チャレンジでは、認証局が認証対象の名前に対して port 80 経由でファイルを取得します。DNS が新しいサーバーを指すまで、この取得先は古いサーバーになるため、新しいサーバーの更新に失敗します。
最初の方法は、既存の証明書と更新状態をコピーすることです。証明書は、どちらのサーバーに置かれていても有効期限まで有効です。
# old server
sudo rsync -aHAX -e 'ssh -i /root/.ssh/id_ed25519' \
/etc/letsencrypt/ root@203.0.113.20:/etc/letsencrypt//etc/letsencrypt/renewal/ 配下の各ファイルには、証明書の発行に使用した authenticator plugin が記録されています。そのため、新しいサーバーにも同じ plugin(たとえば python3-certbot-nginx)をインストールしてください。インストールしないと、最初の更新時に unknown authenticator に関するエラーが発生します。更新に依存する前に、更新が機能することを確認してください。
# new server, after DNS has moved
sudo certbot renew --dry-run2 つ目の方法は、DNS-01 チャレンジを使用して新しいサーバーで新しい証明書を発行することです。この方法では TXT レコードを使って管理権限を証明し、port 80 には接続しません。名前がまだ古いサーバーを解決している移行前でも機能するため、DNS provider を自動化できる場合はこちらの方が適しています。DNS-01 チャレンジで証明書を発行するでは、plugin と認証情報の設定について説明しています。
どちらの方法でも、DNS を変更せずに、新しいサーバーが実際に提示する証明書を確認してください。
# your laptop
echo | openssl s_client -connect 203.0.113.20:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -dates -issuer-servername は SNI(server name indication)を送信します。これにより、Web サーバーは正しい virtual host を選択できます。-servername を省略すると、その IP に対するデフォルトの証明書が返されます。実際の問題ではないにもかかわらず、問題があるように見える不一致が発生します。
DNS の TTL を切り替え前に短縮する
DNS では、慎重に移行しても問題が起きることがあります。遅延が組み込まれており、当日に短縮できないためです。A レコードをキャッシュしたリゾルバーは、指定された TTL(time to live)の期間、そのレコードを返し続けます。10 分前に古い値でレコードをキャッシュしたリゾルバーには、今 TTL を短縮しても効果がありません。古い TTL の残りの期間は古い値を保持し、その後で初めて新しい短い TTL を認識します。そのため、切り替えの少なくとも古い TTL の期間全体より前に TTL を短縮してください。1 日前に実施するのが安全です。ここで扱う要素に慣れていない場合は、レコード、リゾルバー、キャッシュの解説を参照してください。
以下の数値は、測定値ではなく TTL 自体から算出したものです。
The data behind this chart
[
{
"label": "TTL 3600 s (common default)",
"ttl_seconds": 3600,
"worst_case_stale_minutes": 60
},
{
"label": "TTL 900 s",
"ttl_seconds": 900,
"worst_case_stale_minutes": 15
},
{
"label": "TTL 300 s (lowered for cutover)",
"ttl_seconds": 300,
"worst_case_stale_minutes": 5
},
{
"label": "TTL 60 s (short window)",
"ttl_seconds": 60,
"worst_case_stale_minutes": 1
}
]TTL が 3600 秒の A レコードは、変更後も 60 分間、ユーザーを古い IP に誘導する可能性があります。これを 300 秒に短縮すると、最悪の場合の時間は 5 分になります。これらの数値は保証値ではなく、下限として扱ってください。リゾルバーによっては独自の最小 TTL を適用し、それより短い値を無視します。また、アプリケーションランタイムによっては、プロセスの存続期間中、解決済みアドレスをキャッシュします。そのため、変更前に起動したクライアントは、再起動するまで再び名前解決を行わないことがあります。
短い TTL が有効になっていることを確認するときは、自分のキャッシュではなく権威 DNS サーバーの応答を確認してください。
# your laptop: ask the zone's own nameserver, so no cache is involved
dig +short NS example.com
dig +noall +answer @$(dig +short NS example.com | head -n1) example.com Aその応答行の 2 番目のフィールドが、秒単位の TTL です。次に、見落とされやすいレコードを確認します。旧サーバーが IPv6 を使用していた場合の AAAA レコード、CNAME ではなく個別の A レコードになっている www 名、サーバー自体を指す MX レコード、旧 IP を記載した SPF レコード、新しいアドレスの逆引き DNS(PTR)レコードです。サーバーからメールを送信する場合は、切り替え前にプロバイダーのコントロールパネルで PTR を設定してください。受信側のメールサーバーが PTR を確認するため、PTR がないと、他の処理がすべて正常に見えた数時間後にメールが拒否されることがあります。
DNS を変更する前に IP で新しいサーバーを確認する
DNS がまだ旧サーバーを指している間に、新しいサーバー上でアプリケーション全体をテストできます。1 回のリクエストだけ名前解決を上書きします。
# your laptop: force one name to the new IP, for this request only
curl -sS --resolve example.com:443:203.0.113.20 \
-o /dev/null -w '%{http_code} %{ssl_verify_result}\n' https://example.com/--resolve は接続先だけを変更します。TLS 証明書は実際の名前に対して引き続き検証されるため、証明書とサービスの両方を確認できます。チェーンの検証に成功すると、%{ssl_verify_result} は 0 を出力します。
ブラウザーでサイトを操作する場合は、ノート PC の /etc/hosts、または Windows の C:\Windows\System32\drivers\etc\hosts に 1 行追加して、マシン全体の名前解決を上書きします。
203.0.113.20 example.com www.example.comその後、ユーザーと同じ手順でアプリケーションを操作します。ログインします。データベースから読み込むページを表示します。データベースに書き込むフォームを送信します。ファイルをアップロードし、ディスク上に保存されたことを確認します。メールを送信する処理を実行し、実際に届くことを確認します。新しい IP からの外向き SMTP は、よくある想定外の問題だからです。完了したら、すぐに hosts の行を削除します。残したままにすると、他のユーザーには問題なく表示されているサイトを、1 時間かけてデバッグすることになります。
切り替えの手順
- 数日前: TTL を短くし、データ全体を rsync で同期し、新しいサーバーを構築して hosts の上書き設定経由でテストします。
- 当日、作業時間帯の前: すべてのサードパーティーの許可リストに新しい IP を追加し、新しいサーバーのバックアップジョブが設定済みで、リポジトリを保存先として指定していることを確認します。
- 作業時間帯を開始: 古いサーバーでアプリケーションをメンテナンスモードにし、書き込みを受け付けないようにします。
- 最終的なデータベースダンプを取得し、
--deleteを指定して最終の rsync を実行します。 - 新しいサーバーでダンプをリストアし、サービスを起動します。
--resolveと hosts の上書き設定を経由して再度テストします。実際の書き込みも 1 回実行します。- A レコードと AAAA レコードを新しい IP に変更します。
- 両方のサーバーを監視します。古いサーバーのアクセスログには、まだそこへ接続しているクライアントが記録されます。件数は TTL の期間中に 0 に近づくはずです。
- メンテナンスページを解除します。
- 古いサーバーは停止も変更もせず、少なくとも 1 週間稼働させたままにします。
メンテナンスモードの手順は省略されがちですが、これがシステムを保護します。新しいデータベースで書き込みを受け付けた後にロールバックする場合、その書き込みを失うか、新しいデータベースをダンプして古いデータベースに戻す必要があります。数分間の読み取り専用時間は、わずかなコストで済みます。2 つのデータベースが両方とも書き込みを受け付けると、手作業による調整に数日かかります。
ロールバック計画
ロールバックは 1 つの操作で完了します。DNS レコードを 198.51.100.10 に戻します。これが機能するのは、事前に次の 4 つを実施しているためです。
- 旧サーバーは、サービスが稼働し、データが保持された状態で動き続けています。旧サーバーへの書き込みを停止しただけで、廃止はしていません。
- TTL はまだ短いため、切り戻しも切り替えと同じように迅速に行えます。
- 旧アドレスを置き換えるのではなく、新しい IP をサードパーティの許可リストに追加しました。旧アドレスを削除すると、決済ゲートウェイへのロールバック経路が失われます。
- 新サーバーでは、把握できない書き込みは発生していません。これまでの書き込みは、自分で実行したテストトランザクションだけです。
切り替え時間帯が始まる前に、何をロールバックのトリガーにするかを決めておきます。トリガーは 2 つで十分です。固定した時間内に診断できないエラーが発生した場合と、データ損失が少しでも発生した場合です。これを事前に書き出しておくことで、10 分間の障害を長時間の障害に変える、1 時間にわたる推測を防げます。
移行が成功したことを確認する
サイトが表示されただけでは、移行は完了していません。後からしか失敗しない項目を確認します。
# new server
systemctl --failed
journalctl -p err -b --no-pager | tail -n 40
sudo certbot certificates
sudo systemctl list-timers --all --no-pagersystemctl --failed のレポート 0 loaded units listed が、確認したい結果です。certbot certificates には想定した有効期限が表示され、list-timers にはインベントリにあるすべてのスケジュール済みジョブと、空欄ではない実際の次回実行時刻が表示される必要があります。
次に、新しいサーバーを意図的に 1 回再起動し、その動作を監視します。手動で起動したまま自動起動を有効にしていないサービスは、午前 3 時の予期しない再起動が発生するまで正常に動作します。
# new server
sudo reboot
# your laptop, once it comes back
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/アプリケーションをコンテナで実行している場合は、同じ問題が別の形で発生します。再起動後に復帰させるには、compose スタックに明示的な再起動ポリシーが必要です。
最後の確認は、後回しにしやすい一方で最も重要です。バックアップジョブを確認します。バックアップされていないサーバーで移行を終えると、1 つのリスクを別のリスクに置き換えたことになります。新しいサーバーでバックアップを手動実行し、そのバックアップから一つのファイルを一時ディレクトリへ復元します。実際に復元をテストした restic リポジトリがあれば、必要なときに役立ちます。古いサーバーと新しいサーバーを 1 週間並行して稼働させる場合は、各ホストへ一貫した方法で接続し、設定できる運用により、両方が稼働している間に設定が分岐するのを防げます。
切り替え後: 旧サーバーと最後の作業
旧サーバーは 1〜2 週間維持します。解約する予定だったプランの 1 か月分の費用はかかりますが、利用できる唯一のロールバック手段です。その後、残りの作業を完了します。
- 新しいサーバーで
~/.ssh/configに同じホスト名を再利用すると、最初の接続でWARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!が発生します。その名前が異なるホストキーを返すようになったためです。変更理由を確認できたら、ssh-keygen -R example.comで古いエントリを削除します。同じ警告は中間者攻撃の兆候でもあるため、反射的に削除してはいけません。移行は、どの鍵からどの対象へアクセスできるかを見直すよい機会でもあります。この作業については 小規模なサーバー群での SSH 鍵管理 を参照してください。 - 旧サーバーの最後のスナップショットまたはバックアップを 1 つ取得し、旧プロバイダー以外の場所に保存します。
- 旧 IP を監視チェック、SPF レコード、サードパーティーの allowlist から、この順番で、最後に削除します。
- 最後のコピーを別の場所で読み取れることを確認してから、旧プランを解約します。
FAQ
新しい VPS へのサーバー移行にはどのくらいかかりますか?
ユーザーから見える停止時間は、通常、最終的なデータベースダンプ、最後の rsync パス、サービスの起動にかかる時間です。小規模なアプリケーションであれば、10 分から 30 分程度です。DNS TTL を切り替えの少なくとも古い TTL 期間前に下げる必要があるため、暦上の所要時間は長くなります。1 日前に下げておくと、より安全です。大量のデータコピーも数日前から計画してください。稼働中のサーバーに対して実行でき、後で再実行すれば前回以降に変更されたデータだけを転送できます。
稼働中の MySQL または PostgreSQL データベースをダンプせずに rsync できますか?
いいえ。rsync はファイル単位でコピーする一方、データベースは複数のファイルへ同時に書き込むため、コピーには異なる時点のページが混在します。その結果、データベースに実際には存在しなかった状態になります。起動を拒否される場合もあれば、起動後にクエリが破損したページへ到達した時点で失敗する場合もあります。pg_dump と pg_dumpall --globals-only、または mysqldump --single-transaction を使用してください。あるいは、先にデータベースを停止してからファイルをコピーします。大規模な PostgreSQL クラスターでは、pg_basebackup により稼働中のサーバーから一貫性のある物理コピーを作成できます。
DNS を変更する前に新しい VPS をテストするにはどうすればよいですか?
自分のマシンで名前解決を上書きします。単一のリクエストでは、curl --resolve example.com:443:203.0.113.20 https://example.com/ により接続先を新しい IP に変更しつつ、証明書は実際の名前に対して検証できます。ブラウザーでテストする場合は、ノート PC の /etc/hosts に 203.0.113.20 example.com を追加します。ログイン、データベースの読み取り、フォームへの書き込み、ファイルのアップロードを順に実行し、テスト後にその行を削除します。証明書だけを確認するには、openssl s_client -connect 203.0.113.20:443 -servername example.com を実行します。
どの TTL を設定し、いつ下げるべきですか?
A レコードと AAAA レコードを 300 秒に下げ、切り替えの少なくとも古い TTL 期間全体より前に実施してください。変更前にレコードをキャッシュしたリゾルバーは、古い TTL の残りの期間、古い値を保持します。そのため、古い TTL が 86400 の場合、1 時間前に下げても効果はありません。移行後数日が経過し、旧サーバーのアクセスログが記録されなくなったことを確認してから、通常の値に戻してください。
TLS 証明書をコピーするべきですか。それとも新しいサーバーで発行するべきですか?
どちらでも構いません。/etc/letsencrypt/ をコピーすると、既存の有効期限まで証明書を使用できます。ただし、新しいサーバーにも同じ certbot 認証プラグインをインストールする必要があります。インストールしないと最初の更新に失敗するため、DNS の切り替え後に certbot renew --dry-run を実行して確認してください。DNS-01 challenge を使用できる場合は、新規発行の方が構成を整理できます。TXT レコードによって管理権限を証明でき、DNS が新しいサーバーを指す前でも動作するためです。HTTP-01 challenge は、DNS が移動するまで新しいサーバーでは使用できません。検証リクエストが旧サーバーに到達するためです。