将 WordPress 整体迁移到 Docker 容器

信息
本文更新于 2023 年 7 月 5 日。如果有任何问题欢迎在评论区指出。

Docker 作为这几年兴起的虚拟化解决方案,具有便于移植、开销较低的优势。这篇知乎回答很好的概括了 Docker 的特性,感兴趣的话可以看一看。把 WordPress 迁移到 Docker 中后,不仅可以方便的备份和克隆,更可以轻松的在不同的系统和配置之间迁移。一切网站需要的依赖项(LAMP等)都会包含在容器里,所以移植的时候只需要把容器打包带走即可。话不多说,下面教程中我会尽可能详细的阐述迁移的每一步,希望对诸君能有帮助。

致谢:本文的灵感和早期的研究方向主要来自于这篇文章,感谢作者 Stephen AfamO。

在开始之前,请务必备份 WordPress 工作目录下的文件和相关的数据库。无论是否迁移,经常备份站点有利无害。

我推荐诸君做好以下准备,以确保迁移成功:

  • 目标机器运行 Ubuntu 18.04 LTS 或者更高。CentOS 以及其他 Linux 发行版理论上都可以适用这套教程,但我不能保证成功。特别是 Nginx 的配置,会因为系统不同而略有出入。
  • 目标机器在防火墙开通了 80 (HTTP) 和 443 (HTTPS) 端口。一些提供商,如阿里云,需要自行在控制台的安全组中添加相关规则。
  • 已经将你的域名(或者一个子域名)解析到新的系统。下面的示例我们将用 example.com,请根据自身情况修改。
  • 目标机器最好和原始机器是两台不同的实例。同一台机器中的迁移理论上可以成功,但一旦失败极易将已有的系统搞得乱七八糟,甚至导致你的博客不再可访问。迁移到新的机器还能实现平滑过渡,在你验证新的机器正常工作后可以再移除老的机器。
  • 当然,你需要能 SSH 到目标机器和原始机器。本文所有操作均在 root 身份下进行。如果你用非 root 账号,需要在部分命令前面加入 sudo

WordPress 有很多著名的备份插件,而我要推荐的是这款下载量最多、也是好评最高的这款插件:All-in-One WP Migration。虽然插件没有中文翻译,但过程非常简单。可以导出的内容非常全面,包括文章、媒体、评论、主题、插件、设定等。安装后在仪表盘 All-in-One WP Migration > Export 打开导出界面,然后参考下面的图片开始导出:

导出设定

按照你的需求选择导出的内容后,按下这个诱人的 EXPORT TO 按钮,然后在弹出的窗口中选择 FILE 下载到本地(你也可以选择其他的保存方式,但我没试过)。根据你网站的配置和文件数量,存档大小从几十M到几百M都有可能。假设我们这次下载的文件叫做 example.com-20180704-012345-678.wpress

其他的反代选择

下面的教程中我们将手动配置 Nginx。如果你更喜欢图形化或者傻瓜式的配置,我建议使用 NPM (Nginx Proxy Manager) 进行操作。具体操作可以参考这个教程

另一个选项是使用 Traefik 进行反代。这里你可以参考我另一篇教程

1
2
apt update
apt install nginx

这里可以打开官方文档,选择自己服务器的发行版,然后按照文档上的步骤进行安装。

请注意,如果你是以非 root 身份运行下面的脚本,安装完成后可以将自己加到 docker 用户组内,这样无需使用 sudo 也可以执行 Docker 命令

以 Ubuntu 为例:

1
2
sudo groupadd docker
sudo usermod -aG docker $USER

完成后重新登录或者重启机器,让新权限生效。

另外,可以将 Docker 设为自动启动,这样服务器重启后 Docker 会一起启动。

1
2
sudo systemctl enable docker.service
sudo systemctl enable containerd.service

虽说有了 Docker-compose 这样的利器,但我们先得创建一个定义文件,其中我们会指定需要的镜像、他们之间的关系、环境变量等等。

