Docker Compose
文章目录
- 什么是compose?定义“多个容器怎么一起跑”
-
- Docker Compose 的核心就干三件事,批量保存 docker run 的参数,建立容器间的“内部对讲机”(网络隔离与互通),严格控制启动顺序(Compose 里的 depends_on 参数可以指定)。参见docker compose章节
- Docker Compose 的配置对象叫 services,但它最终会把每个 service 启动成一个或多个容器。service = 容器的启动模板 / 配置定义,container = 根据 service 真正启动出来的运行实例。参见docker compose章节
- Compose 默认会给你的应用创建一个网络,每个服务的容器都会加入这个默认网络,并且容器之间可以直接通过服务名互相访问
-
- 容器名(container name)/主机名(hostname)/服务名(service name,Compose 里)
- compose创建的默认网络,主机名,服务名的作用
- Compose 创建的是什么网络
- 和你手动创建网络是什么关系
- --driver:决定“网络是什么类型”,比如 bridge、overlay。容器互联:是“多个容器接到同一个网络里”
- 总结
- Compose 使用的三个步骤
- Compose 安装
- 示例
-
- composetest/app.py
- 创建 Dockerfile 文件
- 创建 compose.yml
- 使用 Compose 命令构建和运行您的应用
- 容器的日志
- 重新启动
- redis 怎么查看
- yml 配置指令参考
-
- build在告诉 Compose,这个服务的镜像要怎么构建
- cap_add,cap_dropcap_add 和 cap_drop 用来给容器增加或移除 宿主机Linux capabilities(内核能力)
- cgroup_parent为容器指定父 cgroup 组,意味着将继承该组的资源限制。
- Compose 里的 command,就是在启动容器时改掉镜像默认的 CMD
- container_name指定自定义容器名称,而不是生成的默认名称。不要让 Compose 自动生成容器名,而是强行把这个容器命名成xx
- depends_on 主要管“启动/停止顺序”,不等于“等对方完全可用再启动”
- deploy: 是“部署策略”配置,主要给编排平台用。
- devices 宿主机上的设备文件映射进容器里
- dns:给这个服务对应的容器,单独指定它要用哪些 DNS 服务器
- dns_search 自定义 DNS 搜索域。可以是单个值或列表。
- entrypoint覆盖容器默认的 entrypoint。
- env_file,env_file 用来把一个或多个环境变量文件里的键值,批量传给这个服务对应的容器
- environment 是在 Compose 里直接给容器设置环境变量
- expose expose 只是在容器网络内部声明/开放端口,不映射到宿主机。暴露给其他服务访问的内部端口
- extra_hosts 是手动往容器里的 /etc/hosts 加“名字 → IP”的映射
- healthcheck:给这个服务定义一个健康检查。Compose 会定期运行检查命令,判断容器是不是 healthy。它的行为和 Dockerfile 里的 HEALTHCHECK 基本一致,默认值也一致。
- image:这个服务启动容器时要用的镜像来源
- logging 用来配置这个服务容器的日志怎么收集、存到哪、怎么轮转
- network_mode 这个服务容器用哪种网络模式
- networks
- restart :容器退出以后,要不要自动再拉起来。
- secrets
- security_opt 给容器补充或修改安全限制
- stop_grace_period
- stop_signal 停止容器时,先发哪个信号。
- sysctls
- tmpfs 在容器里面挂一个“临时内存文件系统”
- ulimits 就是:覆盖这个容器默认的资源上限
- volumes 将主机的数据卷或者文件挂载到容器里。
- Compose 文件里 ${VAR} 这种“变量替换”的来源
- 容器内环境变量的覆盖顺序
- 多个 Compose 文件合并时,核心规则是:前面的文件是基础配置;后面的文件在前面的基础上覆盖、追加、合并
- Compose 是否会替换/删除容器,主要看 project name、service name
什么是compose?定义“多个容器怎么一起跑”
把一整套多容器应用,写进一个配置文件里,然后用一条命令统一启动
compose 就是把“多个容器如何一起运行”写到一个 YAML 文件里,再用一条命令统一管理整套应用。



Docker Compose 的核心就干三件事,批量保存 docker run 的参数,建立容器间的“内部对讲机”(网络隔离与互通),严格控制启动顺序(Compose 里的 depends_on 参数可以指定)。参见docker compose章节
Docker 关注的是单个容器的打包和运行(怎么把我的 Python 代码连同环境塞进一个集装箱)。
Docker Compose 关注的是多容器的协同(怎么把网页、数据库、缓存这堆集装箱用一根线连起来,并且一键全部启动)

