Docker Compose 实战:一条命令拉起个人应用栈,WordPress+数据库+反向代理全搞定


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 的核心价值有三点:

  1. 配置即代码:所有容器定义、端口、卷、网络、环境变量都沉淀在 YAML 文件里,可版本管理、可复用。
  2. 一键编排:up -d 启动整个应用栈,down 一键清理,依赖关系由 Compose 自动处理。
  3. 项目隔离:同一个 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,然后对整个项目目录和卷做一次快照备份,出问题能快速回滚。

六、常见坑与解决方案

  1. 端口冲突:Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use,说明 80 端口被占用,用 ss -tlnp 查占用进程,或改映射端口。
  2. 数据卷权限:MySQL 容器偶发 Permission denied,多出现在挂载了宿主机目录时,给目录 755 权限或使用命名卷。
  3. 容器内连不上数据库:检查服务名是否拼对,depends_on 是否声明;不要用 localhost 连数据库,要用服务名 mysql。
  4. 改了配置不生效:改完 docker-compose.yml 需要 docker compose up -d 重新应用,Nginx 配置改了要 docker compose exec nginx nginx -s reload。
  5. 容器内定时任务不执行:容器里默认没有 cron,建议把定时任务放宿主机执行 docker compose exec 调用,或用专用调度容器。

结语

Docker Compose 把"多容器应用"从手工拼命令变成了写一份声明式配置,对个人站长和 VPS 玩家来说,是性价比极高的技能。学会之后,不仅是 WordPress,Nextcloud、Gitea、监控面板、图床,任何由多个组件组成的应用,都能用同一套思路在 VPS 上优雅落地。下一篇可以聊聊怎么用 systemd 把 Compose 项目管成开机自启的常驻服务,敬请期待。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

手握多台小鸡怎么统一盯?哪吒探针自建监控面板,全网机器一屏看完

KVM、LXC、OpenVZ有什么区别?看懂虚拟化架构,下单前心里有数

评 论
更换验证码