version
指令顶级一级指令,指定 compose 指定文件格式版本
version: "3.8"
services:
不同版本支持的功能不同。常用版本有 ‘2’, ‘3’, ‘3.8’ 等。
services
指令顶级一级指令,定义应用程序的各个服务(容器),每个服务对应一个容器。
version: "3.8"
services:
容器1:
各种配置项.....
容器2:
各种配置项.....
常用服务配置项:
image: 指定使用的镜像,如 nginx:alpine
container_name:指定容器名称,如 my_redis
build: 指定构建镜像的 Dockerfile 路径
build
指令用于指定如何构建一个服务所依赖的镜像。你可以通过这个指令来定义构建上下文、Dockerfile 路径以及构建参数等。
主要用途
build:
context: ./dir # Dockerfile 所在目录
# 多环境构建,可选,指定哪个 Dockerfile 进行构建,默认 Docker 会寻找工作目录下的 Dockerfile 文件。
dockerfile: Dockerfile-dev
# 定义构建时变量,对应Dockerfile中的 ARG 指令,允许你在构建时传递构建参数
args:
buildno: 1
# 列出可以作为缓存来源的镜像列表,加速构建过程。
cache_from:
- user/app:v1
# 在镜像上添加元数据标签。
labels:
- "com.example.description=Accounting webapp"
# 设置 /dev/shm 的大小,对于某些应用可能需要更大的共享内存空间。
shm_size: '2gb'
# 资源限制
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
# 可选,多阶段构建目标阶段,用于多阶段构建,指定构建目标阶段,可显著减小最终镜像大小(多层构建,可以指定构建哪一层)
target: production
# 是否禁用缓存
no_cache: false
# 安全构建
security_opt:
- seccomp=unconfined
extra_hosts:
- "somehost:162.242.195.82"
多阶段构建优化
# Dockerfile
FROM node:14 as builder
WORKDIR /app
COPY . .
RUN npm install && npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
# docker-compose.yml
services:
web:
build:
context: .
target: builder # 只构建builder阶段
构建控制命令
# 构建但不启动:
docker-compose build
# 强制重建:
docker-compose build --no-cache
# 构建特定服务:
docker-compose build webapp
# 查看构建参数:
docker-compose config --services
最佳实践
上下文优化:
.dockerignore
文件减少上下文大小node_modules
等大目录构建缓存利用:
build:
cache_from:
- myapp:latest
多阶段构建:
参数化构建:
build:
args:
- VERSION=${TAG:-latest}
如何查看构建过程输出?
docker-compose build --no-cache --progress=plain
docker-compose build
本质上是调用 docker build
,但提供了更便捷的项目级构建管理。
restart: 重启策略
restart: always # no, always, on-failure, unless-stopped
ports: 端口映射,格式 HOST:CONTAINER
ports:
- "80:80" # 映射所有接口的80端口
- "127.0.0.1:8000:8000" # 只映射本地回环地址
volumes
卷挂载。
volumes:
- /var/lib/mysql # 1、匿名卷
- ./data:/var/lib/mysql # 2、主机路径挂载
- db-data:/var/lib/mysql # 3、命名卷
实际当中,选择上述3种的1种即可
重要-注意:
截止到文档的这个位置,上述介绍的指令,除了
build
指令外,其它指令是编写docker-compose.yml 文件必写的指令。如果使用别人制作好的镜像,我们就不需要
build
指令。下面罗列常用可选的指令。
常用服务可选配置项:
environment: 环境变量
environment:
RACK_ENV: development
SHOW: 'true'
或使用文件:
env_file:
- .env
Tips:
简单近似的理解为定义容器性全局的变量,对 Java 来说,可以使用
system.getProperty("pname")
来获取;对于 Python 来说,可以使用os.getenv("pname")
来获取;对于 MySql 来说,可以设置初始化数据库的 root 密码等等之类的。具体使用见总结的environment部分
。
depends_on: 服务依赖
depends_on:
- db
- redis
其作用就是,等待 db 和 redis 容器服务启动之后。关闭时,先关闭主服务,再关闭 db 和 redis 服务。
networks: 加入的网络
networks:
- frontend
- backend
其作用就是,使用我们自己预先定义好的网络。
command: 覆盖默认命令
command: bundle exec thin -p 3000
**devices:**指定设备映射列表。
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
**dns:**自定义 DNS 服务器,可以是单个值或列表的多个值。
dns: 8.8.8.8
dns:
- 8.8.8.8
- 9.9.9.9
**dns_search:**自定义 DNS 搜索域。可以是单个值或列表。
dns_search: example.com
dns_search:
- dc1.example.com
- dc2.example.com
privileged: 覆盖默认命令
在Docker Compose配置文件中,privileged
是一个布尔类型的选项,用于给容器提供扩展权限。当设置为true
时,这个选项允许容器获得几乎所有的能力(capabilities),并且禁用了安全机制如AppArmor、SELinux以及seccomp的限制。简单来说,它使得容器内的进程拥有类似宿主机上运行的进程的权限。
作用
使用示例
version: '3.8'
services:
myservice:
image: myimage
privileged: true
特权模式的作用
services:
# 1. 允许访问所有设备
docker-in-docker:
image: docker:dind
privileged: true # 必须启用才能运行Docker-in-Docker
# 2. 允许修改内核参数
network-tool:
image: network-tools
privileged: true # 需要修改网络栈参数
# 3. 允许加载内核模块
device-driver:
image: custom-driver
privileged: true # 需要加载内核模块
安全风险警告:
特权模式会带来严重安全风险:
- 容器逃逸:攻击者可能从容器获取宿主机root权限
- 系统破坏:恶意容器可能破坏宿主机系统
- 数据泄露:可以访问宿主机的所有数据
替代方案(更安全的选择)
services:
# 1. 精细控制能力
device-access:
image: device-tool
cap_add:
- SYS_ADMIN # 只添加需要的权限
- NET_ADMIN
- ALL # 开启全部权限
cap_drop:
- SYS_PTRACE # 关闭 ptrace权限
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0" # 只挂载需要的设备
# 2. 特定设备访问
gpio-service:
image: gpio-control
devices:
- "/dev/gpiomem:/dev/gpiomem"
实际应用场景
services:
# 1. 必须使用特权模式的场景
kubelet:
image: kubelet
privileged: true # Kubernetes组件需要
# 最佳实践建议
# 审计特权容器
docker ps --filter 'label=privileged=true'
# 结合用户命名空间
userns_mode: "host" # 与privileged一起使用更安全
特权模式的技术细节
当 privileged: true
时,容器会:
/dev/mem
)检查容器权限:
# 检查容器是否特权模式运行
docker inspect --format='{{.HostConfig.Privileged}}' <container>
# 查看授予的capabilities
docker inspect --format='{{.HostConfig.CapAdd}}' <container>
企业级安全建议:
- 集群部署时:使用PodSecurityPolicy/OPA Gatekeeper禁止特权容器
- CI/CD管道中:添加特权模式检查步骤
- 监控方面:对特权容器进行特别审计
- 替代架构:考虑使用无特权容器设计
stdin_open:控制标准输入(STDIN)行为的配置项
stdin_open
是一个在 Docker Compose 文件中用于服务配置的选项,它保持容器的标准输入(stdin)打开。默认情况下,Docker 容器启动时并不会保持标准输入打开,这意味着一旦启动命令执行完毕,如果没有其他进程继续保持容器运行,容器将会退出。
通过设置 stdin_open: true
,你可以让容器的标准输入保持打开状态,这通常与 -i
参数在 docker run
命令中的作用相似,允许你以交互模式与容器进行通信。
使用场景
示例
下面是一个简单的例子展示了如何在 Docker Compose 文件中使用 stdin_open
:
version: '3.8'
services:
myservice:
image: ubuntu
stdin_open: true # 保持标准输入打开
tty: true # 分配一个伪TTY,通常与stdin_open一起使用,提供交互界面
command: /bin/bash
在这个例子中,我们使用了 Ubuntu 镜像,并且通过设置 stdin_open: true
和 tty: true
来保持标准输入打开并分配一个伪TTY。这样做的结果是当你运行这个 Compose 文件时,它会启动一个可以交互的 Bash shell,让你能够直接与容器内部进行交互。
核心作用
services:
# 1. 启用交互式会话
python:
image: python:3
stdin_open: true # 允许输入Python代码,例如:docker-compose run python python3(进入交互式Python解释器)
tty: true
# 2. 保持后台容器的输入能力
shell:
image: alpine
stdin_open: true
command: /bin/sh # 保持shell运行
与 tty
的区别
配置项 | 作用 | 典型使用场景 |
---|---|---|
stdin_open: true |
保持输入流开放 | 需要向容器发送输入 |
tty: true |
分配伪终端 | 需要终端特性(如clear、Ctrl+C) |
请注意,虽然 stdin_open
可以为你的容器提供更多的交互性,但在生产环境中通常不需要这样做,因为大多数服务应用并不需要直接的用户交互。因此,该选项更多地适用于开发、测试或需要直接与容器交互的场景。
实际应用场景
services:
# 1. 开发环境调试
dev:
build: .
stdin_open: true # 允许输入测试数据
tty: true
# 2. 数据库管理控制台
mysql-client:
image: mysql
command: mysql -uroot -p
stdin_open: true # 允许输入密码
# 3. 交互式教学环境
tutorial:
image: learnpython
stdin_open: true # 允许学生输入代码
tty: true
## 最佳实践
# 1、生产环境应禁用(除非明确需要):stdin_open: false(显式关闭更安全)
# 2、开发环境按需启用:stdin_open: ${ENABLE_INTERACTIVE:-false}(通过环境变量控制)
# 3、结合 docker-compose run 使用:
docker-compose run --service-ports web bash # 交互式访问
与 docker exec -it
的区别?
stdin_open
是创建时的配置exec -it
是后期附加到运行中容器tty:虚拟终端
设置为 true
时,它会为容器分配一个虚拟终端(TTY)。这个选项通常与 stdin_open: true
一起使用,以便提供交互式的 shell 或命令行界面。
类似于 Dockers 如下命令:
docker exec -it <container> sh # 后期附加终端
security_opt:安全配置
security_opt
是 Docker Compose 中用于配置容器安全选项的重要参数,它允许你精细控制容器的安全设置,特别是与 Linux 安全模块(如 SELinux 和 AppArmor)的交互方式。
security_opt
允许你:设置 SELinux 标签、配置 AppArmor 策略、调整容器能力(capabilities)、禁用默认的安全配置
基础语法
services:
secured-service:
image: nginx
security_opt:
- label:user:USER # SELinux用户标签
- label:role:ROLE # SELinux角色标签
- label:type:TYPE # SELinux类型标签
- label:level:LEVEL # SELinux级别标签
- apparmor=PROFILE # AppArmor配置
- no-new-privileges # 禁止特权升级
- seccomp=PROFILE.json # seccomp配置
核心安全选项
# 1. SELinux 标签控制
security_opt:
- label:user:system_u
- label:role:object_r
- label:type:svirt_lxc_net_t
- label:level:s0:c100,c200
**Tips: **
可以理解为设置 Linux 系统的SELinux 参数;
SELinux 选项:
label:user
设置用户标签 system_u
label:role
设置角色标签 object_r
label:type
设置类型标签 svirt_lxc_net_t
label:level
设置MLS/MCS级别 s0:c100,c200
links:安全配置
links
字段用于定义服务之间的链接。通过这种方式,可以允许一个服务连接到另一个服务,并且能够使用服务名称作为主机名来访问被连接的服务。这个概念源自于较早版本的 Docker 网络模型,它创建了容器间的依赖关系,并提供了服务发现机制。
links
在传统 Docker 部署中曾非常重要,但在现代 Docker 网络中,建议优先考虑使用自定义网络实现服务发现和连接。
作用
links
,你可以使用被链接服务的名字作为主机名来访问该服务。这对于那些需要明确知道如何连接到其他服务的应用来说非常有用。links
可以用来更精细地控制这种可见性,尤其是在使用默认桥接网络时。注意事项
在现代 Docker 使用中,推荐的方式是通过定义自定义网络来实现服务间通信,而不是使用 links
。这是因为自定义网络提供了更加灵活的服务发现机制,并且支持更为复杂的网络配置,同时避免了使用 links
带来的某些限制和复杂性。
自 Docker 网络功能增强以来,links
已经不再推荐用于新的项目中。自定义网络不仅简化了多服务应用的网络配置,还提高了其可维护性和扩展性。
推荐使用自定义网络,了解即可,老的项目中可能还在使用。
volumes
指令在 Docker Compose 文件中,volumes
是一个非常重要的配置项,它用于管理容器与主机之间的数据持久化和共享。
基本概念
volumes (卷) 在 Docker 中有两个主要用途:
三种主要类型
在 docker-compose.yml 中,volumes 可以分为三种类型:
1. 匿名卷 (Anonymous volumes)
volumes:
- /var/lib/mysql
docker volume prune
清理Tips:
也就是说,我只是在
容器里内
声明了一个数据卷而已,对应宿主机的位置,由宿主机的docker
自己管理,一般是在 宿主机的
/var/lib/docker/volumes/
这个地方,/_data 且名字是一串随机哈希(如
3a9f8e8b...
),所以不易查找和管理。
2. 命名卷 (Named volumes)
在文件顶部需要先声明:
volumes:
db-data:
然后才可以在容器服务中使用:
version "3.8"
service:
mysql:
image: mysql:latest
volumes:
- db-data:
/var/lib/docker/volumes/
目录下Tips:
可以近似的简单的理解为和
匿名卷
其实是一样的,也是由宿主机的docker
自己管理,也是在宿主机的
/var/lib/docker/volumes/
这个地方,只不过/_data 匿名卷
的名称是一串 随机哈希值 ,而 命名卷 是我们在文件中申明的名称而已,所以易于查找和管理。
3. 主机绑定挂载 (Host bind mounts)
就是直接绑定 宿主机的目录
和 容器里目录
的映射关系。
version "3.8"
service:
mysql:
image: mysql:latest
volumes:
- ./data:/var/lib/mysql
高级配置选项
只读挂载
volumes:
- ./config:/etc/config:ro
添加 :ro
表示容器只能读取不能修改
自定义卷驱动
volumes:
db-data:
driver: local
driver_opts:
type: 'nfs'
o: 'addr=10.40.0.199,nolock,soft,rw'
device: ':/path/to/dir'
卷标签
volumes:
db-data:
labels:
- "com.example.description=Database volume"
networks
指令networks
指令用于定义和管理服务之间的网络连接。通过使用 networks
,你可以创建自定义的网络来控制哪些服务可以相互通信以及如何通信。这提供了比默认网络配置更高级的网络管理功能,使得容器间的互联更加灵活、安全。
作用
核心网络类型
网络类型 | 特点 | 适用场景 |
---|---|---|
bridge (默认) | 单主机虚拟网络,自动DNS解析 | 开发环境,单机部署 |
overlay | 多主机网络,支持集群 | Swarm集群部署 |
host | 直接使用主机网络栈 | 高性能需求,端口冲突避免 |
macvlan | 为容器分配MAC地址 | 需要直接接入物理网络 |
none | 完全禁用网络 | 特殊安全需求 |
基本用法
如何定义和使用自定义网络:
version: '3.8'
services:
web:
image: example/web:latest
networks:
- frontend
- backend
admin:
image: example/admin:latest
networks:
- backend
networks:
frontend: # 网络名称
driver: bridge # 网络驱动类型
driver_opts: # 驱动选项
com.docker.network.bridge.name: my-bridge
attachable: true # 是否允许其他容器附加
internal: false # 是否内部网络(无外部访问)
labels: # 元数据
- "com.example.description=Frontend network"
backend:
driver: bridge
说明:定义了两个网络:frontend
和 backend
。web
服务同时连接到这两个网络,而 admin
服务仅连接到 backend
网络。这意味着 web
服务可以通过网络名称作为主机名与 admin
服务进行通信,但 admin
服务不能直接与 web
服务通信(除非也加入 frontend
网络)。
高级用法
指定静态IP地址:在某些情况下,你可能希望为服务分配一个静态IP地址。虽然这不是所有网络驱动都支持的功能,但在某些特定环境下是可行的。
networks:
app_net1:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
app_net2:
ipam:
driver: default
config:
- subnet: 172.20.0.0/24
gateway: 172.20.0.1
ip_range: 172.20.0.128/25
services:
app:
image: example/app:latest
# 服务特定配置
networks:
app_net1:
ipv4_address: 172.20.1.10
aliases: # 额外域名别名
- database
- mysql
外部网络:如果你想要连接到已经存在的 Docker 网络,可以将网络标记为外部。
# 这表示该服务将会连接到名为 my-pre-existing-network 的现有 Docker 网络,而不是创建一个新的网络。
networks:
default:
external:
name: my-pre-existing-network # 使用已存在的网络
别名:你还可以给服务在网络中的名字设置别名,以便于其他服务更容易地找到它。
services:
some-service:
networks:
some-network:
aliases:
- alias1
- alias3
other-network:
aliases:
- alias2
在这个例子中,some-service
在 some-network
网络中有两个别名 alias1
和 alias3
,而在 other-network
中有一个别名 alias2
。
实际应用场景
1. 多网络隔离
services:
web:
networks:
- frontend
db:
networks:
- backend
networks:
frontend:
backend:
2. 微服务架构
services:
gateway:
networks:
- ingress
service1:
networks:
- ingress
- service_net
service2:
networks:
- service_net
networks:
ingress:
service_net:
3. 生产环境配置
networks:
app_net:
driver: overlay
attachable: true
ipam:
config:
- subnet: 10.0.1.0/24
网络连接原理
DNS解析:
端口访问:
ports
映射连接隔离:
networks:
secure_net:
internal: true # 禁止外部访问
操作命令
# 1、查看网络:
docker network ls
docker network inspect <network_name>
# 2、连接/断开容器:
docker network connect <network> <container>
docker network disconnect <network> <container>
# 3、清理未用网络:
docker network prune
最佳实践
# 1、环境隔离:
networks:
dev_net:
prod_net:
# 2、安全分层:
services:
api:
networks:
- api_net
db:
networks:
- db_net
# 3、Swarm模式:
networks:
cluster_net:
driver: overlay
attachable: true
Tips:
1、不定义networks会怎样?
Compose会创建默认的bridge网络,所有服务加入其中(不推荐生产环境使用)
2、如何实现容器与主机通信?
使用
host
网络或通过主机IP访问:services: app: network_mode: host
3、跨Compose项目的网络共享?
networks: shared_net: external: true name: project1_shared_net
4、与Kubernetes网络的对比?
Docker网络更简单直接,K8s网络插件(如Calico、Flannel)提供更复杂的策略控制。
总结
通过合理利用 networks
指令,不仅可以更好地组织和隔离应用的不同部分,还能增强安全性并简化服务发现过程。这对于构建复杂的应用架构尤其重要,因为它提供了一种有效的方式来管理和优化容器间的网络交互。
高级配置
在Docker Compose配置文件中,healthcheck
指令用于定义容器的健康检查机制。这个机制允许你检查你的服务容器是否已经准备好接受流量,即容器中的应用程序是否正在按预期运行,可以自动检测容器内的应用是否真正"就绪"而不仅仅是容器进程是否运行。通过这种方式,可以确保服务之间的依赖关系只有在相关服务完全准备好之后才进行连接,从而避免了服务启动顺序带来的问题。
健康检查通过定期执行命令来:
restart
策略配合可实现故障自愈基础语法
services:
web:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"] # 检查命令
interval: 30s # 检查间隔
timeout: 10s # 命令超时时间
retries: 3 # 失败重试次数
start_period: 5s # 容器启动后的初始化时间(期间不标记失败)
检查方式
1. 命令检查(推荐)
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"] # PostgreSQL检查
# 或
test: ["CMD-SHELL", "mysqladmin ping -uroot -p${MYSQL_ROOT_PASSWORD}"] # MySQL
2. 禁用健康检查
healthcheck:
disable: true
3. TCP端口检查
healthcheck:
test: ["TCP", "8080"] # 只检查端口是否监听
实际应用示例
services:
db:
image: mysql:8.0
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -uroot -p$$MYSQL_ROOT_PASSWORD"]
interval: 5s
timeout: 5s
retries: 3
services:
web:
build: .
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
start_period: 10s # 给应用启动时间
健康状态的影响
docker-compose ps
显示状态:Name Command State Ports
-------------------------------------------------------------------
web npm start Up (healthy) 0.0.0.0:3000->3000/tcp
db docker-entrypoint.sh Starting (unhealthy)
services:
app:
depends_on:
db:
condition: service_healthy
高级配置技巧
// Express示例
app.get('/health', (req, res) => {
db.ping() // 检查数据库连接
.then(() => res.status(200).send('OK'))
.catch(() => res.status(503).send('Service Unavailable'))
})
healthcheck:
test: ["CMD-SHELL", "test -f /tmp/ready && curl -f http://localhost"]
docker inspect --format='{{json .State.Health}}' <container>
# 查看健康检查日志?
docker logs <container> 2>&1 | grep healthcheck
# 生产环境建议配置?
healthcheck:
test: ["CMD", "custom-health-check-script.sh"]
interval: 30s
timeout: 15s
retries: 5
start_period: 60s # 给复杂应用足够的启动时间
例子:
version: "3.8"
services:
web:
image: my-web-app
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
Tips:
用的情况应该比较少和简单。
资源限制(resource constraints)是指对服务容器所能够使用的计算资源进行限制。这些资源包括CPU、内存等,通过设置这些限制,可以控制容器的资源使用情况,从而优化性能、避免某个容器过度消耗资源而影响其他容器或主机系统的正常运行。
在Docker Compose文件中,可以通过deploy.resources
字段来指定资源限制和预留值,不过请注意,deploy
字段主要用于Swarm模式下,如果你不使用Swarm集群而是直接使用Docker Compose,则需要确保你的Compose版本支持这些选项,并且正确地应用它们。
非Swarm模式使用resources
而非deploy.resources
。
例如
services:
my_service:
image: my_image
deploy:
resources:
# 可以使用相对单位如1.0代表单个CPU的核心
limits:
memory: 512M # 限制一个服务最多使用512MB的RAM
cpus: '0.5' # 限制一个服务使用不超过0.5个CPU核心
pids: 100 # 最大进程数
devices:
- capabilities: ["gpu"] # GPU限制
# 除了限制以外,还可以设置资源预留,确保容器至少有这么多资源可用。这对于保证关键服务的性能非常有用
reservations:
cpus: '0.2' # 保证至少0.2个CPU核心
memory: 256M # 保证分配的最小内存
上述deploy
字段中的资源管理特性主要针对Docker Swarm环境。如果你仅使用Docker Compose而不打算部署到Swarm集群中,确保你的使用场景与Compose版本相匹配。对于非Swarm环境下的资源限制,可以在创建容器时直接使用resources
字段,但具体的语法和支持可能会有所不同。
实际应用场景
1. 生产环境数据库配置
services:
mysql:
deploy:
resources:
limits:
cpus: '2'
memory: 4G
pids: 500
reservations:
memory: 2G
2. 微服务资源配额
services:
api:
deploy:
resources:
limits:
cpus: '1'
memory: 1G
depends_on:
redis:
condition: service_healthy
redis:
deploy:
resources:
limits:
memory: 500M
资源限制工作原理
cgroups memory subsystem
CFS scheduler
device cgroup controller
查看资源使用情况
# 查看容器资源统计
docker stats
# 详细资源分配信息
docker inspect <container> | grep -i resources -A 20
# 查看cgroup配置
docker exec <container> cat /sys/fs/cgroup/memory/memory.limit_in_bytes
最佳实践建议
# 1、内存限制:始终设置内存限制防止系统崩溃;预留10-20%给系统进程
memory: ${MEM_LIMIT:-500M}
# 2、CPU限制:对计算密集型服务使用小数核心(如0.3);对延迟敏感服务使用cpuset固定CPU核心
cpus: '0.3'
cpuset: '0,1' # 绑定到特定核心
# 3、混合部署:
services:
frontend:
deploy:
resources:
limits:
cpus: '0.2'
memory: 200M
backend:
deploy:
resources:
limits:
cpus: '1'
memory: 1G
扩展字段是 Docker Compose 文件中以 x-
开头的自定义配置项,它们允许你在不破坏 Compose 文件规范的情况下添加自己的元数据或配置。
作用
直接配置
version: '3.8'
services:
web:
image: example/web:latest
x-custom-labels:
- "environment": "production"
- "team": "backend"
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
x-deployment-notes: "This service is critical for production and should always have at least one replica running."
在这个例子中,x-custom-labels
和x-deployment-notes
都是扩展字段。前者可能被某个自定义脚本用于给服务添加特定标签,后者则可能是对部署过程的一些额外说明或注意事项。
引用配置
version: '3.8'
x-common-env: &common-env # 定义扩展字段(锚点)
TZ: Asia/Shanghai
LANG: en_US.UTF-8
services:
web:
image: nginx
environment:
<<: *common-env # 引用扩展字段
NGINX_PORT: 8080
db:
image: postgres
environment:
<<: *common-env
POSTGRES_PASSWORD: example
使用 x-
前缀定义自定义字段:
x-custom:
foo: bar
services:
web:
image: nginx
x-custom: ${x-custom}
实际应用场景
x-network: &network
aliases:
- app
ipv4_address: 172.20.0.100
services:
frontend:
networks:
app_net: *network
networks:
app_net:
driver: bridge
x-env: &env
- .env
- .env.${DEPLOY_ENV:-development}
services:
app:
env_file: *env
x-gitlab:
variables:
DOCKER_DRIVER: overlay2
only:
- master
- tags
services:
runner:
image: gitlab/gitlab-runner
x-myapp-docs: # 文档示例
description: |
此扩展字段用于定义所有服务的公共健康检查配置
会被各服务通过YAML锚点引用
其它
# 1、查看扩展字段?
docker-compose config | grep x-
# 2、Kubernetes 使用 annotations(注解)实现类似功能
metadata:
annotations:
mycompany.com/description: "自定义描述"
在Docker Compose配置文件中,变量与环境替换是一种允许你通过外部变量或环境变量动态地设置配置值的方法。这种方法极大地增强了配置文件的灵活性和可重用性,使得你可以轻松地在不同的环境中(如开发、测试、生产)使用同一份配置文件,只需调整相应的环境变量即可。
变量与环境替换的作用
使用方法
1. 使用环境变量
在docker-compose.yml
文件中,可以通过${VARIABLE}
的形式引用环境变量。例如:
version: '3.8'
services:
db:
image: "postgres:${POSTGRES_VERSION}"
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
# 在这个例子中,POSTGRES_VERSION, POSTGRES_USER, 和 POSTGRES_PASSWORD都是从环境变量中读取的值。
# 严格模式变量
environment:
API_KEY: ${API_KEY?err} # 未设置时报错"err"
2.设置默认值
你还可以为环境变量指定一个默认值,以防它们未被定义。语法如下:${VARIABLE:default_value}
。例如:
version: '3.8'
services:
web:
build: .
ports:
- "${WEB_PORT:80}:${WEB_PORT:80}"
# 如果WEB_PORT环境变量未定义,则会使用默认端口80。
3. 使用.env
文件
通常,环境变量会被放置在一个名为.env
的文件中,该文件应该位于与docker-compose.yml
相同的目录下。Docker Compose会自动加载这个文件中的变量。例如,你的.env
文件可能看起来像这样:
POSTGRES_VERSION=13
POSTGRES_USER=myuser
POSTGRES_PASSWORD=mypassword
然后,在docker-compose.yml
中可以直接引用这些变量,而不需要额外的操作。
高级用法
# 1. 多环境配置
docker-compose --env-file .env.prod up # 命令行指定不同环境文件
# 2、复合变量
environment:
DB_URL: "postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}"
# 3、Shell 命令结果
environment:
BUILD_NUMBER: $(git rev-parse --short HEAD) # 注入Git提交哈希
实际应用示例
# 生产环境配置
# .env.prod 文件:
APP_ENV=production
DB_PASSWORD=secure123!
# docker-compose.yml 文件:
services:
app:
image: myapp:${APP_ENV:-development}
environment:
NODE_ENV: ${APP_ENV}
DATABASE_URL: "mysql://user:${DB_PASSWORD}@db:3306/app"
# 多阶段构建
# docker-compose.yml 文件:
services:
builder:
image: builder:${BUILD_TAG:-latest}
environment:
- NPM_TOKEN=${NPM_TOKEN} # 从主机环境注入
变量加载顺序
Docker Compose 按以下顺序加载变量(后面的覆盖前面的):
.env
文件(自动加载)--env-file
指定的文件environment
部分直接定义的值最佳实践
# 1、敏感信息管理:
# 从保密管理工具注入
export DB_PASSWORD=$(vault read -field=password secret/db)
docker-compose up
# 2、环境文件组织:
/config
.env.dev
.env.test
.env.prod
docker-compose.yml
# 3、文档化变量:
x-env-docs:
required:
- DB_PASSWORD: 数据库密码
optional:
- LOG_LEVEL: 日志级别(default=info)
其它
# 1、查看最终替换后的配置?
docker-compose config
# 2、变量未设置会怎样
${VAR}:空字符串
${VAR:-default}:使用默认值
${VAR?error}:显示错误信息并退出
# 3、如何在容器内访问这些变量?
# 在容器内执行
echo $LOG_LEVEL
服务的日志记录配置。
driver:指定服务容器的日志记录驱动程序,默认值为json-file。有以下三个选项
driver: "json-file"
driver: "syslog"
driver: "none"
指定与服务的部署和运行有关的配置。只在 swarm 模式下才会有用。
version: "3.7"
services:
redis:
image: redis:alpine
deploy:
mode:replicated
replicas: 6
endpoint_mode: dnsrr
labels:
description: "This redis service label"
resources:
limits:
cpus: '0.50'
memory: 50M
reservations:
cpus: '0.25'
memory: 20M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
详细可以参考 官方文档
或者 https://www.runoob.com/docker/docker-compose.html
介绍。
environment
是 Docker Compose 文件中用于设置容器环境变量的配置项,它允许你向容器内部传递配置参数和运行时设置。
这些环境变量可以用来配置服务的行为、连接字符串、认证信息等,它们对容器内的应用程序来说就像是操作系统的环境变量一样。
基本概念
环境变量在容器中的作用:
基本语法
1. 直接键值对形式(推荐)
services:
web:
image: nginx
environment:
NGINX_PORT: 8080
DEBUG: "true"
2. 数组形式
services:
web:
image: nginx
environment:
- NGINX_PORT=8080
- DEBUG=true
高级用法
1. 使用变量扩展
services:
web:
image: nginx
environment:
HOSTNAME: ${HOSTNAME} # 使用宿主机的环境变量
VERSION: ${VERSION:-1.0} # 默认值
2. 从文件加载(适合敏感信息)
services:
db:
image: postgres
env_file:
- .env # 从文件加载环境变量
- db.env
3. 多环境配置
services:
app:
image: myapp
environment:
- NODE_ENV=${APP_ENV:-development}
- API_URL=${API_URL:-http://localhost:3000}
实际应用示例(常用)
1. 数据库配置
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
MYSQL_DATABASE: app_db
MYSQL_USER: app_user
MYSQL_PASSWORD: user_password
Tips:
意思就是,在启动初始化数据库时候,设置 root 的密码,和 创建一个 app_db 数据库,和 app_user 用户及对应的密码。
如果没有持久化数据库,重启容器后,之前的数据库信息会丢失;如果持久化数据库了,则也就是存在相应的文件,无论怎么重启数据库,一开始初始化数据库的信息都还在。
2. 应用配置
services:
app:
build: .
environment:
RAILS_ENV: production
DATABASE_URL: postgres://user:pass@db:5432/app_prod
REDIS_URL: redis://redis:6379/1
这里的 db
是我们定义的容器,因为在 Docker 网络内部,服务名称(mysql)会自动解析为容器的IP地址。
Tips:
这是给我们自己的 app 服务定于环境信息,就是说 可以在 app 项目代码里面使用这些信息。
如果是 python:
# Python 示例 import os db_url = os.getenv("DATABASE_URL") # 读取环境变量 env = os.getenv('DEBUG') # 读取开发环境变量
如果是 javascript:
// Node.js 示例 const dbUrl = process.env.DATABASE_URL; // 读取环境变量 const env = process.env.DEBUG // 读取开发环境变量
3. 微服务配置
services:
auth_service:
image: auth-service
environment:
JWT_SECRET: ${JWT_SECRET}
TOKEN_EXPIRY: 3600
api_gateway:
image: api-gateway
environment:
AUTH_SERVICE_URL: http://auth:3000
LOG_LEVEL: debug
安全最佳实践
1.敏感信息处理:
environment:
DB_PASSWORD: ${DB_PASSWORD} # 从宿主环境读取
然后运行时传入:
DB_PASSWORD=secret docker-compose up
2.使用 secrets(生产环境推荐):
services:
db:
image: postgres
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txt
3.不要硬编码密码:
environment:
DB_PASSWORD: "123456" # 直接写在文件中不安全
调试技巧
查看容器的环境变量:
docker exec -it <container> env
检查变量是否生效:
docker-compose config # 查看最终解析的配置
使用 --env-file
覆盖:
docker-compose --env-file .env.prod up
command
是 Docker Compose 文件中用于覆盖容器默认启动命令的配置项,它允许你自定义容器启动时执行的命令。
基本概念
当你在 Docker Compose 文件中使用 command
时,它会:
CMD
指令ENTRYPOINT
(除非使用 shell 格式)基本用法
1. 字符串格式
services:
web:
image: nginx
command: nginx -g "daemon off;"
2. 列表格式(推荐)
services:
web:
image: nginx
command: ["nginx", "-g", "daemon off;"]
与 Dockerfile 的关系
Dockerfile 指令 | 是否可被覆盖 | 覆盖方式 |
---|---|---|
ENTRYPOINT |
是 | entrypoint 配置项 |
CMD |
是 | command 配置项 |
实际应用场景
1. 修改默认参数
services:
redis:
image: redis
command: redis-server --appendonly yes
2. 运行不同的可执行文件
services:
node:
image: node:14
command: npm start
3. 调试容器
services:
web:
image: myapp
command: sh -c "while true; do echo 'Debugging...'; sleep 5; done"
entrypoint
指令用于覆盖 Dockerfile 中定义的默认入口点(ENTRYPOINT)。它允许你在启动容器时指定一个自定义的命令或脚本作为容器的第一个进程。通过这种方式,你可以改变容器的行为,使其执行不同的任务或初始化步骤。
作用
entrypoint
,你可以让容器运行不同于其设计初衷的应用程序或脚本。CMD
不同,entrypoint
设置的是固定的执行命令,而 CMD
可以被 docker run 命令行参数覆盖。通常,entrypoint
定义了可执行文件,而 CMD
提供该可执行文件的默认参数。使用方法
entrypoint
可以接受两种形式的值:字符串(shell 形式)或数组(exec 形式)。推荐使用数组形式,因为它避免了 shell 的额外处理(如变量替换等),提供了更精确的控制。
1. 字符串格式(自动包装为 /bin/sh -c
)
services:
myapp:
entrypoint: /app/start.sh --debug
entrypoint: "/bin/echo Hello, world"
2. 列表格式(推荐,直接执行)
services:
web:
build: .
entrypoint: /docker-entrypoint.sh # 初始化脚本
command: ["nginx", "-g", "daemon off;"]
redis:
image: redis
entrypoint: ["redis-server", "--appendonly", "yes"] # 覆盖默认入口点
myapp:
entrypoint:
- /app/start.sh
- "--debug"
entrypoint: ["/bin/echo", "Hello, world"]
debug:
image: node
entrypoint: ["sleep"] # 调试容器
command: ["infinity"] # 保持容器运行
3.结合 CMD 使用
entrypoint 和 cmd 经常一起使用。entrypoint 定义了要执行的命令,而 cmd 提供了该命令的默认参数。如果只定义了 entrypoint 而没有 cmd,则 entrypoint 后面不需要参数;如果有 cmd,则它的值会被作为 entrypoint 命令的参数。
version: '3.8'
services:
myservice:
image: myimage
entrypoint: ["/bin/echo"]
cmd: ["Hello, world"]
这里,容器启动时将执行 /bin/echo "Hello, world"
。
实际应用案例
设你有一个基于 Nginx 的服务,并希望在容器启动前进行一些检查或配置调整,可以通过 entrypoint
来实现:
version: '3.8'
services:
web:
image: nginx
entrypoint: ["/docker-entrypoint.sh"]
cmd: ["nginx", "-g", "daemon off;"]
在这个例子中,/docker-entrypoint.sh
是一个自定义脚本,它可能包含了对环境变量的检查、日志清理等操作,之后再调用 nginx -g 'daemon off;'
来启动 Nginx 服务器。
高级用法
1. 多命令组合
entrypoint:
- sh
- -c
- |
echo "Initializing..."
exec "$@"
- placeholder # 会被command替换
command: ["npm", "start"]
2. 环境变量扩展
entrypoint:
- sh
- -c
- 'exec $$0 $$@' # 使用shell变量
- /app/main.sh
3. 与用户权限结合
entrypoint:
- gosu
- "1000:1000"
- "/app/start.sh"
调试技巧
# 1、查看最终命令:
docker inspect --format='{{json .Config.Entrypoint}}' <container>
# 2、临时覆盖入口点:
docker-compose run --entrypoint bash myapp
# 3、日志检查:
docker-compose logs --tail=100 myapp
最佳实践
1、生产环境推荐:
entrypoint:
- /docker-entrypoint.sh # 封装初始化逻辑
command:
- nginx
- -g
- daemon off;
2、开发环境调试:
entrypoint: ["sleep", "infinity"] # 保持容器运行
3、多阶段初始化:
# Dockerfile
COPY entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
# docker-compose.yml
command: ["--debug"] # 作为参数传给entrypoint.sh
Tips:
YAML 多行字符串的标记符号的使用。
entrypoint:
- sh
- -c
- |
echo "Initializing..."
exec "$@"
- placeholder
这里的 |
表示:
对比其他 YAML 字符串标记
符号 | 名称 | 特点 | 适用场景 | |
---|---|---|---|---|
` | ` | Literal Block | 保留所有换行和缩进 | 脚本、配置文件 |
> |
Folded Block | 折叠换行为空格 | 长段落文本 | |
` | -` | Literal+Strip | 保留换行但删除末尾空行 | 严格控制的脚本 |
>- |
Folded+Strip | 折叠换行并删除末尾空行 | 紧凑文本 |
实际应用示例
1. 多行脚本(推荐用 |
)
command: |
/bin/sh -c '
echo "Starting app..."
exec node server.js
'
2. 长命令折叠(用 >
)
description: >
This is a very long description
that will be folded into a single
line when parsed.
3. 严格控制的脚本(用 |-
)
entrypoint: |-
#!/bin/sh
set -e
exec "$@"
注意
# 1、缩进规则:
- | # 正确缩进
Line 1
Line 2
- | # 错误!缩进不足
Line 1
Line 2
# 2、特殊字符转义:
- |
echo "Hello \"World\"" # 需要转义引号
# 3、与 JSON 的兼容性:
// 等效JSON表示
{"command": "line1\nline2\n"}
调试技巧
# 1、验证 YAML 解析:
python -c 'import yaml, sys; print(yaml.safe_load(sys.stdin))' < docker-compose.yml
# 2、查看最终命令:
docker-compose config | grep -A 5 'entrypoint:'
command: [
'mysqld', # 主命令:启动MySQL服务器
'--innodb-buffer-pool-size=80M', # 设置InnoDB缓冲池大小为80MB
'--character-set-server=utf8mb4', # 设置默认字符集为utf8mb4
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci', # 设置默认排序规则
'--collation-server=utf8mb4_general_ci',
'--default-time-zone=+8:00', # 设置默认时区为东八区(北京时间)
'--explicit_defaults_for_timestamp=true',
'--lower-case-table-names=1', # 设置表名大小写不敏感(值为1)
# 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配)
'--default-authentication-plugin=mysql_native_password'
]
# 注意:上面设置,根据情况选择,一定不要全部写上
各参数详细说明:
mysqld
MySQL 服务器的可执行文件名,启动 MySQL 服务的主命令
--innodb-buffer-pool-size=80M
设置 InnoDB 存储引擎的缓冲池大小为 80MB;这是 MySQL 最重要的性能参数之一,影响缓存表数据和索引的内存大小;适用于内存受限的环境(如小规格容器);通常建议设为可用内存的50-70%;
--character-set-server=utf8mb4
设置服务器默认字符集为 utf8mb4,支持完整的 Unicode 字符(包括emoji表情),替代旧的 utf8 编码(只支持最多3字节字符)
--collation-server=utf8mb4_unicode_ci
设置默认排序规则为 utf8mb4_unicode_ci;影响字符串比较和排序行为;ci
表示大小写不敏感(case insensitive)
--default-time-zone=+8:00
设置数据库默认时区为 UTC+8(北京时间),影响 TIMESTAMP 类型字段的显示和存储,确保应用和数据库时区一致
--lower-case-table-names=1
设置表名大小写不敏感(值为1);Linux系统上默认区分大小写,设为1使行为与Windows一致,避免因大小写问题导致的查询错误
Linux 上我们可以从 Github 上下载它的二进制包来使用,最新发行的版本地址:https://github.com/docker/compose/releases。
直接下载需要的版本即可,比如:docker-compose-linux-x86_64
然后修改名称:mv docker-compose-linux-x86_64 docker-compose
我们放在 /usr/local/bin 目录中。
将可执行权限应用于二进制文件:chmod +x /usr/local/bin/docker-compose
创建软链:ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
测试是否安装成功:
$ docker-compose version
docker-compose version 1.24.1, build 4667896b
version: "3.8"
services:
java-api-server:
container_name: my-java-server-name
image: openjdk:8-jdk
restart: always
privileged: true
ports:
- "8080:8080"
volumes:
- ./app:/home/data # 工作目录,这里是存放我的jar包
- ./files:/home/file # 附件目录,上传我的附件目录
working_dir: /home/data
depends_on:
- redis
environment:
TZ: Asia/Shanghai
command: java -jar app.jar
version: "3.8"
services:
redis:
container_name: redis
image: redis:7.0.0
restart: always
privileged: true
ports:
- "6379:6379"
volumes:
- ./redis:/data
command: redis-server redis.conf
# 工作目录和数据目录默认都是 /data
# 此处没写工作目录,默认的工作目录是/data,所以上述的 redis.conf 也是在 /data 目录下。
redis.conf
配置文件:
# 配置密码
requirepass 123456
# 控制当后台持久化(BGSAVE)失败时 Redis 是否拒绝写入操作,默认yes,拒绝写入直到下一次成功备份
stop-writes-on-bgsave-error no
version: "3.8"
services:
mysql:
container_name: mysql
image: mysql:8.0.32
restart: always
privileged: true
ports:
- "3306:3306"
volumes:
- ./mysql/conf:/etc/mysql/conf.d
- ./mysql/logs:/var/log
- ./mysql/data:/var/lib/mysql
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: xxxxx
./mysql/conf/my.conf
文件参考:
[client]
user="root"
password="123456"
port=3306
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4 # 设置mysql客户端默认字符集
[mysqld]
port=3306
max_connections=1000
max_connect_errors=10 # 允许连接失败的次数。这是为了防止有人从该主机试图攻击数据库系统
max_allowed_packet=512M # 允许最大数据包大小
default-storage-engine=INNODB # 创建新表时将使用的默认存储引擎
interactive_timeout=1800 # MySQL连接闲置超过一定时间后(单位:秒)将会被强行关闭
wait_timeout=1800 # MySQL默认的wait_timeout 值为8个小时, interactive_timeout参数需要同时配置才能生效
# Metadata Lock最大时长(秒), 一般用于控制 alter操作的最大时长sine mysql5.6
# 执行 DML操作时除了增加innodb事务锁外还增加Metadata Lock,其他alter(DDL)session将阻塞
lock_wait_timeout=3600
# 设置分组模式
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'
init_connect='SET collation_connection = utf8mb4_general_ci' # 设置全局聚合方式
init_connect='SET NAMES utf8mb4'
authentication_policy=mysql_native_password # 设置密码验证规则
character-set-server=utf8mb4 # 服务端使用的字符集默认为UTF8
collation-server=utf8mb4_general_ci
default-time-zone='+08:00' # 设置默认时区
activate_all_roles_on_login=ON # 设置开启默认角色
# plugin-load-add=validate_password.so # 配置密码复杂度策略
# validate-password=FORCE_PLUS_PERMANENT
# validate_password_length=8
# validate_password_policy=MEDIUM
# validate_password_check_user_name=ON
# default_password_lifetime=3 # 配置密码过期策略
# password_history=6
# password_reuse_interval=365
# plugin-load-add=connection_control.so # 配置登录失败次数限制
# connection-control=FORCE_PLUS_PERMANENT
# connection-control-failed-login-attempts=FORCE_PLUS_PERMANENT
# connection-control-failed-connections-threshold=5
# connection-control-min-connection-delay=60000
# require_secure_transport=ON # 使用SSL加密连接
[mysqldump]
# user="root"
# password="123456"
version: "3.8"
services:
nginx:
container_name: nginx
image: nginx:latest
restart: always
privileged: true
ports:
- "80:80"
volumes:
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/log:/var/log/nginx
- ./nginx/html:/usr/share/nginx/html
- ./files:/home/data/files # 静态资源代理
完毕。