Docker Compose 的配置对象叫 services,但它最终会把每个 service 启动成一个或多个容器。service = 容器的启动模板 / 配置定义,container = 根据 service 真正启动出来的运行实例。参见docker compose章节
Compose 管理多个容器 是对最终运行结果说的;而 YAML 里写 services 是对“容器应该怎么启动”的抽象定义
然后根据每个 service 的配置去创建、启动、停止、管理对应的容器。



Compose 默认会给你的应用创建一个网络,每个服务的容器都会加入这个默认网络,并且容器之间可以直接通过服务名互相访问
为什么 Compose 也会创建网络,因为 Compose 的目标就是让一组服务一起运行并互相通信。Docker 官方说明,Compose 默认会为你的应用创建一个网络,每个服务对应的容器都会加入这个默认网络,并且能通过服务名互相发现和访问。

容器名(container name)/主机名(hostname)/服务名(service name,Compose 里)



通常说的主机名,不是部署 Docker 的那台真实宿主机名字
主机名的作用是:
👉 让别的容器“找到你在哪”(解析到 IP)
compose创建的默认网络,主机名,服务名的作用
Docker Compose 会创建一个默认网络(bridge 网络),把所有容器都加入到这个网络里( 同一个 compose 里的服务默认都在同一个网络),Docker 的网络驱动会给每个容器分配 IP(但注意:这个 IP 是 动态的每次重启可能变),并自动把 服务名注册到Docker 在容器网络里内置的DNS 服务即:
服务名(redis)
↓ Docker 自动注册
内部 DNS 表
↓
容器 IP (172.x.x.x)
后续各个容器直接可以通过服务名访问


在 Docker 里:不是所有程序都默认监听 0.0.0.0。但在容器里,如果你想“外部/其他容器访问”,通常必须监听 0.0.0.0
在 Docker 网络里:“谁要被访问,谁就必须监听 0.0.0.0”










dns准确的说是,名字->ip的解析器

Docker Compose 中,Docker 自己内置了一个“局域网 DNS”,服务名 → 容器 IP






Compose 会创建默认网络,每个服务会用服务名注册到内部 DNS,容器之间可以直接通过服务名访问,容器名确实也会作为网络里的可解析名字之一,不推荐写容器名




既然已经可以用 service name 访问了,那主机名有啥用?
hostname 不是给网络用的
你要记住一句非常关键的话:
❗ 网络访问靠 service name(DNS)
❗ hostname 是“容器内部身份”
因为在 Docker Compose 默认模式下:
👉 service name ≈ hostname(经常一样)









宿主机怎么访问容器呢?通过端口映射
在 Docker 里:
👉 宿主机访问容器 = 通过端口映射(port mapping)
而不是 service name,也不是容器 IP




容器网络里只能有一个容器监听 5000,否则 Docker 不知道给谁”?错误





容器怎么访问宿主机?
在 Docker 里:
👉 容器访问宿主机,本质是:走宿主机的“特殊地址”或“网关”
不是 service name



在 Docker 里:
👉 host.docker.internal 是“宿主机的名字(别名)”
👉 host-gateway 是“Docker 自动帮你填的宿主机 IP”












在 Docker 网络里有三种访问方式

容器 → 容器(内部通信):用服务名

宿主机 → 容器:用端口映射

外部网络 → 宿主机 → 容器:用端口映射


Compose 创建的是什么网络

和你手动创建网络是什么关系

–driver:决定“网络是什么类型”,比如 bridge、overlay。容器互联:是“多个容器接到同一个网络里”
–driver 只解决第 1 步里的“这个网络是什么类型”,不等于“容器已经互联了”

总结

Compose 使用的三个步骤





