引言

Docker Compose是Docker生态系统中的一个重要工具,它极大地简化了多容器应用的定义和运行。通过使用简单的YAML配置文件,开发者可以轻松地定义、启动和管理由多个相互关联的容器组成的应用程序。本文将全面介绍Docker Compose的基本概念、配置选项、使用方法以及最佳实践,帮助您更高效地管理容器化应用。

目录

什么是Docker Compose

Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。它使用YAML文件来配置应用程序的服务、网络和卷,使开发人员能够在一个地方定义和管理多个容器。通过简单的命令,您可以创建和启动配置中定义的所有服务,极大地简化了容器化应用的开发和部署流程。

安装Docker Compose

安装Docker Compose

在大多数Linux系统上,可以通过以下命令安装Docker Compose:

1
sudo apt install docker-compose

对于其他操作系统或更新版本的安装,可以参考Docker官方文档

检查安装

安装完成后,可以通过以下命令检查Docker Compose的版本:

1
docker-compose --version

基本概念

版本与名称

版本(Version)

在docker-compose.yml文件中,可以指定使用的Compose文件格式版本。虽然在最新版本中已不再强制要求,但为了兼容性考虑,仍可以指定:

1
version: '3'

名称(Name)

可以为项目指定一个名称,这在管理多个项目时非常有用:

1
name: myapp

服务(Service)

服务是Docker Compose中定义的一个容器,它可以是一个应用程序、数据库、缓存等。每个服务都有自己的配置,包括镜像、端口、环境变量、依赖关系等。

镜像(Image)

指定服务使用的Docker镜像:

1
image: nginx:latest

端口(Ports)

将容器端口映射到主机端口:

1
2
ports:
- "80:80"

环境变量(Environment Variables)

设置容器的环境变量:

1
2
3
environment:
- "DB_HOST=db"
- "DB_PORT=3306"

也可以使用对象语法:

1
2
3
environment:
DB_HOST: db
DB_PORT: 3306

或者使用环境变量文件:

1
2
3
env_file:
- ./common.env
- ./app.env

依赖关系(Depends On)

指定服务之间的依赖关系,确保服务按正确的顺序启动:

1
2
depends_on:
- db

命令(Command)

覆盖容器的默认命令:

1
command: "python app.py"

网络(Network)

网络是用于连接服务之间的通信通道。Docker Compose会自动创建一个默认网络,但也可以定义自定义网络。

自定义网络

1
2
3
networks:
mynetwork:
driver: bridge

服务连接网络

1
2
3
4
5
6
7
8
9
10
11
12
13
services:
web:
image: nginx:latest
networks:
- mynetwork
db:
image: mysql:latest
networks:
- mynetwork

networks:
mynetwork:
driver: bridge

服务指定网络配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
services:
web:
image: nginx:latest
networks:
mynetwork:
ipv4_address: 172.16.238.10
ports:
- "80:80"
db:
image: mysql:latest
networks:
mynetwork:
ipv4_address: 172.16.238.11
environment:
- "MYSQL_ROOT_PASSWORD=secret"

networks:
mynetwork:
driver: bridge
ipam:
config:
- subnet: 172.16.238.0/24

卷(Volume)

卷是用于持久化存储数据的容器。Docker Compose会自动创建一个默认卷,但也可以定义自定义卷。

自定义卷

1
2
3
volumes:
myvolume:
driver: local

服务指定卷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
web:
image: nginx:latest
volumes:
- myvolume:/var/www/html
db:
image: mysql:latest
volumes:
- dbdata:/var/lib/mysql

volumes:
myvolume:
driver: local
dbdata:
driver: local

挂载主机目录

1
2
3
4
5
6
7
8
9
services:
web:
image: nginx:latest
volumes:
- ./html:/var/www/html
- ./nginx/conf.d:/etc/nginx/conf.d
##
# volumes:
# - 主机路径:容器路径:权限

项目(Project)

项目是一个包含多个服务的Docker Compose应用程序。每个项目都有自己的配置文件(docker-compose.yml),用于定义服务、网络和卷。

常用命令

启动项目

1
docker-compose up

使用-d参数可以在后台运行:

1
docker-compose up -d

停止项目

1
docker-compose down

添加--volumes参数可以同时删除卷:

1
docker-compose down --volumes

查看项目服务

1
docker-compose ps

查看项目日志

1
docker-compose logs

使用-f参数可以持续查看日志:

