现在来编写你的第一个 docker-compose.yml 文件,并了解如何高效管理和部署容器。
一、第一个 docker-compose.yml:一个经典的 Web 应用栈
我们将用一个 Nginx + PHP + MySQL 的示例来展示 Compose 的强大。创建一个新目录:
bash
mkdir my-webapp && cd my-webapp
touch docker-compose.yml
1.1 基础版:单个 Nginx 服务
先写一个最简版本,熟悉语法。
yaml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
version:Compose 文件格式版本(目前主流3.8)。services:定义容器(这里只有一个叫web的服务)。image:使用的镜像。ports:宿主机端口8080映射到容器80端口。volumes:将当前目录下的html文件夹挂载到 nginx 默认站点目录。
运行:
bash
mkdir html
echo "<h1>Hello from Docker Compose!</h1>" > html/index.html
docker compose up -d
访问 http://localhost:8080 就能看到页面。
1.2 完整版:Nginx + PHP + MySQL
现在扩展成一个典型的 LEMP 栈。
yaml
version: '3.8'
services:
# Nginx Web 服务器
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php
- db
# PHP-FPM 处理动态请求
php:
image: php:8.2-fpm-alpine
volumes:
- ./html:/var/www/html
# MySQL 数据库
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: myapp
MYSQL_USER: user
MYSQL_PASSWORD: pass
volumes:
- db_data:/var/lib/mysql
ports:
- "3306:3306"
volumes:
db_data:
关键概念:
depends_on:服务启动顺序(web会等php和db启动后再启动)。environment:传递环境变量给容器。volumes:命名卷db_data用于持久化数据库数据,即使容器删除也不会丢失。nginx.conf:需要准备一个简单的站点配置(放在项目根目录)。
nginx.conf 示例:
nginx
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php index.html;
location ~ \.php$ {
fastcgi_pass php:9000; # 注意这里 php 是服务名
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
启动:docker compose up -d
二、如何高效管理和部署容器
2.1 常用 Compose 命令速查
| 命令 | 作用 |
|---|---|
docker compose up -d |
后台启动所有服务(首次会自动拉取镜像) |
docker compose down |
停止并删除容器、网络,但保留卷 |
docker compose down -v |
同时删除卷(彻底清理) |
docker compose logs -f |
实时查看所有日志 |
docker compose ps |
查看服务状态 |
docker compose exec <service> sh |
进入某个服务容器内部 |
docker compose restart |
重启所有服务 |
docker compose build |
重新构建自定义镜像(如果有 Dockerfile) |
2.2 高效技巧:环境差异化
开发、测试、生产环境往往需要不同配置。推荐使用 多 Compose 文件:
bash
# 开发环境
docker compose -f docker-compose.yml -f docker-compose.override.yml up
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up
docker-compose.override.yml默认会被自动加载(适合开发时的本地覆盖)。- 生产文件可以定义不同的资源限制、不同镜像标签等。
2.3 使用 .env 文件管理敏感信息
在项目根目录创建 .env 文件:
ini
MYSQL_ROOT_PASSWORD=verysecret
MYSQL_DATABASE=myapp
MYSQL_USER=user
MYSQL_PASSWORD=pass
然后在 docker-compose.yml 中引用:
yaml
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
⚠️ 注意 :.env 文件不要提交到 Git ,加入 .gitignore。
2.4 健康检查与依赖等待
depends_on 只保证容器启动顺序,但不保证服务就绪(比如 MySQL 还没准备好接受连接)。更健壮的方式是使用 健康检查:
yaml
services:
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
web:
image: nginx:alpine
depends_on:
db:
condition: service_healthy
这样 web 会等待 db 健康后再启动。
2.5 资源限制与性能调优
防止单个容器耗尽宿主机资源:
yaml
services:
web:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
注意:
deploy仅在 swarm 模式下生效,但在docker compose up中也可以使用--compatibility标志来模拟限制。
2.6 构建与镜像仓库
如果你有自己的代码需要打包,可以自定义 Dockerfile,然后在 Compose 中直接构建:
yaml
services:
app:
build: .
image: myapp:latest # 可选:指定生成的镜像名
然后 docker compose build 构建。
生产部署时,将镜像推送到私有仓库(如阿里云容器镜像服务、Docker Hub):
bash
docker tag myapp:latest registry.example.com/myapp:1.0
docker push registry.example.com/myapp:1.0
然后在服务器上用 Compose 直接拉取镜像运行。
三、一个完整的部署流程示例
假设你要将一个应用部署到云服务器:
-
本地开发 :使用
docker compose up验证。 -
构建镜像 :
docker compose build。 -
打标签并推送 :
bashdocker tag myapp:latest myregistry.com/prod/myapp:v1 docker push myregistry.com/prod/myapp:v1 -
登录服务器 ,克隆
docker-compose.yml和.env(注意生产环境用不同的环境变量)。 -
拉取镜像并启动 :
bashdocker compose pull docker compose up -d -
滚动更新 :重新构建新版本镜像,推送,然后在服务器上:
bashdocker compose pull docker compose up -d --force-recreate
当你需要将 Docker Compose 应用到真实项目时,CI/CD 集成 和多环境配置是两个最核心的进阶场景。下面我会分别给出实用的配置示例和最佳实践。
四. 多环境配置:开发、测试、生产一套配置管理
推荐方式:基准文件 + 环境专属覆盖
使用 docker-compose.yml 作为基础配置 ,然后用 docker-compose.override.yml 覆盖开发环境,用 docker-compose.prod.yml 覆盖生产环境。
目录结构示例:
bash
project/
├── docker-compose.yml # 基础配置(所有环境共享)
├── docker-compose.override.yml # 开发环境自动加载(gitignore 建议忽略)
├── docker-compose.prod.yml # 生产环境覆盖
├── .env # 默认环境变量
├── .env.prod # 生产环境变量
└── src/ ...
基础配置 docker-compose.yml
yaml
version: '3.8'
services:
app:
image: myapp:${TAG:-latest}
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
environment:
- NODE_ENV=${NODE_ENV:-development}
- DB_URL=postgresql://${DB_USER}:${DB_PASS}@db:5432/${DB_NAME}
depends_on:
- db
- redis
networks:
- backend
db:
image: postgres:15
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASS}
- POSTGRES_DB=${DB_NAME}
volumes:
- db_data:/var/lib/postgresql/data
networks:
- backend
redis:
image: redis:7-alpine
networks:
- backend
volumes:
db_data:
networks:
backend:
开发覆盖 docker-compose.override.yml (开发时自动生效)
yaml
version: '3.8'
services:
app:
build:
target: development # 使用 Dockerfile 中的 development stage
volumes:
- ./src:/app/src # 挂载源码实现热重载
environment:
- DEBUG=true
ports:
- "9229:9229" # 暴露调试端口
db:
ports:
- "5432:5432" # 暴露数据库端口便于本地工具连接
redis:
ports:
- "6379:6379"
生产配置 docker-compose.prod.yml
yaml
version: '3.8'
services:
app:
restart: always
environment:
- NODE_ENV=production
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
deploy:
resources:
limits:
cpus: '2'
memory: 1G
db:
restart: always
volumes:
- db_data:/var/lib/postgresql/data
deploy:
resources:
limits:
cpus: '1'
memory: 512M
环境变量文件 .env (默认,可提交模板)
bash
TAG=latest
NODE_ENV=development
DB_USER=postgres
DB_PASS=devpassword
DB_NAME=myapp_dev
.env.prod (生产环境,不提交到 Git)
bash
TAG=v1.2.3
NODE_ENV=production
DB_USER=prod_user
DB_PASS=strongpassword
DB_NAME=myapp_prod
使用命令
bash
# 开发环境(自动加载 .env 和 docker-compose.override.yml)
docker compose up -d
# 生产环境(指定环境变量文件和配置文件)
docker compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml up -d
# 生产环境启动并保证服务异常重启
docker compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml up -d --restart always
五. CI/CD 集成(以 GitHub Actions 为例)
场景:在 PR 中运行测试,合并后构建镜像并部署
GitHub Actions 工作流 .github/workflows/ci-cd.yml
yaml
name: CI/CD Pipeline
on:
pull_request:
branches: [ main, develop ]
push:
branches: [ main ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Start test environment
run: |
docker compose -f docker-compose.yml up -d db redis
docker compose -f docker-compose.yml run --rm app npm test
env:
NODE_ENV: test
DB_USER: test
DB_PASS: test
DB_NAME: testdb
- name: Tear down
if: always()
run: docker compose down -v
build-and-push:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}, ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build-and-push
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to server via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_KEY }}
script: |
cd /opt/myapp
docker compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose --env-file .env.prod -f docker-compose.yml -f docker-compose.prod.yml up -d --remove-orphans
docker image prune -f
六. 扩展技巧:配置服务、Secrets 与健康检查
使用 Docker Secrets (Swarm 模式,单机也可用)
yaml
services:
app:
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
健康检查
yaml
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
依赖等待(使用 wait-for-it 或 depends_on + condition)
yaml
services:
app:
depends_on:
db:
condition: service_healthy
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
七. 本地开发最佳实践
- 使用
.env存储敏感信息,不提交到版本库,提供.env.example模板。 - 利用
docker-compose.override.yml覆盖开发专用配置(如挂载源码、开启调试端口)。 - 开发时使用
docker compose up并保持前台运行查看日志。 - 定期清理:
docker compose down -v完全清理测试数据。