很多站长第一次接触 Docker 时,会把它和虚拟机混为一谈。实际上 Docker 不虚拟硬件,只隔离进程,所以启动一个容器通常只要一两秒,占用内存也小得多。对只有 2 核 2G 的 VPS 来说,用 Docker 把 Nginx、PHP、MySQL 拆成独立容器,既方便升级,也方便迁移。这篇文章按实际操作顺序,从安装到用 Compose 跑起一个完整站点,把容易踩的坑一次讲清楚。
一、Docker 适合什么场景
Docker 最大的价值是环境一致性:本地跑得起来的镜像,换一台服务器同样能跑,不用再为 PHP 版本、扩展、依赖库折腾半天。适合的场景包括多站点隔离、快速回滚、迁移服务器,以及同一台机器上跑多个不同版本的服务。
但如果你的站点只有一个 WordPress,服务器配置也够用,直接装宝塔或 LNMP 反而更简单。Docker 带来便利的同时,也多了一层网络和存储的抽象,出问题时需要多看一层日志。
二、安装 Docker 与配置镜像加速
主流 Linux 发行版用官方脚本安装最省事:
curl -fsSL https://get.docker.com | sh systemctl enable --now docker docker version docker compose version
国内机器拉取镜像经常卡住,需要配置加速地址。新建或编辑 /etc/docker/daemon.json:
{
"registry-mirrors": ["https://docker.1ms.run", "https://docker.xuanyuan.me"],
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}然后执行 systemctl daemon-reload && systemctl restart docker 生效。日志大小限制一定要加,否则某个容器疯狂写日志,几天就能把系统盘写满。
三、镜像、容器、数据卷别搞混
- 镜像(image):只读模板,相当于装系统用的 ISO,本身不产生数据。
- 容器(container):镜像运行起来的实例,容器删除后,写在容器内部的数据会一起消失。
- 数据卷(volume):独立于容器存在的存储,删容器不影响数据,是生产环境必须用的东西。
- 端口映射:-p 8080:80 表示访问宿主机 8080 端口,转发到容器内的 80 端口。
四、跑一个 Nginx 静态站
mkdir -p /data/www /data/nginx/conf.d /data/nginx/logs docker run -d --name nginx \ -p 80:80 -p 443:443 \ -v /data/www:/usr/share/nginx/html:ro \ -v /data/nginx/conf.d:/etc/nginx/conf.d:ro \ -v /data/nginx/logs:/var/log/nginx \ --restart unless-stopped \ nginx:1.27-alpine
几个参数值得注意:--restart unless-stopped 让容器跟随 Docker 服务自启;挂载网站目录和配置目录,改完执行 docker exec nginx nginx -s reload 即可生效;alpine 版本体积小、攻击面也小。
不要把网站文件 COPY 进镜像。代码一改就要重新构建,既慢又容易丢数据。镜像只放运行环境,代码和数据一律挂载。
五、MySQL 容器化:数据卷是底线
docker run -d --name mysql \ -e MYSQL_ROOT_PASSWORD=换成强密码 \ -e MYSQL_DATABASE=wordpress \ -v /data/mysql:/var/lib/mysql \ -p 127.0.0.1:3306:3306 \ --restart unless-stopped \ mysql:8.4 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci
两个细节:端口写成 127.0.0.1:3306:3306,只允许本机访问,千万不要把 3306 直接暴露到公网;/data/mysql 必须挂载到宿主机,容器删了数据还在。数据库容器不要用 --rm,更不要随手 docker rm -f。
六、用 Compose 编排整站
容器多了以后,一条条 docker run 难以维护。Compose 用一个 YAML 文件描述所有服务,一条命令启停。下面是一个最小可用的 WordPress 编排示例:
services:
db:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: 换成强密码
MYSQL_DATABASE: wordpress
volumes:
- ./mysql:/var/lib/mysql
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
web:
image: wordpress:php8.3-fpm
restart: unless-stopped
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_PASSWORD: 换成强密码
volumes:
- ./html:/var/www/html
depends_on:
- db
nginx:
image: nginx:1.27-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./html:/var/www/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- web同一网络里的容器可以直接用服务名通信,web 连接数据库写 db 就行,不需要记 IP。启动用 docker compose up -d,看状态用 docker compose ps,看日志用 docker compose logs -f nginx,改完配置用 docker compose up -d 重建。
还要注意 PHP-FPM 的 Nginx 配置,fastcgi_pass 要指向 web:9000,而不是 127.0.0.1:9000,否则容器之间根本连不通,页面直接 502。
七、日常运维常用命令
| 场景 | 命令 |
|---|---|
| 查看运行中的容器 | docker ps |
| 进入容器排查 | docker exec -it nginx sh |
| 查看容器日志 | docker logs -f --tail 100 nginx |
| 查看资源占用 | docker stats |
| 重启服务 | docker compose restart nginx |
| 查看磁盘占用 | docker system df |
八、生产环境要守住的几条线
- 不要用 latest 标签:升级后行为可能变化,固定版本号才能可控回滚。
- 限制资源:Compose 里设置 cpus 和 mem_limit,避免一个容器吃光整台机器。
- 日志必须限制大小:daemon.json 的 max-size、max-file 是最容易被忽略的一项。
- 数据卷要单独备份:容器可以随时重建,数据卷丢了就真没了。
- 端口暴露要收敛:数据库、Redis 只监听 127.0.0.1,公网只开 80 和 443。
- 防火墙别和 Docker 打架:Docker 会自行写 iptables 规则,ufw 的默认策略可能被绕过,端口控制优先在 Compose 里做。
九、常见问题
- 容器起来了但网站 502:Nginx 连不上 PHP-FPM,检查 fastcgi_pass 是否指向容器名和 9000 端口,两个容器是否在同一网络。
- 改了配置不生效:挂载单文件时,宿主机编辑器保存会替换 inode,容器里看到的还是旧文件。改成挂载整个目录,或重启容器。
- 磁盘涨得很快:先 docker system df 看镜像、容器、数据卷各占多少,再分别清理,不要一上来就 prune -a。
- 数据库连不上:确认密码没有特殊字符导致 YAML 解析错误,服务名和端口是否写对。
总结
Docker 部署网站的核心就四件事:镜像固定版本、数据挂载到宿主机、日志限制大小、端口只暴露必要的。把这四条守住,再用 Compose 管理多容器,迁移和升级都会比传统装环境轻松很多。新手建议先在一台测试机上跑通整站,再动生产环境。