02-发布版:从零部署 WordPress + MySQL

02-发布版:从零部署 WordPress + MySQL

发布版说明

  • 已对真实域名、公网 IP、具体云厂商指代做脱敏处理。
  • 原始学习笔记未改动,这一版用于公开发布。

实战记录:从零部署 WordPress + MySQL


一、这次要学的是什么

前一篇已经走通了:

  • Uptime Kuma
  • FileBrowser

它们的共同点是:

  • 都是单容器应用
  • 不需要单独数据库
  • 核心是“应用 -> 本机端口 -> Nginx -> 域名 -> HTTPS”

这次换成 WordPress + MySQL,目的是进入下一层:

从“单应用部署”升级到“应用栈部署”

这次要学的重点不是 WordPress 本身,而是:

  1. docker compose 如何同时起多个服务
  2. 应用容器怎么连数据库容器
  3. 什么东西应该持久化
  4. WordPress 这类动态网站和前面的单应用有什么本质区别

二、这次的目标结构

这次的目标部署结构:

浏览器
  ->
域名 blog.example.com
  ->
Nginx 80/443
  ->
127.0.0.1:8082
  ->
WordPress 容器
  ->
MySQL 容器

和前两次的差别在于:

  • 前两次 Nginx 只反代到一个应用
  • 这次 WordPress 自己还依赖 MySQL

所以这次第一次真正接触:

“主应用 + 依赖服务”的部署模型


三、先理解 WordPress 为什么和前两次不一样

3.1 WordPress 不是纯静态站点

WordPress 访问时不是直接读几个 HTML 文件,而是:

  1. 请求先到 Web 服务
  2. WordPress PHP 程序执行
  3. PHP 去连数据库
  4. 把内容动态拼出来
  5. 再返回给浏览器

所以和 FileBrowserUptime 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-mysql
  • wp-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 失败

优先怀疑:

  1. DNS 没生效
  2. 挑战目录被反代吞掉
  3. 云安全组 80/443 没放行
  4. Cloudflare 不是“仅 DNS”

15.4 本机 HTTPS 通,公网 HTTPS 不通

优先怀疑:

  • 云厂商安全组 443 没放行

验证:

curl -vkI --resolve "blog.example.com:443:127.0.0.1" "https://blog.example.com"

如果这条通,而公网不通,基本就是外层网络问题。


十六、这篇做完后要补哪些东西

等真正实操时,补下面这些实际内容:

  1. 实际使用的域名
  2. 实际 compose.yml
  3. 实际出错日志
  4. 实际踩到的坑
  5. 最终验证成功的命令输出

这样这篇才会从“手册”升级成“真实复盘”。


十七、一句话提醒未来的自己

这次不是为了学会 WordPress,而是为了学会:

当一个应用依赖另一个服务时,如何用 Docker Compose 把它们组成一个最小可用的应用栈,再通过 Nginx 和 HTTPS 暴露出去。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容