提示
下面所有的 example 等字样均为示例,需要替换成你自己的内容。
mkdir -pv /var/www/example.com/
cd /var/www/example.com/

-pv 选项会创建必要的上级目录,如果它们还尚未存在。

touch docker-compose.yml

接下来,用你喜欢的文本编辑器打开 docker-compose.yml,并输入以下内容(推荐复制/粘贴,因为 YAML 文件对空格和缩进有严格的要求,少个空格可能会导致编译失败)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
version: '3'

services:
  db:
    image: mysql:5.7
    container_name: db_example
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: eXAMpLe     # 随便起,和下面的 phpMyAdmin 中的需要一致
      MYSQL_DATABASE: wp_example       # 这三行设定需要和下面一致
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress123

  wp:
    depends_on:
      - db
    image: wordpress:latest
    container_name: example_site
    ports:
      - "1234:80"                      # 可以根据需要改成其他端口,但下文的实例将使用1234
    restart: always
    links:
      - db:mysql
    environment:
      WORDPRESS_DB_NAME: wp_example
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress123
    volumes:
     - ./src:/var/www/html  

  phpmyadmin:                          # 如果不需要 phpMyAdmin,可以删掉33-47行相关部分
    depends_on:
      - db
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    links:
      - db:mysql
    restart: always
    ports:
      - "8081:80"
    volumes:
      - /sessions
    environment:
      MYSQL_ROOT_PASSWORD: eXAMpLe

volumes:
    db_data:

下面对部分代码稍作解释:

8、49行:volumes 指令把容器内的 /var/lib/mysql/ 下的数据库内容映射到当前目录下的文件夹中(默认为 database)文件夹,这样数据库中产生的变化会保存到本地系统。详情可以参考这篇文章

31行:同第8行类似,但把容器内的 /var/www/html/ 下的 WordPress 插件、主题等文件映射到当前目录下的 /src 文件夹下。有兴趣的可以读一读这篇官方文档了解 volume

假如去掉 volumes 指令,那么在你重启 Docker 容器后对 WordPress 所有的更改都会消失!(想象一下你的文章、主题、插件全部消失,取而代之的是“著名的5分钟安装”…不要问我为什么知道…)

25行:link 指令将 mysql 容器和 WordPress 联系起来,这样 WordPress 实例就可以访问数据库了。请注意,虽然我们用 Docker-compose 可以协调多个 Docker 容器之间的合作,但每个容器之间仍然是互相独立的。也就是说,除非是你把 Docker 容器暴露给外部世界(通过 port 或 volumes 等指令映射),它们是完全“绝缘”的。当然我们也可以给 MySQL 实例创建端口映射(一些教程也是这么做的),但在我们这个范例中是完全没必要的。毕竟,端口开得越多一个,安全威胁就多一分。(注:link 指令在最新版 Docker 中被标记为“已过时”,但我懒得去搜新的设定了…)

激动人心的时候到了,是时候启动我们的 Docker 配置了!

docker compose up

现在 Docker 会下载并解压 MySQL、WordPress、phpMyAdmin 容器以及它们需要的所有依赖项。接着你眼前就会闪过一大堆log(别急,这只是初次启动时候方便排查问题用的)。假如一切正常,我们就可以按 Ctrl+C 键关闭这套配置。如果在log中注意到问题,也欢迎你在评论中讨论或者求助。在关闭 Docker 容器后,其对应的镜像会被保留,方便日后使用。

由于 Docker 中的 WordPress 实例只提供基础的 HTTP 接入,为了方便部署 SSL 证书和创建映射,我们需要用 Nginx 创建一个简单的反向代理(反代)。延伸阅读:什么是正向代理和反向代理

1
2
    cd /etc/nginx/conf.d
    touch www.example.com.conf

