美文网首页Docker容器
Docker之构建上下文详解

Docker之构建上下文详解

作者: JustinSu | 来源:发表于2019-11-05 17:18 被阅读0次

    昨天写了使用 Dockerfile 定制镜像。其中构建上下文这一块没有写,今天把这一块单独拿出来写一下。

    Docker镜像构建

    简单说下构建镜像步骤:

    1. cd Dockerfile 所在目录;

    2. 执行 docker build 构建命令:

      docker build -t <imageName:imageTag> .

    通过上面的工作流,很容易形成这样的理解误区:

    1. docker build 后面的 . 为 Dockerfile 所在的目录;
    2. Dockerfile 文件名 必须为 Dockerfile;

    其实上面这种理解是错误的,要想准确理解其含义,首先我们需要先了解下 Docker 的架构和 docker build 的工作原理。

    Docker架构

    Docker 使用C/S (客户端/服务器)体系的架构,Docker 客户端与 Docker 守护进程通信,Docker 守护进程负责构建,运行和分发 Docker 容器。Docker 客户端和守护进程可以在同一个系统上运行,也可以将 Docker 客户端连接到远程 Docker 守护进程。Docker 客户端和守护进程使用 REST API 通过UNIX套接字或网络接口进行通信。

    docker-structrue.png

    docker build 的工作原理

    1. Client端执行 docker build . 命令 ;
    2. Docker 客户端会将构建命令后面指定的路径(.)下的所有文件打包发送给 Docker 服务端;
    3. Docker 服务端收到客户端发送的包,然后解压,根据 Dockerfile 里面的指令进行镜像的分层构建;

    镜像构建上下文(Context)

    当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

    这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。如果在 Dockerfile 中这么写:

    COPY ./package.json /app/
    

    这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制 上下文(context) 目录下的 package.json。

    因此,COPY这类指令中的源文件的路径都是相对路径。这也是初学者经常会问的为什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。

    示例1 :

    [root@192 test]# ls
    Dockerfile
    [root@192 test]# cat Dockerfile
    FROM alpine:latest
    ADD  /root/mydocker/apache-tomcat-9.0.27.tar.gz /data/soft
    [root@192 test]# ls /root/mydocker/apache-tomcat-9.0.27.tar.gz
    /root/mydocker/apache-tomcat-9.0.27.tar.gz
    [root@192 test]# docker build  .
    Sending build context to Docker daemon  3.072kB
    Step 1/2 : FROM alpine:latest
     ---> 965ea09ff2eb
    Step 2/2 : ADD /root/mydocker/apache-tomcat-9.0.27.tar.gz /data/soft
    ADD failed: stat /var/lib/docker/tmp/docker-builder904012777/root/mydocker/apache-tomcat-9.0.27.tar.gz: no such file or directory
    
    

    可以看出:

    1. 镜像构建上下文路径并不是 Dockerfile文件所在的路径;
    2. Dockerfile 中指令的工作目录是服务端解压客户端传输包的路径,因为 ADD 指令失败了,意味着当前目录并没有 apache-tomcat 文件;

    理解构建上下文对于镜像构建是很重要的,可以避免犯一些不应该的错误。比如有些初学者在发现 COPY /opt/xxxx /app 不工作后,于是干脆将 Dockerfile 放到了硬盘根目录去构建,结果发现 docker build 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 docker build 打包整个硬盘,这显然是使用错误。

    一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个.dockerignore,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。

    那么为什么会有人误以为 . 是指定 Dockerfile 所在目录呢?这是因为在默认情况下,如果不额外指定 Dockerfile 的话,会将上下文目录下的名为 Dockerfile 的文件作为 Dockerfile。

    这只是默认行为,实际上 Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用-f ../Dockerfile.php参数指定某个文件作为 Dockerfile。

    当然,一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。

    示例2

    [root@192 test]# docker images
    REPOSITORY                         TAG                         IMAGE ID            CREATED             SIZE
    tomcat                             jdk8-adoptopenjdk-hotspot   1a2bfb3e6eee        15 hours ago        318MB
    openjdk                            8-jre-slim                  d0cfe439ce3d        13 days ago         184MB
    btnguyen2k/oraclejdk8_jre-alpine   latest                      fab475620d00        4 years ago         208MB
    [root@192 test]# cat ../mynginx/test
    FROM alpine:latest
    [root@192 test]# ls
    apache-tomcat-9.0.27.tar.gz
    [root@192 test]#  docker build -f ../mynginx/test  -t test:v1 .
    Sending build context to Docker daemon  10.99MB
    Step 1/1 : FROM alpine:latest
    latest: Pulling from library/alpine
    89d9c30c1d48: Already exists
    Digest: sha256:c19173c5ada610a5989151111163d28a67368362762534d8a8121ce95cf2bd5a
    Status: Downloaded newer image for alpine:latest
     ---> 965ea09ff2eb
    Successfully built 965ea09ff2eb
    Successfully tagged test:v1
    [root@192 test]# docker images
    REPOSITORY                         TAG                         IMAGE ID            CREATED             SIZE
    tomcat                             jdk8-adoptopenjdk-hotspot   1a2bfb3e6eee        15 hours ago        318MB
    alpine                             latest                      965ea09ff2eb        11 days ago         5.55MB
    test                               v1                          965ea09ff2eb        11 days ago         5.55MB
    openjdk                            8-jre-slim                  d0cfe439ce3d        13 days ago         184MB
    btnguyen2k/oraclejdk8_jre-alpine   latest                      fab475620d00        4 years ago         208MB     
    

    可以看出:

    Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用-f ../mynginx/test参数指定某个文件作为 Dockerfile。

    相关文章

      网友评论

        本文标题:Docker之构建上下文详解

        本文链接:https://www.haomeiwen.com/subject/pldebctx.html