一文弄懂docker的缓存机制
作者:Cherry Xie
docker的缓存机制
镜像层缓存(Image Layer Cache)
Docker 镜像是分层构建的,每一个 Dockerfile 指令都会生成一个新的镜像层。
Docker 会缓存每一个镜像层,当构建新的镜像时,如果检测到某个层之前已经构建过,就会直接复用该层,而不需要重新构建。
FROM ubuntu:18.04 RUN apt-get update RUN apt-get install -y nginx COPY app/ /var/www/html/ CMD ["nginx", "-g", "daemon off;"]
在上面的 Dockerfile 中, 前两个 RUN 指令对应的镜像层会被缓存。当再次构建镜像时,如果这两个层没有变化,Docker 就会直接复用缓存,而不需要重新执行这两条命令,大大加快了构建速度。
容器层缓存(Container Layer Cache)
每个容器都有自己的可写容器层,位于镜像层之上,用于存储容器的动态数据。
Docker 也会缓存容器层,当重新启动容器时,Docker 会复用之前的容器层,避免重新创建。
# 创建并启动容器 docker run -d --name my-app my-app:v1 # 停止并重启容器 docker stop my-app docker start my-app
在上面的示例中,当我们停止并重启容器时,Docker 会复用之前的容器层,避免了重新创建容器的开销,使容器启动速度更快。
构建缓存(Build Cache)
Docker 还提供了一个专门的构建缓存机制,可以进一步优化镜像构建过程。
构建缓存会将之前构建过程中用到的中间层缓存起来,下次构建时如果检测到相同的指令,就会复用这些缓存层。
用户可以通过设置 --cache-from 参数来指定需要复用的缓存层。
# 构建镜像时启用构建缓存 docker build --cache-from my-app:v1 -t my-app:v2 .
在上面的示例中,我们指定了 --cache-from my-app:v1 参数,告诉 Docker 复用之前构建 my-app:v1 时使用的缓存层,这样可以进一步加快构建速度。
Registry 缓存
Docker Registry 也提供了缓存机制,可以缓存之前拉取的镜像层,下次拉取时直接从缓存中获取,提高拉取速度。
这种缓存机制对于分布式环境下的镜像分发非常有用,可以减轻 Registry 服务器的压力。
综上所述,Docker 的缓存机制涵盖了镜像层、容器层、构建过程以及 Registry 等多个方面,充分利用缓存可以大幅提升 Docker 的性能和效率。如果您还有其他问题,欢迎继续提出
docker的几种写法
指令顺序优化
- Dockerfile 中指令的顺序非常重要,因为 Docker 是根据指令顺序来构建镜像层的。
- 通常将较稳定不变的指令放在前面,易变的指令放在后面,这样可以最大限度地利用缓存。
例如:
# 不好的写法 RUN apt-get update COPY . /app RUN pip install -r requirements.txt # 更好的写法 FROM python:3.9 WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . .
在第二个例子中,我们先复制 requirements.txt 并安装依赖包,这部分内容相对稳定,可以很好地利用缓存。而后面的 COPY . . 指令是最易变的,放在最后可以最大化利用缓存。
分层构建
- 充分利用 Docker 的分层特性,把 Dockerfile 拆分成多个阶段,每个阶段构建一部分功能。
- 这样可以确保稳定的基础层能够最大化利用缓存,而变更的部分只需重新构建对应层即可。
例如:
# 基础层 FROM ubuntu:18.04 RUN apt-get update && apt-get install -y nodejs npm # 应用层 FROM base-image COPY . /app WORKDIR /app RUN npm install CMD ["node", "server.js"]
在这个例子中,我们将 Dockerfile 划分为两个阶段:基础层和应用层。基础层负责安装 Node.js 环境,这部分内容相对稳定,可以很好地利用缓存。应用层则负责复制源代码并安装依赖,这部分内容更容易变更。
多阶段构建
- Docker 17.05 版本引入了多阶段构建的功能,可以进一步优化镜像大小和构建过程。
- 通过在 Dockerfile 中定义多个 FROM 指令,可以实现将构建过程拆分为多个独立的阶段。
例如:
# 构建阶段 FROM golang:1.16 AS builder WORKDIR /app COPY . . RUN go build -o myapp # 运行阶段 FROM alpine:latest COPY --from=builder /app/myapp /myapp CMD ["/myapp"]
在这个例子中,我们先在 builder 阶段编译 Go 代码,然后在 runtime 阶段只复制编译好的二进制文件到最终镜像中。这种方式可以大幅减小最终镜像的体积,同时也能很好地利用缓存。
关于docker的builder阶段
在多阶段构建的场景中,即使在 builder 阶段复制了源代码,最终也只会把编译好的二进制文件复制到运行时容器中。这是因为 COPY --from=builder 指令会选择性地复制指定阶段的文件。
让我们更详细地解释一下:
- 在 builder 阶段,我们将源代码复制到工作目录并执行编译命令go build。此时,容器中包含了源代码和编译好的二进制文件。
- 在 runtime 阶段,我们使用 COPY --from=builder 指令从 builder 阶段复制二进制文件 /app/myapp 到当前容器的 /myapp 路径。
- 这样做的好处是:
1、 只将最终需要的二进制文件复制到运行时容器中,大大减小了镜像大小。
2、builder 阶段的源代码和编译过程不会被带入到最终镜像中,从而避免了不必要的信息泄露。
在多阶段构建中,builder 阶段的源代码确实会被保存下来,但不会被包含在最终的容器镜像中。这里有几点需要说明:
Docker 缓存
- Docker 在构建镜像时会利用缓存机制,加快后续镜像的构建过程。
- 这意味着 builder 阶段产生的中间层镜像会被暂时保留在本地 Docker 环境中,以便后续构建时复用。
最终镜像
- 通过 COPY --from=builder 指令,只有 builder 阶段产出的二进制文件会被复制到最终的运行时容器中。
- 源代码和编译过程中产生的其他文件不会被包含在最终镜像中。
开发与部署分离
- 多阶段构建的一个重要目的就是将开发和部署环境进行分离。
- 开发阶段的源代码和构建过程保留在 builder 容器中,不会泄露到最终的部署容器中。
到此这篇关于一文弄懂docker的缓存机制的文章就介绍到这了,更多相关docker 缓存机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!