Docker 部署 WordPress 完整指南:从安装到常见问题排查

目录

一、Docker 安装(Ubuntu/Linux)

1.1 确认系统环境

在开始之前,先确认当前系统环境是否满足 Docker 安装要求:

# 查看操作系统版本
cat /etc/os-release

# 查看内核版本(Docker 需要 3.10+,推荐 5.x)
uname -r

# 检查是否已有 Docker
docker --version
docker-compose --version

本文环境:Ubuntu 22.04 LTS / 24.04 LTS,内核 5.15+,宝塔面板 8.0+。

1.2 一键安装 Docker(官方脚本)

对于国内服务器,推荐使用阿里云镜像加速安装,避免访问 Docker Hub 过慢的问题:

# 一键安装 Docker(自动识别系统,使用阿里云镜像)
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

# 安装 Docker Compose(独立二进制)
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

# 验证安装
docker --version          # Docker version 27.x.x, build xxx
docker-compose --version  # Docker Compose version v2.x.x

1.3 手动 APT 方式安装

如果官方脚本安装失败,可以使用手动 APT 方式:

# 卸载旧版本(如果有)
apt remove docker docker-engine docker.io containerd runc

# 安装依赖
apt update
apt install -y ca-certificates curl gnupg lsb-release

# 添加 Docker GPG 密钥
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

# 添加 Docker APT 源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker Engine
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 将当前用户加入 docker 组(免 sudo)
usermod -aG docker $USER
newgrp docker

1.4 配置 Docker 镜像加速

国内访问 Docker Hub 极慢,需要配置镜像加速器。编辑 /etc/docker/daemon.json

# 创建配置目录
mkdir -p /etc/docker

# 写入镜像配置(使用阿里云加速,需替换为自己的加速地址)
cat > /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://docker.aliyun.com",
    "https://mirror.ccs.tencentyun.com"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "storage-driver": "overlay2"
}
EOF

# 重载 Docker 配置
systemctl daemon-reload
systemctl restart docker

1.5 宝塔面板安装 Docker

如果使用宝塔面板,可以直接在"软件商店"→"Docker管理器"安装。宝塔会自动配置好 Docker 和 Docker Compose 环境,适合不想手动敲命令的用户。

二、Docker 网络规划

2.1 Docker 网络模式理解

Docker 提供多种网络模式,部署 Web 应用时最常用的是 bridge(桥接网络)。理解这一点至关重要:

  • bridge 网络(默认):容器分配独立 IP(通常 172.17.x.x / 172.18.x.x),容器间可以通过 IP 互相通信
  • host 网络:容器共享宿主机网络命名空间,没有网络隔离
  • overlay 网络:跨多台 Docker 宿主机通信(Swarm 模式)
  • none 网络:完全禁用网络

关键概念:当你运行 docker run -p 8080:80 时,格式是 <宿主机端口>:<容器端口>。这创建了一个从宿主机端口到容器端口的端口映射,外部请求通过这个映射访问容器内的服务。

2.2 创建 Docker 网络

为容器间通信清晰,建议为每个应用创建独立网络:

# 创建桥接网络
docker network create --driver bridge --subnet 172.18.0.0/16 --gateway 172.18.0.1 my_wp_network

# 查看网络列表
docker network ls

# 查看网络详情
docker network inspect my_wp_network

2.3 Docker 内部 DNS 机制

同一个 Docker 网络中的容器可以通过容器名(container name)互相解析。例如 WordPress 容器可以通过 mysql 作为主机名连接 MySQL 容器——这是因为 Docker 内置了一个 DNS 服务器(127.0.0.11),为同一网络中的容器提供容器名到 IP 的解析。

三、Docker 部署 WordPress + MySQL

3.1 准备目录结构

建议将所有数据存储在宿主机指定目录下,避免容器删除后数据丢失:

