如何理解 Dockerfile 指令
- 结合 supervisor 的配置和 shell 命令去类比去理解,有很多相似的地方。
- 是 DSL,和最近做的 makefile 挺类似。它们都规定了一些语法,这些语法最终会按照解释器执行。然而这些语法又不完备,所以常常需要借助 shell 的语法。
注释
# this is comment
Dockerfile 语句换行
跟普通的 shell 程序一样换行.
注意,换行符不能和前面的字符有空格,/batch_test/requirement.txt\ 必须要联在一起。RUN "pip install --no-cache -r /batch_test/requirement.txt\-f /Users/yutou/shouxin/sxProject/pysubway/pysubway/gitignore/batch_test/local_source\"
FROM
定义基础镜像,基础镜像应该选择官方的,未经过修改的镜像,这样的镜像体积小,别人使用你的 docekerfile 生成镜像也不会找不到。FROM centos:7
ENV
设置环境变量。环境变量就是 Linux 中 export 设置的那个环境变量。设置环境变量
举例,设置环境变量 PATH 时应该写成如下形式:ENV PATH="/opt/gtk/bin:${PATH}"
使用环境变量
${PATH}
ARG
ARG 指令用来定义外部传入参数。
如果定义了外部传入参数,在打包docker镜像时需要指定传入参数及其值。使用场景
1 个 Dockerfile 多个环境打包,可以使用ARG,这样比起 ENV 没有写死,便于打包不同环境的镜像。Dockerfile 中定义
示例,程序运行时需要指定环境和配置文件:FROM alpine:latestENV projectName=guard_v2ENV projectPort=8080ENV containerBinary=/$projectName/$projectNameARG envARG confRUN mkdir /$projectNameCOPY $PWD/$projectName /$projectNameRUN chmod +X $containerBinaryEXPOSE ${projectPort}RUN ["$containerBinary", "-env=$env", "-conf=$conf"]
build 时传入命令
ARG指令定义的参数,在docker build命令中以—build-arg a_name=a_value形式赋值。docker build . --build-arg env=local
RUN
RUN 只能在镜像中执行
比如这个错误实例中,-f /usr/local_source中的目录是宿主机中的目录。
RUN 命令中出现的目录都是镜像中的目录,不是宿主机中的目录:RUN pip install --no-cache -r /batch_test/requirement.txt\-f /usr/local_source
RUN 后可以加环境变量
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .
COPY
COPY 与 ADD
COPY $PWD/ /batch_test/
COPY 和 ADD 都是进行复制,ADD 在复制压缩包时还能自动解压。
COPY 移动文件夹时,
COPY ./sqlaudit /kylin/sqlaudit/COPY ./sqlaudit /kylin
优雅 COPY
先切换目录,然后直接 copy 就OK了。
WORKDIR /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 /.
COPY $PWD/* /batch_test/
- 宿主机目录后面不要加
*。这样的做法会让$PWD/下的一级子目录都不会在镜像中创建
参考链接,保持目录结构:https://codeday.me/bug/20170910/68286.html
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指令的设置,如:
# docker run -w / myimage1
上面的-w参数把容器的工作目录设置成了根目录,而根目录下没有test.txt文件。所以结果显示:test.txt: No such file or directory。
VOLUME
声明要挂载的目录,只是声明而已,并不是说在创建容器时真的会挂载
EXPORT
声明要暴露的端口,只是声明而已,并不是说在创建容器时真的会暴露。
ENTRYPOINT
ENTRYPOINT 与 CMD 的不同:
ENTRYPOINT 我认为最牛的地方在于,能够接收启动容器时的参数。
CMD
CMD ["/bin/bash","-c","supervisord -c ${SUPERVISORD_CONF}\nwhile true;do sleep 100;done\n"]
CMD的主要是为一个正运行的容器提供默认执行命令。如果存在多个CMD指令,那么只有最后一个会被执行。
如果在容器运行时指定了命令,则CMD指定的默认内容会被替代。
为 ENTRYPOINT 提供默认参数
CMD ["参数1", "参数2"]
CMD 调试技巧
把 CMD 执行的命令用 RUN 来执行,如果 build 镜像时能正常启动,说明 CMD 后面跟的命令是正确的。
例子
docker 内部署 supervisor 和 web app 的启动命令:
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, 如果在项目根目录下,写一个
.。docker build -f <Dockerfile> -t <imageName>:<tag> <path>
踩坑
path 使用绝对路径
path 使用相对路径,直接执行docker build时没有问题,但是将打包命令放到 shell 脚本时执行就容易出现问题。
使用绝对路径示例:docker login --username=${aliyun_registry_username} registry.cn-shanghai.aliyuncs.com --password ${aliyun_registry_password}
例子1 supplier
supplier 是 guard 2.0 中抽出的供应商服务。调试
打印变量
$变量名, 使用RUN echo打印 Dockerfile 中的变量是否符合预期。RUN echo $变量名