使用 Dockerfile 定义应用程序的环境
第一步不一定非要有 Dockerfile。
如果某个服务直接使用现成镜像,比如 nginx、mysql,Compose 文件里写 image: 就够了;只有当你要从自己的源码构建镜像时,才通常会配合 Dockerfile 和 build: 使用。Docker 官方明确说明,Compose 里的 build 是可选的
使用 docker-compose.yml/compose.yaml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行
是配置文件,用来写“这套应用要启动哪些服务、每个服务怎么启动、端口怎么映射、卷怎么挂、网络怎么连”。Docker 官方把 Compose 文件定义成用来描述应用的服务、网络、卷等配置
docker-compose.yml 的配置案例
有两个服务:一个是自己从当前目录构建出来的 web,一个是直接用现成镜像的 redis;web 把宿主机当前目录挂到容器 /code,把一个叫 logvolume01 的卷挂到 /var/log,并把容器 5000 端口映射到宿主机 5000。 Compose 默认会给它们放进同一个网络里,服务之间通常可以直接按服务名通信








build: . 的意思是:不要直接使用现成镜像,而是用当前目录下的 Dockerfile 构建一个镜像,然后再用这个镜像启动容器
Docker 官方文档里也说明了:build 的路径可以是绝对路径或相对路径;如果是相对路径,就从 Compose 文件所在目录 来解析
Dockerfile 里的路径:
通常相对于 build context,也就是 build: . 指定的目录。
compose.yaml 里的 build: .:
相对于 compose.yaml 所在目录。




volumes(宿主机路径/卷名:容器内路径): - .:/code。把宿主机上的 . 这个目录,挂载到容器里的 /code 目录


最后,执行 docker-compose up/docker compose up 命令来启动并运行整个应用程序。docker compose up 可以理解成一句话:读取 compose.yaml 文件,然后按照里面的 services 配置,把需要的容器、网络、数据卷都创建并启动起来。也就是说,你不用自己写很多条 docker run,Compose 会根据配置文件自动帮你做
执行命令,会读取当前目录里的 Compose 配置,然后把这套应用构建、创建、启动起来
compose.yaml:图纸
docker compose up:按图纸开工
compose.yaml 负责“定义怎么跑”,docker compose up 负责“真正跑起来”
docker compose up 和 docker run 的区别
docker run:
我手动启动一个容器。
docker compose up:
我把一堆容器怎么启动写到 compose.yaml 里,
然后让 Docker Compose 自动帮我创建网络、拉镜像、建容器、启动容器。


Compose 安装
最简单的官方推荐安装方式是仓库安装,比如 Ubuntu / Debian:
sudo apt-get update
sudo apt-get install docker-compose-plugin
docker compose version
RPM 系发行版则是:
sudo yum update
sudo yum install docker-compose-plugin
docker compose version
这些都是官方当前写法
docker compose version

示例
composetest/app.py



Compose 默认会创建一个网络,每个服务加入后,都可以通过服务名被其他容器访问;容器可以把服务名解析成对应容器的 IP

host=‘redis’,去同一个 Compose 网络里,找名字叫 redis 的那个服务
详情见:compose创建的默认网络,主机名,服务名的作用 章节
创建 Dockerfile 文件
Dockerfile 通常不需要后缀。
标准写法就是文件名直接叫:
Dockerfile
不是:
Dockerfile.txt
Dockerfile.docker
Dockerfile.yml
默认推荐文件名就是 Dockerfile,没有后缀;如果你改了名字,就要用 -f 指定
为什么没有后缀也可以? 因为 Docker 默认找的就是这个固定名字: Dockerfile 所以你执行: docker build .
时,Docker 会默认在当前目录找这个文件。
如果你不用这个名字,也不是不行。 比如你写成: mydockerfile Dockerfile.dev Dockerfile.test
那构建时要手动指定: docker build -f Dockerfile.dev . 这里 -f 就是告诉 Docker: 这次用哪个
Dockerfile。




apk 是 Alpine Linux 的包管理器命令,相当于:
Ubuntu / Debian 里的 apt
CentOS / RHEL 里的 yum / dnf
Alpine 官方文档直接说明了,apk 是 Alpine Package Keeper,用来管理 Alpine 系统里的软件包





默认情况下,COPY . . 会把当前 build context 里的所有文件再复制一遍,其中也包括 requirements.txt


COPY . . COPY requirements.txt requirements.txt 重复了吧,为什么不只保留COPY . .
先单独 COPY requirements.txt,再 pip install,最后 COPY . .,是为了让“安装依赖”这一层尽量复用缓存;不是重复,而是优化构建速度



某一层一旦变了,后面所有层都要重新执行
Docker 看的是层有没有变,某一层一旦变了,后面所有层都要重新执行