1
docker-compose logs -f

重启项目服务

1
docker-compose restart

停止项目服务

1
docker-compose stop

启动项目服务

1
docker-compose start

查看项目网络

1
docker-compose network ls

查看项目卷

1
docker-compose volume ls

执行容器命令

1
docker-compose exec web bash

构建或重建服务

1
docker-compose build

构建(Build)

从Dockerfile构建镜像:

1
build: ./dir

指定Dockerfile和构建参数:

1
2
3
4
5
6
build:
context: ./dir
dockerfile: Dockerfile-alternate
args:
buildno: 1
gitcommithash: cdc3b19

重启策略(Restart)

配置容器的重启策略:

1
restart: "no" # 不自动重启
1
restart: always # 总是重启
1
restart: on-failure # 仅在失败时重启
1
restart: unless-stopped # 除非手动停止,否则总是重启

健康检查(Healthcheck)

配置容器的健康检查:

1
2
3
4
5
6
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s

禁用健康检查:

1
2
healthcheck:
disable: true

日志配置(Logging)

配置容器的日志驱动:

1
2
3
4
5
logging:
driver: json-file
options:
max-size: "200k"
max-file: "10"

用户(User)

指定运行容器的用户:

1
user: postgres

或者使用UID:GID格式:

1
user: "1000:1000"

网络(Network)

网络是用于连接服务之间的通信通道。Docker Compose会自动创建一个默认网络,但也可以定义自定义网络。

自定义网络

1
2
3
networks:
mynetwork:
driver: bridge

服务连接网络

1
2
3
4
5
6
7
8
9
10
11
12
13
services:
web:
image: nginx:latest
networks:
- mynetwork
db:
image: mysql:latest
networks:
- mynetwork

networks:
mynetwork:
driver: bridge

服务指定网络配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
services:
web:
image: nginx:latest
networks:
mynetwork:
ipv4_address: 172.16.238.10
ports:
- "80:80"
db:
image: mysql:latest
networks:
mynetwork:
ipv4_address: 172.16.238.11
environment:
- "MYSQL_ROOT_PASSWORD=secret"

networks:
mynetwork:
driver: bridge
ipam:
config:
- subnet: 172.16.238.0/24

卷(Volume)

卷是用于持久化存储数据的容器。Docker Compose会自动创建一个默认卷,但也可以定义自定义卷。

自定义卷

1
2
3
volumes:
myvolume:
driver: local

服务指定卷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
web:
image: nginx:latest
volumes:
- myvolume:/var/www/html
db:
image: mysql:latest
volumes:
- dbdata:/var/lib/mysql

volumes:
myvolume:
driver: local
dbdata:
driver: local

挂载主机目录

1
2
3
4
5
6
services:
web:
image: nginx:latest
volumes:
- ./html:/var/www/html
- ./nginx/conf.d:/etc/nginx/conf.d

项目(Project)

项目是一个包含多个服务的Docker Compose应用程序。每个项目都有自己的配置文件(docker-compose.yml),用于定义服务、网络和卷。

常用命令

启动项目

1
docker-compose up

使用-d参数可以在后台运行:

1
docker-compose up -d

停止项目

1
docker-compose down

添加--volumes参数可以同时删除卷:

1
docker-compose down --volumes

查看项目服务

1
docker-compose ps

查看项目日志

1
docker-compose logs

使用-f参数可以持续查看日志:

1
docker-compose logs -f

重启项目服务

1
docker-compose restart

停止项目服务

1
docker-compose stop

启动项目服务

1
docker-compose start

查看项目网络

1
docker-compose network ls

查看项目卷

1
docker-compose volume ls

执行容器命令

1
docker-compose exec web bash

构建或重建服务

1
docker-compose build

拉取服务镜像

1
docker-compose pull

推送服务镜像

1
docker-compose push

查看配置

1
docker-compose config

验证配置

1
docker-compose config --quiet

查看容器运行的进程

1
docker-compose top

暂停服务

1
docker-compose pause

恢复服务

1
docker-compose unpause

强制停止服务

1
docker-compose kill

实际应用场景

Web应用 + 数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
version: '3'

services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- app

app:
build: ./app
environment:
- DB_HOST=db
- DB_USER=root
- DB_PASSWORD=example
- DB_NAME=myapp
depends_on:
- db

db:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=example
- MYSQL_DATABASE=myapp
volumes:
- db_data:/var/lib/mysql

