总算把 WordPress 装到 Docker Compose 里面去了

当前这个网站,好久没维护了。之前是部署在一台比较老旧的 TencentOS 系统上,并且是比较老旧的 apache 版本,整台服务器都很难维护,迁移就比较麻烦,然后就一直放着没动它。原本部署的这台服务器配置也低,虽然一年几十元,但不打算续费了,今年年底就要到期了,就花了点时间捣鼓一下,先将它所有环境从宿主机迁入到 Docker Compose 中,然后整个 Docker Compose 项目打包迁移。当看到这篇文章的时候,是已经迁移成功了,目前部署在 RackNerd 的美国VPS服务器上,我买的是这款配置(超划算RackNerd服务器),非常不错(还拿来搭建了VPN服务器🚀),配置如下:

$29.89 /年 💰 (29.89 美元/年)

  • 🖥️ 3 vCPU 核心 (3 vCPU Cores)

  • 💾 60 GB SSD 存储 (60 GB SSD Storage)

  • 🧠 3.5 GB 内存 (3.5 GB RAM)

  • 📡 5 TB 月流量 (5 TB Monthly Transfer)

  • 🌐 1Gbps 网络端口 (1Gbps Network Port)

  • 🔑 完全根访问权限 (Full Root Access)

  • 🌍 1 个 IPv4 地址 (1 IPv4 Address)

  • ⚙️ KVM/SolusVM

如果购买的话,尽可能选择 DC3 机房,DC3 机房在中国访问的速度比较友好。在中国访问的速度与国际出入口带宽有关。

目前网站运行状况良好,除了昨天迁移时反复修改了几次 DNS 导致缓存产生而出现 SSL 安全提示之外,今早起来发现也恢复正常了。

现在来回顾一下昨天的迁移过程,怎样把 WordPress 装到 Docker Compose 里面来。主要工作分为三个部分:

第一部分,从宿主机导出 Mysql 数据库。

这个网站使用的是 Mysql 8.0.28 版本,服务器中还有其他网站的数据库,现在需要单独把 WordPress 博客的数据库导出来,也非常简单,只需要在命令行执行以下命令:

1
2
3

mysqldump -u root -p --databases <数据库名称> --routines --events --triggers --single-transaction > <导出保存的名称>.sql

执行之后会提示输出 root 密码,稍等几秒就导出完成了。

此时还需要把原来的 WordPress 网站目录整个复制到 Docker compose 项目根目录下(website)。


需要去 wp-config.php 修改 DB_HOSTmysql

第二部分,编写 docker-compose.yml 以及 Dockerfile。

为了把 WordPress 装到容器里,我给它准备了 Dockerfile 和 docker-compose.yml ,服务器本身就有 docker compose 环境,这里就不把环境准备展开赘述了。

Dockerfile:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63

FROM php:8.3.24-fpm

# 接收构建时代理参数

# ARG http_proxy

# ARG https_proxy

# 安装 mysqli, pdo, pdo_mysql 模块

RUN docker-php-ext-install mysqli pdo pdo_mysql

# 设置 PHP 配置(例如:max_execution_time)

RUN echo 'max_execution_time = 90' >> /usr/local/etc/php/conf.d/my-php.ini

# 安装 gd 模块

RUN apt-get update && apt-get install -y \

        libfreetype6-dev \

        libjpeg62-turbo-dev \

        libpng-dev \

    && docker-php-ext-configure gd --with-freetype --with-jpeg \

    && docker-php-ext-install gd

# 安装 exif 模块

RUN docker-php-ext-install exif

# 安装 imagick 模块

RUN apt-get install -y libmagickwand-dev --no-install-recommends \

    && (pecl install imagick || true) \

    && docker-php-ext-enable imagick

# 安装 zip 模块

RUN apt-get install -y \

        libzip-dev \

        zip \

  && docker-php-ext-install zip

# 安装 intl 模块

RUN apt-get install -y libicu-dev \

    && docker-php-ext-install intl

# 清理安装后的文件

