WordPress 迁移实战:从 NAS 到 VPS (Rocky Linux + Nginx + Cloudflare)

继上一篇分享解决 WordPress Docker 容器在我 NAS 上突然 “Unhealthy” 的经历后,今天,我就将上次的 WordPress 站点从我的群晖 NAS 迁移到搬瓦工(Bandwagon Host)的 Rocky Linux VPS,并配置 Nginx 反向代理及 Cloudflare CDN 的完整过程记录下来,希望能给大家提供一份详尽的参考。AI 助手依然是完成这次工程的得力伙伴。

迁移目标

  • 将 NAS Docker 环境中的 WordPress 站点(文件和数据库)完整迁移到搬瓦工 VPS。
  • 在 VPS 上使用 Docker Compose 部署 WordPress、MariaDB、phpMyAdmin。
  • 引入 Nginx 作为反向代理,并配置 SSL (HTTPS)。
  • 集成 Cloudflare CDN 进行加速和安全防护。
  • 确保所有 Docker 服务最终都处于 healthy 状态。

阶段一: 在 NAS 上准备迁移数据

迁移前,务必对现有 WordPress 站点进行完整备份。

1. 备份 WordPress 文件

包含所有核心文件、wp-content 目录及 wp-config.php

# 进入wordpress所在目录
cd /volume1/docker/
# 将 wordpress 目录打包并压缩,YYYYMMDD 替换为当天日期
sudo tar -czvf wordpress_nas_backup_20250518.tar.gz wordpress/

