13
10
月
2026
一个应用往往需要多个容器配合:Web + 数据库 + 缓存 + 消息队列。用 docker run 一个个启动,命令又长又容易忘,还没法一起管理。
Docker Compose 用一份 YAML 文件描述整个应用栈,一条命令启动、停止、重建全部服务。
多容器应用需要足够的内存。财富云计算 提供多种规格云服务器;内部服务、测试环境可选 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上)。
一、安装
新版 Docker 自带 Compose 插件:
docker compose version如果没有,单独安装:
# 插件方式(推荐)
apt install docker-compose-plugin -y
# 或下载二进制
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 \
-o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version老命令是docker-compose(带横线),新版是docker compose(空格)。功能一致。
二、第一个 compose 文件
mkdir -p /opt/myapp && cd /opt/myapp
nano docker-compose.ymlversion: '3.8'
services:
web:
image: nginx:alpine
container_name: myapp-web
ports:
- "127.0.0.1:8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stoppeddocker compose up -d # 后台启动
docker compose ps # 查看
docker compose logs -f # 日志
docker compose down # 停止并删除三、完整示例:Web + MySQL + Redis
version: '3.8'
services:
# Web 应用
app:
image: myapp:latest
build: . # 也可以直接从 Dockerfile 构建
container_name: myapp
restart: unless-stopped
ports:
- "127.0.0.1:8080:3000"
environment:
- NODE_ENV=production
- DB_HOST=db
- DB_USER=app
- DB_PASSWORD_FILE=/run/secrets/db_password
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
volumes:
- ./uploads:/app/uploads
networks:
- backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 512M
# 数据库
db:
image: mysql:8.0
container_name: myapp-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: myapp
MYSQL_USER: app
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
TZ: Asia/Shanghai
volumes:
- db-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- backend
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
command: --default-authentication-plugin=mysql_native_password
# 缓存
cache:
image: redis:7-alpine
container_name: myapp-cache
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- cache-data:/data
networks:
- backend
# Nginx 反向代理(对外)
nginx:
image: nginx:alpine
container_name: myapp-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- app
networks:
- backend
networks:
backend:
driver: bridge
volumes:
db-data:
cache-data:配套 .env 文件:
MYSQL_ROOT_PASSWORD=强密码1
MYSQL_PASSWORD=强密码2
REDIS_PASSWORD=强密码3⚠️.env要加进.gitignore,不要提交到代码仓库。
四、常用命令
# 启动(首次会构建/拉取)
docker compose up -d
docker compose up -d --build # 强制重新构建
docker compose up # 前台运行(看日志,Ctrl+C 停止)
# 停止
docker compose stop # 停止但不删除容器
docker compose down # 停止并删除容器、网络
docker compose down -v # 连数据卷一起删(⚠️ 数据没了)
docker compose down --rmi all # 连镜像一起删
# 查看
docker compose ps
docker compose ps -a
docker compose logs
docker compose logs -f # 实时
docker compose logs -f app # 只看某个服务
docker compose top # 进程
# 重启
docker compose restart
docker compose restart app
# 进入容器
docker compose exec app sh
docker compose exec db mysql -uroot -p
# 单次运行命令
docker compose run --rm app npm test
# 扩缩容
docker compose up -d --scale app=3
# 配置检查
docker compose config # 输出解析后的最终配置
docker compose config --quiet # 只校验不输出
# 拉取/构建
docker compose pull
docker compose build
docker compose build --no-cache指定文件:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d五、关键配置详解
ports vs expose
ports:
- "8080:80" # 映射到宿主机(外部可访问)
- "127.0.0.1:8080:80" # 只映射到本地(推荐,更安全)
expose:
- "80" # 仅容器间可见,不映射到宿主机推荐:内部服务(数据库、Redis)用 expose,对外服务才用 ports,且尽量加 127.0.0.1 前缀配合宿主机 Nginx 反代。
depends_on
depends_on:
- db # 只保证启动顺序,不等待就绪
depends_on:
db:
condition: service_healthy # 等健康检查通过(推荐)重要:depends_on 默认只保证启动顺序,不保证服务就绪。数据库可能还在初始化,应用就连上去失败。用 condition: service_healthy + healthcheck。
volumes
volumes:
- ./local:/container # 挂载宿主机目录
- mydata:/var/lib/mysql # 使用命名卷
- ./conf:/etc/conf:ro # 只读挂载顶层声明命名卷:
volumes:
mydata:
driver: localenvironment vs env_file
environment:
- KEY=value
- PASSWORD=${FROM_ENV_FILE} # 从 .env 读取
env_file:
- ./.env.prod # 从文件读取全部networks
services:
app:
networks:
- frontend
- backend
db:
networks:
- backend # 数据库只在后端网络,前端访问不到
networks:
frontend:
backend:网络隔离是好习惯:数据库不应该和对外服务在同一网络。
restart 策略
restart: no # 不自动重启(默认)
restart: always # 总是重启(包括 Docker 重启)
restart: unless-stopped # 除非手动停止(推荐)
restart: on-failure # 失败时重启资源限制
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 256M(新版 Compose 也支持直接在 service 下写 mem_limit、cpus)
六、实战场景
场景 1:WordPress 一键部署
version: '3.8'
services:
wordpress:
image: wordpress:php8.2-fpm-alpine
restart: unless-stopped
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wp
WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
WORDPRESS_DB_NAME: wordpress
volumes:
- wp-data:/var/www/html
depends_on:
db:
condition: service_healthy
networks:
- backend
db:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: wordpress
MYSQL_USER: wp
MYSQL_PASSWORD: ${WP_DB_PASSWORD}
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
retries: 5
networks:
- backend
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wp-data:/var/www/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- wordpress
networks:
- backend
volumes:
wp-data:
db-data:
networks:
backend:场景 2:开发环境(前后端分离)
version: '3.8'
services:
frontend:
image: node:18-alpine
working_dir: /app
command: npm run dev
volumes:
- ./frontend:/app
ports:
- "3000:3000"
environment:
- NODE_ENV=development
backend:
build: ./backend
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./backend:/app
ports:
- "8000:8000"
depends_on:
- db
- redis
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: devdb
POSTGRES_PASSWORD: devpass
volumes:
- pg-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
pg-data:开发时挂载源码目录,改代码即时生效。
场景 3:监控栈(Prometheus + Grafana)
version: '3.8'
services:
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prom-data:/prometheus
ports:
- "127.0.0.1:9090:9090"
restart: unless-stopped
grafana:
image: grafana/grafana
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
volumes:
- grafana-data:/var/lib/grafana
ports:
- "127.0.0.1:3000:3000"
depends_on:
- prometheus
restart: unless-stopped
node-exporter:
image: prom/node-exporter
ports:
- "9100:9100"
restart: unless-stopped
volumes:
prom-data:
grafana-data:七、部署与更新
# 更新应用
cd /opt/myapp
git pull # 拉最新代码
docker compose build # 重新构建镜像
docker compose up -d # 滚动更新(只重启变化的服务)
# 查看变化
docker compose ps
# 回滚
git checkout 上一个版本
docker compose up -d --build零停机更新的简单做法:
- 用 Nginx 反代 + upstream
- 起新容器(不同端口)
- 切 Nginx 配置
- 停旧容器
生产环境建议用 Kubernetes 或 Docker Swarm 做编排,Compose 适合单机和小规模。
八、常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
yaml: invalid | YAML 缩进错误 | 用 docker compose config 检查,注意只能用空格 |
| 服务连不上数据库 | 启动顺序问题 | 加 healthcheck + condition: service_healthy |
| 端口冲突 | 宿主机端口被占 | ss -tulnp 查,换端口 |
| 数据丢失 | 用了 down -v | 数据卷要备份,慎用 -v |
| 容器一直重启 | 启动就失败 | docker compose logs 看错误 |
| 环境变量没生效 | .env 路径不对 | .env 要放 compose 文件同目录 |
| 镜像拉取慢 | 网络问题 | 配 registry-mirrors |
| 权限问题 | 挂载目录权限 | chown 或加 user: 指定 |
| 修改配置不生效 | 没重建容器 | docker compose up -d(会自动重建变化的服务) |
九、最佳实践
- 数据用命名卷,不要用容器内存储
- 敏感信息放 .env 或 secrets,别写进 YAML
- 内部服务用 expose,不映射宿主机端口
- 配 healthcheck,保证依赖就绪
- 设 restart 策略,容器挂了自动拉起
- 限制资源,防止一个容器拖垮整机
- 网络隔离,数据库只在后端网络
- 镜像指定版本,不要用
latest(不可复现) - 用 .dockerignore,减小构建上下文
- 定期备份数据卷
# 备份数据卷
docker run --rm -v myapp_db-data:/data -v $(pwd):/backup \
alpine tar -czf /backup/db-$(date +%F).tar.gz /data一套 compose 文件搞定整个应用栈,部署效率提升明显。财富云计算 的云服务器完整支持 Docker 与 Compose;内部服务、测试环境可选 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上),适合搭建内部系统和开发测试环境。
相关阅读:
- 《Docker 安装与基础命令》
- 《Docker 常见故障排查》
- 《Nginx 反向代理与多站点配置》
延伸阅读
- 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
- 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
- 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
- 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
- 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
- RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
- 短链接服务搭建:Shlink 自托管,链接数据自己掌握
- 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道

