如何理解 Dockerfile 指令

  • 结合 supervisor 的配置和 shell 命令去类比去理解,有很多相似的地方。
  • 是 DSL,和最近做的 makefile 挺类似。它们都规定了一些语法,这些语法最终会按照解释器执行。然而这些语法又不完备,所以常常需要借助 shell 的语法。

    注释

    1. # this is comment

    Dockerfile 语句换行

    跟普通的 shell 程序一样换行.
    注意,换行符不能和前面的字符有空格,/batch_test/requirement.txt\ 必须要联在一起。
    1. RUN "pip install --no-cache -r /batch_test/requirement.txt\
    2. -f /Users/yutou/shouxin/sxProject/pysubway/pysubway/gitignore/batch_test/local_source\"

    FROM

    定义基础镜像,基础镜像应该选择官方的,未经过修改的镜像,这样的镜像体积小,别人使用你的 docekerfile 生成镜像也不会找不到。
    1. FROM centos:7

    ENV

    设置环境变量。环境变量就是 Linux 中 export 设置的那个环境变量。

    设置环境变量

    举例,设置环境变量 PATH 时应该写成如下形式:
    1. ENV PATH="/opt/gtk/bin:${PATH}"

    使用环境变量

    1. ${PATH}

    ARG

    ARG 指令用来定义外部传入参数。
    如果定义了外部传入参数,在打包docker镜像时需要指定传入参数及其值。

    使用场景

    1 个 Dockerfile 多个环境打包,可以使用 ARG ,这样比起 ENV 没有写死,便于打包不同环境的镜像。

    Dockerfile 中定义

    示例,程序运行时需要指定环境和配置文件:
    1. FROM alpine:latest
    2. ENV projectName=guard_v2
    3. ENV projectPort=8080
    4. ENV containerBinary=/$projectName/$projectName
    5. ARG env
    6. ARG conf
    7. RUN mkdir /$projectName
    8. COPY $PWD/$projectName /$projectName
    9. RUN chmod +X $containerBinary
    10. EXPOSE ${projectPort}
    11. RUN ["$containerBinary", "-env=$env", "-conf=$conf"]

    build 时传入命令

    ARG指令定义的参数,在docker build命令中以—build-arg a_name=a_value形式赋值。
    1. docker build . --build-arg env=local

    RUN

    RUN 只能在镜像中执行

    比如这个错误实例中, -f /usr/local_source 中的目录是宿主机中的目录。
    RUN 命令中出现的目录都是镜像中的目录,不是宿主机中的目录:
    1. RUN pip install --no-cache -r /batch_test/requirement.txt\
    2. -f /usr/local_source

    RUN 后可以加环境变量

  1. RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .

COPY

COPY 与 ADD

  1. COPY $PWD/ /batch_test/

COPY 和 ADD 都是进行复制,ADD 在复制压缩包时还能自动解压。
COPY 移动文件夹时,

  1. COPY ./sqlaudit /kylin/sqlaudit/
  2. COPY ./sqlaudit /kylin

优雅 COPY

先切换目录,然后直接 copy 就OK了。

  1. WORKDIR /
  2. COPY . .

COPY —from

参考链接:https://docs.docker.com/develop/develop-images/multistage-build/
参考多级打包。

踩坑

  • 镜像文件件必须以 / 结尾。

COPY $PWD/* /batch_test , 抛出的异常:When using COPY with more than one source file, the destination must be a directory and end with a /.

  1. COPY $PWD/* /batch_test/
  • 宿主机目录后面不要加 * 。这样的做法会让 $PWD/ 下的一级子目录都不会在镜像中创建

参考链接,保持目录结构:https://codeday.me/bug/20170910/68286.html

  1. COPY $PWD/* /batch_test/

WORKDIR

WORKDIr 相当于 shell 中的 cd

WORKDIR 相当于 cd,cd 到哪个目录,RUN 后面的命令就在哪里执行。这就是所谓的工作目录。最后一个 WORKDIR 指定的目录,也是 CMD 执行的目录。——毕竟 CMD 也是靠 sh 来执行的。

目录不存在会自动创建

WORKDIR 指定的目录如果不存在会自动创建,所以不必使用 RUN mkdir xx 来创建。

WORKDIR 可以被覆盖

可以在docker run命令中用-w参数覆盖掉WORKDIR指令的设置,如:

  1. # docker run -w / myimage1

上面的-w参数把容器的工作目录设置成了根目录,而根目录下没有test.txt文件。所以结果显示:test.txt: No such file or directory。

VOLUME

声明要挂载的目录,只是声明而已,并不是说在创建容器时真的会挂载

EXPORT

声明要暴露的端口,只是声明而已,并不是说在创建容器时真的会暴露。

ENTRYPOINT

ENTRYPOINT 与 CMD 的不同:
ENTRYPOINT 我认为最牛的地方在于,能够接收启动容器时的参数。

CMD

  1. CMD ["/bin/bash","-c","supervisord -c ${SUPERVISORD_CONF}\nwhile true;do sleep 100;done\n"]

CMD的主要是为一个正运行的容器提供默认执行命令。如果存在多个CMD指令,那么只有最后一个会被执行。
如果在容器运行时指定了命令,则CMD指定的默认内容会被替代。

为 ENTRYPOINT 提供默认参数

  1. CMD ["参数1", "参数2"]

CMD 调试技巧

把 CMD 执行的命令用 RUN 来执行,如果 build 镜像时能正常启动,说明 CMD 后面跟的命令是正确的。

例子

  • docker 内部署 supervisor 和 web app 的启动命令:

    1. CMD ["/bin/bash","-c","supervisord -c ${SUPERVISORD_CONF}\nwhile true;do sleep 100;done\n"]

    build 生成镜像

    进入 dockerfile 所在的目录,执行:

  • . 代表当前目录,build 命令会打包当前的文件。

  • -f 指定 Dockerfile 文件,默认名称为 Dockerfile.
  • -t , 镜像名称。
  • --build-arg , ARG 中传递的参数。
  • path, 如果在项目根目录下,写一个 .
    1. docker build -f <Dockerfile> -t <imageName>:<tag> <path>

    踩坑

    path 使用绝对路径

    path 使用相对路径,直接执行 docker build 时没有问题,但是将打包命令放到 shell 脚本时执行就容易出现问题。
    使用绝对路径示例:
    1. docker login --username=${aliyun_registry_username} registry.cn-shanghai.aliyuncs.com --password ${aliyun_registry_password}

    例子1 supplier

    supplier 是 guard 2.0 中抽出的供应商服务。

    调试

    打印变量

    $变量名 , 使用 RUN echo 打印 Dockerfile 中的变量是否符合预期。
    1. RUN echo $变量名