在生产环境中进行云服务商迁移,核心难点并非硬件配置差异,而是如何在业务流量不中断的前提下完成数据同步与流量切换。本文以一个真实的生产迁移案例为蓝本,从技术视角深度解析从 DigitalOcean 迁移到 Hetzner 的零 downtime 实施路径,提供可直接落地的参数配置与监控要点。
一、迁移架构与核心挑战
本次迁移涉及的业务规模如下:30 个 MySQL 数据库共 248 GB 数据、34 个 Nginx 站点、GitLab EE 实例(42 GB 备份)、Neo4j 图数据库(30 GB),同时承载数十万移动端用户的实时请求。迁移前后分别运行在 CentOS 7(已停止维护)与 AlmaLinux 9.7 环境,数据库版本从 MySQL 5.7 升级至 8.0。单纯从资源配置来看,Hetzner AX162-R 机型提供 48 核 96 线程的 AMD EPYC 处理器、256 GB DDR5 内存与 1.92 TB NVMe Gen4 RAID1 存储,月费用仅为 233 美元,而原 DigitalOcean 同等配置 droplet 费用高达 1432 美元,年节省超过 14000 美元。
面对如此大规模的数据迁移,零 downtime 的实现依赖六个阶段的精心设计:完整环境搭建、数据文件 rsync 同步、MySQL 主从复制、DNS TTL 预热、 Nginx 反向代理配置、最后完成 DNS 切换。整个过程中,业务流量始终被正确响应 —— 要么直接由原服务器服务,要么通过反向代理透明转发。
二、MySQL 主从复制配置参数
2.1 初始数据导出
对于 248 GB 级别的 MySQL 数据,传统的 mysqldump 在单线程模式下可能耗时数天。实际采用 mydumper 进行并行导出,核心参数如下:
mydumper \
--threads 32 \
--compress \
--trx-consistency-only \
--skip-definer \
--chunk-filesize 256 \
-v 3 \
--outputdir /root/mydumper_backup/
--threads 32 利用新服务器 48 核 CPU 进行并行导出;--compress 对每个 chunk 进行压缩,显著降低网络传输时间;--trx-consistency-only 确保事务一致性。导出完成后,metadata 文件会记录精确的 binlog 位置,这是后续启动复制链路的关键参数。假设记录为 File: mysql-bin.000004, Position: 21834307,这将成为主从复制的起始坐标。
2.2 数据导入与复制启动
在新服务器端使用 myloader 进行并行导入:
myloader \
--threads 32 \
--overwrite-tables \
--ignore-errors 1062 \
--skip-definer \
-v 3 \
--directory /root/mydumper_backup/
完成导入后,配置复制链路:
CHANGE MASTER TO
MASTER_HOST='OLD_SERVER_IP',
MASTER_USER='replicator',
MASTER_PASSWORD='...',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000004',
MASTER_LOG_POS=21834307;
START SLAVE;
2.3 常见问题与修复策略
复制启动后,错误 1062(Duplicate Key)是最常见的问题。这是由于 mydumper 多线程导出过程中存在时间窗口,新写入数据与已导出数据产生主键冲突。解决方案为启用幂等模式:
SET GLOBAL slave_exec_mode = 'IDEMPOTENT';
START SLAVE;
IDEMPOTENT 模式会静默跳过重复键与缺失行错误,确保复制链路顺利完成。监控上应持续关注 Seconds_Behind_Master 指标,接近零时表示主从数据已完全同步。
另一个关键陷阱是 MySQL 5.7 到 8.0 升级后的用户表结构问题。导入后 mysql.user 表可能出现列数不匹配,导致 mysql.infoschema 缺失而无法认证。修复方法为:
systemctl stop mysqld
mysqld --upgrade=FORCE --user=mysql &
若失败并报错 'sys.innodb_buffer_stats_by_schema' is not VIEW,说明 sys 库被错误导入为普通表,需先清理后重新执行升级。
2.4 权限检查与 read_only 验证
当主从复制同步后,需要验证新服务器的 read_only 模式是否真正生效。实际运维中发现一个隐蔽问题:若应用数据库用户被授予 SUPER 权限,该权限会绕过 read_only 限制,导致从服务器可以写入数据。检查方式为:
SHOW GRANTS FOR 'some_db_user'@'localhost';
若返回结果包含 SUPER 权限,需执行:
REVOKE SUPER ON *.* FROM 'some_db_user'@'localhost';
FLUSH PRIVILEGES;
此步骤应在所有 24 个应用用户上逐一执行,确保从服务器真正处于只读状态。
三、DNS 预热与 TTL 参数
DNS 切换是整个迁移过程中唯一可能产生中断的环节。默认 TTL 通常为 3600 秒(一小时),若直接修改 A 记录,全球缓存可能导致部分用户在未来一小时内仍然访问原 IP。正确的预热策略如下:
第一步,批量降低 TTL。通过 DigitalOcean API 将所有 A 与 AAAA 记录的 TTL 从 3600 秒降至 300 秒(五分钟)。关键原则是仅修改 A 与 AAAA 记录,MX 与 TXT 记录保持不变 —— 修改邮件记录 TTL 可能触发 Gmail 等邮件服务商的投递问题。
# 仅处理 A 和 AAAA 记录
if record["type"] in ("A", "AAAA"):
update_record_ttl(domain, record["id"], 300)
第二步,等待旧 TTL 过期。降 TTL 后需等待原 TTL 时长(通常一小时)确保全球递归 DNS 服务器已更新缓存。此后真正切换时,传播窗口将缩短至五分钟以内。
四、Nginx 反向代理平滑切换
DNS 切换存在传播延迟,在此期间部分用户仍会访问原服务器。为实现无感知切换,需要将原服务器的 Nginx 从内容服务器转换为反向代理,将请求透明转发至新服务器。
针对 34 个站点配置,手动修改每个 server 块效率低下且容易出错。编写 Python 脚本批量转换的核心思路是:解析每个配置文件,提取 server_name 与 SSL 证书路径,生成指向新服务器 IP 的 proxy_pass 配置:
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
location / {
proxy_pass https://NEW_SERVER_IP;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_ssl_verify off;
proxy_read_timeout 150;
}
}
其中 proxy_ssl_verify off 是必要的 —— 新服务器的 SSL 证书是颁发给域名的,而非 IP 地址,关闭验证可避免证书 CN 不匹配的报错。
五、切换执行清单
当主从复制 Seconds_Behind_Master 降至零、反向代理配置完成且 TTL 已预热后,即可执行正式切换。推荐执行顺序如下:
首先在新服务器上停止复制并将数据库切换为读写模式:STOP SLAVE; SET GLOBAL read_only = 0; RESET SLAVE ALL;。然后启动应用相关服务:supervisorctl start all。接着在旧服务器上重载 Nginx 使反向代理生效:nginx -t && systemctl reload nginx,并停止本地应用服务:supervisorctl stop all。之后执行 DNS 切换脚本,将所有 A 记录指向新服务器 IP,此过程通常在十秒内完成。等待约五分钟后,在旧服务器上注释掉所有 crontab 任务,避免残留任务继续写入原数据库。
切换完成后,需额外检查 GitLab 项目 webhook—— 这些 Webhook 地址可能仍指向旧服务器 IP,需通过 GitLab API 批量更新。
六、关键监控指标
整个迁移过程应重点监控以下指标:主从复制延迟(Seconds_Behind_Master)、Nginx 错误日志中 502/504 错误频率、应用层数据库连接池耗尽情况、DNS 解析延迟分布。建议在切换前后各安排一名工程师在线值守,保持实时通信渠道畅通。
零 downtime 迁移的本质是创建一条并行服务路径,通过主从复制保持数据实时同步,通过反向代理消除 DNS 传播窗口的业务影响。掌握上述参数配置与执行顺序,即可完成生产级云服务迁移。
资料来源:本文技术细节参考 Isa Yeter 博客迁移案例(https://isayeter.com/posts/digitalocean-to-hetzner-migration/),相关迁移脚本已开源至 GitHub(https://github.com/isayeter/digitalocean_to_hetzner)。