# 创建 WordPress 数据目录
mkdir -p /www/dk_project/dk_app/wordpress
mkdir -p /www/dk_project/dk_app/wordpress/wordpress    # WordPress 文件
mkdir -p /www/dk_project/dk_app/mysql                  # MySQL 数据目录

# 设置权限
chmod -R 777 /www/dk_project

重要原则:容器内的 /var/www/html(WordPress)和 /var/lib/mysql(MySQL)数据应该通过 -v 挂载到宿主机目录。

3.2 部署 MySQL 容器

MySQL 是 WordPress 的数据存储核心,优先启动:

# 创建并启动 MySQL 容器
docker run -d \
  --name mysql-mysql-1 \
  --network my_wp_network \
  -e MYSQL_ROOT_PASSWORD=YourStrongPassword \
  -e MYSQL_DATABASE=wordpress \
  -e MYSQL_USER=wordpress_user \
  -e MYSQL_PASSWORD=wordpress_pass \
  -v /www/dk_project/dk_app/mysql:/var/lib/mysql \
  --restart=always \
  mysql:8.0 \
  --character-set-server=utf8mb4 \
  --collation-server=utf8mb4_unicode_ci \
  --default-authentication-plugin=mysql_native_password

# 查看容器状态
docker ps

# 查看 MySQL 日志(确认启动正常)
docker logs mysql-mysql-1 --tail 20

参数说明:

  • --network:加入指定 Docker 网络
  • -e:设置环境变量(MySQL 初始化参数)
  • -v:目录挂载,将容器内数据卷映射到宿主机
  • --restart=always:容器异常退出后自动重启
  • mysql:8.0:指定版本,生产环境建议锁定版本号

3.3 部署 WordPress 容器

MySQL 就绪后,启动 WordPress:

# 创建并启动 WordPress 容器
docker run -d \
  --name wordpress-wordpress-1 \
  --network my_wp_network \
  -e WORDPRESS_DB_HOST=mysql-mysql-1 \
  -e WORDPRESS_DB_USER=wordpress_user \
  -e WORDPRESS_DB_PASSWORD=wordpress_pass \
  -e WORDPRESS_DB_NAME=wordpress \
  -p 21080:80 \
  -v /www/dk_project/dk_app/wordpress/wordpress:/var/www/html \
  --restart=always \
  wordpress:6.7-php8.2-apache

# 查看容器状态
docker ps

# 查看 WordPress 启动日志
docker logs wordpress-wordpress-1 --tail 20

3.4 使用 Docker Compose 统一管理(推荐)

手动 docker run 参数过多,容易出错。推荐使用 docker-compose.yml 统一管理所有容器配置:

version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: mysql-mysql-1
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: YourStrongPassword
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress_user
      MYSQL_PASSWORD: wordpress_pass
    volumes:
      - /www/dk_project/dk_app/mysql:/var/lib/mysql
    networks:
      - wp_network
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

  wordpress:
    image: wordpress:6.7-php8.2-apache
    container_name: wordpress-wordpress-1
    restart: always
    depends_on:
      - mysql
    environment:
      WORDPRESS_DB_HOST: mysql-mysql-1
      WORDPRESS_DB_USER: wordpress_user
      WORDPRESS_DB_PASSWORD: wordpress_pass
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - /www/dk_project/dk_app/wordpress/wordpress:/var/www/html
    ports:
      - "21080:80"
    networks:
      - wp_network

networks:
  wp_network:
    driver: bridge
# 启动所有服务
docker-compose up -d

# 查看服务状态
docker-compose ps

# 查看所有服务日志
docker-compose logs -f

# 停止并删除容器
docker-compose down

四、Nginx 反向代理配置

4.1 理解 Docker 端口映射与反向代理

WordPress 容器内部运行在 80 端口,通过 -p 21080:80 映射到宿主机的 21080 端口。外部用户无法直接访问 21080(仅监听在 127.0.0.1),需要通过 Nginx 反向代理转发请求。

