🖥️ VPS · 2026年10月31日 · 阅读约12分钟

VPS Ubuntu 使用 Docker Compose
运行多容器应用完整指南

从安装 Docker Engine 到运行包含多个服务的完整 stack——WordPress + MySQL,或 Node.js + PostgreSQL + Redis,包含生产环境最佳实践

VPS Ubuntu 使用 Docker Compose 运行多容器应用

目录

  1. 什么是 Docker Compose,为什么要使用它?
  2. 在 Ubuntu 上安装 Docker Engine 和 Docker Compose
  3. docker-compose.yml 文件结构
  4. 示例:WordPress + MySQL + phpMyAdmin
  5. 示例:Node.js + PostgreSQL + Redis
  6. 常用 Docker Compose 命令
  7. 网络与数据卷
  8. 使用 .env 文件安全管理密钥
  9. 生产环境最佳实践
  10. 常见问题排查
  11. 常见问题解答(FAQ)

什么是 Docker Compose,为什么要使用它?

现代应用程序很少只运行在单个容器中。典型的电商系统需要 Web 服务器、数据库、缓存、队列 worker 和反向代理协同工作。Docker Compose 通过单个 YAML 文件定义和管理所有这些多容器应用。

对比项Docker (docker run)Docker Compose
容器数量逐个管理同时管理多个服务
网络手动创建自动创建项目网络
数据卷通过参数指定在 YAML 中声明
依赖关系手动管理depends_on + healthcheck
重启策略每个容器单独设置YAML 中统一配置

Docker Compose 适合开发环境和单台 VPS 上的中小型生产部署。如需跨多台服务器扩展,可考虑 Docker Swarm 或 Kubernetes。

在 Ubuntu 上安装 Docker Engine 和 Docker Compose

推荐使用 Ubuntu 22.04 LTS 或 24.04 LTS 用于生产环境。

第一步 — 更新软件包并安装依赖

sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg lsb-release

第二步 — 添加 Docker GPG 密钥和软件源

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

第三步 — 安装 Docker Engine 和 Compose 插件

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

第四步 — 允许非 root 用户使用 Docker

sudo usermod -aG docker $USER
newgrp docker   # 或退出后重新登录

验证安装

docker --version          # Docker version 26.x.x
docker compose version    # Docker Compose version v2.x.x
💡 注意 Docker Compose v2 使用 docker compose(无连字符)而非旧版的 docker-compose 命令。本文全程使用 v2 语法。

docker-compose.yml 文件结构

services:            # 服务/容器列表
  web:               # 服务名(同时作为网络中的主机名)
    image: nginx:alpine
    ports:
      - "80:80"      # 宿主机端口:容器端口
    volumes:
      - ./html:/usr/share/nginx/html:ro
    depends_on:
      - app
    restart: unless-stopped

  app:
    build: .         # 从当前目录的 Dockerfile 构建
    environment:
      - NODE_ENV=production
    env_file:
      - .env
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    restart: unless-stopped

volumes:             # 命名卷(持久化存储)
  pgdata:

示例:WordPress + MySQL + phpMyAdmin

mkdir ~/wordpress-stack && cd ~/wordpress-stack
# docker-compose.yml
services:
  wordpress:
    image: wordpress:6.7-php8.2-apache
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - wp_data:/var/www/html
    depends_on:
      mysql:
        condition: service_healthy
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  phpmyadmin:
    image: phpmyadmin:5-apache
    ports:
      - "8081:80"
    environment:
      PMA_HOST: mysql
    depends_on:
      - mysql
    restart: unless-stopped

volumes:
  wp_data:
  mysql_data:
# .env(绝对不要提交到 Git)
MYSQL_ROOT_PASSWORD=StrongRootPass2026!
MYSQL_DATABASE=wordpress_db
MYSQL_USER=wpuser
MYSQL_PASSWORD=StrongUserPass2026!
docker compose up -d
docker compose ps

示例:Node.js + PostgreSQL + Redis

# docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
      REDIS_URL: redis://redis:6379
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: redis-server --save 20 1 --loglevel warning
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  pg_data:
  redis_data:

常用 Docker Compose 命令

