文章目录
在本章中,主要学习如何构建Docker镜像,其实在Docker公共仓库中已经有大量的镜像,但是并不一定满足我们对镜像的需求,所以我们需要学习如何的构建自己所需要的镜像。
Docker镜像构建
Docker镜像可以通过Docker hub或者阿里云等仓库中获取,这些镜像是由官方或者社区人员提供的,对于Docker用户来说并不能满足我们的需求,但是从无开始构建镜像成本大。常用的数据库、中间件、应用软件等都有现成的Docker官方镜像或社区创建的镜像,我们只需要稍作配置就可以直接使用。
使用现成镜像的好处除了省去自己做镜像的工作量外,更重要的是可以利用前人的经验。特别是使用那些官方镜像,因为Docker的工程师知道如何更好的在容器中运行软件。
当然,某些情况下我们也不得不自己构建镜像,比如找不到现成的镜像,比如自己开发的应用程序,需要在镜像中加入特定的功能。
Docker提供了三种构建镜像的方法:
(1) docker commit命令
(2) 基于本地模板导入
(3) Dockerfile构建文件
1、docker commit
docker commit命令可以基于容器创建镜像,创建过程大致分为三步,先创建容器,在容器中安装我们所需要的内容,再使用docker commit将容器打包为镜像即可。
下面展示一个示例:在centos的base镜像中安装vim-common并保存为新镜像。
1、先基于centos7运行容器,容器名为cy,并使用-it生成终端进入容器。
[root@docker ~]# docker run --name cy -it centos:7
2、在容器中安装vim-common。
[root@2dc0f7d06aa8 /]# yum -y install vim-common
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
* base: mirrors.bupt.edu.cn
* extras: mirrors.cqu.edu.cn
* updates: mirrors.cqu.edu.cn
base | 3.6 kB 00:00:00
extras | 2.9 kB 00:00:00
updates | 2.9 kB 00:00:00
(1/4): base/7/x86_64/group_gz | 153 kB 00:00:00
(2/4): extras/7/x86_64/primary_db | 247 kB 00:00:00
(3/4): base/7/x86_64/primary_db | 6.1 MB 00:00:13
(4/4): updates/7/x86_64/primary_db | 16 MB 00:00:22
Installed:
vim-common.x86_64 2:7.4.629-8.el7_9
Dependency Installed:
vim-filesystem.x86_64 2:7.4.629-8.el7_9
Complete!
[root@2dc0f7d06aa8 /]#
3、退出容器后,使用docker commit将cy容器打包为镜像,新镜像名为centoscy:7
[root@docker ~]# docker commit cy centoscy:7
sha256:282e38de865e6063499df3829e1a6b3854a178b03976472ef3e2b8ffd40a0008
[root@docker ~]# docker images | grep centos
centoscy 7 282e38de865e 24 seconds ago 391MB
centos cy 0311eef88179 25 minutes ago 204MB
centos 7 eeb6ee3f44bd 9 months ago 204MB
[root@docker ~]# docker run --name cy2 -it centoscy:7
[root@f5f2a1d6548c /]# rpm -q vim-common
vim-common-7.4.629-8.el7_9.x86_64
[root@f5f2a1d6548c /]#
这样一个新的镜像就构建完成了,centoscy:7镜像是在centos:7镜像基础之上创建的,通过查看镜像属性,发现centoscy:7要比centos:7镜像大一些。
然而,Docker并不建议用户通过这种方式构建镜像。这是一种手工创建镜像的方式,容易出错,效率低且可重复性弱。更重要的,使用者并不知道镜像是如何创建出来的,里面是否有恶意程序。也就是说无法对镜像进行审计,存在安全隐患。
2、基于本地模板导入
用户可以直接从一个操作系统模板文件导入一个镜像,主要使用 docker [container] import 命令。命令 格式为 docker [image] import [OPTIONS] file|URL|-[REPOSITORY[:TAG]] ,要直接导入一个镜像,可以使用 OpenVZ 提供的模板来创建,或者用其他已导入的镜像模板来创建。OpenVZ 模板的下载地址为 http://openvz.org/Download/templates/precreated。
如:下载了 ubuntu:12.04 的模板压缩包,之后使用以下命令导入即可:
[root@docker ~]# cat ubuntu-12.04-x86-minimal.tar.gz | docker import - ubuntu:12.04
sha256:88216a4bfde304bde7e1195756880acab7921b368da20e3a8591e71afc81b11e
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 12.04 88216a4bfde3 5 seconds ago 146MB
3、Dockerfile构建文件
(1).docker基本概念
Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是Dockerfile。
Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile上添加或者修改指令,重新生成镜像即可,省去了敲命令的麻烦。
(2)文件格式
Dockerfile分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如ADD指令。每执行一条ADD指令,镜像添加新的一层,并提交;最后是CMD指令,来指明运行容器时的操作命令。
示例如下,我们构建一个httpd镜像。
[root@docker ~]# ls
Dockerfile
[root@docker ~]# vim Dockerfile
1、第一行必须指定,基础镜像信息
FROM centos:7
2、维护者信息
MAINTAINER chenyu@example.com
3、镜像操作指令
RUN yum install -y httpd
EXPOSE 80
4、容器启动执行指令
CMD ["/bin/bash"]
把构建容器所需要的指令都存放在Dockerfile文件中,这个文件的名字是固定的,不能够更改,再使用docker build命令构建容器,使用-t定义新的镜像名,如果构建镜像的Dockerfile文件不在当前目录下可以使用-f指定Dockerfile文件路径,示例如下:
[root@docker ~]# docker build -t httpd:cycy /root/
Sending build context to Docker daemon 152MB
Step 1/5 : FROM centos:7
---> eeb6ee3f44bd
Step 2/5 : MAINTAINER chenyu@example.com
---> Running in d41c5db5b3ec
Removing intermediate container d41c5db5b3ec
---> a9c67ae8f88e
Step 3/5 : RUN yum -y install httpd
---> Running in e4777ad252f6
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
* base: mirrors.neusoft.edu.cn
* extras: mirrors.neusoft.edu.cn
* updates: mirrors.neusoft.edu.cn
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.4.6-97.el7.centos.5 will be installed
--> Processing Dependency: httpd-tools = 2.4.6-97.el7.centos.5 for package: httpd-2.4.6-97.el7.centos.5.x86_64
--> Processing Dependency: system-logos >= 7.92.1-1 for package: httpd-2.4.6-97.el7.centos.5.x86_64
--> Processing Dependency: /etc/mime.types for package: httpd-2.4.6-97.el7.centos.5.x86_64
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.4.6-97.el7.centos.5.x86_64
--> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.4.6-97.el7.centos.5.x86_64
--> Running transaction check
---> Package apr.x86_64 0:1.4.8-7.el7 will be installed
---> Package apr-util.x86_64 0:1.5.2-6.el7 will be installed
---> Package centos-logos.noarch 0:70.0.6-3.el7.centos will be installed
---> Package httpd-tools.x86_64 0:2.4.6-97.el7.centos.5 will be installed
---> Package mailcap.noarch 0:2.1.41-2.el7 will be installed
--> Finished Dependency Resolution
Dependencies Resolved
Installed:
httpd.x86_64 0:2.4.6-97.el7.centos.5
Dependency Installed:
apr.x86_64 0:1.4.8-7.el7
apr-util.x86_64 0:1.5.2-6.el7
centos-logos.noarch 0:70.0.6-3.el7.centos
httpd-tools.x86_64 0:2.4.6-97.el7.centos.5
mailcap.noarch 0:2.1.41-2.el7
Complete!
Removing intermediate container e4777ad252f6
---> e7e8c35f9b15
Step 4/5 : EXPOSE 80
---> Running in 56b747392c13
Removing intermediate container 56b747392c13
---> 65c05b81e846
Step 5/5 : CMD ["/bin/bash"]
---> Running in 6f6b7c08c6ab
Removing intermediate container 6f6b7c08c6ab
---> b44f420b6834
Successfully built b44f420b6834
Successfully tagged httpd:cycy
通过以上镜像的构建过程可以看出,Dockerfile文件内的指令会逐一运行,构建过程如下:
1、下载centos7镜像。
2、添加镜像构建者信息
3、基于centos7镜像启动容器,安装httpd软件,安装完毕后将容器打包为镜像。
4、基于上一步生成的镜像启动容器,将80端口打开,打开后将容器打包为镜像。
5、基于上一步生成的镜像启动容器,添加容器启动后需要执行的指令,再打包为镜像。
也就说一条指令就是一层镜像,还可以通过docker history查看镜像的构建过程,这样我们构建的镜像就呈现出透明化,整个构建的过程都可以看到。
[root@docker ~]# docker history httpd:cycy
IMAGE CREATED CREATED BY SIZE COMMENT
b44f420b6834 10 minutes ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
65c05b81e846 10 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
e7e8c35f9b15 10 minutes ago /bin/sh -c yum -y install httpd 203MB
a9c67ae8f88e 13 minutes ago /bin/sh -c #(nop) MAINTAINER chenyu@example… 0B
eeb6ee3f44bd 9 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 9 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 9 months ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
(3)镜像缓存
使用DockerFile文件构建完镜像以后,Docker会把构建过程中的每一层临时镜像进行缓存。在构建新镜像时,可以直接使用之前缓存的镜像层,这样能加速镜像的构建。镜像缓存示例如下:
[root@docker ~]# vim Dockerfile
1、第一行必须指定,基础镜像信息
FROM centos:7
2、维护者信息
MAINTAINER chenyu@example.com
3、镜像操作指令
RUN mkdir /cy
修改之前的Dockerfile文件,然后我们再构建新的镜像,构建过程如下,通过构建过程可以得知,DockerFile文件里面共三条指令,前两条指令都是用之前构建镜像的缓存,只有第三个指令才重新构建了缓存层。如果希望在构建镜像时不使用缓存,可以在docker build命令中加上–no-cache参数。
(4)dockerfile语法
Dockerfile是由一系列指令和参数构成的脚本,一个Dockerfile里面包含了构建整个镜像的完整指令。Docker通过docker build执行Dockerfile中的一系列指令自动构建镜像,常用的Dockerfile指令有以下几种。
1、FROM
指令必须为Dockerfile文件开篇的第一个非注释行,用于指定构建镜像所使用的基础镜像,后续的指令运行都要依靠此基础镜像所提供的的环境。实际使用中,如果没有指定仓库,docker build会先从本机查找是否有此基础镜像,如果没有会默认去Docker Hub Registry上拉取,再找不到就会报错,格式如下。
FROM [:]
FROM @
Digest:镜像的哈希码,防止镜像被冒名顶替。
2、MAINTAINER
指令用于让Dockerfile的作者提供个人的信息,Dockerfile并不限制MAINTAINER指令的位置,但是建议放在FROM指令之后,在较新的Docker版本中,已经被LABEL替代,格式如下。
MAINTAINER “cy@example.com”
3、LABEL
指令用于让用户为镜像指定各种元数据(键值对的格式),格式如下。
LABEL = =
4、COPY
指令用于复制宿主机上的文件到目标镜像中,格式如下。
COPY …
COPY [“”,… “”]
:要复制的源文件或者目录,支持通配符
:目标路径,即正创建的镜像的文件系统路径,建议使用绝对路径,否则,COPY指令会以WORKDIR为其起始路径。如果路径中如果包含空白字符,建议使用第二种格式用引号引起来,否则会被当成两个文件。
5、ADD
指令跟COPY类似,不过它还支持使用tar文件和URL路径。当拷贝的源文件是tar文件时,会自动展开为一个目录并拷贝进新的镜像中;然而通过URL获取到的tar文件不会自动展开。主机可以再联网的情况下,docker build可以将网络上的某文件引用下载并打包到新的镜像中,格式如下。
ADD …
ADD [“”,… “”]
6、WORKDIR
指令用于指定工作目录,可以指多个,每个WORKDIR只影响他下面的指令,直到遇见下一个WORKDIR为止。WORKDIR也可以调用由ENV指令定义的变量。,格式如下。
WORKDIR 相对路径或者绝对路径
7、VOLUME
指令用于在镜像中创建一个挂载点目录。Volume有两种类型:绑定挂载卷和docker管理的卷。在Dockerfile中只支持Docker管理的卷,也就是说只能指定容器内的路径,不能指定宿主机的路径,格式如下。
VOLUME
VOLUME [“”]
8、EXPOSE
指令用于指定容器中待暴露的端口。比如容器提供的是一个https服务且需要对外提供访问,那就需要指定待暴露443端口,然后在使用此镜像启动容器时搭配-P的参数才能将待暴露的状态转换为真正暴露的状态,转换的同时443也会转换成一个随机端口,跟-p :443一个意思。EXPOSE指令可以一次指定多个端口,例如:EXPOSE 11111/udp 11112/tcp,格式如下。
EXPOSE [/] [[/] …]
9、ENV
指令用于为镜像定义所需的环境变量,并可被ENV指令后面的其它指令所调用。调用格式为 v a r i a b l e n a m e 或者 variable_name或者 variablename或者{variable_name},使用docker run启动容器的时候加上-e的参数为variable_name赋值,可以覆盖Dockerfile中ENV指令指定的此variable_name的值。但是不会影响到Dockerfile中已经引用过此变量的文件名,格式如下。
ENV
ENV = …
10、RUN
指令运行于docker build过程中运行的程序,可以是任何命令。RUN指令后所执行的命令必须在FROM指令后的基础镜像中存在才行,格式如下。
RUN
RUN [“executable”, “param1”, “param2”]
通常是一个shell命令,系统默认会把后面的命令作为shell的子进程来运行,以"/bin/sh -c"来运行它。
第二种格式的参数是一个JSON格式的数组,其中"executable"为要运行的命令,后面的"paramN"为传递给命令的选项或参数。
11、CMD
指令用于用户指定启动容器的默认要运行的程序,也就是PID为1的进程命令,且其运行结束后容器也会终止。如果不指定,默认是bash。CMD指令指定的默认程序会被docker run命令行指定的参数所覆盖。Dockerfile中可以存在多个CMD指令,但仅最后一个生效。因为一个Docker容器只能运行一个PID为1的进程。类似于RUN指令,也可以运行任意命令或程序,但是两者的运行时间点不同。RUN指令运行在docker build的过程中,而CMD指令运行在基于新镜像启动容器时,格式如下。
CMD command param1 param2
CMD [“executable”,“param1”,“param2”]
CMD [“param1”,“param2”]
前两种语法格式同RUN指令。第一种用法对于CMD指令基本没有意义,因为它运行的程序PID不为1。
第三种则需要结合ENTRYPOINT指令使用,CMD指令后面的命令作为ENTRYPOINT指令的默认参数。如果docker run命令行结尾有参数指定,那CMD后面的参数不生效。
12、ENTRYPOINT
指令类似CMD指令的功能,用于为容器指定默认运行程序。Dockerfile中可以存在多个ENTRYPOINT指令,但仅最后一个生效,与CMD区别在于,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且这些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序,格式如下。
ENTRYPOINT command param1 param2
ENTRYPOINT [“executable”, “param1”, “param2”]
不过,docker run的–entrypoint选项的参数可覆盖ENTRYPOINT指定的默认程序。
13、USER
R用于指定docker build过程中任何RUN、CMD等指令的用户名或者UID。默认情况下容器的运行用户为root,格式如下。
USER [:]
USER [:]
容器实例
[root@localhost ~]# cat Dockerfile
//基于基础镜像centos:7
FROM centos:7
//维护者信息
LABEL MAINTAINER="lty@qq.com"
//添加Centos-7仓库
//添加epel-7仓库
ADD Centos-7.repo /etc/yum.repos.d
ADD epel-7.repo /etc/yum.repos.d
//安装nginx软件包
RUN yum -y install nginx
//暴露指定端口88
EXPOSE 88
CMD ["/usr/sbin/nginx","-g","daemon off;"] //指定镜像生成容器后在前台运行
7、构建镜像,镜像名称为nginx:v1
[root@localhost ~]# docker build -t nginx:v1 /root/
//生成镜像步骤
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
STEP 1/10: FROM centos
STEP 2/10: LABEL MAINTAINER="lty@qq.com"
--> Using cache 58001e044238c3242028d609c57721ae7eab0b5cfcd4435983f7e00c494f9004
--> 58001e04423
STEP 3/10: WORKDIR /usr/src
--> Using cache 754530092c199ee2dfda687adc28e683123020a227a36323a3acab3e2f5913c7
--> 754530092c1
STEP 4/10: ADD Centos-7.repo /etc/yum.repos.d
--> ff29ef241ad
STEP 5/10: ADD epel-7.repo /etc/yum.repos.d
--> 030508fd3b2
STEP 6/10: RUN yum -y install nginx
Loaded plugins: fastestmirror, ovl
Repository base is listed more than once in the configuration
Repository updates is listed more than once in the configuration
Repository extras is listed more than once in the configuration
Repository centosplus is listed more than once in the configuration
Determining fastest mirrors
* base: mirrors.ustc.edu.cn
* extras: mirrors.ustc.edu.cn
* updates: mirrors.ustc.edu.cn
Resolving Dependencies
Installed:
nginx.x86_64 1:1.20.1-9.el7
Dependency Installed:
centos-indexhtml.noarch 0:7-9.el7.centos
centos-logos.noarch 0:70.0.6-3.el7.centos
gperftools-libs.x86_64 0:2.6.1-1.el7
make.x86_64 1:3.82-24.el7
nginx-filesystem.noarch 1:1.20.1-9.el7
openssl.x86_64 1:1.0.2k-25.el7_9
openssl11-libs.x86_64 1:1.1.1k-4.el7
Dependency Updated:
openssl-libs.x86_64 1:1.0.2k-25.el7_9
Complete! yum源安装nginx服务
--> 511945168d6
STEP 7/10: EXPOSE 80
--> ce67bae6c31
STEP 8/10: ENV PATH /usr/local/nginx/bin:$PATH
--> 8e5053c64ad
STEP 9/10: CMD ["-D","FOREGROUND"]
--> 25e1d03c59b
STEP 10/10: ENTRYPOINT ["/usr/local/nginx/bin/httpd"]
COMMIT nginx:v1
--> 4d412883b9f
Successfully tagged localhost/nginx:v1
4d412883b9fbb792c40d99215dd1a0e01acce4ed2e65206a79431a23b806fe75
8、运行容器,容器名称为自己名字的全拼,映射端口88:80
[root@localhost ~]# docker run -it --name liutianyang -p 88:80 4742ec39c004
"9ca386dd2515627326208f9ee9be8d94f52d311fb8d2b99869a0a4ac50745770"
[root@localhost ~]# docker ps -a
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9ca386dd2515 localhost/nginx:v1 /usr/sbin/nginx -... 2 minutes ago Up 2 seconds ago 0.0.0.0:88->80/tcp liutianyang
[root@localhost ~]# ss -antl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 0.0.0.0:88 0.0.0.0:*
9、进入容器,删除默认网页,写入新的默认网页,网页内容为你们自己名字的全拼
[root@localhost ~]# docker exec -it 9ca386dd2515 /bin/bash
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
[root@9ca386dd2515 /]# cd /usr/share/nginx/
[root@9ca386dd2515 nginx]# ls
html modules
[root@9ca386dd2515 nginx]# cd html/
[root@9ca386dd2515 html]# ls
404.html 50x.html en-US icons img index.html nginx-logo.png poweredby.png
[root@9ca386dd2515 html]# rm -rf index.html
[root@9ca386dd2515 html]# ls
404.html 50x.html en-US icons img nginx-logo.png poweredby.png
[root@9ca386dd2515 html]# echo 6666 > index.html
[root@9ca386dd2515 html]# ls
404.html 50x.html en-US icons img index.html nginx-logo.png poweredby.png
[root@9ca386dd2515 html]# cat index.html
liutianyang
[root@localhost ~]# curl http://192.168.47.137:88
liutianyang
10、通过浏览器,能够正常访问nginx