[...]docker system df -v # 明细到每个镜像/容器/卷Dockerfile 编写规范见Dockerfile 编写与镜像优化,网站部署全流程见Docker 部署网站实战,Compose 编排基础见Docker Compose 编排实战。数据卷和网络是 Docker 的两条命脉:卷决定数据活多久,网络决定容器怎么说话。记住三件事——要留的数据进卷、容器互访用服务名、U[...]
2026-10-15 回复
[...]docker run -d --name blog --memory=1g ...容器化部署详见Docker 部署游戏服务器和Docker Compose 编排实战。2. 资源限额游戏服是大户,必须限额,防止它 OOM 时把网站一起杀掉:# 容器内存限额(Docker 方式)[...]
2026-10-18 回复
[...]会了 Docker 基础命令后,最实用的玩法是把常用服务做成一套 compose 文件:博客、网盘、监控面板、数据库,一条命令全起来,换机器时一个目录拷走全恢复。这篇给出一套开箱即用的组合,覆盖个人服务器的常用服务。本文基于 财富云计算 NAT 型云服务器(无独立公网 IP、价格比独立 IP 机型省一半以上)实践。前置基础见Docker 安装与基础命令,编排进阶见Docker Compose 编排[...]
2026-10-18 回复
[...]netsh advfirewall firewall add rule name="GameRange" dir=in action=allow protocol=UDP localport=26901-26903完整的 Windows 环境准备(远程桌面连接、运行库安装、SteamCMD 用法、开机自启、8 个常见坑)见:《Windows 云服务器开服通用前置教程》→ htt[...]
2026-10-18 回复
[...]vw-data:docker compose up -d先开注册再关:初始把 SIGNUPS_ALLOWED 设成 true,启动后用浏览器访问注册自己的账号,然后把值改回 false,docker compose up -d 重建生效。开着公开注册的密码库,是在公网裸奔。Docker 基础命令见Docker 安装与基础命令,compose 编排见Docker Compose 编排实战。三、Ngi[...]
2026-10-18 回复
[...]db-data:换服务器 = 拷 compose 文件 + 数据卷,docker compose up -d,十分钟搬家完成。没有"我这环境怎么配的来着"的噩梦。Docker 系列教程站内很全:Docker 安装与基础命令、Compose 编排实战、一键部署常用服务、数据卷与网络详解。Docker 的安全功课:别拉来路不明的镜像、定期更新基础镜像、容器别用 root 跑、挂载 socket 要小心[...]
2026-10-18 回复