Docker 缓存:“上次 build 做过、而且结果还能复用的那些中间结果。每一条指令都会变成最终镜像中的一层
上次 build 做过、而且结果还能复用的那些中间结果。”
Docker 官方说得很直接:Dockerfile 里的每一条指令都会变成最终镜像中的一层;当你再次构建同一个镜像时,只要某一层没有变化,Docker 就可以直接复用那一层,不必重新执行


创建 compose.yml
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: redis:alpine
:用 Compose 一次启动两个服务:一个你自己构建的 web,一个现成的 redis。 Compose 会默认给这两个服务创建一个应用网络,服务之间可以直接用服务名互相访问



使用 Compose 命令构建和运行您的应用
docker compose up/docker-compose up
般要在 docker-compose.yml 或 compose.yaml 所在的目录执行 docker compose up
因为 docker compose up 默认会在当前目录查找 Compose 配置文件。Docker 官方当前说明,默认会查找 compose.yaml、compose.yml、docker-compose.yaml、docker-compose.yml
如果你不在这个目录里,也不是完全不能执行。可以显式指定文件






执行 docker compose up 之后,当前窗口会持续显示各个容器打印出来的输出。默认 attached mode 下,你会看到所有容器的日志


docker compose up -d

-d 之后怎么看日志,docker compose logs


容器启动后,只要程序还在往标准输出/标准错误里打印内容,这些都算容器日志
docker compose logs
看到的是:
从容器启动以来,到你执行这条命令这一刻为止的日志。

容器的日志
docker compose logs 看的主要就是容器进程写到 STDOUT(标准输出) 和 STDERR(标准错误) 的内容。 Docker 官方对
docker logs 的说明就是“获取容器的 STDOUT 和 STDERR 日志”,而 docker compose up
默认也是把各服务的输出聚合显示出来
主要能看到容器的标准输出和标准错误;Docker 默认使用日志驱动记录容器日志,默认通常是 json-file。如果程序只把日志写进容器内文件,而不输出到 stdout/stderr,那 docker logs / docker compose logs 一般看不到


单个容器查看日志:docker logs <容器名或容器ID>/docker logs -f <容器名或容器ID>


Compose 启动的一组容器
把这一组容器的日志一起看,用docker compose logs/docker compose logs -f


只看其中某一个服务的日志:docker compose logs/docker compose logs -f 服务名

容器日志:某一个具体容器的日志,服务日志:某个服务下面一个或多个容器的日志,Compose 帮你按服务名聚合着看
service 定义是:服务是一个抽象定义,背后由一组容器支撑。
Compose 看的是“服务”的日志
但这些日志来源仍然是服务对应容器的 stdout/stderr
Compose 不是不能看容器日志,而是它默认帮你按“服务”把容器日志组织起来看;单个容器的精确日志则更适合用 docker logs。





2者看起来有区别的例子



重新启动
docker compose restart/docker compose restart 服务名 重新启动现有服务/某个服务不会重新构建镜像




docker compose down 连容器一起删掉,再重新创建启动
把这套 Compose 应用停掉,并且把它创建出来的容器和网络删除掉。 Docker 官方对 down 的定义就是:停止容器并移除容器、网络。默认也会移除 Compose 项目里定义的默认网络
命名卷默认不会被删
匿名卷默认也不会被删
但如果你加 -v,卷也会一起删掉。Docker 官方对 down --volumes 的说明就是移除命名卷和匿名卷
docker compose down = 停掉并拆掉这套 Compose 应用的运行环境,但默认不删镜像,也不删卷







docker compose down --rmi local/–rmi all 把相关镜像也删掉



–rmi local,“tag”理解成镜像的“名字:版本”




docker compose down -v,docker compose rm -v,删除卷

查看卷 docker volume ls,docker volume inspect <卷名>

docker volume inspect 就是看“这个卷叫什么、什么时候建的、数据实际存在哪”



删除卷
如果你知道卷名,直接删:
docker volume rm <卷名>
这个是“删除一个或多个卷”。正在被容器使用的卷不能直接删,必要时可以加 -f 强制
docker volume prune
它会删除未使用的本地卷;默认只删匿名卷


代码或 Dockerfile 改了,要重新构建再启动,docker compose up -d --build
第一,先重新构建需要 build 的镜像。
这里的 --build 表示在启动容器前先 build 镜像。
第二,再把容器在后台启动起来。
这里的 -d 是 detached mode,也就是后台运行,不一直占着当前终端。
你可以把它翻成人话:
“我改了代码环境或 Dockerfile,先重新做镜像,再把这套服务启动起来,而且放到后台跑。”
–build = “该重做镜像的我先帮你重做”,不是“你得先自己停掉、删掉再执行”


