Docker Compose 实战:一条命令拉起个人应用栈,WordPress+数据库+反向代理全搞定
导读:很多 VPS 玩家熟悉 docker run,但当一个项目要同时跑数据库、缓存、应用、反代四五个容器时,docker run 命令又长又难记,每次重建都要回忆端口和参数。Docker Compose 就是解决这个问题的:把所有容器的配置写进一个 docker-compose.yml,docker compose up -d 一条命令全部拉起,停、删、升级、迁移都变得有章可循。这篇从零讲透 Compose 的写法,并带一个 WordPress + MySQL + Redis + Nginx 反向代理的完整实战,让你一台 VPS 稳稳当当跑起整站。
一、为什么你需要 Docker Compose
先看没有 Compose 的日子。部署一个 WordPress 站点,理论上要执行:
docker run -d --name wp-db -e MYSQL_ROOT_PASSWORD=xxx mysql:8.0 docker run -d --name wp-app --link wp-db -p 8080:80 wordpress:php8.3
这只是两个容器。如果再加 Redis 做缓存、Nginx 做反代、再挂载数据卷,命令会迅速膨胀到让人崩溃。而且 --link 早已被官方废弃,容器之间应该通过自定义网络通信,每次 docker run 都要手动指定 --network,端口一旦冲突还得逐个排查。
Compose 的核心价值有三点:
- 配置即代码:所有容器定义、端口、卷、网络、环境变量都沉淀在 YAML 文件里,可版本管理、可复用。
- 一键编排:
up -d启动整个应用栈,down一键清理,依赖关系由 Compose 自动处理。 - 项目隔离:同一个 VPS 上跑多个项目时,每个项目有独立的网络和命名空间,互不干扰。
它不需要单独的容器编排工具,Docker Engine 自带 Compose 插件(docker compose),绝大多数系统镜像都预装了,或一条命令补装:
apt install docker-compose-plugin # Debian/Ubuntu docker compose version # 验证
二、Compose 文件长什么样
docker-compose.yml 是 YAML 格式,核心结构如下:
version: "3.8"
services:
服务名A:
image: 镜像:标签
container_name: 自定义容器名
restart: unless-stopped
ports:
- "主机端口:容器端口"
volumes:
- 卷名或路径:容器内路径
environment:
- 键=值
networks:
- mynet
volumes:
卷名:
networks:
mynet:
几个要点先记住:
services下每个键是一个服务,服务之间用服务名互相访问,比如应用容器里连数据库就写mysql,不需要 IP。restart决定容器退出后的策略,unless-stopped是个人服务器最常用的,重启后自动拉起。ports是"主机:容器",只有需要对外暴露的服务才写;内部通信的服务(如数据库)不要映射端口,更安全。volumes声明持久化卷,容器删了数据还在;匿名卷或路径挂载也都可以。
三、实战:WordPress + MySQL + Redis + Nginx
目标:一台 VPS 上跑一个 WordPress 博客,包含 MySQL 数据库、Redis 缓存、Nginx 反向代理,并且全部由 Compose 管理。
3.1 目录规划
先在项目根目录建好目录结构:
mkdir -p /opt/blog && cd /opt/blog
目录里会有三个文件:docker-compose.yml、.env(存放敏感变量)、以及供 WordPress 和 Nginx 使用的挂载目录。
3.2 编写 docker-compose.yml
services:
mysql:
image: mysql:8.0
container_name: blog-mysql
restart: unless-stopped
volumes:
- db_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
networks:
- blognet
redis:
image: redis:7-alpine
container_name: blog-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- blognet
wordpress:
image: wordpress:php8.3-apache
container_name: blog-wp
restart: unless-stopped
volumes:
- wp_data:/var/www/html
environment:
- WORDPRESS_DB_HOST=mysql
- WORDPRESS_DB_USER=${DB_USER}
- WORDPRESS_DB_PASSWORD=${DB_PASSWORD}
- WORDPRESS_DB_NAME=${DB_NAME}
- WORDPRESS_CONFIG_EXTRA=
define('WP_REDIS_HOST', 'redis');
define('WP_REDIS_PORT', 6379);
depends_on:
- mysql
- redis
networks:
- blognet
nginx:
image: nginx:alpine
container_name: blog-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/certs:/etc/nginx/certs:ro
- wp_data:/var/www/html:ro
depends_on:
- wordpress
networks:
- blognet
volumes:
db_data:
redis_data:
wp_data:
networks:
blognet:
3.3 环境变量文件 .env
敏感信息放到 .env,避免写死在 YAML 里(.gitignore 记得排除它):
DB_ROOT_PASSWORD=请改成强密码 DB_NAME=blog DB_USER=blog_user DB_PASSWORD=另一个强密码
3.4 Nginx 反代配置
在 ./nginx/conf.d/blog.conf 里写反代规则,把 80/443 的请求转发给 WordPress 容器:
server {
listen 80;
server_name blog.example.com;
location / {
proxy_pass http://wordpress:80;
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;
}
}
注意 proxy_pass 后面写的是服务名 wordpress,Compose 的网络会把它解析到 WordPress 容器的 IP,这就是自定义网络带来的便利。
3.5 一键启动
cd /opt/blog docker compose up -d docker compose ps # 查看所有容器状态
首次运行会自动拉取镜像、创建网络和卷,稍等一两分钟,访问 http://blog.example.com 就能看到 WordPress 安装界面。之后所有的管理都围绕 Compose 命令展开。
四、常用命令速查
命令 作用 docker compose up -d 构建并后台启动整个应用栈 docker compose ps 查看各容器状态 docker compose logs -f 跟踪所有容器的日志 docker compose logs -f wordpress 只看某个服务的日志 docker compose exec wordpress bash 进入容器执行命令 docker compose restart nginx 重启单个服务 docker compose down 停止并删除容器(默认保留数据卷) docker compose down -v 连数据卷一起删除(慎用!数据会没) docker compose pull && docker compose up -d 升级所有镜像 五、生产环境必做的几件事
个人博客跑起来了只是第一步,想让它在低配 VPS 上稳定服役,下面这些配置建议加上。
1. 健康检查(healthcheck):让 Compose 知道服务是否真的健康,依赖它的服务才能按序启动。
mysql:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
2. 资源限制:小内存 VPS 上最怕某个容器把内存吃满导致整机 OOM。给每个服务加限制:
wordpress:
deploy:
resources:
limits:
memory: 512M
3. 时区问题:容器默认 UTC,定时任务、日志时间会差 8 小时。通过环境变量或挂载解决:
environment: - TZ=Asia/Shanghai volumes: - /etc/localtime:/etc/localtime:ro
4. 镜像升级前先备份:升级前 docker compose down,然后对整个项目目录和卷做一次快照备份,出问题能快速回滚。
六、常见坑与解决方案
- 端口冲突:
Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use,说明 80 端口被占用,用ss -tlnp查占用进程,或改映射端口。 - 数据卷权限:MySQL 容器偶发
Permission denied,多出现在挂载了宿主机目录时,给目录 755 权限或使用命名卷。 - 容器内连不上数据库:检查服务名是否拼对,
depends_on是否声明;不要用localhost连数据库,要用服务名mysql。 - 改了配置不生效:改完
docker-compose.yml需要docker compose up -d重新应用,Nginx 配置改了要docker compose exec nginx nginx -s reload。 - 容器内定时任务不执行:容器里默认没有 cron,建议把定时任务放宿主机执行
docker compose exec调用,或用专用调度容器。
结语
Docker Compose 把"多容器应用"从手工拼命令变成了写一份声明式配置,对个人站长和 VPS 玩家来说,是性价比极高的技能。学会之后,不仅是 WordPress,Nextcloud、Gitea、监控面板、图床,任何由多个组件组成的应用,都能用同一套思路在 VPS 上优雅落地。下一篇可以聊聊怎么用 systemd 把 Compose 项目管成开机自启的常驻服务,敬请期待。


