docker - ENV、RUN 生成层、图像或容器? (理解文档问题)

标签 docker dockerfile

我搜索过 site:stackoverflow.com dockerfile: ENV, RUN - layers or images并已阅读 Does Docker EXPOSE make a new layer?What are Docker image "layers"? .

在阅读文档时 Best practices for writing Dockerfiles并试图理解这部分:

Each ENV line creates a new intermediate layer, just like RUN commands. This means that even if you unset the environment variable in a future layer, it still persists in this layer and its value can be dumped.



我想起了上面的那部分:

In older versions of Docker, it was important that you minimized the number of layers in your images to ensure they were performant. The following features were added to reduce this limitation:

Only the instructions RUN, COPY, ADD create layers. Other instructions create temporary intermediate images, and do not increase the size of the build.



我已阅读 How to unset "ENV" in dockerfile? .并重做文档页面上给出的示例,它确实证明了 ENV未设置:
$ docker build -t alpine:envtest -<<HITHERE
> FROM alpine
> ENV ADMIN_USER="mark"
> RUN unset ADMIN_USER
> HITHERE
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM alpine
latest: Pulling from library/alpine
89d9c30c1d48: Already exists 
Digest: sha256:c19173c5ada610a5989151111163d28a67368362762534d8a8121ce95cf2bd5a
Status: Downloaded newer image for alpine:latest
 ---> 965ea09ff2eb
Step 2/3 : ENV ADMIN_USER="mark"
 ---> Running in 5d34f829a387
Removing intermediate container 5d34f829a387
 ---> e9c50b16c0e1
Step 3/3 : RUN unset ADMIN_USER
 ---> Running in dbcf57ca390d
Removing intermediate container dbcf57ca390d
 ---> 2cb4de2e0257
Successfully built 2cb4de2e0257
Successfully tagged alpine:envtest
$ docker run --rm alpine:envtest sh -c 'echo $ADMIN_USER'
mark

并且输出对 ENV 都说相同的“正在移除 中间容器 ”和 RUN .
我最近下载了 docker ,别以为它那么老:
$ docker --version
Docker version 19.03.5, build 633a0ea

也许 RUN instructionRUN command是另一回事吗?ENV , RUN - 他们是否创建层、图像或容器?

最佳答案

Docker作为基于镜像和容器两个主要概念的容器化系统。它们之间的主要区别是顶部可写层。当您生成一个新容器时,新的可写层将放在最后一个图像层之上。这一层通常称为容器层。

所有底层图像内容保持不变,运行容器中的每个更改(创建新文件、修改现有文件等)都将复制到这个薄的可写层中。

在这种情况下,Docker 只存储实际的容器数据和一个镜像实例,从而减少了存储使用并简化了底层工作流程。我将它与C语言中的静态和动态链接进行比较,因此Docker使用动态链接。

图片是layers的组合.每一层只是与它之前的层的一组差异。

文档说:

Only the instructions RUN, COPY, ADD create layers. Other instructions create temporary intermediate images, and do not increase the size of the build.



这里的描述既不太清楚也不准确,一般来说,不仅这些说明在最新版本的 Docker 中创建层,文档概述的方式。

例如,默认情况下 WORKDIR如果给定的路径不存在,则创建给定的路径并将目录更改为该路径。如果创建了新路径 WORKDIR将生成一个新层。

顺便说一句,ENV不会导致图层创建。数据将永久存储在图像和容器配置中,没有简单的方法可以摆脱它。基本上,有两个选项,如何组织工作流:
  • 临时环境变量 ,它们将在当前 RUN 结束之前可用。指令:
  • RUN export NAME='megatron' && echo $NAME # 'megatron'
    RUN echo $NAME # blank
    
  • 清除环境变量 ,如果没有 env 或它的空白内容对您来说没有区别,那么您可以这样做:
  • ENV NAME='megatron'
    # some instructions
    ENV NAME=''
    RUN echo $NAME
    

    在 Docker 的上下文中,命令和指令之间没有区别。对于 RUN任何不更改文件系统内容的命令都不会触发永久层的创建。考虑以下 Dockerfile :
    FROM alpine:latest
    RUN echo "Hello World" # no layer
    RUN touch file.txt # new layer
    WORKDIR /no/existing/path # new layer
    

    最后,输出将是:
    Step 1/4 : FROM alpine:latest
     ---> 965ea09ff2eb
    Step 2/4 : RUN echo "Hello World"
     ---> Running in 451adb70f017
    Hello World
    Removing intermediate container 451adb70f017
     ---> 816ccbd1e8aa
    Step 3/4 : RUN touch file.txt
     ---> Running in 9edc6afdd1e5
    Removing intermediate container 9edc6afdd1e5
     ---> ea0040ec0312
    Step 4/4 : WORKDIR /no/existing/path
     ---> Running in ec0feaf6710d
    Removing intermediate container ec0feaf6710d
     ---> f2fe46478f7c
    Successfully built f2fe46478f7c
    Successfully tagged envtest:lastest
    

    inspect用于检查 Docker 对象的命令:
    docker inspect --format='{{json .RootFS.Layers}}' <image_id>
    

    这向我们展示了三层获取 FROM、第二个 RUN 和 WORKDIR 指令的 SHA 列表,我建议使用 dive用于探索 docker 镜像中的每一层。

    那么为什么说去除中间容器而不是去除中间层呢?实际执行RUN命令 Docker 需要使用中间镜像实例化一个容器,直到 Dockerfile 的那一行并运行实际命令。然后它将容器的状态作为新的中间镜像“提交”并继续构建过程。

    关于docker - ENV、RUN 生成层、图像或容器? (理解文档问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59264643/

    相关文章:

    python - 无法在docker容器中启动rabbitmq-server,这个Dockerfile怎么写?

    docker - 是否可以在Docker构建期间通过Dockerfile创建命名卷?

    docker - dotnet docker/bin/sh : 1: [dotnet,:未找到

    docker - 在 Dockerfile 中附加到/etc/hosts 的最佳方式是什么

    docker - Docker数据容器,boot2docker和本地文件系统

    node.js - 如何在 Node pg 中设置 postgres 数据库和表

    java - 如何在基于java的docker镜像中安装包管理器

    docker - 如何将变量从 .gitlab.ci.yml 传递到 Dockerfile

    docker-compose - 公开链接服务端口

    reactjs - Docker-Compose 用于 react 和 flask 使用 docker-compose up 执行容器