本文旨在通过Dockerfile的指令展开Docker镜像的原理,不再介绍Docker是什么了。
一、核心指令及其增强语法
1. 基础指令
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
| # FROM 每个 Dockerfile 都必须以 FROM 指令开始
FROM ubuntu:20.04
# RUN 执行命令并将结果打包到镜像中,通常用于安装软件或执行某些配置操作
RUN apt-get update && apt-get install -y nginx
# CMD 指定容器启动时默认执行的命令。不同于 RUN,CMD 只在容器启动时执行。
CMD ["nginx", "-g", "daemon off;"]
# ENTRYPOINT 设置容器启动时要执行的主命令。与 CMD 类似,但更适合于设置不可更改的启动命令。
# 一个Dockerfile只能有一个ENTRYPOINT,出现多个时,前面会被最后一个所覆盖
# 通常来说可以和CMD配合使用,如
# ENTRYPOINT ["nginx"]
# CMD ["-g", "daemon off;"]
# 这样启动的时候,可以通过 docker run my_image -g "daemon on;" 这种方式覆盖CMD同时保留默认启动参数的效果
ENTRYPOINT ["nginx", "-g", "daemon off;"]
WORKDIR /app
# COPY 从构建主机将文件或目录复制到镜像中
# ADD 与 COPY 类似,但可以处理本地 tar 文件的自动解压以及 URL 的下载
COPY . /app
ADD https://example.com/app.tar.gz /app
ENV APP_VERSION 1.0
# 声明容器的外部端口,但不自动发布端口
EXPOSE 80
# VOLUME 声明挂载点,将容器的数据目录映射到宿主机目录或其他容器。
# 即使不使用该指令,在docker run启动时,仍然可以使用-v或--mount进行挂载
# docker run -v <宿主机目录>:<容器内目录> <镜像名>
# docker run --mount type=bind,source=<宿主机目录>,target=<容器内目录> <镜像名>
VOLUME ["/data"]
# ONBUILD 用于定义`延迟执行`的构建指令,即当该镜像被用作基础镜像来构建其他镜像时才会执行。一般情况下,它不会在构建当前镜像时触发。
# 举我用到的场景:
# 1. 同步私有仓库管理的config文件,可能有因为不同项目或者语言导致相同的配置数据出现异构
# 2. 安装依赖包搭建编译环境
# 一般来说都是在构建派生镜像时需要拉取最新配置或者依赖包时,可以通过ONBUILD降低派生镜像Dockerfile的复杂度
ONBUILD COPY . /app
ONBUILD RUN cd /app && npm install
|
上面命令基本足够编写一个复杂的镜像构建脚本,但个别有一些高级选项,可以继续研究一下
2.基础指令上高阶用法
RUN
其实有两种缓存layer的方式,分别是
1
2
3
| RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
RUN --mount=type=bind,source=/path/to/local/.m2,target=/root/.m2 mvn install
|
COPY
和ADD
都有一个link选项,用于让Docker在将文件或目录从宿主机复制到镜像时,创建硬链接而不是直接复制文件。
在普通Web项目里面很少用到,但在AI训练机器上会很常见,例如这里Depot AI。
因为AI的数据集非常庞大,不可能完全复制到镜像当中,这会导致构建的镜像过大而占用制品库空间,可以通过link
的形式将文件链接到镜像的文件系统中。
1
2
3
4
| FROM python:3.10
COPY --link --from=depot.ai/runwayml/stable-diffusion-v1-5 /v1-inference.yaml .
COPY --link --from=depot.ai/runwayml/stable-diffusion-v1-5 /v1-5-pruned.ckpt .
|
二、镜像构建原理,Layers之间的关系
我觉得需要搞明白一件事情就是,Docker不是VM,任意基础镜像都不存在安装一个完整的操作系统。