一、背景:
podman镜像可以通过Docker hub或者阿里云仓库中获取,这些镜像由官方或者社区人员提供的,对于podman用户来说并不能满足我们的需求,但是从无开始构建成本大。常用的数据库、中间件、应用软件等都有现成的podman 官方镜像或社区创建的镜像,我们只需要稍微配置就可以直接使用。
使用现成的镜像的好处除了省去自己做镜像的工作量除外,更重要的是可以利用前任的经验。特别是使用那些官方的镜像,因为容器工程师知道如何更好地在容器中运行软件。
当然,某些情况下我们也不得不自己构建镜像,比如自己开发的应用程序,需要在镜像中加入特定的功能。
二、构建镜像的三种方法:
1.podman commit命令:
此命令可以基于容器创建镜像,创建过程可以分为三步,先创建容器,在容器中安装我们所需要的内容,再使用podman commit将容器打包为镜像即可。
示例:在centos的base镜像中安装vim-common并保存为新镜像。
(1)先基于centos7运行容器,容器名为centos-test,并用it生成终端进入容器。
podman run --name centos-test -it docker.io/library/cetos7:7(7为版本信息)
(2)在容器中安装vim-common软件包
e52d /]#yum -y install vim-common
rpm -q vim-common//查看
(3)退出容器后,使用podman commit将centos-test容器打包为镜像,新镜像名为centostest:7
podman ps -a //退出容器后查看所有容器
podman commit centos-test centostest:7
podman images | grep centos //查看新生成的镜像信息
这样,一个新的镜像就构建完成了,cetostest:7镜像是在centos:7镜像的基础上创建的,通过查看镜像属性发现cetostest:7要比centos:7镜像大一些。
这是一种手工创建镜像的方法,容易出错,效率低且可重复性弱,更重要的是,使用者并不知道镜像是如何创建出来的,里面是否有恶意程序,即无法对进行进行审计,存在安全隐患。
构建镜像的过程(即镜像生成容器,生成所需的指令的过程)
注:镜像层只有只读的权限,容器层有可写的权限。
centos7对应的基础镜像(base),如何添加维护者信息,将容器变为镜像?(A、B、C即为临时容器)
原理:此时先将centos7的镜像运行为容器A,在A容器中添加维护者信息,再停掉A容器,将A打包成镜像层继而累加存入镜像中,然后删除A容器,(即合成一个更大的镜像层)。暴露端口:暴露完端口后再停掉容器,打包生成新的镜像,删除C容器。
2.基于本地模版导入:
用户可以直接从一个操作系统模板文件导入一个镜像,主要使用podman import命令,要直接导入一个镜像,可以使用Open VZ模版的下载地址为http://openvz:org/Download/template/precreated。
如:下载ubuntu:12.04的模版压缩包,之后使用以下命令即可:
如:下载了ubuntuL12.04的模版压缩包,之后使用以下命令导入即可:(Xshell:搭载yum源时首先进行挂载,mount /dev/cdrom /mnt,首先安装一个软件,yum -y install lrzsz,然后拖文件,rz -E,)
cat ubuntu-12.04-x86-minimal.tar.gz | podman import - ubuntu:12.04:v1 (v1为版本)
3.podmanfile构建文件(增加了镜像的安全性能):
(1)基本概念:
podmanfile镜像是一个特殊的文件系统,除了提供容器运行所需的程序、库、资源、配置等文件外,还包含一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。
镜像的定制实际上就是定制每一层所添加的配置、文件,如果我们可以把每一层修改、安装、构建、操作的命令都写成一个脚本,用这个脚本来构建、定制镜像,那么之前提到的无法重复的问题、镜像构建透明性的问题、体积问题就都会解决,这个脚本就是podmanfile。
podmanfile是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容就是描述该层如何构建。有了podmanfile,当我们需要在podmanfile上添加或修改指令,重新生成镜像即可,省去了敲命令的麻烦。
(2)文件内格式:
podmanfile分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动指令。一开始必须要指明所基于的镜像名称,接下来会说维护者信息,后面则是镜像操作指令。例如ADD指令,每执行一条ADD指令,镜像添加新的一层,并提交;最后是CMD指令来指明运行容器时的操作指令。
示例:构建一个httpd镜像
vim Podmanfile
1)第一行必须指定基础镜像信息
FROM docker.io/library/centos:7
2)维护者信息
MAINTAINER test@example.com
3)镜像操作指令
RUN yum -y install httpd
EXPOSE 80
4)容器启动执行指令
CMD [“/bin/bash”]
把构建容器所需要的指令都存放在Podmanfile文件中,这个文件的名字是固定的,不能够更改,再使用Podman build命令构建容器,使用-t定义新的镜像名,-f指定Podmanfile。
命令格式:podman build -f Podmanfile -t http:test
podman history localhost/http:test //查看新镜像的构建过程
呈现出透明化,整个构建过程可见。
三、Podmanfile构建镜像的全过程:
1.下载centos7镜像
2.添加镜像构建者信息
3.基于centos7镜像启动容器,安装httpd软件,安装完成后将容器打包为镜像。
4.基于上一步生成的镜像启动容器后,将80端口打开,打开后将容器打包为镜像。
5.基于上一步生成的镜像启动容器,添加容器启动后需要执行的指令,再打包为镜像。
6.常见的podman指令:
(1)FROM指令必须为Podmanfile文件开篇的第一个非注释行,用于指定构建镜像所使用的基础镜像,后续的指令运行都要依靠此基础镜像所提供的环境。使用时,若没有指定仓库,docker build会先从本机查找是否有此基础的镜像,若没有,会默认去Docker Hub Registry上拉取。
(2)MAINTAINER指令用于让Podmanfile的作者提供个人信息,Podmanfile并不限制此指令的位置,但建议在FROM指令之后。在最新的版本中,此指令已经被LABEL替代。
格式:MAINTAINER “test@example.com”
(3)LABEL指令用于让用户为镜像指定各种元数据。
(4)COPY指令用于复制宿主机上的文件到目标镜像中。
格式:COPY <src> <dest>
<src>:要复制的源文件或目录,支持通配符
<dest>:目标路径,即正创建的镜像的文件系统路径,建议使用绝对路径,否测,COPY指令会以WORKRID为起始路径,
路径中包含空白字符,建议用”[]”。
(5)ADD指令,跟COPY指令类似,区别在于它还具有解压功能。即支持tar和URL路径。当拷贝的源文件是tarw文件时,会自动展开为一个目录并拷贝到新的镜像中,然而通过URL获取到的tar文件不会自动展开。
(6)WORKRID用于切换工作目录,(外面用的cd)可以指多个,每个WORKERID影响它下面的指令。WORKRID也可以调用由ENV指令定义的变量。
(7)VOLUME指令用于在镜像中创建一个挂载点目录,其有两种类型:绑定挂载卷和podman管理的卷,即只能指定容器内的路径,不能指定宿主机的路径。
格式:VOLUME <mountpoint> VOLUME [“<mountpoint>”]
(8)EXPORT指令用于指定容器中待暴露的端口号。
如http——>80端口;https——>443端口。有时需要将容器中的80端口和宿主机的8080端口映射,此时需要-p和-P参数。区别为:-P——>随机生成一参数;-p——>根据需求指定固定端口。
(9)ENV指令用于镜像定义所需的环境变量,并可以被ENV指令后面的其他指令所调用。格式为:ENV <key> <vaule>,使用podman run启动容器时加-e的参数,可以覆盖ENV后面定义的变量,但变量的确是被定义了,也不会影响Dockerfile中已经引用过此变量的文件名。
如:ENV a=123
RUN touch $a
podman run -e ‘a=111’,此时111覆盖掉原来的123。
(10)RUN指令运行于podman build过程中运行的程序,可以是任意命令。RUN指令后所执行的命令必须在FROM指令后的基础镜像中存在才行。
格式:RUN <command> //command通常为一个shell命令,系统默认会把后面的命令作为shell子进程来运行,以”/bin/sh-c”来运行它。
RUN[“executable”,”param1”,”param2”]
(11)CMD指令用于用户指定启动容器默认要运行的命令,即PID为1 的进程命令,且运行结束后容器也会终止。如果不指定,默认的是bash。CMD指令指定的默认程序会被podman run会被podman run命令行指定的参数覆盖。Podmanfile中可以存在多个CMD指令,但仅最后一个生效。因为一个podman容器只能运行一个PID为1的进程。类似于RUN指令,也可以运行任意命令或程序,但是两者的运行时间点不同。
RUN和CMD的区别:
RUN:构建镜像的时候运行的指令;CMD:基于新镜像启动时运行的指令。
格式:CMD command param1 param2
CMD [“executable”,”param1”,”param2”]
这两种同RUN指令,第一种用法对于CMD指令基本没有意义,因为它运行的程序PID不为1,即直接运行。
CMD[“param1”,”param2”]
这种需要结合ENTRYPOINT指令结合使用,CMD指令后面的命令作为ENTRYPOINT指令的默认参数。若podman run命令行结尾有参数指定,那CMD后面的参数不生效(即ENTRYPOINT指令优先级高于CMD指令)。
(12)ENTRYPOINT指令类似CMD指令的功能,用于为容器指定默认的运行程序,Podmanfile中可以存在多个ENTRYPOINT指令,但仅最后一个生效,与CMD的区别在于,由ENTRYPOINT指令启动的程序不会被podman run命令行指定的参数所覆盖,而CMD会被覆盖掉。而且这些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序。
(13)USER用于指定podman build过程中任何RUN、CMD等指令的用户名或者UID。默认情况下容器运行用户为root。
格式:USER <user> [:<group>] USER <UID> [:<GID>]