redis 怎么查看
按 Compose 服务名进去 docker compose exec 服务名redis-cli。按具体容器名进去 docker exec -it <实际容器名/容器id> redis-cli
docker compose exec redis redis-cli
= 在 redis 服务 对应的容器里执行 redis-cli
docker exec -it 容器名 redis-cli
= 在 某个具体容器 里执行 redis-cli
docker exec -it 容器ID redis-cli
= 同上,只是把容器名换成了容器 ID






KEYS * 查看当前数据库里的所有键。看某个键的值 GET

看当前数据库有多少个键:
DBSIZE
看某个键是什么类型:
TYPE hits
删除某个键:
DEL hits
SCAN 0 是 Redis 的游标式遍历键命令


怎么判断redis有没有用卷做持久化
看 Compose 文件里 Redis 服务有没有 volumes:docker compose config


检查容器挂载信息:docker inspect <redis容器名/容器id>

Compose 文件redis没写 volumes:,检查结果里还是出现了 Mounts
没在 Compose 文件里手写 volumes:,但 Redis 还是挂了一个卷。原因是:
Redis 官方镜像自己就在镜像里声明了 /data 是一个 VOLUME。 Dockerfile 里的 VOLUME
会让容器运行时在这个位置创建挂载点;如果你没手动指定卷,Docker 会创建一个匿名卷。Redis
官方镜像文档明确提到,启用持久化时数据存放在 VOLUME /data


redis逻辑数据库
Redis 可以有多个逻辑库
编号从 0 开始
有多少个库是可配置的
很多情况下默认是 16 个,也就是 0 到 15

切换逻辑数据库 select x.怎么看当前 Redis 一共有多少个库 CONFIG GET databases



yml 配置指令参考
build在告诉 Compose,这个服务的镜像要怎么构建


context

dockerfile

args





target

cap_add,cap_dropcap_add 和 cap_drop 用来给容器增加或移除 宿主机Linux capabilities(内核能力)
cap_add / cap_drop 是在调容器的“内核能力清单”,不是简单的文件权限



cgroup_parent为容器指定父 cgroup 组,意味着将继承该组的资源限制。
cgroup 理解成:
Linux 用来管进程资源的一种分组机制






Compose 里的 command,就是在启动容器时改掉镜像默认的 CMD






container_name指定自定义容器名称,而不是生成的默认名称。不要让 Compose 自动生成容器名,而是强行把这个容器命名成xx
container_name 就是手动指定容器名;好处是名字固定好找,代价是这个服务不能方便地扩成多个副本










depends_on 主要管“启动/停止顺序”,不等于“等对方完全可用再启动”
depends_on 默认是“排队启动”,不是“等对方完全可用”;要等可用,得配 healthcheck + condition: service_healthy
不设置:别依赖顺序
设置 depends_on:有顺序
还想等服务真正可用:再加 healthcheck + condition: service_healthy



services:
web:
build: .
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
redis:
image: redis
db:
image: postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
retries: 5
start_period: 30s
timeout: 10s

数据库容器启动后,先给它 30 秒热身;然后每 10 秒检查一次“Postgres 是否已经能接收连接”;单次检查最多等 10 秒;如果连续失败 5 次,就认为它不健康





deploy: 是“部署策略”配置,主要给编排平台用。
在 Compose 规范里它是可选部分;如果当前实现不支持,就会被忽略。在 Docker 的 Swarm 场景里,这一块最有用;而本地普通
docker compose up 下,很多 deploy 字段并不会按你想的那样生效
mode / replicas:决定跑几个实例
endpoint_mode:决定别人怎么访问这组实例
resources:决定资源上限和预留
restart_policy:决定失败后怎么重启
update_config / rollback_config:决定升级和回滚怎么做
devices 宿主机上的设备文件映射进容器里
devices 不是挂普通文件,而是把宿主机真实硬件设备“接”给容器用



dns:给这个服务对应的容器,单独指定它要用哪些 DNS 服务器


daemon.json 管“默认大家都用什么”,Compose 的 dns: 管“这个服务例外用什么”,容器里最终看到的是 /etc/resolv.conf 那一层的结果