volumes:
db_data:

微服务架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
version: '3'

services:
gateway:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
depends_on:
- service1
- service2

service1:
build: ./service1
environment:
- DB_HOST=db
depends_on:
- db
- redis

service2:
build: ./service2
environment:
- DB_HOST=db
depends_on:
- db
- redis

db:
image: postgres:13
environment:
- POSTGRES_PASSWORD=example
- POSTGRES_DB=microservices
volumes:
- db_data:/var/lib/postgresql/data

redis:
image: redis:6
volumes:
- redis_data:/data

volumes:
db_data:
redis_data:

前端 + 后端 + 数据库的完整应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
version: '3'

services:
# 前端服务
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
environment:
- API_URL=http://backend:3000

# 后端API服务
backend:
build: ./backend
ports:
- "3000:3000"
depends_on:
- db
- redis
environment:
- DB_HOST=db
- DB_USER=postgres
- DB_PASSWORD=example
- DB_NAME=app
- REDIS_HOST=redis
volumes:
- ./backend:/app
- /app/node_modules

# 数据库服务
db:
image: postgres:13
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=example
- POSTGRES_DB=app
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"

# Redis缓存服务
redis:
image: redis:6
volumes:
- redis_data:/data
ports:
- "6379:6379"

volumes:
postgres_data:
redis_data:

CI/CD流水线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
version: '3'

services:
# Jenkins CI/CD服务
jenkins:
image: jenkins/jenkins:lts
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false

# SonarQube代码质量分析
sonarqube:
image: sonarqube:latest
ports:
- "9000:9000"
environment:
- SONARQUBE_JDBC_URL=jdbc:postgresql://sonardb:5432/sonar
- SONARQUBE_JDBC_USERNAME=sonar
- SONARQUBE_JDBC_PASSWORD=sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
depends_on:
- sonardb

# SonarQube数据库
sonardb:
image: postgres:12
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
- POSTGRES_DB=sonar
volumes:
- sonardb_data:/var/lib/postgresql/data

# Nexus制品库
nexus:
image: sonatype/nexus3
ports:
- "8081:8081"
volumes:
- nexus_data:/nexus-data

volumes:
jenkins_home:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
sonardb_data:
nexus_data:

开发环境配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
version: '3'

services:
# 开发环境的应用服务
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true # 在Docker中启用热重载
command: npm run dev

# 测试服务
test:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/node_modules
command: npm run test
environment:
- NODE_ENV=test

# 数据库服务
db:
image: postgres:13
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=dev
ports:
- "5432:5432"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d # 初始化脚本

# 数据库管理工具
adminer:
image: adminer
ports:
- "8080:8080"
depends_on:
- db

volumes:
postgres_dev_data:

监控系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
version: '3'

services:
# Prometheus监控服务
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'

# Grafana可视化服务
grafana:
image: grafana/grafana
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
depends_on:
- prometheus

# Node Exporter服务器指标收集
node_exporter:
image: prom/node-exporter
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'

# cAdvisor容器指标收集
cadvisor:
image: gcr.io/cadvisor/cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro

volumes:
prometheus_data:
grafana_data:

常见问题与解决方案

容器间通信问题

问题:服务无法通过服务名访问其他服务。

解决方案

  • 确保服务在同一网络中
  • 使用服务名作为主机名进行访问
  • 检查网络配置是否正确

卷挂载权限问题

问题:容器无法写入挂载的卷。

解决方案

  • 检查宿主机目录的权限
  • 在容器中使用适当的用户运行应用
  • 使用命名卷而不是绑定挂载
1
2
3
4
5
6
7
8
9
services:
app:
image: myapp
user: "1000:1000" # 使用特定UID:GID运行
volumes:
- myvolume:/app/data

volumes:
myvolume:

服务启动顺序问题

问题:依赖的服务尚未准备好,导致应用启动失败。

解决方案

  • 使用depends_on指定依赖关系
  • 实现健康检查
  • 在应用中实现重试逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
services:
app:
image: myapp
depends_on:
db:
condition: service_healthy

db:
image: mysql:5.7
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 5s
retries: 5

使用环境变量

使用.env文件和环境变量来配置服务,避免在配置文件中硬编码敏感信息:

1
2
3
4
5
6
services:
db:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_NAME}

分离开发和生产环境

