Dockerfile 是应用一系列自定义的命令和格式构成文本文件从而简化镜像构建的过程。但如果处理不当,也会跌坑里。
应用最好不要跑在特权用户( root )底下
Docker 默认所有的应用都会跑在root 用户下,但是这样会造成一些潜在的安全隐患,在生产环境中跑的 Container (容器)最好是通过 USER 命令跑在非特权用户底下,而不是采取默认的用户(root)。
安全隐患是说:如果这个容器中运行的应用是恶意的或者有各种影响系统的问题的,那么跑在root用户下,那对应权限是全部的,可能会影响到系统的配置以及系统的正常运行, root 权限,比如 unload 某个系统 module ,更改了某个系统文件),也会影响其他的 container ( share 一个 kernel )
避免使用 apt-get upgrade
Upgrade 命令是用来升级当前基础镜像的。非特权用户无法 Upgrade 一些核心的应用/基础镜像。而且 Upgrade 命令会打乱已经缓存的镜像,使得整体编译时间加长。在一般情况下,选用正确的基础镜像是不需要升级的,如果真的需要,最好联系基本镜像的维护方,这样所有使用者都能从中获益。如果只是需要安装/更新某个程序 foo ,那么使用 apt-get install(安装)/update(更新) – y foo 就能到达这个目的。
尽量合并命令
Dockerfile 中的每一个命令都会创建一个新的 layer ,而一个容器能够拥有的最多 layer 数是有限制的。所以尽量将逻辑上连贯的命令合并可以减少 layer 的层数,合并命令的方法可以包括将多个可以合并的命令( EXPOSE , ENV , VOLUME , COPY )合并。
Dockerfile 中的每一个命令都会创建一个新的 layer ,而一个容器能够拥有的最多 layer 数是有限制的。所以尽量将逻辑上连贯的命令合并可以减少 layer 的层数,这也可以加快编译速度?
将多个可以合并的命令( EXPOSE , ENV , VOLUME , COPY )合并,比如:
使用“&&”来连接 RUN 命令,比如:
不过过度合并命令可能会影响 Dockerfile 的可读性,所以需要在优化代码和可读性之间做出权衡。
合理安排命令的顺序
命令的顺序会影响编译所需要的时间。每一个命令都会产生一个 layer (层)。如果一个 layer(层) 已经在缓存中,那么生成这个 layer 所需要的时间就很短。从第一个不在缓存的 layer (层)起,所有以后的命令都会被重新编译。因为这个原因,我们推荐将不常变动的命令放在前面,这样可以使得更多的 layer(层) 被成功缓存,从而减少编译时间。
避免在容器中存储数据
容器需要是无状态的,这样方便启动新的 Container(容器) 来替代 down 掉的 Container (容器)。如果容器中包含状态,那么就需要额外的运维人员来恢复 down 掉 Container(容器) 的状态。
使用.dockerignore
使用.dockerignore 可以减少拷贝不必要的文件到 Container(容器) ,这样可以减少镜像大小。比如很多地方都有.git 文件,但是这个文件就不需要拷贝到 Container(容器) 里面。
避免安装不必要的软件
安装不必要的软件既浪费空间又增加编译时间。比如一个数据库的 Container(容器) 就没有必要安装文字编辑软件。 更进一步,如果程序能够在本地编译,那么就不用在 Container(容器) 里安装编译程序所需要的软件和 lib 了。直接把本地编译好的 binary(如: jar包) 拷贝到 Container 里就好。