dns_search 自定义 DNS 搜索域。可以是单个值或列表。





entrypoint覆盖容器默认的 entrypoint。
在 Compose 里,entrypoint 用来覆盖镜像原本的 ENTRYPOINT
Compose 里的 entrypoint 是用来改“容器启动时固定执行的入口程序”的;字符串形式像直接写一条入口命令,数组形式像把程序和参数一个个写清楚


env_file,env_file 用来把一个或多个环境变量文件里的键值,批量传给这个服务对应的容器
env_file = 从外部文件批量给容器注入环境变量;多个文件时后面的覆盖前面的,environment 又会覆盖 env_file
不用管谁写在前面或后面。
在 Compose 里,只要同一个服务同时设置了 env_file 和 environment,environment 一定优先,官方原话就是:environment has precedence over env_file





.env 文件本身怎么写?

environment 是在 Compose 里直接给容器设置环境变量
environment 就是直接在 Compose 文件里给容器设置环境变量;像 true、false 这种值最好加引号,避免被 YAML 当成布尔值




expose expose 只是在容器网络内部声明/开放端口,不映射到宿主机。暴露给其他服务访问的内部端口
expose 只是在容器网络内部声明/开放端口,不映射到宿主机。
Docker Compose 官方写得很清楚:expose 定义的是容器对外暴露给其他服务访问的内部端口,这些端口不应该发布到宿主机,而且这里只能写容器内部端口



extra_hosts 是手动往容器里的 /etc/hosts 加“名字 → IP”的映射


healthcheck:给这个服务定义一个健康检查。Compose 会定期运行检查命令,判断容器是不是 healthy。它的行为和 Dockerfile 里的 HEALTHCHECK 基本一致,默认值也一致。



CMD 是直接执行“程序 + 参数”,CMD-SHELL 是把整行命令交给 shell 去解释。





image:这个服务启动容器时要用的镜像来源
image = 这个服务启动容器时要用的镜像来源









logging 用来配置这个服务容器的日志怎么收集、存到哪、怎么轮转

driver表示用哪种日志驱动。
Compose 文档里说,logging.driver 指定服务容器使用的日志驱动;可用值跟平台有关。Docker Engine
层面默认日志驱动通常是 json-file。Docker 官方还特别提醒:json-file 默认不做日志轮转,而 local
驱动默认会做轮转,很多场景下更推荐 local

driver: local


driver: json-file 把容器日志保存成 JSON 文件
把容器日志保存成 JSON 文件。这是 Docker Engine 的默认日志驱动。它适合直接用 docker logs / docker compose logs 查看。官方同时提醒,如果不做轮转,日志文件可能越积越大

driver: syslog 把日志发到宿主机或远端的 syslog 系统
把日志发到宿主机或远端的 syslog 系统。Docker 官方说明,syslog 驱动会把日志写到 syslog facility,前提是宿主机上有可用的 syslog 服务

driver: none 完全不记录日志
options表示这个驱动自己的配置项,
不同驱动支持的选项不同。Compose 文档说明,驱动相关选项通过 options 这个键值对来传
即使你没在 Compose 里配置 logging:,docker compose logs 也通常能看到日志,因为 Docker daemon 本身有一个默认日志驱动

日志存到哪?

docker info --format '{{.LoggingDriver}}'
docker inspect -f '{{.HostConfig.LogConfig.Type}}' <容器名或容器ID>
docker inspect -f '{{.LogPath}}' <容器名或容器ID>


删除日志





logging 是“Docker 怎么收集和保存容器日志的配置”,而“容器的日志”通常指容器进程写到 STDOUT/STDERR 的那些输出
容器日志:程序往控制台打出来的内容
logging:Docker 用什么方式接住并保存这些内容


network_mode 这个服务容器用哪种网络模式


network_mode:none


network_mode: “host”

network_mode: “service:[service name]”


network_mode: “container:[container name/id]”

networks


顶层 networks:这是在定义有哪些网络,以及这些网络怎么创建,比如用什么驱动

顶层 networks: 里的 driver 是在指定这张网络用哪种网络驱动创建

服务里的 networks:这是在说:这个服务要接入哪些网络。如果一个服务接入同一网络,才能和这个网络上的其他服务通信

aliases 就是这个服务在某张网络上的别名。

restart :容器退出以后,要不要自动再拉起来。




