发布版说明
- 已对真实域名、公网 IP、具体云厂商指代做脱敏处理。
- 原始学习笔记未改动,这一版用于公开发布。
实战记录:从零部署 WordPress + MySQL
一、这次要学的是什么
前一篇已经走通了:
Uptime KumaFileBrowser
它们的共同点是:
- 都是单容器应用
- 不需要单独数据库
- 核心是“应用 -> 本机端口 -> Nginx -> 域名 -> HTTPS”
这次换成 WordPress + MySQL,目的是进入下一层:
从“单应用部署”升级到“应用栈部署”
这次要学的重点不是 WordPress 本身,而是:
docker compose如何同时起多个服务- 应用容器怎么连数据库容器
- 什么东西应该持久化
- WordPress 这类动态网站和前面的单应用有什么本质区别
二、这次的目标结构
这次的目标部署结构:
浏览器
->
域名 blog.example.com
->
Nginx 80/443
->
127.0.0.1:8082
->
WordPress 容器
->
MySQL 容器
和前两次的差别在于:
- 前两次 Nginx 只反代到一个应用
- 这次 WordPress 自己还依赖 MySQL
所以这次第一次真正接触:
“主应用 + 依赖服务”的部署模型
三、先理解 WordPress 为什么和前两次不一样
3.1 WordPress 不是纯静态站点
WordPress 访问时不是直接读几个 HTML 文件,而是:
- 请求先到 Web 服务
- WordPress PHP 程序执行
- PHP 去连数据库
- 把内容动态拼出来
- 再返回给浏览器
所以和 FileBrowser、Uptime Kuma 相比,多出来一个关键点:
数据库已经变成部署的一部分
3.2 这次先不用 PHP-FPM 复杂玩法
为了先理解最核心链路,这次先走一个简化版:
- WordPress 官方镜像自己提供应用能力
- MySQL 官方镜像做数据库
- Nginx 只做最外层反代和 HTTPS
先把“应用栈”理解透,再碰完整 LNMP/LDNMP。
四、目录设计
这次统一放在:
/home/docker/wordpress-stack
下面拆成:
/home/docker/wordpress-stack/mysql
/home/docker/wordpress-stack/wordpress
为什么这么拆:
mysql目录放数据库持久化数据wordpress目录放站点持久化内容
以后迁移、备份、排障都清楚。
五、准备目录
sudo mkdir -p "/home/docker/wordpress-stack/mysql"
sudo mkdir -p "/home/docker/wordpress-stack/wordpress"
sudo chown -R 1000:1000 "/home/docker/wordpress-stack"
说明:
- 先把目录单独准备好
- 别一上来就把所有东西塞进一个目录
六、写 docker compose
文件路径:
/home/docker/wordpress-stack/compose.yml
内容:
services:
db:
image: mysql:8.0
container_name: wp-mysql
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: change_me_wp_pass
MYSQL_ROOT_PASSWORD: change_me_root_pass
volumes:
- ./mysql:/var/lib/mysql
wordpress:
image: wordpress:latest
container_name: wp-app
restart: unless-stopped
depends_on:
- db
ports:
- "127.0.0.1:8082:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: change_me_wp_pass
volumes:
- ./wordpress:/var/www/html
七、先理解 compose 里最重要的几个点
7.1 db 为什么能直接写成数据库地址
这一句:
WORDPRESS_DB_HOST: db:3306
里边的 db 不是公网地址,也不是本机 IP。
它是 docker compose 自动创建的服务名 DNS。
也就是说:
wordpress容器可以直接通过db这个名字找到mysql容器
这一步是多容器部署的关键认知点。
7.2 为什么 MySQL 不需要映射到公网
这次 MySQL 故意没有写:
ports:
因为数据库不应该直接暴露给公网。
它只需要:
- 被 WordPress 容器访问
所以它只留在 Compose 内部网络里。
7.3 为什么 WordPress 仍然只绑定本机端口
这句:
- "127.0.0.1:8082:80"
继续沿用前面两次的安全思路:
- WordPress 只给本机访问
- 外网统一走 Nginx
八、启动服务
cd "/home/docker/wordpress-stack"
docker compose up -d
docker compose ps
启动后重点看两个容器都在不在:
wp-mysqlwp-app
九、先验证应用栈有没有活
9.1 看日志
docker logs --tail 100 "wp-mysql"
docker logs --tail 100 "wp-app"
先判断:
- MySQL 有没有初始化成功
- WordPress 有没有因为数据库连不上而报错
9.2 本机访问 WordPress
curl -s "http://127.0.0.1:8082/" | head -n 20
如果返回的是 WordPress 安装页面 HTML,就说明:
- WordPress 服务正常
- WordPress 能对外响应
- 整个“应用栈”至少活了一半
如果这里直接报数据库错误,那就回去看容器日志。
十、准备域名
建议用新的子域名,比如:
blog.example.com
在 DNS 里加:
- 类型:
A - 名称:
blog - 内容:服务器公网 IP
- 代理状态:
仅 DNS
验证:
dig +short "blog.example.com"
只有返回正确公网 IP,后面才有意义。
十一、配置 Nginx 反代
配置文件路径:
/etc/nginx/sites-available/blog.example.com
内容:
server {
listen 80;
listen [::]:80;
server_name blog.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type "text/plain";
try_files $uri =404;
}
location / {
proxy_pass http://127.0.0.1:8082;
proxy_http_version 1.1;
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;
}
}
启用:
sudo ln -sf "/etc/nginx/sites-available/blog.example.com" "/etc/nginx/sites-enabled/blog.example.com"
sudo nginx -t
sudo systemctl reload nginx
十二、先验证 HTTP 是否打通
本机带 Host 头验证:
curl -I -H "Host: blog.example.com" "http://127.0.0.1"
公网验证:
curl -s "http://blog.example.com/" | head -n 20
看到 WordPress 安装页 HTML,就说明:
- DNS 正常
- Nginx 正常
- 反代正常
- WordPress 正常
十三、申请 HTTPS 证书
先确保挑战目录存在:
sudo mkdir -p "/var/www/certbot/.well-known/acme-challenge"
sudo chown -R "www-data":"www-data" "/var/www/certbot"
先测试挑战文件:
echo "hello-certbot-wordpress" | sudo tee "/var/www/certbot/.well-known/acme-challenge/test-wordpress" > /dev/null
curl "http://blog.example.com/.well-known/acme-challenge/test-wordpress"
只有返回:
hello-certbot-wordpress
才能继续申请证书。
申请:
sudo certbot certonly \
--webroot \
-w "/var/www/certbot" \
-d "blog.example.com"
十四、把 Nginx 改成 HTTPS
站点配置改成:
server {
listen 80;
listen [::]:80;
server_name blog.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type "text/plain";
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name blog.example.com;
ssl_certificate /etc/letsencrypt/live/blog.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.example.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
location / {
proxy_pass http://127.0.0.1:8082;
proxy_http_version 1.1;
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;
}
}
然后:
sudo nginx -t
sudo systemctl reload nginx
验证:
curl -vkI --resolve "blog.example.com:443:127.0.0.1" "https://blog.example.com"
curl -vkI "https://blog.example.com"
十五、这次最可能遇到的坑
15.1 MySQL 还没准备好,WordPress 就先启动了
现象:
wp-app起了- 但访问页面报数据库连接错误
处理:
- 先看
wp-mysql日志 - 再看
wp-app日志 - 有时候要等 MySQL 初始化完成后,重启一下 WordPress 容器
命令:
docker logs --tail 100 "wp-mysql"
docker logs --tail 100 "wp-app"
docker restart "wp-app"
15.2 Nginx 能打开默认页,但不是 WordPress
这和前面 Uptime Kuma 踩过的坑一样。
说明:
- Nginx 活着
- 但不是我的站点配置在工作
优先检查:
ls -l /etc/nginx/sites-enabled/
sudo nginx -T | sed -n '/server_name blog.example.com/,+40p'
15.3 certbot 失败
优先怀疑:
- DNS 没生效
- 挑战目录被反代吞掉
- 云安全组
80/443没放行 - Cloudflare 不是“仅 DNS”
15.4 本机 HTTPS 通,公网 HTTPS 不通
优先怀疑:
- 云厂商安全组
443没放行
验证:
curl -vkI --resolve "blog.example.com:443:127.0.0.1" "https://blog.example.com"
如果这条通,而公网不通,基本就是外层网络问题。
十六、这篇做完后要补哪些东西
等真正实操时,补下面这些实际内容:
- 实际使用的域名
- 实际
compose.yml - 实际出错日志
- 实际踩到的坑
- 最终验证成功的命令输出
这样这篇才会从“手册”升级成“真实复盘”。
十七、一句话提醒未来的自己
这次不是为了学会 WordPress,而是为了学会:
当一个应用依赖另一个服务时,如何用 Docker Compose 把它们组成一个最小可用的应用栈,再通过 Nginx 和 HTTPS 暴露出去。
















暂无评论内容