Docker compose 快速指南
- 2025-12-26 20:58:00
- 丁国栋
- 原创 10
本文含有AI生成内容
Docker Compose 完整指南
一、Docker Compose 简介
1.1 什么是 Docker Compose
Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个 YAML 文件(通常是 docker-compose.yml),您可以配置应用程序的所有服务,然后通过单个命令创建和启动所有服务。
1.2 解决的问题与适用场景
解决的问题:
- 简化多容器应用的部署和管理
- 解决容器间依赖关系和启动顺序
- 统一管理容器配置
- 快速搭建开发、测试、生产环境
- 实现容器初始化顺序控制
适用场景:
- 微服务架构应用
- Web 应用 + 数据库组合
- 开发环境标准化
- CI/CD 流水线
- 本地开发和测试环境
- 需要初始化脚本的应用
1.3 Docker Compose 与 Docker 的关系
- Docker:专注于单个容器的生命周期管理
- Docker Compose:专注于多容器应用的编排和管理
- Docker Compose 使用 Docker API 来管理容器
- 通常与 Docker Engine 配合使用
- Compose 文件是 Docker 运行命令的声明式配置
二、安装与配置
2.1 系统要求
- Linux 系统(Debian/Ubuntu 为例)
- Docker Engine 已安装
- 用户拥有 sudo 权限
- 至少 2GB 可用磁盘空间
2.2 安装方法(Linux - Debian/Ubuntu 为例)
2.2.1 通过包管理器安装
步骤1:更新包索引
sudo apt-get update
步骤2:安装依赖包
sudo apt-get install -y \
ca-certificates \
curl \
gnupg \
lsb-release
步骤3:添加 Docker 官方 GPG 密钥
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
步骤4:设置 Docker 仓库
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
步骤5:安装 Docker Compose Plugin
sudo apt-get update
sudo apt-get install -y docker-compose-plugin
2.2.2 通过二进制方式安装
步骤1:下载最新版本
# 获取最新版本号
COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)
# 下载二进制文件
sudo curl -L "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose
步骤2:设置执行权限
sudo chmod +x /usr/local/bin/docker-compose
步骤3:创建符号链接(可选)
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
2.2.3 官方文档遵循说明
- 始终从官方渠道获取安装包
- 验证下载文件的完整性(通过校验和)
- 关注安全公告和更新
- 参考官方文档:https://docs.docker.com/compose/install/
2.3 验证安装
# 检查版本
docker-compose --version
# 或使用插件版本
docker compose version
# 验证功能
docker-compose --help
2.4 基本配置
创建 Docker Compose 配置文件目录:
mkdir ~/docker-projects
cd ~/docker-projects
三、核心概念
3.1 Compose 文件结构
version: '3.8' # Compose 文件版本
services: # 定义服务
web: # 服务名称
image: nginx:latest
db:
image: postgres:13
init-container: # 初始化容器
image: alpine:latest
restart: "no"
networks: # 定义网络
frontend:
volumes: # 定义卷
db-data:
3.2 服务(Services)
- 应用中的每个容器定义为一个服务
- 可以指定镜像、构建配置、依赖关系
- 支持扩展和缩放
- 包括常规服务和初始化服务
3.3 网络(Networks)
- 容器间的通信隔离
- 支持自定义网络驱动
- 可配置网络拓扑
- 支持固定IP地址
3.4 卷(Volumes)
- 持久化数据存储
- 数据独立于容器生命周期
- 支持多种存储驱动
- 包括命名卷和主机路径绑定
3.5 环境变量
- 配置容器运行时参数
- 支持从文件加载
- 支持不同环境的不同配置
- 可在初始化容器中使用
3.6 初始化容器
- 执行前置任务的容器
- 完成后自动退出
- 控制服务启动顺序
- 确保数据准备就绪
四、docker-compose.yml 文件详解
4.1 文件结构示例
version: '3.8'
services:
# 初始化容器
init-db:
image: postgres:13-alpine
command: >
sh -c "
until pg_isready -h db -U postgres; do sleep 2; done &&
psql -h db -U postgres -c 'CREATE DATABASE IF NOT EXISTS appdb;'
"
depends_on:
db:
condition: service_healthy
restart: "no"
# 主服务
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: secret
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 20
webapp:
build: .
ports:
- "8000:8000"
depends_on:
init-db:
condition: service_completed_successfully
networks:
- app-network
volumes:
postgres-data:
networks:
app-network:
driver: bridge
4.2 常用配置项说明
| 配置项 | 说明 | 示例 |
|---|---|---|
image |
使用的镜像 | nginx:latest |
build |
构建上下文 | ./app |
ports |
端口映射 | "80:80" |
volumes |
卷挂载 | ./data:/app/data |
environment |
环境变量 | DEBUG=true |
depends_on |
服务依赖 | - db |
networks |
网络连接 | - frontend |
restart |
重启策略 | always |
healthcheck |
健康检查 | 自定义检查命令 |
command |
覆盖默认命令 | 自定义启动命令 |
4.3 服务配置详解
services:
# 初始化服务示例
init-service:
image: alpine:latest
# 初始化容器关键配置
restart: "no" # 只运行一次
command: >
sh -c "
echo '开始初始化...' &&
# 执行初始化任务
sleep 5 &&
echo '初始化完成'
"
# 依赖关系
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
# 网络配置
networks:
- app-network
# 环境变量
environment:
INIT_TIMEOUT: 30
# 卷挂载
volumes:
- ./init-scripts:/scripts:ro
# 工作目录
working_dir: /scripts
# 用户权限
user: "1000:1000"
# 日志配置
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# 常规服务
web:
image: nginx:alpine
# 依赖初始化容器
depends_on:
init-service:
condition: service_completed_successfully
# 健康检查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
4.4 网络配置
4.4.1 网络类型
networks:
# 桥接网络(默认)
bridge-network:
driver: bridge
driver_opts:
com.docker.network.bridge.name: br0
# 主机网络
host-network:
driver: host
# 无网络
none-network:
driver: none
# 覆盖网络(用于Swarm)
overlay-network:
driver: overlay
# 自定义网络
custom-network:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: "172.20.0.0/16"
gateway: "172.20.0.1"
- subnet: "2001:db8:abcd::/64"
gateway: "2001:db8:abcd::101"
4.4.2 容器固定IP地址设置
services:
web:
image: nginx:alpine
networks:
app-net:
ipv4_address: 172.20.0.10
ipv6_address: 2001:db8:abcd::10
db:
image: postgres:13
networks:
app-net:
ipv4_address: 172.20.0.20
# 初始化容器也可以设置固定IP
init-container:
image: alpine:latest
networks:
app-net:
ipv4_address: 172.20.0.100
restart: "no"
networks:
app-net:
driver: bridge
ipam:
config:
- subnet: "172.20.0.0/16"
- subnet: "2001:db8:abcd::/64"
注意事项:
- 必须使用自定义网络
- IP地址必须在子网范围内
- 避免IP地址冲突
- 适用于需要静态IP的场景(如负载均衡配置)
4.4.3 容器固定MAC地址设置
services:
database:
image: postgres:13
mac_address: "02:42:ac:11:00:02"
networks:
- app-network
# 初始化容器MAC地址
init-service:
image: alpine:latest
mac_address: "02:42:ac:11:00:ff"
restart: "no"
MAC地址设置指南:
- 格式:
"XX:XX:XX:XX:XX:XX"(字符串形式) - 前三个字节通常为
02:42:ac - 后三个字节自定义
- 确保网络内唯一性
完整网络配置示例:
version: '3.8'
services:
# 初始化容器
init-setup:
image: alpine:latest
container_name: init-setup
command: >
sh -c "
echo '初始化网络设置...' &&
# 可以在这里配置网络相关初始化
echo '172.20.0.10 web-server' >> /etc/hosts &&
echo '初始化完成'
"
networks:
app-network:
ipv4_address: 172.20.0.250
restart: "no"
web1:
image: nginx:alpine
container_name: web-server-1
networks:
app-network:
ipv4_address: 172.20.0.10
depends_on:
init-setup:
condition: service_completed_successfully
networks:
app-network:
driver: bridge
ipam:
driver: default
config:
- subnet: "172.20.0.0/24"
gateway: "172.20.0.1"
4.5 卷配置
4.5.1 卷(Volumes)类型与使用
定义卷:
volumes:
db-data: # 卷名称
driver: local
driver_opts:
type: none
o: bind
device: /path/to/data
# 初始化数据卷
init-data:
external: true
使用卷:
services:
# 初始化容器使用卷
data-init:
image: alpine:latest
volumes:
- init-data:/data
command: >
sh -c "
echo '初始化数据...' &&
echo 'Initial data' > /data/init.txt &&
echo '数据初始化完成'
"
restart: "no"
database:
image: postgres:13
volumes:
- db-data:/var/lib/postgresql/data
- init-data:/docker-entrypoint-initdb.d:ro
4.5.2 主机路径(Host Path)绑定
services:
# 初始化容器使用主机路径
config-init:
image: alpine:latest
volumes:
# 主机绝对路径
- /host/init-scripts:/scripts:ro
# 相对路径(相对于docker-compose.yml)
- ./init:/app/init:ro
# 只读绑定初始化配置
- ./config/init.conf:/etc/app/init.conf:ro
# 初始化数据写入
- ./data/init:/data:rw
command: /scripts/init.sh
restart: "no"
app:
image: myapp:latest
volumes:
# 共享初始化容器准备的数据
- ./data/init:/app/data:ro
4.5.3 卷与主机路径的区别
| 特性 | 卷(Volume) | 主机路径绑定(Bind Mount) |
|---|---|---|
| 存储位置 | Docker管理的位置(/var/lib/docker/volumes/) |
主机文件系统任意位置 |
| 生命周期 | 独立于容器,需显式删除 | 与主机文件同生命周期 |
| 移植性 | 高,不依赖主机路径 | 低,依赖特定主机路径 |
| 性能 | 通常较好 | 依赖于主机文件系统 |
| 备份 | Docker工具支持 | 需手动备份 |
| 多主机 | 支持卷驱动(如NFS) | 需要共享文件系统 |
| 权限 | Docker管理,UID/GUID一致 | 使用主机文件权限 |
卷(Volume)在初始化场景示例:
volumes:
mysql-init:
name: mysql-init-data
labels:
- "purpose=init"
services:
mysql-init:
image: mysql:8.0
volumes:
- mysql-init:/docker-entrypoint-initdb.d
command: >
sh -c "
echo '准备初始化数据...' &&
cp -r /init-data/* /docker-entrypoint-initdb.d/ &&
echo '数据准备完成'
"
restart: "no"
mysql:
image: mysql:8.0
volumes:
- mysql-data:/var/lib/mysql
- mysql-init:/docker-entrypoint-initdb.d:ro
# 容器启动时会自动执行init目录下的SQL文件
主机路径绑定在初始化场景示例:
services:
app-init:
image: alpine:latest
volumes:
# 开发时挂载初始化脚本
- ./init-scripts:/scripts:ro
# 初始化输出目录
- ./generated-config:/output
command: >
sh -c "
echo '生成配置文件...' &&
/scripts/generate-config.sh /output &&
echo '配置生成完成'
"
restart: "no"
app:
image: myapp:latest
volumes:
# 使用初始化容器生成的配置
- ./generated-config:/app/config:ro
# 挂载源代码
- ./src:/app/src
depends_on:
app-init:
condition: service_completed_successfully
4.5.4 适用场景对比
使用卷(Volumes)的初始化场景:
-
数据库初始化数据
volumes: - db-init-scripts:/docker-entrypoint-initdb.d -
共享初始化配置
volumes: - shared-init-data:/shared-init -
初始化容器传递数据
# 初始化容器写入数据 init-container: volumes: - config-volume:/config command: generate-config.sh /config # 应用容器读取数据 app: volumes: - config-volume:/app/config:ro -
备份和恢复初始化数据
# 备份初始化卷 docker run --rm -v init-data:/data -v $(pwd):/backup \ alpine tar czf /backup/init-backup.tar.gz /data
使用主机路径绑定(Bind Mounts)的初始化场景:
-
开发环境初始化脚本
volumes: - ./init:/app/init - ./scripts/init.sh:/scripts/init.sh -
配置文件生成
volumes: - ./templates:/templates:ro - ./generated:/output -
主机文件处理
volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro -
初始化日志收集
volumes: - ./logs/init:/var/log/init
初始化专用临时卷:
services:
init-process:
image: alpine:latest
volumes:
# 临时卷用于中间处理
- type: volume
source: temp-data
target: /temp
volume:
nocopy: true
command: >
sh -c "
echo '处理数据...' &&
process-data.sh /temp/input /temp/output &&
echo '处理完成'
"
restart: "no"
app-process:
image: app:latest
volumes:
- type: volume
source: temp-data
target: /processed-data
volume:
nocopy: true
depends_on:
- init-process
volumes:
temp-data:
driver: local
driver_opts:
type: tmpfs
device: tmpfs
4.6 环境变量配置
services:
# 初始化容器环境变量
init-config:
image: alpine:latest
# 方式1:直接定义
environment:
INIT_MODE: full
DATABASE_HOST: db
DATABASE_PORT: 5432
INIT_TIMEOUT: 300
# 方式2:数组形式
environment:
- INIT_MODE=full
- DATABASE_HOST=db
- DATABASE_PORT=5432
# 方式3:从文件加载
env_file:
- .env.init
- .env.secret
# 方式4:扩展环境变量
environment:
- DB_HOST=${DB_HOST:-localhost}
- DB_PORT=${DB_PORT:-5432}
- INIT_RETRIES=${INIT_RETRIES:-3}
# 使用已存在的环境变量
environment:
- HOSTNAME
- USER
command: >
sh -c "
echo '初始化模式: \$INIT_MODE' &&
echo '数据库主机: \$DATABASE_HOST' &&
echo '超时时间: \$INIT_TIMEOUT秒' &&
# 执行初始化逻辑
if [ \"\$INIT_MODE\" = \"full\" ]; then
echo '执行完整初始化...'
else
echo '执行快速初始化...'
fi
"
restart: "no"
app:
image: myapp:latest
environment:
# 可以使用初始化容器设置的环境
- CONFIG_PATH=/config
- INIT_COMPLETE=true
depends_on:
init-config:
condition: service_completed_successfully
.env.init 文件示例:
# 初始化环境变量配置文件
INIT_MODE=full
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_NAME=mydb
INIT_TIMEOUT=300
SKIP_DATA_IMPORT=false
# 阶段控制
INIT_PHASE_1=true
INIT_PHASE_2=true
INIT_PHASE_3=false
五、常用命令
5.1 启动与停止
# 启动所有服务(包含初始化容器)
docker-compose up -d
# 启动并构建镜像
docker-compose up -d --build
# 启动指定服务(跳过初始化)
docker-compose up -d web db
# 只运行初始化容器
docker-compose up init-db
docker-compose up init-config
# 停止所有服务
docker-compose down
# 停止并删除卷
docker-compose down -v
# 停止但不删除资源
docker-compose stop
# 重启服务
docker-compose restart
# 重新运行初始化
docker-compose rm -f init-db
docker-compose up -d init-db
# 查看初始化状态
docker-compose ps init-db
5.2 构建与重建
# 构建镜像(包括初始化镜像)
docker-compose build
# 构建指定服务
docker-compose build web init-service
# 强制重建
docker-compose build --no-cache
# 构建初始化镜像
docker-compose build init-container
# 拉取镜像
docker-compose pull
# 拉取并启动
docker-compose up -d --pull always
5.3 查看状态
# 查看所有容器状态
docker-compose ps
# 详细状态
docker-compose ps -a
# 查看初始化容器状态
docker-compose ps | grep init
# 查看服务日志
docker-compose logs
# 查看初始化容器日志
docker-compose logs init-db
docker-compose logs init-config
# 跟踪初始化日志
docker-compose logs -f init-db
# 查看资源使用
docker-compose top
# 查看初始化容器退出代码
docker inspect $(docker-compose ps -q init-db) --format='{{.State.ExitCode}}'
5.4 日志管理
# 查看所有日志
docker-compose logs
# 查看并跟随日志
docker-compose logs -f
# 查看初始化日志
docker-compose logs init-service
# 查看最近N行
docker-compose logs --tail=100 init-container
# 带时间戳
docker-compose logs -t
# 过滤初始化日志
docker-compose logs init-db | grep -i "error\|complete\|success"
# 导出初始化日志
docker-compose logs init-container > init_logs.txt
# 清理日志
docker-compose logs --tail=0 -f
5.5 执行命令
# 在初始化容器执行命令(运行前)
docker-compose run --rm init-db sh
# 在运行中的容器执行命令
docker-compose exec web bash
# 在初始化容器执行诊断命令
docker-compose run --rm init-service env
docker-compose run --rm init-service ls -la /scripts
# 手动运行初始化脚本
docker-compose run --rm init-db /scripts/init.sh
# 测试初始化连接
docker-compose run --rm init-db ping db
docker-compose run --rm init-db nc -zv db 5432
5.6 扩展服务
# 扩展服务实例
docker-compose up -d --scale web=3
# 查看扩展状态
docker-compose ps
# 负载均衡示例
docker-compose up -d --scale web=5
# 注意:初始化容器通常不进行扩展
# 初始化容器应该只运行一个实例
六、实战示例
6.1 示例1:Web应用 + 数据库 + 初始化容器
项目结构:
myapp/
├── docker-compose.yml
├── init-scripts/
│ ├── init-db.sh
│ ├── init-redis.sh
│ └── wait-for-it.sh
├── web/
│ ├── Dockerfile
│ └── app.py
├── sql/
│ ├── schema.sql
│ └── seed.sql
└── .env
docker-compose.yml:
version: '3.8'
services:
# 阶段1:等待基础设施
wait-for-services:
image: alpine:latest
container_name: wait-for-infra
command: >
sh -c "
echo '等待基础设施服务就绪...' &&
# 等待PostgreSQL
echo '等待PostgreSQL...' &&
until nc -z postgres 5432; do
echo 'PostgreSQL未就绪,等待2秒...'
sleep 2
done &&
echo 'PostgreSQL就绪 ✓' &&
# 等待Redis
echo '等待Redis...' &&
until nc -z redis 6379; do
echo 'Redis未就绪,等待2秒...'
sleep 2
done &&
echo 'Redis就绪 ✓' &&
echo '所有基础设施就绪!'
"
networks:
- app-network
restart: "no"
depends_on:
- postgres
- redis
# 阶段2:数据库初始化
init-database:
image: postgres:13-alpine
container_name: init-db
command: >
sh -c "
echo '开始数据库初始化...' &&
# 等待数据库完全就绪
until pg_isready -h postgres -U postgres; do
echo '等待数据库连接...'
sleep 2
done &&
echo '创建应用数据库和用户...' &&
psql -h postgres -U postgres -c '
CREATE DATABASE IF NOT EXISTS appdb;
CREATE USER IF NOT EXISTS appuser WITH PASSWORD '\''apppassword'\'';
GRANT ALL PRIVILEGES ON DATABASE appdb TO appuser;
' || echo '数据库/用户可能已存在' &&
echo '执行数据库脚本...' &&
for sql_file in /sql/*.sql; do
echo \"执行: \${sql_file}\" &&
psql -h postgres -U postgres -d appdb -f \${sql_file} || true
done &&
echo '数据库初始化完成!'
"
volumes:
- ./sql:/sql:ro
environment:
PGPASSWORD: postgres
networks:
- app-network
restart: "no"
depends_on:
wait-for-services:
condition: service_completed_successfully
# 阶段3:Redis初始化
init-redis:
image: redis:6-alpine
container_name: init-redis
command: >
sh -c "
echo '开始Redis初始化...' &&
# 等待Redis就绪
until redis-cli -h redis ping | grep PONG; do
echo '等待Redis连接...'
sleep 1
done &&
echo '设置Redis初始数据...' &&
redis-cli -h redis << 'EOF'
HMSET app:config version \"1.0.0\"
HMSET app:config environment \"production\"
HMSET app:config initialized \"true\"
HMSET app:config init_time \"\$(date '+%Y-%m-%d %H:%M:%S')\"
# 设置缓存键
SET cache:settings:max_users 1000
SET cache:settings:session_timeout 3600
# 初始化集合
SADD app:default_roles admin user guest
EOF &&
echo 'Redis初始化完成!'
"
networks:
- app-network
restart: "no"
depends_on:
wait-for-services:
condition: service_completed_successfully
# 阶段4:应用初始化
init-app:
image: alpine:latest
container_name: init-app
command: >
sh -c "
echo '开始应用初始化...' &&
# 等待数据库初始化完成
echo '等待数据库初始化完成...' &&
until docker inspect init-db --format='{{.State.Status}}' | grep -q exited; do
sleep 2
done &&
# 检查数据库初始化是否成功
DB_EXIT_CODE=\$(docker inspect init-db --format='{{.State.ExitCode}}')
if [ \"\$DB_EXIT_CODE\" -ne 0 ]; then
echo '数据库初始化失败!'
exit 1
fi &&
echo '生成应用配置文件...' &&
cat > /config/app.conf << EOF
database:
host: postgres
port: 5432
name: appdb
user: appuser
password: apppassword
redis:
host: redis
port: 6379
app:
name: MyApplication
version: 1.0.0
environment: production
EOF &&
echo '创建必要的目录结构...' &&
mkdir -p /data/uploads /data/logs /data/cache &&
chmod 755 /data/uploads /data/logs /data/cache &&
echo '应用初始化完成!'
"
volumes:
- ./config:/config
- ./data:/data
networks:
- app-network
restart: "no"
depends_on:
init-database:
condition: service_completed_successfully
init-redis:
condition: service_completed_successfully
# 基础设施服务
postgres:
image: postgres:13-alpine
container_name: app-postgres
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: postgres
volumes:
- postgres-data:/var/lib/postgresql/data
- ./sql:/docker-entrypoint-initdb.d:ro
ports:
- "5432:5432"
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 20
start_period: 10s
restart: unless-stopped
redis:
image: redis:6-alpine
container_name: app-redis
command: redis-server --appendonly yes
volumes:
- redis-data:/data
ports:
- "6379:6379"
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
restart: unless-stopped
# 应用服务
webapp:
build: ./web
container_name: app-web
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://appuser:apppassword@postgres:5432/appdb
- REDIS_URL=redis://redis:6379/0
- CONFIG_PATH=/app/config
volumes:
- ./config:/app/config:ro
- ./data/uploads:/app/uploads
- ./data/logs:/app/logs
- ./data/cache:/app/cache
networks:
- app-network
depends_on:
init-app:
condition: service_completed_successfully
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
networks:
app-network:
driver: bridge
ipam:
config:
- subnet: "172.20.0.0/16"
gateway: "172.20.0.1"
volumes:
postgres-data:
name: app-postgres-data
redis-data:
name: app-redis-data
操作命令:
# 1. 启动基础设施
docker-compose up -d postgres redis
# 2. 运行初始化序列
docker-compose up wait-for-services
docker-compose up init-database
docker-compose up init-redis
docker-compose up init-app
# 3. 启动应用
docker-compose up -d webapp
# 4. 查看初始化状态
docker-compose ps | grep init
# 5. 查看初始化日志
docker-compose logs init-database
docker-compose logs init-redis
docker-compose logs init-app
# 6. 一键启动(不推荐用于生产)
docker-compose up -d
6.2 示例2:多服务应用 + 复杂初始化
version: '3.8'
x-init-config: &init-config
restart: "no"
networks:
- app-network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
x-app-config: &app-config
restart: unless-stopped
networks:
- app-network
services:
# 阶段0:基础设施检查
check-prerequisites:
<<: *init-config
image: alpine:latest
container_name: check-prereq
command: >
sh -c "
echo '检查系统前提条件...' &&
# 检查必要的目录
for dir in ./data ./config ./logs; do
if [ ! -d \"\$dir\" ]; then
echo \"创建目录: \$dir\" &&
mkdir -p \"\$dir\" || {
echo \"无法创建目录: \$dir\"
exit 1
}
fi
done &&
# 检查权限
for dir in ./data ./logs; do
if [ ! -w \"\$dir\" ]; then
echo \"目录不可写: \$dir\"
exit 1
fi
done &&
# 检查环境变量
if [ -z \"\${REQUIRED_ENV}\" ]; then
echo '警告: REQUIRED_ENV 未设置'
fi &&
echo '前提条件检查通过!'
"
volumes:
- .:/app:ro
# 阶段1:数据库初始化
db-migration:
<<: *init-config
image: myapp-db-migrate:latest
container_name: db-migrate
build:
context: ./db
dockerfile: Dockerfile.migrate
environment:
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
MIGRATION_DIR: /migrations
volumes:
- ./db/migrations:/migrations:ro
command: >
sh -c "
echo '开始数据库迁移...' &&
# 等待数据库
until pg_isready -h postgres -U postgres; do
echo '等待数据库...'
sleep 2
done &&
# 运行迁移
echo '运行迁移脚本...' &&
for migration in \$(ls /migrations/*.sql | sort); do
echo \"应用迁移: \$(basename \$migration)\" &&
psql -h postgres -U postgres -f \$migration || {
echo \"迁移失败: \$migration\"
exit 1
}
done &&
echo '数据库迁移完成!'
"
depends_on:
- postgres
- check-prerequisites
# 阶段2:数据种子
db-seed:
<<: *init-config
image: postgres:13-alpine
container_name: db-seed
environment:
PGPASSWORD: postgres
volumes:
- ./db/seed:/seed:ro
command: >
sh -c "
echo '开始数据种子...' &&
# 等待迁移完成
until docker inspect db-migrate --format='{{.State.Status}}' | grep -q exited; do
sleep 2
done &&
MIGRATE_EXIT=\$(docker inspect db-migrate --format='{{.State.ExitCode}}')
if [ \"\$MIGRATE_EXIT\" -ne 0 ]; then
echo '数据库迁移失败,跳过种子'
exit 1
fi &&
echo '导入种子数据...' &&
for seed_file in /seed/*.sql; do
echo \"导入: \$(basename \$seed_file)\" &&
psql -h postgres -U postgres -d appdb -f \$seed_file || {
echo \"种子导入失败: \$seed_file\"
exit 1
}
done &&
echo '数据种子完成!'
"
depends_on:
- db-migration
# 阶段3:缓存预热
cache-warmup:
<<: *init-config
image: redis:6-alpine
container_name: cache-warmup
command: >
sh -c "
echo '开始缓存预热...' &&
# 等待Redis
until redis-cli -h redis ping | grep PONG; do
sleep 1
done &&
# 预热数据
echo '预热缓存数据...' &&
redis-cli -h redis << 'EOF'
# 设置配置
HMSET app:cache:config max_size 1000000
HMSET app:cache:config ttl 3600
# 预热常用数据
SET cache:homepage '<html>...</html>'
SET cache:menu '[{\"id\":1,\"name\":\"Home\"}]'
# 统计信息
HMSET app:stats startups 1
HMSET app:stats last_start \"\$(date)\"
EOF &&
echo '缓存预热完成!'
"
depends_on:
- redis
- db-seed
# 阶段4:文件系统准备
fs-prepare:
<<: *init-config
image: alpine:latest
container_name: fs-prepare
volumes:
- ./data:/data
- ./uploads:/uploads
- ./static:/static
command: >
sh -c "
echo '准备文件系统...' &&
# 创建目录结构
mkdir -p \
/data/cache \
/data/sessions \
/data/temp \
/uploads/images \
/uploads/documents \
/uploads/videos \
/static/css \
/static/js \
/static/images &&
# 设置权限
chmod -R 755 /static &&
chmod -R 775 /uploads &&
chmod -R 777 /data/cache /data/temp &&
# 复制默认文件
if [ -d '/app/default-static' ]; then
echo '复制默认静态文件...'
cp -r /app/default-static/* /static/
fi &&
# 创建符号链接
ln -sf /uploads /static/uploads || true &&
echo '文件系统准备完成!'
"
depends_on:
- cache-warmup
# 阶段5:最终验证
final-verification:
<<: *init-config
image: alpine:latest
container_name: final-verify
command: >
sh -c "
echo '执行最终验证...' &&
# 验证服务连通性
echo '验证数据库连接...' &&
if ! pg_isready -h postgres -U postgres; then
echo '数据库连接失败'
exit 1
fi &&
echo '验证Redis连接...' &&
if ! redis-cli -h redis ping | grep -q PONG; then
echo 'Redis连接失败'
exit 1
fi &&
# 验证文件系统
echo '验证文件系统...' &&
for dir in /data/cache /uploads /static; do
if [ ! -d \"\$dir\" ]; then
echo \"目录不存在: \$dir\"
exit 1
fi
done &&
# 验证数据完整性
echo '验证数据完整性...' &&
RECORD_COUNT=\$(psql -h postgres -U postgres -d appdb -t -c \
\"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public'\" | tr -d '[:space:]')
if [ \"\$RECORD_COUNT\" -eq 0 ]; then
echo '数据库中没有表'
exit 1
fi &&
echo '=======================================' &&
echo '所有验证通过!应用可以启动。' &&
echo '=======================================' &&
# 创建完成标志
touch /tmp/app.ready &&
echo '初始化完成时间: \$(date)' > /tmp/init-complete.txt
"
volumes:
- ./data:/data:ro
- ./uploads:/uploads:ro
- ./static:/static:ro
depends_on:
- fs-prepare
# 基础设施服务
postgres:
<<: *app-config
image: postgres:13-alpine
container_name: app-postgres
environment:
POSTGRES_PASSWORD: postgres
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 20
redis:
<<: *app-config
image: redis:6-alpine
container_name: app-redis
command: redis-server --appendonly yes
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
# 应用服务
api:
<<: *app-config
build: ./api
container_name: app-api
ports:
- "8080:8080"
environment:
- DB_HOST=postgres
- REDIS_HOST=redis
- APP_ENV=production
volumes:
- ./data:/app/data
- ./uploads:/app/uploads
- ./static:/app/static
depends_on:
final-verification:
condition: service_completed_successfully
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
web:
<<: *app-config
image: nginx:alpine
container_name: app-web
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./static:/usr/share/nginx/html:ro
- ./uploads:/usr/share/nginx/html/uploads
depends_on:
- api
- final-verification
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
networks:
app-network:
driver: bridge
volumes:
postgres-data:
redis-data:
6.3 示例3:开发环境配置 + 初始化
version: '3.8'
services:
# 开发数据库
postgres-dev:
image: postgres:13-alpine
container_name: postgres-dev
environment:
POSTGRES_PASSWORD: devpassword
POSTGRES_USER: devuser
POSTGRES_DB: devdb
ports:
- "5432:5432"
volumes:
- postgres-dev-data:/var/lib/postgresql/data
- ./dev-init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- dev-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U devuser"]
interval: 5s
timeout: 5s
retries: 20
# 开发环境初始化
dev-setup:
image: alpine:latest
container_name: dev-setup
command: >
sh -c "
echo '设置开发环境...' &&
# 等待数据库
until pg_isready -h postgres-dev -U devuser; do
sleep 2
done &&
echo '安装开发依赖...' &&
apk add --no-cache postgresql-client curl jq &&
echo '设置测试数据...' &&
psql -h postgres-dev -U devuser -d devdb << 'EOF'
-- 创建测试表
CREATE TABLE IF NOT EXISTS test_users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入测试数据
INSERT INTO test_users (username, email) VALUES
('test1', 'test1@example.com'),
('test2', 'test2@example.com'),
('test3', 'test3@example.com')
ON CONFLICT (username) DO NOTHING;
EOF &&
echo '创建开发配置文件...' &&
mkdir -p /app/config &&
cat > /app/config/development.yaml << 'EOF'
database:
host: postgres-dev
port: 5432
database: devdb
username: devuser
password: devpassword
server:
port: 3000
debug: true
hot_reload: true
features:
enable_debug_tools: true
enable_test_routes: true
EOF &&
echo '开发环境设置完成!'
"
volumes:
- ./config:/app/config
- ./src:/app/src:ro
networks:
- dev-network
depends_on:
postgres-dev:
condition: service_healthy
restart: "no"
# 开发服务器
dev-server:
build:
context: .
dockerfile: Dockerfile.dev
container_name: dev-server
ports:
- "3000:3000"
- "9229:9229" # Node.js调试端口
volumes:
- ./src:/app/src
- ./config:/app/config:ro
- /app/node_modules
environment:
- NODE_ENV=development
- DEBUG=true
- CHOKIDAR_USEPOLLING=true
networks:
- dev-network
depends_on:
dev-setup:
condition: service_completed_successfully
command: npm run dev
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# 开发工具
adminer:
image: adminer:latest
container_name: dev-adminer
ports:
- "8080:8080"
environment:
ADMINER_DESIGN: dracula
ADMINER_DEFAULT_SERVER: postgres-dev
networks:
- dev-network
restart: unless-stopped
networks:
dev-network:
driver: bridge
driver_opts:
com.docker.network.bridge.name: br-dev
volumes:
postgres-dev-data:
6.4 示例4:生产环境配置 + 初始化容器
version: '3.8'
x-common: &common
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
x-init: &init-common
<<: *common
restart: "no"
networks:
- production-network
services:
# 初始化序列
# 1. 基础设施检查
health-check:
<<: *init-common
image: alpine:latest
container_name: health-check
command: >
sh -c "
echo '执行健康检查...' &&
# 检查必要的卷
echo '检查数据卷...' &&
for vol in postgres-data redis-data; do
if docker volume inspect \$vol >/dev/null 2>&1; then
echo \"卷存在: \$vol ✓\"
else
echo \"卷不存在: \$vol\"
fi
done &&
# 检查网络
echo '检查网络...' &&
if docker network inspect production-network >/dev/null 2>&1; then
echo '网络存在 ✓'
else
echo '网络不存在'
fi &&
# 检查端口
echo '检查端口可用性...' &&
for port in 80 443 5432 6379; do
if netstat -tuln | grep -q \":\$port \"; then
echo \"端口被占用: \$port\"
exit 1
fi
done &&
echo '所有健康检查通过!'
"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# 2. 数据库备份恢复(如果需要)
db-restore:
<<: *init-common
image: postgres:13-alpine
container_name: db-restore
command: >
sh -c "
echo '检查数据库恢复...' &&
# 等待数据库
until pg_isready -h postgres -U postgres; do
sleep 2
done &&
# 检查是否需要恢复
if [ -f \"/backup/latest.dump\" ] && [ \"\${RESTORE_DB}\" = \"true\" ]; then
echo '开始数据库恢复...' &&
pg_restore -h postgres -U postgres -d postgres -c /backup/latest.dup || {
echo '数据库恢复失败'
exit 1
} &&
echo '数据库恢复完成!'
else
echo '跳过数据库恢复'
fi
"
environment:
PGPASSWORD: \${DB_PASSWORD}
RESTORE_DB: \${RESTORE_DB:-false}
volumes:
- ./backups:/backup:ro
depends_on:
health-check:
condition: service_completed_successfully
postgres:
condition: service_healthy
# 3. 应用初始化
app-init:
<<: *init-common
build:
context: ./app
dockerfile: Dockerfile.init
container_name: app-init
environment:
- NODE_ENV=production
- DATABASE_URL=\${DATABASE_URL}
- SECRET_KEY=\${SECRET_KEY}
volumes:
- ./app/scripts:/scripts:ro
- ./app/config:/config
command: >
sh -c "
echo '开始应用初始化...' &&
# 等待基础设施
/scripts/wait-for.sh postgres:5432 &&
/scripts/wait-for.sh redis:6379 &&
# 运行初始化脚本
echo '运行数据库迁移...' &&
node /scripts/migrate.js &&
echo '创建管理员用户...' &&
node /scripts/create-admin.js \${ADMIN_EMAIL} \${ADMIN_PASSWORD} &&
echo '生成静态文件...' &&
node /scripts/generate-static.js &&
echo '应用初始化完成!'
"
depends_on:
db-restore:
condition: service_completed_successfully
redis:
condition: service_healthy
# 基础设施
postgres:
<<: *common
image: postgres:13-alpine
container_name: production-postgres
environment:
POSTGRES_PASSWORD: \${DB_PASSWORD}
POSTGRES_USER: \${DB_USER}
POSTGRES_DB: \${DB_NAME}
volumes:
- postgres-data:/var/lib/postgresql/data
- ./postgres/conf:/etc/postgresql:ro
networks:
- production-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
<<: *common
image: redis:6-alpine
container_name: production-redis
command: redis-server --requirepass \${REDIS_PASSWORD} --appendonly yes
volumes:
- redis-data:/data
networks:
- production-network
healthcheck:
test: ["CMD", "redis-cli", "-a", "\${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# 应用
app:
<<: *common
image: \${APP_IMAGE}
container_name: production-app
ports:
- "80:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=\${DATABASE_URL}
- REDIS_URL=\${REDIS_URL}
volumes:
- ./app/config:/app/config:ro
- ./logs:/app/logs
networks:
- production-network
depends_on:
app-init:
condition: service_completed_successfully
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
networks:
production-network:
driver: bridge
internal: false
volumes:
postgres-data:
driver: local
redis-data:
driver: local
七、高级特性
7.1 多环境配置
目录结构:
project/
├── docker-compose.yml
├── docker-compose.override.yml
├── docker-compose.prod.yml
├── docker-compose.init.yml
├── .env
└── scripts/
├── init-dev.sh
└── init-prod.sh
基础配置(docker-compose.yml):
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
environment:
- APP_ENV=${APP_ENV:-development}
networks:
- app-network
networks:
app-network:
初始化配置(docker-compose.init.yml):
services:
# 通用初始化容器
init-common:
image: alpine:latest
command: >
sh -c "
echo '通用初始化任务...' &&
echo '环境: ${APP_ENV}' &&
echo '初始化完成'
"
restart: "no"
networks:
- app-network
# 数据库初始化
init-db:
extends:
file: docker-compose.yml
service: init-common
container_name: init-db-${APP_ENV}
environment:
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}
command: >
sh -c "
echo '数据库初始化: ${APP_ENV}' &&
/scripts/init-db-${APP_ENV}.sh
"
volumes:
- ./scripts:/scripts:ro
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
web:
depends_on:
init-db:
condition: service_completed_successfully
开发环境覆盖(docker-compose.override.yml):
services:
init-db:
environment:
- INIT_MODE=development
- SEED_DATA=true
volumes:
- ./dev-data:/seed-data:ro
web:
build: .
volumes:
- ./src:/app/src
environment:
- DEBUG=true
- HOT_RELOAD=true
生产环境配置(docker-compose.prod.yml):
services:
init-db:
environment:
- INIT_MODE=production
- SEED_DATA=false
volumes:
- ./prod-data:/seed-data:ro
web:
image: registry.example.com/myapp:${TAG}
restart: always
environment:
- DEBUG=false
- LOG_LEVEL=info
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
db:
deploy:
resources:
limits:
cpus: '2'
memory: 4G
使用不同配置:
# 开发环境(加载override和init配置)
docker-compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.init.yml up -d
# 生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.init.yml up -d
# 只运行初始化
docker-compose -f docker-compose.yml -f docker-compose.init.yml up init-db
# 自定义环境
APP_ENV=staging docker-compose -f docker-compose.yml -f docker-compose.init.yml up -d
7.2 扩展配置
version: '3.8'
# 定义扩展字段
x-init-config: &init-config
restart: "no"
networks:
- app-network
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
healthcheck:
test: ["CMD", "echo", "init-container-no-healthcheck"]
interval: 5s
timeout: 5s
retries: 1
x-db-init: &db-init
<<: *init-config
image: postgres:13-alpine
environment:
PGPASSWORD: ${DB_PASSWORD}
volumes:
- ./sql:/sql:ro
x-app-init: &app-init
<<: *init-config
image: alpine:latest
working_dir: /app
user: "1000:1000"
services:
# 数据库初始化
init-db-schema:
<<: *db-init
container_name: init-db-schema
command: >
sh -c "
until pg_isready -h db -U ${DB_USER}; do sleep 2; done &&
for f in /sql/schema/*.sql; do
echo \"执行: \$(basename \$f)\" &&
psql -h db -U ${DB_USER} -d ${DB_NAME} -f \$f
done
"
depends_on:
db:
condition: service_healthy
init-db-data:
<<: *db-init
container_name: init-db-data
command: >
sh -c "
until pg_isready -h db -U ${DB_USER}; do sleep 2; done &&
for f in /sql/data/*.sql; do
echo \"导入: \$(basename \$f)\" &&
psql -h db -U ${DB_USER} -d ${DB_NAME} -f \$f
done
"
depends_on:
init-db-schema:
condition: service_completed_successfully
# 应用初始化
init-app-config:
<<: *app-init
container_name: init-app-config
volumes:
- ./config:/config:ro
- ./generated-config:/output
command: >
sh -c "
echo '生成配置文件...' &&
envsubst < /config/template.yaml > /output/config.yaml &&
echo '配置生成完成'
"
depends_on:
init-db-data:
condition: service_completed_successfully
init-app-setup:
<<: *app-init
container_name: init-app-setup
volumes:
- ./scripts:/scripts:ro
- ./data:/data
command: >
sh -c "
echo '设置应用数据...' &&
/scripts/setup.sh /data &&
echo '应用设置完成'
"
depends_on:
init-app-config:
condition: service_completed_successfully
# 主服务
db:
image: postgres:13-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
healthcheck: ...
app:
image: myapp:latest
depends_on:
init-app-setup:
condition: service_completed_successfully
volumes:
- ./generated-config:/app/config:ro
- ./data:/app/data:ro
7.3 健康检查
version: '3.8'
services:
# 初始化容器健康检查示例
init-service:
image: alpine:latest
# 初始化容器通常不需要健康检查
# 因为它们很快就会完成
healthcheck:
disable: true
command: >
sh -c "
echo '开始初始化...' &&
# 长时间运行的任务
sleep 30 &&
echo '初始化完成' &&
# 创建完成标志
touch /tmp/init.complete
"
# 通过卷共享完成状态
volumes:
- init-status:/status
# 依赖服务的健康检查
db:
image: postgres:13
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 20
start_period: 10s
redis:
image: redis:6-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
# 应用容器的健康检查
app:
image: myapp:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
depends_on:
init-service:
condition: service_completed_successfully
db:
condition: service_healthy
redis:
condition: service_healthy
7.4 资源限制
version: '3.8'
services:
# 初始化容器资源限制
cpu-intensive-init:
image: myapp:latest
# 容器资源限制
deploy:
resources:
limits:
cpus: '2.0' # 最多2个CPU核心
memory: 1G # 最多1GB内存
pids: 200 # 最多200个进程
reservations:
cpus: '0.5' # 至少0.5个CPU核心
memory: 256M # 至少256MB内存
# 或者使用旧格式
cpus: '2.0'
mem_limit: 1G
mem_reservation: 256M
cpu_shares: 512
cpu_quota: 200000
cpu_period: 100000
cpuset: '0-3'
# 设备限制
devices:
- "/dev/sda:/dev/xvda:rwm"
# 内核参数
sysctls:
net.core.somaxconn: 1024
net.ipv4.tcp_syncookies: 0
# 共享内存
shm_size: '512m'
# 临时文件系统
tmpfs:
- /tmp:size=100m,mode=1777
# 特权模式
privileged: false
# 内核功能
cap_add:
- SYS_PTRACE
cap_drop:
- SYS_ADMIN
# 安全选项
security_opt:
- label=user:USER
- label=role:ROLE
- no-new-privileges:true
command: "data-processing-script"
restart: "no"
# 轻量级初始化容器
lightweight-init:
image: alpine:latest
deploy:
resources:
limits:
cpus: '0.2'
memory: 128M
command: "echo '轻量初始化'"
restart: "no"
# 应用容器
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '1'
memory: 512M
depends_on:
cpu-intensive-init:
condition: service_completed_successfully
lightweight-init:
condition: service_completed_successfully
7.5 依赖管理
version: '3.8'
services:
# 阶段1:基础设施
postgres:
image: postgres:13-alpine
healthcheck: ...
redis:
image: redis:6-alpine
healthcheck: ...
# 阶段2:数据库初始化
init-db:
image: postgres:13-alpine
command: "初始化命令"
depends_on:
# 简单依赖
- postgres
- redis
# 带条件的依赖
postgres:
condition: service_healthy
redis:
condition: service_started
restart: "no"
# 阶段3:缓存初始化
init-cache:
image: redis:6-alpine
command: "初始化命令"
depends_on:
# 依赖初始化容器
init-db:
condition: service_completed_successfully
# 依赖基础设施
redis:
condition: service_healthy
restart: "no"
# 阶段4:应用初始化
init-app:
image: alpine:latest
command: "初始化命令"
depends_on:
# 依赖前序初始化
init-db:
condition: service_completed_successfully
init-cache:
condition: service_completed_successfully
# 依赖基础设施
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: "no"
# 阶段5:应用服务
web:
build: .
depends_on:
# 依赖所有初始化完成
init-db:
condition: service_completed_successfully
init-cache:
condition: service_completed_successfully
init-app:
condition: service_completed_successfully
# 链接到其他服务
links:
- postgres:database
- redis:rediscache
# 扩展依赖
extends:
file: common-services.yml
service: webapp
八、最佳实践
8.1 文件组织
推荐的项目结构:
myapp/
├── docker-compose.yml # 主配置文件
├── docker-compose.override.yml # 开发环境覆盖
├── docker-compose.prod.yml # 生产环境配置
├── docker-compose.init.yml # 初始化配置
├── .env # 环境变量
├── .env.example # 环境变量示例
├── .dockerignore # Docker忽略文件
├── scripts/
│ ├── init.sh # 初始化脚本
│ ├── wait-for-it.sh # 等待脚本
│ ├── init-db.sh # 数据库初始化
│ ├── init-redis.sh # Redis初始化
│ └── init-app.sh # 应用初始化
├── sql/
│ ├── schema/ # 数据库schema
│ │ ├── 001_init.sql
│ │ └── 002_tables.sql
│ └── data/ # 初始数据
│ ├── seed_data.sql
│ └── test_data.sql
├── config/
│ ├── development/ # 开发配置
│ ├── production/ # 生产配置
│ └── templates/ # 配置模板
├── init-data/ # 初始化数据
│ ├── uploads/ # 初始上传文件
│ ├── static/ # 静态文件
│ └── backups/ # 备份文件
├── app/ # 应用代码
├── monitoring/ # 监控配置
└── logs/ # 日志目录
.dockerignore 文件:
# 忽略文件
.git
.gitignore
*.log
*.pyc
__pycache__
node_modules
.DS_Store
.env
*.tmp
coverage/
dist/
build/
*.db
*.sqlite3
docker-compose.override.yml
docker-compose.init.yml
8.2 安全性考虑
version: '3.8'
services:
# 安全初始化容器示例
secure-init:
image: alpine:latest
# 1. 非root用户运行
user: "1000:1000"
# 2. 只读根文件系统
read_only: true
# 3. 临时文件系统
tmpfs:
- /tmp
- /run
- /var/run
# 4. 安全选项
security_opt:
- no-new-privileges:true
- apparmor:docker-default
# 5. 移除不需要的内核功能
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# 6. 资源限制
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
pids: 20
# 7. 健康检查(通常禁用)
healthcheck:
disable: true
# 8. 重启策略
restart: "no" # 初始化容器只运行一次
# 9. 只读卷挂载
volumes:
- /etc/localtime:/etc/localtime:ro
- ./scripts:/scripts:ro
# 10. 网络安全
networks:
internal-net:
ipv4_address: 172.20.0.250
# 11. 环境变量保护
env_file:
- .env.secret
environment:
- SECRET_KEY_FILE=/run/secrets/secret_key
command: >
sh -c "
echo '安全初始化...' &&
/scripts/secure-init.sh
"
# 秘密管理
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
external: true
# 内部网络
networks:
internal-net:
internal: true
driver
发表评论