secrets
先在顶层 secrets 里定义 secret,再在具体服务里通过 services..secrets 去引用它;而且 secret 的访问权限是按服务单独授予的。
在 Compose 里,secrets 会把你本机文件里的内容,挂到容器里的 /run/secrets/<名字>
这个位置,而且是按服务单独授权的;只在顶层定义 secret 还不够,服务里还得显式写 secrets: 才能拿到


security_opt 给容器补充或修改安全限制



stop_grace_period





stop_signal 停止容器时,先发哪个信号。



sysctls
sysctls = 给容器自己的“内核参数空间”调参数;能调的前提是这个参数本身支持命名空间隔离,且不能影响宿主机



tmpfs 在容器里面挂一个“临时内存文件系统”
哪个目录被声明成 tmpfs,往那个目录及其子目录写文件,就是写到 tmpfs





ulimits 就是:覆盖这个容器默认的资源上限




volumes 将主机的数据卷或者文件挂载到容器里。
Docker 把容器外部存储大致分成 bind mounts、volumes、tmpfs;其中 bind mount 是“把宿主机上的某个真实文件或目录挂进容器”,而 volume 是“由 Docker 自己管理的持久化存储目录
volumes: 是 Compose 里声明“挂载”的地方;但具体挂载出来的东西,可能是 bind mount,也可能是 Docker volume





bind mount 短语法,宿主机目录 : 容器内目录
Docker Compose 官方文档说明:对于 bind mount,短语法会在宿主机 source path 不存在时自动创建目录,这是为了兼容旧版 docker-compose 行为;长语法里也有 create_host_path,默认是 true。

自动创建的是“目录”,不是文件

创建出来的目录通常是 root 权限
容器运行用户的 UID/GID 要和宿主机挂载目录的 owner 权限匹配

创建出来的目录通常是 root 权限”这句话的意思是:
虽然你是在宿主机上用普通用户执行:
docker compose up -d
但真正帮你创建 bind mount 宿主机目录的,往往不是你当前这个普通用户,而是 Docker daemon。
而 Docker daemon 通常是以 root 用户运行的。


Linux 权限本质上看的是 UID/GID,不是用户名


容器进程是哪个权限在运行
docker compose exec dev-extracting-context ps -o user,uid,gid,comm -p 1

Docker Compose 文档也说明,user 会覆盖容器进程使用的用户;如果 Compose 没设置,则使用镜像 Dockerfile 的 USER,如果镜像也没设置,则默认是 root
docker compose top dev-extracting-context
docker inspect codex-dev-extracting-context --format '{{.Config.User}}'


看 Compose 文件里有没有设置 user
Dockerfile 的 USER 指令就是用来设置后续构建步骤以及容器运行时 ENTRYPOINT / CMD 默认用户的【Dockerfile 里写了 USER 之后,
它后面的 RUN 命令会用这个用户执行;
并且容器启动时,ENTRYPOINT 或 CMD 里的主程序也默认用这个用户执行。】
USER app
意思是:从这一行开始,Dockerfile 后面的命令默认都用 app 这个用户执行;并且容器真正启动时,默认也用 app 这个用户运行程序。
USER 就是在 Dockerfile 里切换默认执行用户;它会影响后面的 RUN,也会影响容器启动时 CMD / ENTRYPOINT 里的主程序
Compose 里的 user 可以覆盖 Dockerfile 的 USER










可以在 docker-compose.yaml 里指定启动用户


如果不想让它自动创建,可以用长语法

Compose 文件里 ${VAR} 这种“变量替换”的来源
Compose 会先从你执行命令的 shell 环境里找变量。Docker 官方文档也说明,变量插值优先来自 shell environment
Compose 也会读取 .env,把 ${USERNAME} 替换掉。Docker 官方文档说明,.env 文件是 Compose 变量插值的默认方式之一
通过 --env-file 指定的 env 文件
对于 ${VAR} 这种 Compose 文件插值,优先级是:
- 当前 shell 环境变量
- 如果没有显式指定 --env-file,则读取当前工作目录 PWD 下的 .env
- –env-file 指定的文件,或者项目目录下的 .env
当前 shell 环境变量

.env 文件

通过 --env-file 指定的 env 文件



总结
Dockerfile 里的 ENV,构建镜像时,后面的 RUN/CMD/ENTRYPOINT 可以用;容器启动后,如果没有被覆盖,容器里也能看到。


