一、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/deny 和 if 指令与 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 状态 → 数据库连接 → 应用日志。按这个顺序排查,大多数问题都能快速定位。