docker-compose
https://github.com/docker/compose
https://docs.docker.com/compose/
结构
docker-compose 将所管理的容器分为三层,
- 工程(project): 如果没有特殊情况, 一个目录即一个工程
- 服务(service)
- 容器(containner)
常用配置注解
version: "3" # 新版本可忽略
services:
# 服务名字
web:
image: myapp:v1.0
# 镜像拉取规则 always, never, missing, build
pull_policy: never
# 容器名字默认 = 当前目录名 + 定义 service 名 + 副本数
container_name: abcd
# 在容器内的主机名
hostname:
# 修改工作目录
working_dir: /www
# 启动顺序依赖关系, 即先启动指定服务
# 启动: redis -> db -> web(自己)
# 停止: web -> db -> redis
depends_on:
- redis
- db
# 带条件的依赖, 表示需要对方启动成功且 服务检测通过
depends_on:
db:
condition: service_healthy
# 重启策略
# 'no' # 默认, 不会重新启动容器
# on-failure # 有错误就重启; 即退出码非 0
# always # 无论情况就重启, 可以开启启动
# unless-stopped # 除手动关闭外 可以重启
# 在 swram 模式下 使用 restart_policy 进行配置
restart: unless-stopped
# 给实例打标签
labels:
service: nginx
service_env: "pro"
# - "service=nginx" # 列表的方式高阶配置注解
services:
web:
# 设置为主机 PID 模式, 跟主机系统共享进程命名空间
# 容器使用这个标签将能够访问和操纵其他容器和宿主机的名称空间
pid: "host"
# 服务的用户命名空间; host 表示使用主机的用户名命名空间
userns_mode: "host"
# 更换容器的停止信号, 默认是 SIGTERM
stop_signal: SIGUSR1
# 在容器内运行一个 pid=1 的 init 进程,用于接收和转发信号
# 使用的 init 二进制文件是特定于平台的
init: true
# 指定符合 oci 规范的运行时
runtime: runc
# 使用只读文件系统创建的服务容器, 这个容器不能写任何文件
read_only: true
# 设置当使用 docker-compose up -d 时不包含 指定的 容器
# 只有 docker-compose --profile abc up -d 可以启动它, 也可以用于忽略某些检查
profiles: ["abc"]
# 为所有容器覆盖默认的标签; 简单说来就是给大家都设置一个标签
security_opt:
- label:user:USER
- label:role:ROLE权限和内核
services:
web:
# 特权模式
privileged: true
# 运行进程的身份, 容器内的用户名
# 默认是 root 用户
# 注意如果挂载文件系统时, 这个 uid 所对应的宿主机uid的关系
# user: "1000:1000"
# user: postgresql
user: nginx:nginx
# 声明容器内的运行用户必须属于某个组,以保障在读写外部文件时具有权限
# 暂不清楚是否会自动附加组的权限
group_add:
- mail
# 设置容器中的内核参数, 可以使用数组或字典格式
sysctls:
net.core.somaxconn: 1024
net.ipv4.tcp_syncookies: 0
# sysctls:
# - net.core.somaxconn=1024
# - net.ipv4.tcp_syncookies=0
# Capabiliity 添加或放弃容器的 Linux 能力
# 严格一点则先 drop 全部规则, 再添加需要的规则
cap_drop:
# - ALL
- NET_ADMIN
- SYS_ADMIN
cap_add:
- ALL
- CHOWN
- DAC_OVERRIDE
- NET_BIND_SERVICE # 绑定 1024 以下端口
- SETGID
- SETUID
# 指定要加入的 cgroup 命名空间, 支持 host 和 private
cgroup: host
cgroup_parent: m-executor-abcd网络
- 默认同一个文件内的创建的服务单独使用一个桥接网络, 互相可以通过名字通信;
- 但如果使用最初的那个默认的 桥接 网络, 则不能通过名字通信, 因为用的是宿主机的 dns 配置
- 多项目部署时,一般推荐单独创建一个网络
services:
web:
image: myapp:v1.0
# 方法1
ports:
- "3000" # 宿主机随机选一个端口
- "8000:5000" # 宿主机端口:容器端口
- "127.0.0.1:8001:8001" # 映射宿主机的指定 ip 和端口
# 方法2
ports:
- target: 80
published: 8080
protocol: tcp
mode: host
# 只是声明, 实际并没有对外部暴露出去, 即不会映射到宿主机;
# 但是 允许 其它服务 link 后访问这个端口
# 即将容器内部的端口暴露给其他容器使用
expose:
- "8080"
- "8088"
# 链接到外部的容器, 需要在同一个网络内 - 已废弃 - 不推荐使用
extrnal_links:
- redis_1
- project_db
- project_a:server2
# 连接到一个容器, 会自动添加名字和实例的 hosts 解析, 以及环境变量
links:
- db
- db:database
- redis
# 添加 hosts 映射
extra_hosts:
- "host1:172.0.0.4"
- "host2:172.0.0.5"
# 网络模式: bridge / host / none | or "service:[name]" or "container:[name]"
network_mode: "bridge"
# host
# 让容器共享主机的网络栈,这意味着容器将与主机具有相同的 IP 地址和网络接口
# 但是容器之间不能互相访问,因为它们共享同一个网络栈,需要用 ip 来访问
# none
# 不为容器分配任何网络资源,容器将没有网络接口
# "service:[name]"
# "container:[name]"
# 字符串或数组
# dns: 8.8.8.8
dns:
- 8.8.8.8
- 9.9.9.9
# 自定义 dns 选项, 会传递给解析程序
dns_opt:
- use-vc
- no-tld-query
# dns_search: example.com
# 字符串或数组
dns_search:
- dc1.example.com
- dc2.example.com
# 声明要用于服务容器的自定义域名,必须是有效的 RFC 1123 主机名
domainname:
# 用于服务容器的自定义主机名
hostname:
# 加入某个网络
networks:
- network1
networks:
network2:
# 给服务设置一个别名, 其他实例可以通过 别名进行访问
# 相同的服务可以在不同的网络有不同的别名
aliases:
- web1
- web2
# 指定接入到此网络时使用的 mac
mac_address:
# 当同时连接到多个网络时, 指定优先级;默认值0为最低值
priority: 1000
# 默认会创建一个 bridge 网络, 名字为 项目名_default
# 同网络内可以使用 服务名 进行访问
networks:
network1:
driver: bridgeDNS解析
# 查看服务的 dns 名称
# docker-compose run <service> nslookup <service>配置2
services:
node1:
restart: unless-stopped
image: xingdao/me-chaind:latest
networks:
me2:
# 指定ip地址时, 顶层的 network 必须包含 ipam 内容
ipv4_address: 172.28.0.5
ipv6_address: 2001:3984:3989::10
networks:
me2:
enable_ipv6:
# 是否是外部创建的网络
external: false
name: me2
driver: bridge
ipam:
driver: default
config:
- subnet: 172.28.0.0/16
ip_range:
gateway:
# 网络驱动程序使用的辅助 IPv4 或 IPv6 地址,作为从主机名到 IP 的映射
aux_addresses:
host1: 172.28.1.5
options:资源控制类
services:
web:
# 共享内存的大小, 如 pg 这类服务需要增大
shm_size: 64M
# 设备映射
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
- "/dev/sda:/dev/xvda:rwm"
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
# 2.x 版本的资源限制方式
# 限制内存使用量, 超出后会重启
# 弃用, 改使用 deploy.limits.memory
# mem_limit: 1000000000
mem_limit: 1G
# 允许容器交换到磁盘的内存量
# 如果 memswap_limit 设置为正整数, 则必须同时设置 memory and memswap_limit
memswap_limit: 2000000000
# 百分比, 供主机内核换出容器使用的匿名内存页比例
# 0 进制交换匿名页, 100 允许交换全部匿名页
mem_swappiness: 20
# 设置cpu资源, 必须与 deploy 中的 cpu 设置一致
cpus: '0.2'
# 使用 0,1,2,3 这 4 个核心
# cpuset: '1,3,4-6'
cpuset: '0-3'
# 可以使用的 cpu 数量, 弃用
cpu_count: 2
# 可以使用的 cpu 百分比
cpu_percent: 20
# 以整数值的形式定义服务容器相对于其他容器的 CPU 权重
cpu_shares: 20
# 当平台基于 Linux 内核时, 配置 CPU CFS (Completely Fair Scheduler)周期
cpu_period:
# 当平台基于 Linux 内核时, 配置 CPU CFS (Completely Fair Scheduler) quota
cpu_quota: 5000
# 支持实时调度器的平台配置 CPU 分配参数; 它可以是一个以微秒为单位的整数值, 也可以是一个持续时间
# cpu_rt_runtime: '400ms'
cpu_rt_runtime: 95000
# cpu_rt_period: '1400us'
cpu_rt_period: 11000
# 限制服务的块 IO 性能
blkio_config:
# 分配比例, 支持 10-1000 之间, 默认值 500
# 理解为 总资源的分配比例优先级
weight: 300
# 支持按设备微调
weight_device:
- path: /dev/sda
weight: 400
# 读操作的每秒字节数限制
device_read_bps:
- path: /dev/sdb
rate: '12mb'
device_read_iops:
- path: /dev/sdb
rate: 120
device_write_bps:
- path: /dev/sdb
rate: '1024k'
device_write_iops:
- path: /dev/sdb
rate: 30
# 3.x 需要在 deploy 下面
deploy:
resources:
limits:
# cpu 资源量
cpus: '0.50'
memory: 512M
# 这个值需要大于 memory, 实际可使用的 swap 为 memswap_limit - memory
memswap_limit: 1G
# 当容器超出内存限制时, 内核不会杀死容器, 只会杀死容器内的进程
oom_kill_disable: true
# 设置容器在内存不足时被杀死的优先级; 值越高, 容器被杀死的可能性越大
oom_score_adj:
# 资源预留
reservations:
memory: 256M
cpus: '0.25' # 似乎不支持 cpus 预留健康检查
services:
web:
# 健康检查, 失败将重启服务
healthcheck:
test: ["java", "-jar", "healthcheck.jar"]
test: curl --fail http://localhost:80 || exit 1
# 表示执行指定的命令或者命令参数
test:
- CMD # 注意这里必须是 CMD
- AGR1
# 表示使用 shell 执行命令(推荐)
test: ["CMD-SHELL", "curl --fail http://localhost:80 || exit 1"]
interval: 1m30s # 间隔
timeout: 10s # 单次超时
retries: 3 # 错误次数
start_period: 40s # 首次检测延迟
start_interval: 5s日志
services:
web:
labels:
- "service=nginx"
- "service_env=pro"
# 给输出的日志添加一个字段
logging:
# or "json-file" or "none"
driver: "json-file"
options:
# 输出的日志内, 新增一个标签(引用), key=被引用的标签名, value=被引用的标签值
# {"attrs": {"service": "nginx","service_env": "pro"}}
labels: "service,service_env" # 可选-将 labels 也记录到日志
# 即将此容器的指定 env 也写入到日志内
env: ""
env-regex:
# 是否压缩
compress: disable
# 单个文件的最大大小; k m f
max-size: 10m
# 日志文件数量
max-file: 2
# 输出到 syslog
logging:
driver: "syslog"
options:
# syslog-address: "tcp://127.0.0.1"
syslog-address: "tcp://127.0.0.1:1514"
# 将应用程序的名称附加到 syslog 消息中,默认情况下使用容器ID的前12位去 标记这个日志信息
tag: "registryctl"
syslog-facility: # syslog 设备
syslog-tls-ca-cert:
syslog-tls-cert:
syslog-tls-key:
syslog-tls-skip-verify:volumes和挂载
version: '3'
services:
nginx:
# 传递给存储驱动程序选项
storage_opt:
size: '1G'
# 访问模式: rw, ro, z, Z
# z: SELinux 选项,指示绑定挂载主机内容在多个容器之间共享
# Z: SELinux 选项,指示绑定挂载主机内容是私有的,并且不与其他容器共享
volumes:
# 方式1
- type: volume
source: html
target: /usr/share/nginx/html
volume:
nocopy: true
# 方式2
- test-nfs:/var/lib/mysql
# 默认自动创建 volume 卷, 挂载到容器
- /abcdd
# 属于 bind
- .:/code
# 只读挂载 volume 卷
- logvolume01:/var/log:ro
# 如果不是本地文件系统, 可以另外指定驱动
volume_driver: mydriver
# 从其它容器挂载数据
volumes_from:
- service_name
- service_name:ro
- container:container_name
- container:container_name:rw
# 挂载临时目录到容器内部
# tmpfs: /run
tmpfs:
- /run
- /tmp
# 顶层卷设置
volumes:
html:
name: html
driver_opts:
type: "nfs"
o: "addr=192.168.1.102,nolock,soft,rw"
device: ":/www/html"
# 复杂版本
test-nfs:
driver_opts:
type: nfs
o: addr=nfs-server-ip,nfsvers=4,minorversion=0,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport
device: :/uploads # nfs 中目录路径
logvolume01: {}
vol1:
external: true # 声明是外部卷
prometheus:
driver: local
driver_opts:
type: none
o: bind
device: /opt/dmgeo/prom/prometheus/dataconfigs
意思是将一个外部文件加载到容器内作为容器的配置文件
相对于手动挂载的好处在于 可以单处定义 多次引用, 以及逻辑拆分
特点
- 存储在 Docker 主机的文件系统中, 可以是本地文件系统, NFS 文件系统或远程 S3 存储等
- 通过文件挂载或 Docker Compose 文件中的 configs 字段来访问
- 生命周期是独立于服务的
- configs 的更新是通过重新部署服务来实现的
services:
web:
image: app:latest
configs:
- my_config # 默认是挂载到根路径下的, 即 /my_config
- my_other_config
- source: my_config
target: /redis_config
uid: "103"
gid: "103"
mode: 0440
# 指定容器使用的配置文件
# 将名为 my-config 的配置文件复制到容器的 /etc/nginx/conf.d/default.conf 目录下
configs:
- source: my-config
target: /etc/nginx/conf.d/default.conf
# 声明配置文件
configs:
my_config1:
file: ./my_config.txt
my_config:
external: true # 外部文件
my_other_config:
external: truebuild-构建镜像
如果没有镜像, 启动服务时会先进行打包, 再运行镜像
services:
web:
# build: . # 简易配置时, 指定编译时的路径
build: # 或者是在下级中指定
context: . # 指定 dockerfile 文件的路径
# 文件名, 如果不是默认名字(dockerfile)时需要指定; 必须和 context 同时存在
dockerfile: myapp-dockerfile.txt
args: # build 过程中的参数; 可以作为多阶段构建时的传参
liteno: 2
id: 11112223332222
# args:
# - a=2
# - b=3
# - c
# 指定缓存的镜像列表
cache_from:
- alpine:12
- redis:latest
# 设置镜像的标签
labels:
- "db.description=mysql"
- "db.ip=127.0.0.1"
# 构建过程中的网络
network: none
shm_size: '1gb' # 设置容器 /dev/shm 分区的大小
target: first # 多层构建, 指定构建哪一层command和entrypoint
注解
- entrypoint 一定会被执行
- 如果 docker-comose.yml 有定义 entrypoint, 则会覆盖 dockerfile 内的 entrypoint;
- 在没有定义 entrypoint 时, dockerfile 内的 CMD 作为启动命令和参数
- 如果 docker-comose.yml 有定义 command, 则会覆盖 dockerfile 内的 CMD;
- 同时定义 entrypoint 和 CMD 时, CMD 会作为 entrypoint 的参数
最佳实践(很多开源项目都采用此方式)
- 简单的环境只定义 command 启动程序即可, 例如 nginx;
- 复杂的场景使用 dockerfile entrypoint 启动初始化环境, 指向一个脚本 /entrypoint.sh
- 程序还是由 dockerfile 的 CMD 命令去启动(传递给脚本);
entrypoint.sh
#!/bin/sh
# entrypoint.sh
set -e
# first arg is `-f` or `--some-option`
# 检查脚本的第一个参数($1)是否以短横线(-)开头,
# 如果是,就在参数列表前插入"memcached"作为新的第一个参数
if [ "${1#-}" != "$1" ]; then
# 重新设置参数列表:添加"memcached"作为第一个参数,后面跟着原来的所有参数
set -- memcached "$@"
fi
exec "$@"
# 效果
# entrypoint.sh -h => memcached -h
# entrypoint.sh memcached -h => memcached -h
# entrypoint.sh bash -h => bash -h
# 直接启动时 会将 CMD 作为参数传递给 entrypoint, 这个脚本会直接启动 CMD 传来的命令配置项
services:
web:
# 覆盖容器启动后默认执行的命令(即 dockerfile 的 CMD)
# 当 Dockerfile 定义了 entrypoint 的时候, docker-comose.yml 定义的 command 会被覆盖
command:
# 格式一: 字符串
command: "xxx"
command: "xxx xx2"
command: xxx xx2
# 格式二: 列表
command: ["-c", "200"]
command:
- -c
- 200
# 格式三: shell
command: ["sh","-c","python manage.py runserver 0.0.0.0:8000"]
command: sh -c "python manage.py runserver 0.0.0.0:8000"
# 覆盖 dockerfile 内的 entrypoint 命令
# 格式一: 字符串
entrypoint: python web
# 格式二: 列表
entrypoint:
- python
- web模板引用
x-insight-template: &insight-template
restart: unless-stopped
image: 10.1.0.1:5000/cwx/insight-${SERVICE_NAME}:${TAG}
services:
insight-admin:
<<: *insight-template
environment:
SERVICE_NAME: admin
TAG: 202503131006
insight-api:
<<: *insight-template
environment:
SERVICE_NAME: api
TAG: 202503131006其它注意事项
命名
容器名字
默认为 = 当前目录名 + 定义 service 名 + 副本数 或使用 container_name 修改
network 命名
如果有名字, 则是 service_name + network_name 没有名字则是 service_name + _default
存储卷名字
service_name + volume_name
环境变量
避免繁琐, 注意区分作用于 docker-compose 文件内的变量和作用于容器内变量的区别;
注意
- os env 只会传递到 compose, 不会传递到容器
- 当需要使用字符串 $ 符号时, 可以使用两个 $$ 符号
compose file作用域
- 可以引用 os env 和 .env 文件内的变量
- 不能访问到 Dockerfile 中定义的变量
- 不能访问到 env_file 设置的文件中变量, 这是给容器的
compose 的获取优先级
- compose 文件内
- shell env
- env file
- dockerfile
- 未定义的变量
${abc}, $abc # 正常引用
# v3 版本开始支持下面语法
${VARIABLE:-default} # 未设置或为空, 则会应用 default 的值
${VARIABLE-default} # 未设置时 才会应用 default 的值
# 强制要求设置变量
${VARIABLE:?err} # 未设置或为空, 退出并输出一条包含 err 的错误信息
${VARIABLE?err} # 未设置, 退出并输出一条包含 err 的错误信息
${version:?The is version env no found} # 示例容器作用域
- docker -e 参数传入
- –env-file= 参数传入
- yaml 文件内定义的
services:
web:
environment:
- "TZ=Asia/Shanghai"
- VAR2=200
# 简洁语法, 如果没有显示赋值, 则获取 os 的值, 如果 os 没有值, 则 compose 解析为 null
- DEBUG
# 也支持 对象 格式
environment:
TZ: "Asia/Shanghai"
LANG: "C.UTF-8"
env_file:
- file1.env # 相对当前目录寻找文件
env_file: .env # 单个文件方式.env 文件
- compose 会读取此文件, 且只作用于 compose 自己, 不会传递;
- 引号不会被特殊处理, 属于 val 的一部分;
- 适用于多个 compose 文件都公用的 env;
- 优先级低于 cli 传入的值;
docker-compose自身的环境变量
# 设置项目名称, 默认为当前工作目录的名称; 工作目录默认为 Compose 配置文件所在目录
# 在项目启动时该值与服务名称会一起添加到容器中
COMPOSE_PROJECT_NAME=
# 指定要使用的 Compose 配置文件的路径, 默认从当前目录逐级往上查找
COMPOSE_FILE=
# 使用旧版本 api
COMPOSE_API_VERSION=
# unix:///var/run/docker.sock
DOCKER_HOST=
# 如果不为空 则与 Docker 守护进程的所有交互都通过 TLS 协议进行加密
DOCKER_TLS_VERIFY=
# 配置用于 TLS 验证的 ca.pem, cert.pem 以及 key.pem 文件的路径; 默认为 ~/.docker
DOCKER_CERT_PATH=
COMPOSE_HTTP_TIMEOUT=
COMPOSE_TLS_VERSION=
# 设置 Compose 可以执行进程的最大并发数; 默认值为 64, 并且设置不能低于 2
COMPOSE_PARALLEL_LIMIT=yaml语法注意事项
YAML 的布尔值(true, false, yes, no, on, off)必须要使用引号引起来(单引号, 双引号均可), 否则会当成字符串解析
多副本
缩放副本
注: 这个是 compose 自己的功能, 和 swarm 无关
业务上需要使用动态端口, 以避免端口冲突, 最好采用 注册发现机制
docker compose up --scale web=5
docker compose up --force-recreate --no-deps web # 滚动更新include载入
载入另外一个yaml文件, 方便大型项目的拆分, 注意冲突;
include:
- my-compose-include.yaml
include:
- path: ../commons/compose.yaml
# compose.yaml 文件内的相对路径
project_directory: ..
# 指定解析这个 compose 文件时使用的 .env 文件, 默认为 project_directory/.env
env_file: ../another/.env
- path:
- ../commons/compose.yaml
- ./commons-override.yamlextends扩展
也是一种抽出公共组件的方式
# common.yml
webapp:
build: ./webapp
environment:
- DEBUG=false
- SEND_EMAILS=false
# development.yml
web:
extends:
file: common.yml
service: webapp # 继承这个文件内的 webapp 服务
ports:
- "8000:8000"
links:
- db
environment:
- DEBUG=true # 覆盖
db:
image: postgresoverride
即单独一个 docker-compose.override.yml 文件的方式。
这个文件是官方作为 docker-compose.yml 文件的补充来使用的;
注意
- 执行结果是二者的并集
- 相同的部分, 并非简单的 override 的关系, 而是先后执行
类似于这种方式, 但是默认使用的
docker-compose \
-f docker-compose.yml \
-f docker-compose.override.yml \
upcmd
docker-compose 和 docker compose 的关系;
早期 docker-compose 是一个独立的工具; 后来官方接入支持插件模式运行了;
更推荐按插件的方式使用, 方便做 shell 补全
# 查看最终生成的配置
docker compose config
docker compose -f xxx.yml # 指定配置文件
docker compose ps # 查看后台运行的服务
docker compose run web env # 运行容器内部的命令
docker compose stop # 停止运行
docker compose down # 删除容器
docker compose down --volumes # 删除容器, 并删除数据卷
docker compose top # show top
docker compose convert # 解析 compose 文件内的变量
docker compose up # 构建, 运行; 当前目录下的 docker-compose.yml 文件
docker compose up -d # 构建, 后台运行
docker compose up name -d # 更新某一个服务
docker compose up -V # 强制更新所有服务, 当之前挂载过一个别的卷时, 指定 -V 可以强制更新挂载为新卷
# 其它参数
--timeout 1 # 设置 1s 超时,不需要再继续等 10 秒的终止信号
--remove-orphans # 移除孤立的容器
-v --rmi all # 删除所有镜像 和 数据卷
# 自定义项目名
-p name
-p=name
--project-name name
--project-name=name
# 模板环境+环境文件的方式
docker compose -f docker-compose.base.yml -f docker-compose.$ENV.yml pull安装
https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-linux-x86_64
# 方法1: 独立二进制使用
mv docker-compose /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose -version
# 方法2: 插件模式-任选一个位置放
$HOME/.docker/cli-plugins/docker-compose
/usr/local/lib/docker/cli-plugins/docker-compose
/usr/local/libexec/docker/cli-plugins/docker-compose
/usr/lib/docker/cli-plugins/docker-compose
/usr/libexec/docker/cli-plugins/docker-compose # 官方插件放置位置
# 方法3: 官方包-插件模式
yum install docker-compose-pluginsystemd管理
# /etc/systemd/system/app.service
[Unit]
Description=Docker Compose Application Service
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/data/containers
ExecStart=/usr/bin/local/bin/docker-compose up -d
ExecStop=/usr/bin/local/bin/docker-compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target