今天我们讨论下,Docker 的用户们关心的问题,如何制作高质量的镜像。首先我们对高质量镜像标准进行定义。
- 镜像兼容性好
- 镜像执行效率高
- 镜像体积小
- 镜像可以被继承
尽量使用Dockerfile制作镜像
有两种方法可以制作docker镜像
- 对一个正在运行的contianer,使用docker commit 创建镜像。
- 基于dockkerfile,使用
docker build
创建镜像
Docker commit提供了一种灵活的制作镜像的方式,但是它的主要作用时对当前docker 容器内容备份。如果你准备做基础镜像,我们是不推荐这种方式。
- Docker commit 会记录docker run时提供的部分参数。
- 没有Dockerfile作为描述文件,不易于镜像管理维护。
- 不容易控制docker镜像的层次
使用Dockerfile做镜像的好习惯:
提供完整的 MAINTAINER
信息
MAINTAINER
指令用与设置 image的作者信息,如果用户出现问题,可以很容易的联系到原始作者。
除非设计目的时把容器当作系统应用程序使用,否则不要使用ENTRYPOINT
有很多关于CMD和ENTRYPOINT 指令如何工作的讨论,下面我们简单复习CMD和ENTRYPOINT的在docker run 时如何工作。
CMD指令的官方解释是:The main purpose of a CMD is to provide defaults for an executing container
ENTRYPOINT指令的官方解释是: An ENTRYPOINT allows you to configure a container that will run as an executable.
CMD,ENTRYPOINT 及Docker run 时指定的命令,参数关系如下:
-
Dockerfile 只设定了
CMD
。 如果docker run 没有指定命令和参数,那么CMD设定的命令作为第一个程序被执行。如果docker run设定了命令和参数,CMD指令将不会被调用, docker run 指定的命令将会作为第一个程序被执行。 -
Dockerfile 设定了
ENTRYPOINT
, 那么ENTRYPOINT指定的命令作为第一个程序被执行。如果docker run 设定了命令和参数,该命令和参数将最为ENTRYPOINT指定命令的参数;如果docker run 没有设定参数,CMD的指令将作为ENTRYPOINT指定命令的参数
所以要小心使用 ENTRYPOINT
,除非你的容器时当作命令来设计的,否则不要使用ENTRYPOINT
使用ARRAY 而不是STRING 来运行程序
你可能已经注意到 CMD和ENTRYPOINT中
ARRAY 和STRING的区别, ARRAY中的命令会被直接执行,即我们所说的exec; 而STRING会被/bin/sh调用执行。
很多镜像使用wrapper scripts 启动应用程序。 如果你有这样的脚本,应该使用exec来执行它。 否则,docker 发出的信号不会被你的程序截获。举个例子:
我使用start.sh 来启动自己的应用。如果 写成 CMD /start.sh 或者 CMD ["/bin/bash/" "/start.sh"], CTRL+C 命令并不能中止我们的程序,正确的写法是 CMD /start.sh
使用shell exec来执行你的wrapper scripts。
很多镜像使用wrapper scripts, 这样可以在启动应用程序之前做一些配置操作。如果你有这样的脚本,请不要忘记 使用 exec 来调用你的应用。 有的时候,docker 发出的信号可能会被wrapper scripts截获。使用exec可以使你的应用进程pid=0.在Dockerfile中,EXPOSE 重要的端口
EXPOSE
指令会使得端口对主机及containers可见。 在Dockerfile中使用 EXPOSE
端口,可以被docker ps/inspect 命令显示,对用户了解如何镜像很用帮助。
应该避免 Volumes的使用
VOLUME
指令和 docker run 的 -v
选项会使用外部的文件系统来代替容器内的文件系统。Volumes 具有容器共享,持久保存数据的优点。但是volume和外部系统关联密切,除非特殊需要,应避免在Dockerfile中设置VOLUME指令。合理的做法是,在容器内部预留目录,用户使用docker run -v 参数挂载。
尽量不要使用 root用户
Dcoker 的系统资源隔离并不彻底,Docker实现了对 进程,网络,挂载,机器名,和共享内存的隔离,但是还存在未被隔离的资源,如:/dev/mem, /dev/sd* file system devices,Kernel Modules。 所以如非特殊需求,请使用non-root运行容器。
要注意Dockerfile的层级关系
Dockerfile中的每条指令都会为镜像增加新的一层。
制作基础镜像,合理的设置指令的先后顺序,能够后续image 生成的时间。在遵循Dockerfile规则的前提下,应该把变化较少的指令放在前面,变化较多的放在后面。
要清除指令产生的临时文件
Docker build过程中,临时文件也会被添加到新的一层中,所以应及时清理临时文件,这样才能保证做出来的镜像个头比较小。 如: 下面命令,中每次安装后清楚缓存就是正确的做法,
yum install mysql;yum clean all
要减少对系统的依赖
一个好的image应该在任何系统上都可以执行,如Linux,Solaris,Windows。这就要求我们的基础镜像,应该尽量及于可控的系统,如centos或者ubuntu. 在基础镜像中完成对系统的配置,如yum 安装,apt安装等。
轻量级更新模式
大量的包安装应该发声在基础镜像阶段,后续软件变更,要抛弃包安装模式,采取轻量的更新方式。最后只更改程序变化的部分。
积累重构
随着时间的推移,可能镜像的层级越来越多,当镜像个头比较大时,可以通过从基础镜像重构来降低容量。