docker - 为 Dockerfile 的最终 CMD 步骤创建中间容器和新图像快照的优势是什么
问题描述
当我们从 Dockerfile 构建镜像时,Dockerfile 中的每个步骤都会导致创建一个中间容器,该容器是从上一步的镜像创建的,并运行该步骤的指令。但是到了CMD的最后一步,在倒数第二步的上面运行CMD命令新建的镜像也会有同样的启动命令CMD吧?
例子:
Step 1. FROM alpine
Step 2. RUN apk --update add redis
Step 3. EXPOSE 6379
Step 4. CMD ["redis-server"]
来自 Alpine 的第 1 步将有图像 I1
第 2 步将使用 I1 和 RUN 命令启动容器 C1,作为启动创建新映像 I2(删除中间容器 C1)
第 3 步将使用镜像 I2 以 EXPOSE 命令作为启动运行一个新的容器 C2,并创建一个新的镜像 I3(删除中间容器 C2)
第 4 步将使用镜像 I3 启动一个新容器 C3 并运行启动命令 CMD 将创建一个新镜像 I4(删除中间容器 C3)
最后 I4 将作为最终图像呈现。现在这个镜像 I4 也将与镜像 I3 相同的 CMD 启动命令。
我的问题是为什么我们必须运行容器 C3 并创建映像 I4?为什么不使用启动命令 CMD 将其留在映像 I3 上,该命令将在用户尝试使用 I3 创建容器时运行
解决方案
它不运行这些容器,这只发生在 RUN 步骤中。也没有添加新层,可以添加文件系统层的唯一步骤是 RUN、COPY 和 ADD。这是对图像配置 json 元数据的修改。图像清单包含对此配置 json 的引用,因此您会为每次配置更改获得一个新的图像摘要。使用经典的构建过程,生成元数据的简单方法是创建一个容器,但正如这里的输出所示,它没有运行,状态仅为created
:
$ DOCKER_BUILDKIT=0 docker build --no-cache --rm=false -f df.intermediate .
Sending build context to Docker daemon 20.99kB
Step 1/4 : FROM alpine
latest: Pulling from library/alpine
ba3557a56b15: Pull complete
Digest: sha256:a75afd8b57e7f34e4dad8d65e2c7ba2e1975c795ce1ee22fa34f8cf46f96a3be
Status: Downloaded newer image for alpine:latest
---> 28f6e2705743
Step 2/4 : RUN apk --update add redis
---> Running in dadc1e8d5044
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
(1/1) Installing redis (6.0.11-r0)
Executing redis-6.0.11-r0.pre-install
Executing redis-6.0.11-r0.post-install
Executing busybox-1.32.1-r3.trigger
OK: 8 MiB in 15 packages
---> 32a040f16a0c
Step 3/4 : EXPOSE 6379
---> Running in 7272af442111
---> 6d8a0569afdf
Step 4/4 : CMD ["redis-server"]
---> Running in 7853be69241a
---> 8027f666c955
Successfully built 8027f666c955
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7853be69241a 6d8a0569afdf "/bin/sh -c '#(nop) …" 9 seconds ago Created awesome_mclean
7272af442111 32a040f16a0c "/bin/sh -c '#(nop) …" 9 seconds ago Created pensive_dewdney
dadc1e8d5044 28f6e2705743 "/bin/sh -c 'apk --u…" 11 seconds ago Exited (0) 9 seconds ago nice_greider
重要的是,图像是引用其他层和配置元数据的 json,并且您不会复制所有层,您只会创建对相同层的额外引用,因此只有几 kb 可以跟踪指向另一个配置文件的另一个 json 文件. 您可以在推送到注册表时看到:
$ docker push localhost:5000/test/intermediate:latest
The push refers to repository [localhost:5000/test/intermediate]
c2b5fcbe3ebc: Pushed
cb381a32b229: Pushed
latest: digest: sha256:f8171dae4c1e77313ce3a6c7496e4c641a5636f55ce1bb15fe4dd6f114680239 size: 739
查看图像配置的历史记录部分,它显示这些步骤没有任何层数据(empty_layer: true
):
$ regctl image inspect localhost:5000/test/intermediate
{
"created": "2021-03-18T13:39:41.050804558Z",
"architecture": "amd64",
"os": "linux",
"config": {
"ExposedPorts": {
"6379/tcp": {}
},
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"redis-server"
]
},
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:cb381a32b2296e4eb5af3f84092a2e6685e88adbc54ee0768a1a1010ce6376c7",
"sha256:c2b5fcbe3ebc0a260ba84a593ab0b7fd9f584242a98170bb0d0b5b89c353d2f0"
]
},
"history": [
{
"created": "2021-02-17T21:19:54.634967707Z",
"created_by": "/bin/sh -c #(nop) ADD file:80bf8bd014071345b1c0364eeb0a5e48f3fb0d87f9c31cb990e57caa652b59b8 in / "
},
{
"created": "2021-02-17T21:19:54.811094842Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer": true
},
{
"created": "2021-03-18T13:39:40.595419392Z",
"created_by": "/bin/sh -c apk --update add redis"
},
{
"created": "2021-03-18T13:39:40.818725501Z",
"created_by": "/bin/sh -c #(nop) EXPOSE 6379",
"empty_layer": true
},
{
"created": "2021-03-18T13:39:41.050804558Z",
"created_by": "/bin/sh -c #(nop) CMD [\"redis-server\"]",
"empty_layer": true
}
]
}
Docker 会分别跟踪其中的每一个,因为它会影响缓存。仅当您从相同的先前状态开始时,重用先前构建中的缓存步骤才有效。
请注意,还有其他方法可以生成此元数据,而 buildkit 无需创建或运行容器即可执行此操作:
$ docker build --no-cache --progress=plain -f df.intermediate .
#1 [internal] load build definition from df.intermediate
#1 sha256:f3ec60f1563d5476385c136badb851fd417c0ae26caf5f61c3e931b8b8b08b86
#1 transferring dockerfile: 121B done
#1 DONE 0.0s
#2 [internal] load .dockerignore
#2 sha256:586943d6f8ffae008b8774450d592ae867dbda8f418150cb140924142d97a698
#2 transferring context: 49B done
#2 DONE 0.0s
#3 [internal] load metadata for docker.io/library/alpine:latest
#3 sha256:d4fb25f5b5c00defc20ce26f2efc4e288de8834ed5aa59dff877b495ba88fda6
#3 DONE 0.0s
#4 [1/2] FROM docker.io/library/alpine
#4 sha256:665ba8b2cdc0cb0200e2a42a6b3c0f8f684089f4cd1b81494fbb9805879120f7
#4 resolve docker.io/library/alpine:latest done
#4 DONE 0.0s
#5 [2/2] RUN apk --update add redis
#5 sha256:655ace4fb184eee9174a1a3911e13e4086750316878b55acc3f07cd5dd0ff2d6
#5 0.510 fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
#5 0.724 fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
#5 0.946 (1/1) Installing redis (6.0.11-r0)
#5 0.960 Executing redis-6.0.11-r0.pre-install
#5 1.042 Executing redis-6.0.11-r0.post-install
#5 1.048 Executing busybox-1.32.1-r3.trigger
#5 1.060 OK: 8 MiB in 15 packages
#5 DONE 1.2s
#6 exporting to image
#6 sha256:e8c613e07b0b7ff33893b694f7759a10d42e180f2b4dc349fb57dc6b71dcab00
#6 exporting layers 0.0s done
#6 writing image sha256:8126afb6ee91f75f6d5ce4c59ae5b66981a708715a217a36e98ccf423c6d025d done
#6 DONE 0.0s
在上面,您可以看到#5
执行 RUN,然后立即写入结果。
推荐阅读
- html - 如何创建相对于其他元素的响应式嵌套 SVG 线?
- azure - 如何在不授予整个订阅所有权的情况下部署 ARM 模板?
- python - 为什么在 except 子句中使用“或”不会导致 SyntaxError?它有有效的用途吗?
- android - 如何通过在android中将文本转换为pdf和pdf到docx来将文本转换为docx
- php - 随机函数范围从1> 10,生成所需的数字
- python - 在 macOS Catalina 中使用 python3 更改 python 语句的问题
- asp.net-core - 为什么我收到错误 AADSTS50011:请求中指定的回复 url 与为应用程序配置的回复 url 不匹配?
- c# - EF 核心不为 InMemoryDatabase 插入所有记录
- android - Android中的模糊imageView
- ios - XML解析中的URL没有响应-Alamofire Swift 5