RUN apt-get clean && rm -rf /var/lib/apt/lists/*

这份 Dockerfile 主要是构建了 php 环境的容器,并且把一些必须的扩展都打包进去了,有了这个环境就能跑 WordPress 或者其他 php 项目。

docker-compose.yml:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

services:

  nginx:

    image: nginx:1.25.3

    restart: always

    ports:

      - "127.0.0.1:8001:80"

    volumes:

      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro

      - ./website:/var/www/website

      - /etc/letsencrypt:/etc/letsencrypt:ro

      - ./ssl:/etc/ssl

      - ./nginxlog:/var/log/nginx

    depends_on:

      - php

    networks:

      - app-network

  php:

    build:

      context: .

      dockerfile: Dockerfile

    image: php:
8.3.24-fpm

    restart: always

    volumes:

      - ./website:/var/www/website

      - ./php/php.ini:/usr/local/etc/php/php.ini

    networks:

      - app-network

  mysql:

    image:  mysql:8.0.28

    container_name: mysql

    restart: always

    environment:

      MYSQL_ROOT_PASSWORD: mysqlrootpassword

    volumes:

      - ./data:/var/lib/mysql

    networks:

      - app-network

  phpmyadmin:

    image: phpmyadmin/phpmyadmin

    restart: always

    ports:

      - "127.0.0.1:8002:80"

    environment:

      PMA_HOST: mysql

      MYSQL_ROOT_PASSWORD: mysqlrootpassword

    depends_on:

      - mysql

    networks:

      - app-network

networks:

  app-network:

    driver: bridge

有了这两个文件,Docker Compose 就可以跑起来了。但还需要补充一下 php.ini 和 nginx.conf,因为我让这个 Docker Compose 同时提供了 php 和 nginx 的服务。

php/php.ini:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

[PHP]

engine = On

short_open_tag = Off

precision = 14

output_buffering = 4096

zlib.output_compression = Off

implicit_flush = Off

unserialize_callback_func =

serialize_precision = -1

disable_functions =

disable_classes =

zend.enable_gc = On

zend.exception_ignore_args = Off

expose_php = On

max_execution_time = 30

max_input_time = 60

memory_limit = 128M

error_reporting = E_ALL

display_errors = On

display_startup_errors = On

log_errors = On

log_errors_max_len = 1024

ignore_repeated_errors = Off

ignore_repeated_source = Off

report_memleaks = On

variables_order = "GPCS"

request_order = "GP"

register_argc_argv = Off

auto_globals_jit = On

post_max_size = 200M

auto_prepend_file =

auto_append_file =

default_mimetype = "text/html"

default_charset = "UTF-8"

doc_root =

user_dir =

enable_dl = Off

file_uploads = On

upload_max_filesize = 60M

max_file_uploads = 20

allow_url_fopen = On

allow_url_include = Off

default_socket_timeout = 60

[CLI Server]

cli_server.color = On

[Date]

[filter]

[iconv]

[imap]

[intl]

[sqlite3]

[Pcre]

[Pdo]

pdo_mysql.default_socket=

[Phar]

[mail function]

SMTP = localhost

smtp_port = 25

mail.add_x_header = Off

[ODBC]

odbc.allow_persistent = On

odbc.check_persistent = On

odbc.max_persistent = -1

odbc.max_links = -1

odbc.defaultlrl = 4096

odbc.defaultbinmode = 1

[MySQLi]

mysqli.max_persistent = -1

mysqli.allow_persistent = On

mysqli.max_links = -1

mysqli.default_port = 3306

mysqli.default_socket =

mysqli.default_host =

mysqli.default_user =

mysqli.default_pw =

mysqli.reconnect = Off

[mysqlnd]

mysqlnd.collect_statistics = On

mysqlnd.collect_memory_statistics = On

[OCI8]

[PostgreSQL]

pgsql.allow_persistent = On

pgsql.auto_reset_persistent = Off

pgsql.max_persistent = -1

pgsql.max_links = -1

pgsql.ignore_notice = 0

pgsql.log_notice = 0

[bcmath]

bcmath.scale = 0

[browscap]

[Session]

session.save_handler = files

session.use_strict_mode = 0

session.use_cookies = 1

session.use_only_cookies = 1

session.name = PHPSESSID

session.auto_start = 0

session.cookie_lifetime = 0

session.cookie_path = /

session.cookie_domain =

session.cookie_httponly =

session.cookie_samesite =

session.serialize_handler = php

session.gc_probability = 1

session.gc_divisor = 1000

session.gc_maxlifetime = 1440

session.referer_check =

session.cache_limiter = nocache

session.cache_expire = 180

session.use_trans_sid = 0

session.sid_length = 26

session.trans_sid_tags = "a=href,area=href,frame=src,form="

session.sid_bits_per_character = 5

[Assertion]

zend.assertions = 1

[COM]

[mbstring]

[gd]

[exif]

[Tidy]

tidy.clean_output = Off

[soap]

soap.wsdl_cache_enabled=1

soap.wsdl_cache_dir="/tmp"

soap.wsdl_cache_ttl=86400

soap.wsdl_cache_limit = 5

[sysvshm]

[ldap]

ldap.max_links = -1

[dba]

[opcache]

[curl]

[openssl]

[ffi]

nginx/nginx.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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

events {

    worker_connections 1024;

}

http {

    include /etc/nginx/mime.types;

    default_type application/octet-stream;

    # 映射 HTTPS 状态(用于 fastcgi_param)

    map $http_x_forwarded_proto $fastcgi_https {

        default off;

        https on;

    }

    server {

        listen 80;

        listen [::]:80;

        server_name localhost;

        error_log /var/log/nginx/error.log debug;

        access_log /var/log/nginx/access.log;

        root /var/www/website;

        index index.html index.htm index.php;

        client_max_body_size 1000M;

        # 处理静态文件请求

        location ~* \.(ico|gif|jpg|jpeg|png|js|css)$ {

            try_files $uri =404;

        }

        # 防止访问特定文件类型

        location ~* \.(bak|inc|lib|sh|tpl|lbi|dwt)$ {

            deny all;

            return 404;

        }

        # 处理 PHP 文件

        location ~ \.php$ {

            include fastcgi_params;

            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

            fastcgi_param HTTPS $fastcgi_https;  # 识别 HTTPS

            fastcgi_pass php:9000;

            fastcgi_index index.php;

        }

        # URL 重写规则

        location / {

            try_files $uri $uri/ @rewrite;

        }

        location @rewrite {

            rewrite ^/(.*)$ /index.php?route=$1 last;

        }

    }

}

至此,第二部分就完成了,使用 docker compose build –no-cache && docker compose up -d 启动容器。

第三部分,数据库导入。

在第二部分中,docker-compose.yml 首次启动时创建的是空的数据库,我们需要把原来备份的数据库文件复制到容器内,并且在容器内恢复数据库以及创建用户和权限。

复制数据库文件到容器:

docker cp <导出保存的名称>.sql.sql mysql:/<导出保存的名称>.sql

进入容器内导入原备份的 .sql 数据到数据库中

1
2
3
4
5
6
7
8
9

docker exec -it mysql bash

ls -lt /<导出保存的名称>.sql

mysql -u root -p < /<导出保存的名称>.sql

SHOW DATABASES;

通过以下三条命令创建用户和权限:

用户名、数据库名、用户密码都必须与 wordpress 一致,并且使用 % 可以从任意 IP 登录。

1
2
3
4
5
6
7
8
9

CREATE USER 'wwwbg7iaecom'@'%' IDENTIFIED BY 'Web3900,.';

GRANT ALL PRIVILEGES ON wwwbg7iaecom.* TO 'wwwbg7iaecom'@'%';

FLUSH PRIVILEGES;

SELECT user, host FROM mysql.user;

完成以上操作之后,退出容器。重启一次 Docker Compose: docker compose down && docker compose up -d

至此,就完成了整个项目的迁移了,现在可以通过  http://127.0.0.1:8001 访问 WordPress 博客了。

还不够,对不对?用户怎么通过 http://127.0.0.1:8001 访问嘛?没错,还需要宿主机的 nginx 反代一下到 http://127.0.0.1:8001 ,或者使用 Cloudflare Tunnel 反代也可以。做完就个动作,整个项目才算真正迁移完毕。

现在你看到的这篇文章,就是已经迁移完的结果了。

(这篇文章发完,才发现这个网站还没支持代码块功能,后面抽个时间给它加上。)