编写 Dockerfile,让你的程序一键部署
容器的出现让我们的应用可以屏蔽环境,实现处处可运行。但是,应用服务修改就需要同步修改应用的镜像(Docker Image),让服务变更的成本增大。所以,在实践中一定要使用 Dockerfile 构建镜像。
Dockerfile 常用命令
FROM Dockerfile 除了注释第一行必须是 FROM。FROM 后面跟镜像名称,代表我们要基于哪个基础镜像构建我们的容器。
RUN 后面跟一个具体的命令,类似于 Linux 命令行执行命令。
ADD 拷贝本机文件或者远程文件到镜像内 ---不建议使用
COPY 拷贝本机文件到镜像内
USER 指定容器启动的用户
ENTRYPOINT 容器的启动命令
CMD 为 ENTRYPOINT 指令提供默认参数,也可以单独使用 CMD 指定容器启动参数
ENV 指定容器运行时的环境变量,格式为 key=value
ARG 定义外部变量,构建镜像时可以使用 build-arg = 的格式传递参数用于构建
EXPOSE 指定容器监听的端口,格式为 [port]/tcp 或者 [port]/udp
WORKDIR 为 Dockerfile 中跟在其后的所有 RUN、CMD、ENTRYPOINT、COPY 和 ADD 命令设置工作目录。
使用 Dockerfile 的好处
使用 Dockerfile 构建镜像有许多好处:
易于版本管理,Dockerfile 本身就是个文件,方便存放在 Git 仓库座版本车型管理,可以方便地实现找到各个版本之间的变更历史。
过程可追溯,Dockerfile 的每一行指令代表一个镜像层。根据 Dockerfile 的内容即可很明确地知道镜像完整的构建过程。
屏蔽构建环境异构,使用 Dokcerfile 构建镜像无需考虑构建环境,基于相同的 Dockerfile 无论在哪里运行,构建结果都一致。
Dockerfile 编写原则
Dockerfile 有诸多好处,但使用不当也会引发许多问题。如镜像构建时间过长,甚至镜像构建失败;镜像层数过多,导致镜像文件过大。所以编写 Dockerfile 也要尽量遵循一定的原则。
单一职责
由于容器的本质是进程,一个容器代表一个进程,因此不同功能的应用应该尽量拆分为不同的容器,每个容器只负责单一业务进程。
提供注释
Dockerfile 也是代码,应该保持良好的编码习惯,尽量为每一行代码添加注释。让协助者可以一目了然地知道每一行代码的作用,方便扩展和使用。
保持容器最小化
避免安装无用的软件包,不仅可以加快容器构建速度,还可以避免镜像体积过大。
合理使用基础镜像
容器的核心是应用,因此只要基础镜像能够满足应用的运行环境即可。
使用 .dockerignore 文件
忽略不需要参与构建的文件
尽量使用构建缓存
Docker 构建过程中,每一条 Dockerfile 指令都会提交为一个镜像层,下一条指令都是基于上一个指令构建的。如果构建时,发现要构建的镜像层的父镜像层已经存在,并且下一条命令使用了相同的指令,即可命中构建缓存。
正确设置时区
DockerHub 拉取的官方镜像多数使用的 UTC 时间。如果使用东八区,需要修改相应的时区信息。
使用国内镜像源加快镜像构建速度
最小化镜像层数
Dockerfile 注意事项
RUN 指令
RUN 指令在构建时将会生成一个新的镜像层并且执行 RUN 指令后面的内容。所以,指令尽量使用一条。
CMD 指令和 ENTRYPOINT 指令
这两个指令都是容器运行的命令入口。
这两个指令的相同之处,CMD 和 ENTRYPOINT 的基本使用格式分为两种。
第一种为 CMD/ENTRYPOINT["command" , "param"]。这种格式是使用 Linux 的 exec 实现的, 一般称为 exec 模式,这种书写格式为 CMD/ENTRYPOINT 后面跟 json 数组,也是 Docker 推荐的使用格式。
另外一种格式为 CMD/ENTRYPOINT command param ,这种格式是基于 shell 实现的, 通常称为 shell 模式。当使用 shell 模式时,Docker 会以 /bin/sh -c command 的方式执行命令。
使用 exec 模式启动容器时,容器的 1 号进程就是 CMD/ENTRYPOINT 中指定的命令,而使用 shell 模式启动容器时相当于我们把启动命令放在了 shell 进程中执行,等效于执行 /bin/sh -c "task command" 命令。因此 shell 模式启动的进程在容器中实际上并不是 1 号进程。
这两个指令的区别:
Dockerfile 中如果使用了 ENTRYPOINT 指令,启动 Docker 容器时需要使用 --entrypoint 参数才能覆盖 Dockerfile 中的 ENTRYPOINT 指令,而使用 CMD 设置的命令则可以被 docker run 后面的参数直接覆盖。
ENTRYPOINT 指令可以结合 CMD 指令使用,也可以单独使用,而 CMD 指令只能单独使用。
看到这里你也许会问,我什么时候应该使用 ENTRYPOINT,什么时候使用 CMD 呢?
如果你希望你的镜像足够灵活,推荐使用 CMD 指令。如果你的镜像只执行单一的具体程序,并且不希望用户在执行 docker run 时覆盖默认程序,建议使用 ENTRYPOINT。无论使用 CMD 还是 ENTRYPOINT,都尽量使用 exec 模式。
ADD 指令和 COPY 指令
ADD 和 COPY 指令功能类似,都是从外部往容器内添加文件。但是 COPY 指令只支持基本的文件和文件夹拷贝功能,ADD 则支持更多文件来源类型,比如自动提取 tar 包,并且可以支持源文件为 URL 格式。
那么在日常应用中,我们应该使用哪个命令向容器里添加文件呢?你可能在想,既然 ADD 指令支持的功能更多,当然应该使用 ADD 指令了。然而事实恰恰相反,我更推荐你使用 COPY 指令,因为 COPY 指令更加透明,仅支持本地文件向容器拷贝,而且使用 COPY 指令可以更好地利用构建缓存,有效减小镜像体积。
WORKDIR 指令
为了使构建过程更加清晰明了,推荐使用 WORKDIR 来指定容器的工作路径,应该尽量避免使用 RUN cd /work/path && do some work 这样的指令。
版权声明: 本文为 InfoQ 作者【技术小生】的原创文章。
原文链接:【http://xie.infoq.cn/article/a8b8f62830b6f6cf8acdc53e1】。文章转载请联系作者。
评论