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 update
和RUN 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功能类似,一般来说,复制是首选。这是因为COPY
比ADD
更加透明,COPY
仅支持本地文件复制到容器。ADD
可以请求远程文件,把打包文件提取到容器。
如果你有多个Dockerfile,都有相同的步骤,要分别COPY
它们。而不是一次COPY
所有。docker会为每一步添加构建缓存,确保每个复制的文件都是一致的。
ENTRYPOINT为image设置主要命令,这样就可以用很简单命令的创建并运行容器。ENTRYPOINT
指令也可以组合shell脚本使用。比如Postgres
的官方镜像。
ENTRYPOINT ["s3cmd"]
CMD ["--help"]
如果一个服务不需要任何权限即可运行,可以使用USER
指令改变为非Root用户。指定的用户必须已经存在。你可以在RUN指令中创建用户。