命令说明
docker compose up -d后台启动所有服务
docker compose down停止并删除容器(保留数据卷)
docker compose down -v停止并删除容器和数据卷
docker compose ps查看所有服务状态
docker compose logs -f实时查看所有服务日志
docker compose logs -f web只查看 "web" 服务日志
docker compose exec app sh进入 "app" 容器的 shell
docker compose restart重启所有服务
docker compose pull拉取最新镜像
docker compose build重新构建镜像
docker compose config验证并查看合并后的配置

网络与数据卷

网络

Docker Compose 自动为项目创建默认网络。同一文件中的所有服务都可以通过服务名作为主机名互相访问:

DATABASE_URL=postgresql://user:pass@postgres:5432/mydb
#                                   ^^^^^^^ 服务名
⚠️ 安全提示 除非必要,不要将数据库端口(3306、5432)暴露给宿主机。尽量只在 Docker 内部网络中使用,并配合防火墙规则保护。

数据卷

使用 .env 文件安全管理密钥

Docker Compose 自动加载同目录下的 .env 文件,在 compose 文件中使用 ${变量名} 引用。

# .gitignore — 绝对不要提交 .env!
.env
*.env
!.env.example
💡 最佳实践 创建 .env.example 文件,包含变量名但不填写真实值,方便团队成员了解需要配置哪些变量。

生产环境最佳实践

1. 重启策略

restart: unless-stopped  # 始终重启,除非手动 docker compose stop

2. 资源限制

deploy:
  resources:
    limits:
      cpus: "0.5"
      memory: 512M
    reservations:
      memory: 256M

3. 健康检查

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

4. Nginx 反向代理

推荐将 Nginx 作为单独服务或外部进程,负责反向代理和 SSL 证书(通过 Let's Encrypt/Certbot)。

5. 数据卷备份

docker run --rm \
  -v wordpress-stack_mysql_data:/data \
  -v $(pwd)/backup:/backup \
  alpine tar czf /backup/mysql-$(date +%Y%m%d).tar.gz /data

常见问题排查

问题原因解决方法
容器启动后立即退出入口脚本错误或健康检查失败运行 docker compose logs <服务名>
端口已被占用端口与其他进程冲突运行 sudo ss -tlnp | grep 端口号
数据卷数据丢失误执行了 down -v执行 down 前确认备份已完成
服务之间无法通信服务不在同一网络用 docker compose config 检查网络配置
数据卷权限被拒绝容器与宿主机 UID/GID 不匹配在服务中添加 user: "UID:GID"

常见问题解答(FAQ)

Docker 和 Docker Compose 有什么区别?
Docker 通过命令行管理单个容器。Docker Compose 通过 YAML 文件同时管理多个容器,可以定义网络、数据卷和服务依赖关系,大大简化了多容器应用的管理。
运行 Docker Compose 需要多少内存?
运行 2-3 个服务(Web + 数据库 + 缓存)最低需要 2GB 内存。如果需要运行 5 个以上服务,建议使用 4GB 或更多内存。AsiaGB VPS 提供从 1GB 到 8GB 的多种规格可供选择。
docker-compose.yml 应该存放在哪里?
为每个项目创建独立目录(如 ~/myapp/),将 docker-compose.yml 和 .env 文件放在该目录中。将 compose 文件纳入 Git 管理,但绝对不要提交 .env 文件。
如何让 Docker Compose 在 VPS 重启后自动运行?
在 docker-compose.yml 的每个服务中添加 restart: unless-stopped。Docker Engine 会在系统启动时自动重启容器。
如何更新 Docker Compose 中的镜像?
运行 docker compose pull 获取最新镜像,然后运行 docker compose up -d 用新镜像重新创建容器。未发生变化的服务不会被重启。
Docker Compose 支持所有 Linux 发行版吗?
支持所有主流 Linux 发行版,包括 Ubuntu、Debian、CentOS 和 AlmaLinux。生产环境推荐使用 Ubuntu LTS,社区支持广泛,安全更新及时。

总结

Docker Compose 为在单台 VPS 上管理多容器应用提供了高效有序的方式。核心步骤:安装 Docker Engine 和 Compose 插件、编写 docker-compose.yml 定义服务、将密钥存储在 .env 文件中、添加重启策略确保容器在重启后自动恢复。

生产环境建议使用至少 2GB 内存和 SSD 存储的 VPS。AsiaGB 提供泰国和新加坡 VPS,配备 SSD 存储,99% 正常运行时间保证,价格实惠。