架构图:

用户浏览器 → Nginx(宿主机:443/80)→ docker-proxy → WordPress容器(172.18.0.x:80)

4.2 宝塔面板配置反向代理

在宝塔面板中:网站 → 添加站点 → 反向代理:

  • 目标 URL:http://127.0.0.1:21080
  • 发送域名:$host
  • 启用代理缓存:否(WordPress 动态内容不适合缓存)

4.3 Nginx 代理配置的关键 header

WordPress 容器内的 Apache/PHP 需要知道真实访问者的 IP,否则日志和防爬虫插件都会记录错误的 IP。在 Nginx 配置中必须传递以下 header:

location / {
    proxy_pass http://127.0.0.1:21080;
    proxy_set_header Host $http_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;
}

五、常见问题诊断与解决

5.1 容器启动失败:端口已被占用

错误表现

Error: Port 80 is already allocated by another container

排查步骤

# 查看哪个进程占用了端口
lsof -i :80
# 或
ss -tlnp | grep :80

# 查看所有容器(包括已停止的)
docker ps -a

# 查看容器详情
docker inspect <container_id> | grep -A 10 Ports

解决方案:停止占用端口的进程,或更改 Docker 端口映射。

5.2 MySQL 连接被拒绝(Connection refused)

错误表现:WordPress 显示 "Error establishing a database connection",或者 docker logs wordpress-wordpress-1 中看到 MySQL 连接错误。

排查步骤

# 1. 确认 MySQL 容器是否在运行
docker ps | grep mysql

# 2. 测试容器间网络连通性
docker exec wordpress-wordpress-1 ping mysql-mysql-1
docker exec wordpress-wordpress-1 nc -zv mysql-mysql-1 3306

# 3. 测试 MySQL 端口是否映射正确
docker port mysql-mysql-1

# 4. 直接从 WordPress 容器内测试 MySQL 连接
docker exec wordpress-wordpress-1 mysql -h mysql-mysql-1 -u wordpress_user -p

解决方案

  • 如果 ping 不通,检查两个容器是否在同一个 Docker 网络:docker network inspect my_wp_network
  • 如果 nc 连接失败,检查 MySQL 是否正常启动:docker logs mysql-mysql-1 --tail 50
  • 如果是密码错误,重新设置密码后重建容器

5.3 容器外访问出现 502 Bad Gateway

错误表现:Nginx 返回 502,宿主机直连容器正常(curl localhost:21080 正常)。

排查步骤

# 1. 检查容器是否仍在运行
docker ps

# 2. 查看 Nginx 错误日志
tail -f /www/wwwlogs/www.yoursite.com.error.log

# 3. 检查 Docker 容器进程状态
docker stats --no-stream

# 4. 查看容器进程内部状态
docker exec wordpress-wordpress-1 ps aux | grep apache
docker exec mysql-mysql-1 ps aux | grep mysql