用文本编辑器打开 www.example.com.conf 文件,并输入以下内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    server_name example.com www.example.com;
    location / {
        proxy_pass http://0.0.0.0:1234;          # 将请求发到本机的1234端口
        proxy_set_header Accept-Encoding "";
        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;
    }
}

有兴趣的可以看这篇官方文档,详细了解 Nginx 反代。注意第7行的端口号需要和先前 docker-compose.yml 中定义的一致。

接下来,重载 Nginx 就可以载入这个配置。

nginx -t && nginx -s reload

重启 Docker 容器。

1
2
cd /var/www/example.com/
docker compose up -d

注意命令中的 -d,将 Docker-compose 放在后台运行 [daemon]。现在你应该看到几条简单的状态消息,但没有瀑布般的输出。

在浏览器里访问你的域名。如果一切正常,你应该能看到 WordPress “著名的5分钟安装”。

虽然这样的网站已经能用,但显然在 8102 年不加密的网站漏洞多多(非加密的网站在搜索引擎中的排名也更低)。

我们首先需要一个合格的 SSL 证书。除了一些动辄几十刀的解决方案外,我相信大家对免费好用的 Let’s Encrypt 也有所耳闻。通过 LE 官方推荐的 Certbot 程序,我们可以简化申请和更新证书的流程,只需一键就可完成。下面安装步骤摘自 Certbot 官方教程,你也可以在这个网站找到其他 Linux 发行版的教程。

1
2
apt update
apt install certbot python3-certbot-nginx

接下来,申请一个新的证书:

sudo certbot --nginx -d example.com -d www.example.com

生成的证书和私钥默认放置在 /etc/letsencrypt/live/example.com/ 下。

最后,我们编辑 Nginx 的配置,修改后的 example.com.conf 类似这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    # RSA certificate
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot

    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

    # Redirect non-https traffic to https
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot

 location / {
   proxy_pass http://0.0.0.0:1234;
   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;
 }

 location /phpmyadmin/ {                                # 将 phpMyAdmin 端口映射到 /phpmyadmin/ 下
   proxy_pass http://0.0.0.0:8081/;
   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;
 }
}

重载 Nginx 配置:

nginx t && nginx -s reload

再次在浏览器中打开你的域名,你应该能看到网站已经自动跳转到 HTTPS 了。访问 /phpmyadmin (如 example.com/phpmyadmin),你可以看到熟悉的 phpMyAdmin 登录界面(可以用 root 账号和你之前定义的 root 密码登录)。至此,现在你已经有一个完整的 WordPress 实例了!

Let’s Encrypt 的证书每 90 天都要续一次,否则会过期。我们可以用一个简单的 cron 脚本来自动续约 SSL 证书。

首先打开 crontab 文件

crontab -e

然后在里面输入以下内容

0 12 * * * /usr/bin/certbot renew --quiet

这个命令会每天 12 点运行 CertBot 并检查是否有证书快要过期。如果有的话它会自动续约这些证书。

还记得我们在一开始创建的存档吗?我们现在要把它导入新的网站。完成“5分钟安装”后,在新站中安装 All-in-One WP Migration,并进入 Import 选单。将存档文件(例如 example.com-20180704-012345-678.wpress)拖入框中,或者点开 IMPORT FROM 并选择 FILE(注意,免费版只支持至多 512M 的文件,超出需要付费升级)。接下来文件会上传并覆盖现有的 WordPress 结构。升级完成后,可能会弹出提示要你点开“固定链接”菜单并按两次“保存更改”。现在打开你的网站,一切应该都和原来一样。

如果你选择不导出插件,你可能需要重新安装这些插件。恭喜,你的博客已经完全了迁移到 Docker!

下面是一些常见问题的解决方案,也欢迎大家提出新的问题,我会尽快解答。

  • 提示”Can’t find a suitable configuration file…”,请确认 docker-compose.yml 文件在你当前的工作目录下(用 pwd 指令查看),如是 /var/www/example.com

 

相关内容