compose 旁边的 .env。.env 默认主要是给 Compose 文件做变量替换 用的
.env 会被 Compose 用于插值
docker compose --env-file .env.dev。不要用默认 .env,改用 .env.dev 来做变量替换
environment 可以直接设置容器内的环境变量


compose 里的 environment,明确把这些变量传进容器



compose 里的 env_file,把文件里的变量作为环境变量批量传进容器



容器内环境变量的覆盖顺序




容器里的环境变量,本质上是:给容器内进程使用的一组配置参数
容器启动时,Docker 会把环境变量交给容器的 1 号进程。
之后由这个 1 号进程启动的子进程,默认都会继承这些环境变量




多个 Compose 文件合并时,核心规则是:前面的文件是基础配置;后面的文件在前面的基础上覆盖、追加、合并
Docker 官方说明:多个 -f 文件会按命令行顺序合并,后面的文件会 override 或 add 前面的配置

同名 service:合并。
单值字段:后面覆盖前面。
普通列表:后面追加前面。
environment:按变量名合并,同名后面覆盖。
ports:一般追加,唯一键冲突才合并。
volumes:按容器内 target 合并,同 target 后面覆盖。
command / entrypoint:后面直接覆盖,不追加。
1. 同名 service 会合并,不是整个替换。mapping规则









.environment:按变量名合并,同名覆盖


2. 普通单值字段:后面直接覆盖前面







3. 普通列表字段:后面追加到前面


ports:通常是追加,不是直接覆盖



ports 又不是普通列表那么简单,它属于 Compose 里的 特殊列表资源。因为端口映射有唯一标识,比如:宿主机端口 + 容器端口 + 协议





为了判断两条 ports 配置是不是“同一个端口资源”,Compose 会看这一组字段:ip + target + published + protocol
普通列表:
只知道一条一条往后加。
ports 这种 unique resources:
先看“唯一键”是不是同一个资源。
如果唯一键一样:认为是同一个资源,合并/覆盖。
如果唯一键不同:认为是不同资源,追加。
ip:绑定到宿主机哪个 IP
target:容器内部端口
published:宿主机暴露端口
protocol:协议,tcp 或 udp






volumes:按容器内挂载目标判断是否覆盖
volumes 在 YAML 里确实也是列表,所以它首先也有“追加”的特征。
但是它和普通列表不一样。volumes 属于 Compose 的 特殊唯一资源 unique resources。官方规则是:volumes 虽然写成 sequence/list,但它有唯一键,唯一键是 target,也就是容器内挂载路径。如果新条目不违反唯一性约束就追加;如果唯一键相同,就合并/覆盖







command、entrypoint 不会追加,只会整体覆盖
command、entrypoint 可以写成字符串,也可以写成列表,但在 Compose 多文件合并规则里,它们被当成 “shell command 类字段”,所以 后面的直接覆盖前面的,不追加。Docker Compose 规范明确说,command、entrypoint、healthcheck.test 这类字段的值由后一个文件覆盖,不会追加








新 service 会直接追加

所有相对路径都相对于第一个 compose 文件

想看最终合并结果,用 config
docker compose -f compose.yaml -f compose_dev.yaml config


Compose 是否会替换/删除容器,主要看 project name、service name
Docker Compose 会把一组服务当成一个 project 管理。默认情况下,project name 通常来自 compose 文件所在目录名;也可以通过 -p、COMPOSE_PROJECT_NAME、compose 顶层 name: 指定。官方文档说明了 Compose 项目名的优先级:-p 最高,其次是 COMPOSE_PROJECT_NAME,再是 compose 文件里的顶层 name:,然后才是项目目录名
Compose 创建的容器会带两个关键标签:
com.docker.compose.project = 项目名
com.docker.compose.service = 服务名
Docker 官方服务文档也说明,Compose 会给资源打上 com.docker.compose.project,给服务容器打上 com.docker.compose.service
1. project name 是什么?







docker ps -a \
--format 'table {{.Names}}\t{{.Label "com.docker.compose.project"}}\t{{.Label "com.docker.compose.service"}}\t{{.Networks}}'

project name 通常来自 compose 文件所在目录名;也可以通过 -p、COMPOSE_PROJECT_NAME、compose 顶层 name: 指定

-p

COMPOSE_PROJECT_NAME







compose 顶层 name



更多推荐



















所有评论(0)