常见原因

  • 容器内 Apache/PHP-FPM 进程崩溃(重启容器:docker restart wordpress-wordpress-1
  • MySQL 连接数过多(进入 MySQL:docker exec -it mysql-mysql-1 mysql -u root -p,执行 SHOW PROCESSLIST
  • 内存不足导致 OOM Kill(检查 dmesg | grep -i oom

5.4 WordPress 上传文件大小限制

问题表现:上传主题/插件/媒体文件时提示文件大小超限。

WordPress 上传限制涉及多个层级,需要逐一排查:

# 1. Nginx 层限制(宝塔面板可在站点设置中修改)
# 宝塔面板:网站设置 → 配置文件的 server 块中添加:
client_max_body_size 100m;

# 2. PHP 限制(在容器内修改)
docker exec wordpress-wordpress-1 sed -i 's/upload_max_filesize = .*/upload_max_filesize = 100M/' /usr/local/etc/php/php.ini
docker exec wordpress-wordpress-1 sed -i 's/post_max_size = .*/post_max_size = 100M/' /usr/local/etc/php/php.ini

# 3. 重启容器使配置生效
docker restart wordpress-wordpress-1

# 4. 检查 .htaccess 配置(WordPress 多站点安装)
# php_value upload_max_filesize 100M
# php_value post_max_size 100M

5.5 容器重启后数据丢失

问题表现:重启 Docker 或服务器后,WordPress 数据丢失,数据库连接报错。

根本原因:没有正确配置数据卷挂载(-v 参数)。

# 检查容器挂载情况
docker inspect wordpress-wordpress-1 | grep -A 20 Mounts

# 正确的挂载应该类似:
# {
#     "Type": "bind",
#     "Source": "/www/dk_project/dk_app/wordpress/wordpress",
#     "Destination": "/var/www/html"
# }

# 如果 Destination 为空,说明没有挂载,需要重新创建容器:
# 1. 先备份数据(如果有)
docker cp wordpress-wordpress-1:/var/www/html /tmp/wordpress_backup

# 2. 停止并删除旧容器
docker stop wordpress-wordpress-1
docker rm wordpress-wordpress-1

# 3. 用正确的 -v 参数重新创建容器
docker run -d ... -v /www/dk_project/dk_app/wordpress/wordpress:/var/www/html ...

最佳实践

  • 永远使用绝对路径挂载:/path/to/host/dir(不能用 ~/dir 或相对路径)
  • MySQL 数据目录必须挂载,否则数据库重置
  • 定期备份宿主机上的数据目录

5.6 Docker 日志占用过多磁盘空间

问题表现:磁盘空间不足,df -h 显示 /var/lib/docker 所在分区已满。

# 1. 查看 Docker 占用的磁盘空间
docker system df -v

# 2. 清理未使用的 Docker 资源
docker system prune -a        # 清理所有未使用的数据(包括镜像)
docker system prune -f        # 自动确认清理
docker image prune -a         # 只清理未使用的镜像

# 3. 限制容器日志大小(推荐在 daemon.json 中配置,见 1.4 节)
# 或者对已有容器设置日志大小:
docker run ... --log-opt max-size=10m --log-opt max-file=3

# 4. 查看并清理大型日志文件
find /var/lib/docker/containers -name "*.log" -exec ls -lh {} \;
cat /dev/null > /var/lib/docker/containers/<container-id>/<container-id>-json.log

5.7 容器 IP 无法从宿主机访问

问题表现:容器之间可以通信,但从宿主机无法直接访问容器 IP(如 curl 172.18.0.3 失败)。

原因:Docker 桥接网络的容器 IP 只能从宿主机通过 docker-proxy 访问,且端口必须通过 -p 显式映射。

解决方案

  • 如果需要从宿主机直接访问容器,使用端口映射(-p 21080:80
  • 不要依赖容器 IP 做固定访问,通过容器名(在同一网络内)或端口映射访问

5.8 更新 WordPress 版本

更新容器化 WordPress 的正确方式是重建容器,而不是在后台直接更新:

# 1. 备份数据(始终)
cp -r /www/dk_project/dk_app/wordpress/wordpress /tmp/wordpress_backup_$(date +%Y%m%d)

# 2. 拉取新版本镜像
docker pull wordpress:6.8-php8.2-apache

# 3. 停止并删除旧容器(数据卷不会丢失)
docker stop wordpress-wordpress-1
docker rm wordpress-wordpress-1

# 4. 用相同的 -v 参数创建新容器
docker run -d \
  --name wordpress-wordpress-1 \
  --network my_wp_network \
  -e WORDPRESS_DB_HOST=mysql-mysql-1 \
  ...(其他参数不变)\
  -p 21080:80 \
  -v /www/dk_project/dk_app/wordpress/wordpress:/var/www/html \
  wordpress:6.8-php8.2-apache

# 5. 验证
docker ps
curl -I http://localhost:21080

注意:不要在容器内通过 WordPress 后台直接更新(会导致容器内文件与宿主机挂载目录不一致)。所有更新都通过重建容器完成。

六、安全加固

6.1 限制 wp-admin 后台访问

容器化的 WordPress,后台访问控制应该在容器内部的 Web 服务器层配置,不应该在 Nginx 层做 IP 白名单(Nginx 的 allow/denyif 指令与 proxy_pass 共用时有兼容性问题,容易失效)。

正确做法:在 Apache 层限制

在容器内创建 Apache 配置文件:

# /etc/apache2/conf-available/wp-admin-ip.conf
<LocationMatch "^/(wp-admin/|wp-login\.php)">
    Require ip 127.0.0.1 ::1 172.17.0.0/16 172.18.0.0/16
</LocationMatch>
# 启用配置
docker exec wordpress-wordpress-1 a2enconf wp-admin-ip && docker exec wordpress-wordpress-1 service apache2 reload

这样只有来自 Docker 内网的请求才能访问 wp-admin,所有外部请求都会被 Apache 直接拒绝,返回 403 Forbidden。

6.2 保护 MySQL 容器

# 禁止 MySQL 容器端口映射到公网
# 确保 -p 参数只映射到 127.0.0.1(如 127.0.0.1:13306:3306)
# 而不是 0.0.0.0:13306:3306

# 使用强密码(已在环境变量中设置)
# 定期检查用户权限
docker exec mysql-mysql-1 mysql -u root -p -e "SELECT user, host FROM mysql.user;"

6.3 定期快照备份

# 备份 MySQL 数据
mysqldump -h 127.0.0.1 -P 13306 -u root -p wordpress wordpress > /tmp/wp_db_$(date +%Y%m%d).sql

# 备份 WordPress 文件
tar -czf /tmp/wordpress_files_$(date +%Y%m%d).tar.gz -C /www/dk_project/dk_app/wordpress/wordpress .

# 备份 Docker 整个数据目录(包含 MySQL 和 WordPress)
tar -czf /tmp/dk_project_$(date +%Y%m%d).tar.gz /www/dk_project/

七、性能优化

7.1 Docker 资源限制

避免容器无限占用宿主机资源:

# 限制 WordPress 容器最大内存 2GB
docker update --memory="2g" wordpress-wordpress-1

# 限制 MySQL 最大连接数和缓冲池
docker exec mysql-mysql-1 mysql -u root -p -e \
  "SET GLOBAL max_connections = 200; \
   SET GLOBAL innodb_buffer_pool_size = 256M;"

7.2 启用 Redis 对象缓存

WordPress 容器内安装 Redis 插件配合宿主机 Redis,显著提升动态页面加载速度:

# 安装 Redis 容器
docker run -d --name redis-redis-1 \
  --network my_wp_network \
  -v /www/dk_project/dk_app/redis:/data \
  --restart=always \
  redis:7-alpine redis-server --appendonly yes

# 在 WordPress 中安装 Redis Object Cache 插件并启用

结语

Docker 容器化部署 WordPress 的核心要点:

  • 数据与容器分离:所有数据通过 -v 挂载到宿主机,容器只是运行环境
  • 网络隔离:使用 Docker 独立网络,容器间通过容器名通信
  • 分层排查:Nginx → Docker Proxy → 容器内 Web 服务器 → PHP-FPM → MySQL,每一层都有独立的日志
  • 优先容器内限制:安全配置(如 IP 白名单)应在容器内 Web 服务器层完成,避免中间层代理的兼容性问题
  • 定期备份:即使容器化很可靠,备份仍是最后一道防线

遇到问题时,先确认是哪一层出问题:网络连通性 → 端口映射 → Web 服务状态 → PHP-FPM 状态 → 数据库连接 → 应用日志。按这个顺序排查,大多数问题都能快速定位。

晓鹏
晓鹏