编写Dockerfile的最佳实践

Docker可以通过Dockerfile自动构建镜像,在Dockerfile文件中包含构建镜像的全部指令,在教程中将会介绍编写Dockerfile构建镜像的最佳的实践

5 min read
By myfreax
编写Dockerfile的最佳实践

Docker可以通过Dockerfile自动构建镜像,在Dockerfile文件中包含构建镜像的全部指令,在教程中将会介绍编写Dockerfile构建镜像的最佳的实践。

使用.dockerignore文件,在很多案例中,最好的方式把Dockerfile放在一个空的目录的中,然后仅仅添加Dockerfile需要构建的文件。

这样可以提高构建速度,也可以通过.dockerignore排除一些不需要的文件与目录,这个文件支持模式匹配,与Git的.gitignore文件类似。

避免不必要的安装包,为了减少复杂性,依赖,文件大小以及构建时间,必须要避免一些不必要的安装包,比如你没有必要在数据库的镜像中安装一个文本编辑器。

一个容器一个进程,几乎在所有情况下,你只需要在一个容器中运行一个进程,解偶应用程序到多个容器中,

这样可以很容易实现水平扩展和容器的重用,如果某个服务依赖其它的服务,可以使用container linking

最小化layers的数量,你需要考虑Dockerfile可读性和长期维护性找到平衡点并尽可能减少layers的使用。

通过使用脚本形式减少RUN指令的使用。例如将apt update和apt install ssh合并为apt update &&  apt install ssh。多参数时使用换行参数。

构建缓存,在构建镜像期间将会按顺序执行Dockerfile中的每一个指令,并检查是否已存在需要用到的镜像,而不是创建一个新的重复镜像,

如果你不想使用缓存,可以在docker build命令后面指定--no-cache=true选项。

介绍Dockerfile

FROM建议基于官方的镜像来构建新的镜像

一如既往,为了让你的Dockerfile有更好的可读性,可理解性以及可维护性,需要在多行中切割复杂的RUN声明。

apt install是在Dockerfile的RUN指令中最常见的命令,因为它可以安装一个包,但你需要注意以下这些问题。

避免RUN apt-get upgrade或者dist-upgrade,因为它升级该发行版,即14.04会升级到16.04。

避免在单独的一行中RUN apt-get update,可能会因为缓存而造成软件安装不成功的问题。例如你的dockerfile存在两个指令RUN apt-get updateRUN apt-get install -y curl nginx

在镜像image构建时,layers存在于Dcoker的缓存中,Docker检查初始化和修改指令并重用上一步的缓存。

apt-get update不会再被执行,因为已经存在一个缓存版本。nginx和curl安装到的有可能会是过时的版本。

RUN apt-get update && apt-get install -y \
    aufs-tools \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && rm -rf /var/lib/apt/lists/*
最佳的编写方式

CMD指令将会创建容器时运行,一般按照这个CMD ["executable", "param1", "param2"…]格式编写CMD指令的声明。

在大多数情况下,应给予CMD指令一个可交互的shell。例如bash,,python, perl,node等。比如CMD ["perl", "-de0"]CMD ["python"]CMD ["php", "-a"]

EXPOSE指令声明容器将会监听来自那个端口的连接,即容器公开那个端口对外服务。

在传统的应用中,比如Apache web服务器镜像暴露80端口EXPOSE 80,MongoDB镜像需要暴露27017端口EXPOSE 27017

对于外部的访问,可以在docker run创建容器时使用-p选项指定端口。

在简化CMD指令声明时,可以使用ENV指令更新PATH环境变量,比如ENV PATH /usr/local/nginx/bin:$PATH

这样就可以不必在CMD的声明中编写nginx可执行文件的绝对路径。这时CMD指令CMD ["/usr/local/nginx"]CMD ["nginx"]是等价的。

nginx命令将会从PATH环境变量中搜索得到。你也可以使用ENV指令自定义环境变量。

虽然ADD与COPY功能类似,一般来说,复制是首选。这是因为COPYADD更加透明,COPY仅支持本地文件复制到容器。ADD可以请求远程文件,把打包文件提取到容器。

如果你有多个Dockerfile,都有相同的步骤,要分别COPY它们。而不是一次COPY所有。docker会为每一步添加构建缓存,确保每个复制的文件都是一致的。

ENTRYPOINT为image设置主要命令,这样就可以用很简单命令的创建并运行容器。ENTRYPOINT指令也可以组合shell脚本使用。比如Postgres的官方镜像。

ENTRYPOINT ["s3cmd"]
CMD ["--help"]

如果一个服务不需要任何权限即可运行,可以使用USER指令改变为非Root用户。指定的用户必须已经存在。你可以在RUN指令中创建用户