2. 备份 WordPress 数据库

  1. 通过 NAS 上的 phpMyAdmin(http://<你的NAS_IP>:2500)登录。
  2. 选择数据库(如 leogaoxhosting_db),
  3. 选择“快速 (Quick)”导出方法,格式 SQL。
  4. 点击“导出 (Export)”。

阶段二: 在搬瓦工 VPS (Rocky Linux) 上准备新家

1. 清理 VPS(如果非全新)

# 停止并移除旧容器
sudo docker stop old_nginx old_wp old_db
sudo docker rm old_nginx old_wp old_db

# 清理卷和网络
sudo docker volume prune -f
sudo docker network prune -f

# 删除旧部署文件夹(谨慎操作)
sudo rm -rf /path/to/old_deployment_folder

2. 安装/确认 Docker & Docker Compose

sudo docker --version
# 示例: Docker version 28.1.1, build 4eba377

sudo systemctl status docker
# ● docker.service … Active: active (running)

docker compose version
# 示例: Docker Compose version v2.35.1

3. 创建目录结构

sudo mkdir -p /srv/docker/wordpress_vps/html
sudo mkdir -p /srv/docker/wordpress_vps/db
sudo mkdir -p /srv/docker/wordpress_vps/nginx/conf.d
sudo mkdir -p /srv/docker/wordpress_vps/nginx/certs
sudo mkdir -p /srv/docker/wordpress_vps/nginx/logs
sudo mkdir -p /srv/docker/wordpress_vps/secrets

4. 配置防火墙 (firewalld)

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

阶段三: 迁移数据

1. 上传备份文件到 VPS 的 worpress_vps 所在文件夹

wordpress_nas_backup_20250518.tar.gzleogaoxhosting_db.sql 上传至 /srv/docker/wordpress_vps/

2. 恢复 WordPress 文件

cd /srv/docker/wordpress_vps/

# 解压并去掉多余目录
sudo tar -xzvf wordpress_nas_backup_20250518.tar.gz -C html/ --strip-components=1

# 设置所有权与权限 (UID/GID=33)
sudo chown -R 33:33 /srv/docker/wordpress_vps/html/
sudo find /srv/docker/wordpress_vps/html/ -type d -exec chmod 755 {} \;
sudo find /srv/docker/wordpress_vps/html/ -type f -exec chmod 644 {} \;

# 对 wp-config.php 设置更严格权限
sudo chmod 600 /srv/docker/wordpress_vps/html/wp-config.php

阶段四:核心配置(此阶段也是整个迁移的难点所在)

1. 在 /srv/docker/wordpress_vps/secrets/ 目录下安全地创建两个密码文件:wordpress_db_password.txtmariadb_root_password.txt

cd /srv/docker/wordpress_vps/
sudo chmod 700 secrets

echo 'YOUR_WORDPRESS_DB_PASSWORD' | sudo tee secrets/wordpress_db_password.txt > /dev/null 
sudo chmod 600 secrets/wordpress_db_password.txt 

echo 'YOUR_MARIADB_ROOT_PASSWORD' | sudo tee secrets/mariadb_root_password.txt > /dev/null 
sudo chmod 600 secrets/mariadb_root_password.txt 

2. 编写 docker-compose.yml

networks:
  app_network:
    driver: bridge

services:
  nginx:
    image: nginx:1.27-alpine
    container_name: wordpress_nginx_proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/certs:/etc/nginx/certs:ro
      - ./html:/var/www/html:ro
      - ./nginx/logs:/var/log/nginx
    networks:
      - app_network
    depends_on:
      - wordpress
    restart: unless-stopped

  wordpress:
    image: wordpress:6.8.1-php8.2-apache
    container_name: WordPress_VPS
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/"]
      interval: 30s
      timeout: 10s
      retries: 5
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - ./html:/var/www/html:rw
      - ./secrets/wordpress_db_password.txt:/run/secrets/wp_db_password:ro
    environment:
      WORDPRESS_DB_HOST: wordpress_db_vps_hostname
      WORDPRESS_DB_USER: leogaox
      WORDPRESS_DB_PASSWORD_FILE: /run/secrets/wp_db_password
      WORDPRESS_DB_NAME: leogaoxhosting_db
      WORDPRESS_TABLE_PREFIX: wp_
    networks:
      - app_network
    restart: on-failure:5

  db:
    image: mariadb:11.3-jammy
    container_name: WordPress_DB_VPS
    healthcheck:
      test: ["CMD-SHELL","healthcheck.sh --su-mysql --connect"]
      interval: 30s
      timeout: 20s
      retries: 3
      start_period: 90s
    environment:
      MYSQL_DATABASE: leogaoxhosting_db
      MYSQL_USER: leogaox
      MYSQL_PASSWORD_FILE: /run/secrets/db_user_password
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      TZ: Asia/Shanghai
    volumes:
      - ./db:/var/lib/mysql:rw
      - ./secrets/wordpress_db_password.txt:/run/secrets/db_user_password:ro
      - ./secrets/mariadb_root_password.txt:/run/secrets/db_root_password:ro
    networks:
      - app_network
    restart: on-failure:5

  phpmyadmin:
    image: phpmyadmin:5.2
    container_name: WordPress_phpMyAdmin_VPS
    ports:
      - - "127.0.0.1:2500:80" # <--- 限制只能本地访问
    environment:
      PMA_HOST: wordpress_db_vps_hostname
      PMA_PORT: 3306
    networks:
      - app_network
    depends_on:
      db:
        condition: service_healthy
    restart: on-failure:5

3. Nginx 反向代理配置

把从 Cloudflare 下载的证书放入 /srv/docker/wordpress_vps/nginx/certs下,在/srv/docker/wordpress_vps/nginx/conf.d/ 目录下创建 Nginx 配置文件,命名为 wordpress_vps.conf

server {
    listen 80;
    listen [::]:80;

    # 改成你的域名
    server_name leogaox.com www.leogaox.com;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    #改成你的域名
    server_name leogaox.com www.leogaox.com; 
    ssl_certificate /etc/nginx/certs/leogaox.com.pem;
    ssl_certificate_key /etc/nginx/certs/leogaox.com.key;

    # 推荐启用的 SSL 配置
    ssl_protocols TLSv1.2 TLSv1.3;
    # 从 Mozilla SSL Configuration Generator 获取推荐的加密套件,以下是一个示例:
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m; # 约40000个会话
    ssl_session_tickets off; # 增强前向保密性

    # 如果您使用的是 Cloudflare Origin CA 证书,OCSP Stapling 可能由 Cloudflare 处理或不需要
    # 如果是其他证书,建议配置 OCSP Stapling
    # ssl_stapling on;
    # ssl_stapling_verify on;
    # resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=300s; # 使用多个DNS解析器
    # resolver_timeout 5s;

    # 可选:添加 HSTS 头部 (强制浏览器使用HTTPS,测试无误后再启用)
    # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    access_log /var/log/nginx/wordpress_access.log;
    error_log /var/log/nginx/wordpress_error.log;

    # 从 Cloudflare 获取真实 IP (请务必从官方链接更新此列表)
    # IPv4: https://www.cloudflare.com/ips-v4
    # IPv6: https://www.cloudflare.com/ips-v6
    set_real_ip_from 173.245.48.0/20;
    set_real_ip_from 103.21.244.0/22;
    set_real_ip_from 103.22.200.0/22;
    set_real_ip_from 103.31.4.0/22;
    set_real_ip_from 141.101.64.0/18;
    set_real_ip_from 108.162.192.0/18;
    set_real_ip_from 190.93.240.0/20;
    set_real_ip_from 188.114.96.0/20;
    set_real_ip_from 197.234.240.0/22;
    set_real_ip_from 198.41.128.0/17;
    set_real_ip_from 162.158.0.0/15;
    set_real_ip_from 104.16.0.0/13;
    set_real_ip_from 104.24.0.0/14;
    set_real_ip_from 172.64.0.0/13;
    set_real_ip_from 131.0.72.0/22;
    set_real_ip_from 2400:cb00::/32;
    set_real_ip_from 2606:4700::/32;
    set_real_ip_from 2803:f800::/32;
    set_real_ip_from 2405:b500::/32;
    set_real_ip_from 2405:8100::/32;
    set_real_ip_from 2a06:98c0::/29;
    set_real_ip_from 2c0f:f248::/32;
    real_ip_header CF-Connecting-IP;

    location / {
        proxy_pass http://wordpress_vps_hostname:80;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade"; # 用于 WebSocket 等协议的升级
        proxy_read_timeout 86400s; # Nginx 1.7.7+ 使用 's' 后缀
        proxy_send_timeout 600s;   # (新增) 发送超时
        proxy_connect_timeout 600s;# (新增) 连接超时

        client_max_body_size 64M; # 调整为一个更合理的值,除非您确实需要上传非常大的文件
    }

    # (可选) 如果您想通过 Nginx 代理 phpMyAdmin (例如通过 yourdomain.com/pma)
    # location /pma { # 使用一个不容易被猜到的路径,并确保末尾没有斜杠
    #    rewrite ^/pma(/.*)$ $1 break; # 移除 /pma 前缀
    #    proxy_pass http://wordpress_phpmyadmin_vps_hostname:80; # 指向 phpMyAdmin 服务的 hostname 和内部端口
    #    proxy_set_header Host $host;
    #    proxy_set_header X-Real-IP $remote_addr;
    #    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #    proxy_set_header X-Forwarded-Proto $scheme;
    # }
}

阶段四:启动服务

1. 拉取镜像并启动

cd /srv/docker/wordpress_vps/
sudo docker compose pull
sudo docker compose up -d --force-recreate --remove-orphans

2. 检查服务状态

sudo docker compose ps
# 确认所有容器均为 Up 或 healthy

阶段五:Cloudflare 配置与最终测试

  1. 在 Cloudflare DNS 中,将 leogaox.com A 记录指向 VPS 公共 IP,设置为 已代理 (Proxied)
  2. 在 SSL/TLS 选项中,选择 Full (Strict) 模式。
  3. 浏览器访问 https://leogaox.com 并验证站点正常。

阶段六:经验总结

至此就完成了博客从 NAS 到 VPS 的迁移。迁移的最大难点在阶段四,要准备和编写好几个文件,特别是compose.yml和ngnix.conf这两个文件,如果后面容器运行报错,多半和这两个文件有关。希望这篇文章对大家有所帮助!

支持

Leave a Comment