使用多个Compose文件来分离开发和生产环境的配置:

  • docker-compose.yml:基本配置
  • docker-compose.override.yml:开发环境特定配置
  • docker-compose.prod.yml:生产环境特定配置
1
2
3
4
5
# 开发环境
docker-compose up

# 生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

使用命名卷

使用命名卷而不是绑定挂载来持久化数据,这样可以更好地管理数据:

1
2
3
4
5
6
7
8
services:
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data

volumes:
postgres_data:

限制资源使用

在生产环境中,限制容器的资源使用:

1
2
3
4
5
6
7
8
9
10
11
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M

使用多阶段构建

使用Docker的多阶段构建来创建更小、更安全的镜像:

1
2
3
4
5
services:
app:
build:
context: ./app
dockerfile: Dockerfile.multistage

对应的Dockerfile.multistage示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 构建阶段
FROM node:14 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 运行阶段
FROM node:14-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm install --production
CMD ["node", "dist/index.js"]

使用健康检查

为服务配置健康检查,确保服务正常运行:

1
2
3
4
5
6
7
8
9
services:
web:
image: nginx:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

使用配置文件

将配置文件挂载到容器中,而不是将配置硬编码到镜像中:

1
2
3
4
5
services:
app:
image: myapp
volumes:
- ./config:/app/config

使用Docker Compose覆盖文件

使用多个Docker Compose文件来覆盖配置,适应不同环境:

1
2
3
4
5
# 基本配置 + 开发环境配置
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up

# 基本配置 + 生产环境配置
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

安全最佳实践

不使用root用户

在容器中避免使用root用户运行应用:

1
2
3
4
services:
app:
image: myapp
user: "1000:1000"

限制容器权限

限制容器的权限,只授予必要的权限:

1
2
3
4
5
6
7
services:
app:
image: myapp
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE

使用只读文件系统

将容器的文件系统设置为只读,只有特定目录可写:

1
2
3
4
5
6
7
8
9
services:
app:
image: myapp
read_only: true
tmpfs:
- /tmp
- /var/run
volumes:
- data:/app/data

高级网络配置

使用外部网络

使用已存在的外部网络:

1
2
3
4
5
6
7
8
9
10
services:
app:
image: myapp
networks:
- external_network

networks:
external_network:
external: true
name: existing_network

配置网络别名

为服务配置网络别名:

1
2
3
4
5
6
7
8
services:
app:
image: myapp
networks:
frontend:
aliases:
- webapp
- app.local

扩展性考虑

使用服务扩展

使用docker-compose up --scale命令扩展服务:

1
docker-compose up --scale web=3 --scale worker=2

对应的配置:

1
2
3
4
5
6
7
8
services:
web:
image: nginx:latest
ports:
- "8080-8082:80" # 动态端口映射

worker:
image: worker:latest

使用Docker Swarm

对于更大规模的部署,考虑使用Docker Swarm:

1
2
3
4
5
6
7
8
9
10
11
12
version: '3.8'

services:
web:
image: nginx:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure

监控和日志管理

集中式日志管理

配置日志驱动将日志发送到集中式日志系统:

1
2
3
4
5
6
7
8
services:
app:
image: myapp
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "docker.{{.Name}}"

使用Prometheus监控

配置Prometheus监控指标:

1
2
3
4
5
6
7
services:
app:
image: myapp
labels:
- "prometheus.enable=true"
- "prometheus.port=8080"
- "prometheus.path=/metrics"

生产环境部署检查清单

在将Docker Compose应用部署到生产环境之前,请检查以下事项:

  1. 移除开发环境特定的配置
  2. 设置适当的资源限制
  3. 配置健康检查
  4. 实现适当的日志管理
  5. 配置备份策略
  6. 设置监控
  7. 实现安全最佳实践
  8. 使用稳定的镜像标签,避免使用latest
  9. 测试高可用性和故障恢复
  10. 文档化部署和操作流程

总结

Docker Compose是一个强大的工具,它简化了多容器应用的定义和管理。通过使用YAML配置文件,开发人员可以轻松地定义服务、网络和卷,使应用程序的部署和管理更加简单和高效。本文介绍了Docker Compose的基本概念、配置选项、使用方法以及最佳实践,希望能帮助您更好地使用Docker Compose来管理容器化应用。

无论是开发环境还是生产环境,Docker Compose都是容器编排的理想选择,特别是对于中小型应用。对于更大规模的部署,可以考虑使用Kubernetes等更强大的容器编排平台。