在生产中,我们往往会需要通过官方镜像制作一些企业的基础镜像,并在自己制作的基础镜像上添加各个项目的代码,以完成项目镜像的构建工作。除非是要进行必要的安全更新或模块的拓展,否则基础镜像不必经常变动。
- 制作镜像的方式
制作镜像主要有两种方式,一种是手动制作镜像,一种是使用Dockerfile自动制作镜像。
1.1 手动制作镜像
手动制作镜像是针对已运行的容器所发生的变动来制作的镜像,如果后期镜像发生了变动,需要重新制作镜像,这必然会给镜像的维护带来麻烦,因此不推荐该方式。
1.2 Dockerfile文件制作镜像
1.2.1 什么是Dockerfile
Dockerfile是一种能被Docker程序解释的脚本,它是由一条条的命令所组成,每条命令对应Linux下面的一条命令,Docker程序将这些Dockerfile命令翻译成真正的Linux命令。
1.2.2 Dockerfile的特点
Dockerfile有自己的书写方式和支持的命令,Docker程序读取Dockerfile并根据指令生成Docker镜像,相比手动制作镜像的方式,Dockerfile能更直观的展示镜像时如何产生的。
有了写好的各种Dockerfile文件,当后期某个镜像有额外的需求时,只需在之前的Dockerfile添加或者修改相应的操作即可重新生成Docker镜像,避免了重复手动制作镜像的麻烦出现。
1.2.3 Dockerfile命令
Dockerfile通常会包含如下命令:
(1)FROM:用于指定父镜像,如centos:7.6.1810,除了注释行,FROM要放在Dockerfile文件的第一行;
(2)ADD:用于添加宿主机的文件、目录等资源到镜像中,会自动解压tar.gz格式压缩包,不会自动解压zip压缩包;
(3)COPY:类似于ADD,也是用于添加宿主机的文件、目录等资源到镜像中,但不会自动解压任何压缩包;
(4)MAINTAINER:标注镜像的作者信息;
(5)LABEL:设置镜像的属性标签;
(6)ENV:用于设置容器的环境变量;
(7)USER:指定运行操作的用户;
(8)RUN:执行shell命令,但必须是非交互式的,例如yum/apt install安装服务一定要加上-y;
(9)VOLUME:用于定义卷,例如将宿主机的某个目录挂载到容器中;
(10)WORKDIR:用于定义工作目录;
(11)EXPOSE:声明要把容器的哪些端口映射到宿主机;
(12)CMD:指定镜像启动为容器时的默认命令或脚本;
(13)ENTRYPOINT:也可以指定容器启动时的命令或脚本,如果和CMD同时使用,会将CMD的命令当做参数传递给ENTRYPOINT后面的脚本。
- Docker镜像分层
利用Dockerfile文件来制作镜像时,我们可以采取一步到位的方式,将官方镜像与我们的最终的业务镜像一次性整合到一起,也可以采取分层制作镜像的方式,通常我们是推荐使用分层制作。
2.1 为何要进行镜像分层
2.1.1 直接构建业务镜像的弊端
生产中,如果是直接将官方镜像构建成业务镜像,往往会出现两个比较严重的弊端:
(1)工程量大:构建业务镜像时涉及到的操作非常多;
(2)镜像变动需重建镜像:一旦后期镜像发生了变动,需要重新开始一步步构建镜像,所耗费的时间成本比较大。
2.1.2 镜像分层的优势
镜像分层的优势表现为:
(1)最明显的优势在于实现了资源共享,由于多个镜像可能都是在同一个基础镜像上构建而来,宿主机上只需保留一份基础镜像即可,极大地节省了磁盘空间。同时,内存只需加载一份基础镜像,就能为所有使用该基础镜像的容器提供服务;
(2)提高了上传和下载的速度,由于采用了分层,基础镜像如果已经存在相同的内容则不会重复进行上传或下载操作,这也为后期镜像的变动需进行的重建提供了极大地便利。
2.2 如何进行镜像分层
镜像分层的原理如下图所示:
镜像分层说明:
(1)在制作镜像时,我们的宿主机通过Docker官方或其它途径获取centos、ubuntu或alpine等镜像来启动容器,并进行一些必要的初始化工作,完成后生成基础镜像;
(2)在已完成初始化工作的基础镜像上,我们可能会需要通过源码编译等方式来部署业务所需的容器运行环境,构成基础镜像之上的第二层、第三层镜像;
(3)通过一层层镜像的叠加,最终形成符合企业要求的业务基础镜像,最上面的一层镜像只需添加具体业务要使用的代码即可。
- 使用Dockerfile创建nginx镜像
因为nginx本身并不是特别复杂的服务,在利用Dockerfile来制作nginx镜像时,整体上我们加上两层镜像即可,一层为官方镜像的初始化工作,一层为nginx镜像。
3.1 制作centos基础镜像
3.1.1 规划目录
为了后期能更加方便地对各种类型的业务或系统进行管理,建议提前规划好Dockerfile的目录,以便后期镜像较多时能进行合理分类。
3.1.2 下载镜像
通过Docker官方下载centos、ubuntu或alpine等镜像,具体使用什么看企业需求和个人操作习惯,因笔者习惯使用centos,就以centos:7.8.2003来演示。
3.1.3 Dockerfile文件制作centos基础镜像
3.1.3.1 注意事项
生成镜像时会在执行命令所处的目录寻找Dockerfile文件,因此要进入到对应的目录中创建Dockerfile文件,并且Dockerfile文件名只能写为Dockerfile或者dockerfile,加其他字符会无法识别。
3.1.3.2 Dockerfile+脚本构建镜像
Dockerfile文件内容如下图所示,主要是安装epel源和一些常用命令。利用Dockerfile生成镜像时需要用到docker build命令,使用-t选项可以设置标签等信息,后期可能会在此基础上修改并生成新的镜像,因此可以将构建镜像的命令写到脚本中。
3.1.4 验证镜像可用性
等待Dockerfile命令执行完毕,查看镜像时已经生成了指定tag的centos基础镜像。
使用该镜像来启动一个容器,进入容器后,可以看到Dockerfile文件中指定的命令都已经完成了安装。
3.2 制作nginx镜像
3.2.1 获取nginx源码包
进入到/opt/dockerfile/web/nginx/目录下,考虑到后期可能还会有其他版本的nginx镜像,因此也可以创建专门给nginx1.18.0的目录,进入目录后通过nginx官网获取1.18.0版本源码包。
3.2.2 创建Dockerfile文件
nginx镜像的Dockerfile文件内容如下图所示:
nginx镜像Dockerfile命令说明:
(1)FROM部分要指定父镜像为上面新构建好的centos基础镜像;
(2)ADD命令将宿主机上的nginx1.18.0版本源码包解压至容器的指定目录;
(3)RUN命令部分需要先进入到解压目录,再执行编译安装,因为是演示,笔者就不加很多参数了,只是指定安装目录。
3.2.3 制作nginx镜像
与centos基础镜像的制作相同,我们可将nginx镜像构建命令写入到脚本中。
3.2.4 验证镜像可用性
Dockerfile文件命令执行完毕,查看镜像时,可以看到nginx1.18.0镜像已经构建完成。
根据已制作好的nginx镜像来启动容器,进入容器后可以看到已经在安装目录下生成了nginx程序,并且可以执行,浏览器通过宿主机的端口映射也能访问到nginx的默认主页,说明镜像是可用的。
- nginx镜像优化
虽然以上的nginx镜像已经制作完成,但很多默认项都不符合实际生产的需求,如果后期运行的容器较多,一个个地去修改,工程量必然很大,因此还要加以完善。
4.1 指定配置文件
4.1.1 配置文件修改内容
宿主机可以提前准备好与镜像相同版本的nginx配置文件,在配置文件中我们可以指定容器运行用户、工作进程数和默认主页面等。
4.1.2 创建默认首页
由于配置文件指定了nginx新的默认主页路径,但容器启动后该路径并不存在对应文件,因此可以提前在宿主机创建好。
例如笔者在nginx镜像Dockerfile文件所属目录下创建code/子目录,并创建对应的默认页面,打包后,后面添加到Dockerfile文件中。
4.1.3 修改Dockerfile文件
根据以上配置文件的修改内容,需要在原有Dockerfile文件的基础上加入创建nginx用户、拷贝nginx配置文件和默认主页。
4.1.4 重新制作nginx镜像
由于Dockerfile中原有的命令并未发生变动,故重新构建nginx镜像时,只是针对新添加的命令做了变动。
4.1.5 验证镜像可用性
以重新构建的nginx镜像启动容器,登录后可以看到index.html文件以自动放在了/data/nginx/html目录下,配置文件也变成了宿主机上的指定配置文件,同时nginx用户也创建好,通过手动开启nginx服务,浏览器也能成功访问到指定web主页内容。
4.2 启动容器自动开启nginx服务
到上一步结束,一个完整的nginx镜像已基本构成,唯一不足的就是启动容器时需要手动开启nginx服务。
4.2.1 修改Dockerfile文件
要想做到启动容器时自动开启服务,需要用到Dockerfile文件中的CMD命令,此外,容器要有一个能在tty前端一直执行的进程才能保持自身的运行。nginx镜像在启动容器时,要想保持自身能处于运行状态,可以加上“daemon off;”参数(也可以将参数直接放在配置文件中,只在Dockerfile文件中添加启动nginx服务命令)。
4.2.2 重新制作镜像
按照之前的方法重新构建nginx镜像。
4.2.3 检测nginx镜像可用性
此时根据已制作的nginx镜像启动容器,进入容器可以看到80端口已经打开,通过浏览器是可以直接访问的,说明启动容器时即自动开启了nginx服务。
此外也可以将nginx导出,发送到其他已安装过Docker的主机中导入镜像使用。例如笔者将nginx镜像导出后发送至ip为10.0.0.7的主机,导入后也可使用镜像来创建容器,并且浏览器也能访问。