镜像概述

基础镜像

  • 用于构建实验用的虚拟机CentOS7镜像iso文件,大小接近9G,而Nginx1.8.1的安装包,大小不足1M。使用docker images查看在Docker中的镜像,可以看到CentOS的镜像大小才200M,而Nginx的镜像大小为130多M,反而大了许多倍,这是为什么呢?

image-20210522192420791

image-20210522192902029

image-20210522192936972

  • Docker中的CentOS镜像,实际上是CentOS的阉割版,只具有最基础的系统功能。很多镜像都是在这样的阉割版系统上构建起来的,这种不依赖其它镜像的迷你操作系统镜像也被称为基础镜像
  • Linux操作系统包含内核空间以及用户空间:内核空间(Kernel)是系统的核心,拥有资源管理等操作的最高权限。用户空间包含了文件系统(rootfs)等工具软件,并通过系统调用调用内核空间的函数以完成相应的功能。基础镜像只需要提供rootfs,直接共享主机的Kernel,大大地缩小了自己的体积。
  • 由于基础镜像直接使用主机的Kernel,容器也只能是使用主机的Kernel而不能对其进行任何的修改。如果需要使用指定版本的Kernel,那么使用虚拟机可能是一个更好的选择。

image-20210522194043125

  • Docker中的CentOS镜像就是一个基础镜像,只包含了基本系统的功能,所以体积非常小。而用于构建虚拟机的iso镜像文件,其中包含了许多的安装包以及桌面程序,体积好几个G也就不足为奇了。而再小的Nginx程序,它也需要一个可以运行它的操作系统,所以Nginx镜像比单纯的安装包大得多。(在DockerHub中可以看到nginx使用到了Debian作为基础镜像)

image-20210522200045999

联合文件系统以及容器的Copy-on-Write机制

  • Nginx镜像使用到了Debian作为基础镜像,在从远程仓库中拉取镜像的时候,也可以看到拉取了许多层。镜像都是通过这样搭积木的形式一层一层地迭代产生新的镜像的,这种分层的结构最大的好处就是可以共享资源。试想:在拉取Nginx镜像的时候,本地以及存在Debian基础镜像了,后面再拉取新的镜像,它也依赖了Debian基础镜像,那么Debian基础镜像就可以直接被共享了,不需要重新到远程仓库中重新拉取。

image-20210522201247089

  • 当启动一个容器时,一个新的容器层会被加到各层镜像的顶部。联合文件系统通过将容器层以及若干镜像层联合起来,得到一个统一的视图。容器层是可读写的,而镜像层是只读的。这种机制与PS的图层机制原理类似,都是通过一层一层地叠加得到一个最终的视图。
  • Copy-on-Write机制:当新增文件时,文件只添加到容器层;删除文件时,只在容器层标记该文件已删除;修改文件时,从上到下查找相应的文件拷贝一份到容器层进行修改;查询文件时,从上到下查找对应的文件读取数据。容器层在Copy-on-Write机制的作用下,文件的增删改查操作都无法影响镜像层,从而保证了镜像层的只读性,使得镜像可以被多个容器共享。

镜像的构建

commit

前面说到,镜像都是一层一层搭积木得到的,为了DIY得到我们自己想要的镜像,我们可以在原有的基础上修修改改缝缝补补得到一个新的镜像。

  • run命令加上-it参数以交互的方式启动并进入CentOS容器,敲入几个命令,好巧啊,都没有。

image-20210522204537277

  • 通过命令yum install -y ncurses 以及yum install -y vim安装对应的工具依赖,然后通过命令exit退出当前容器回到主机(或者使用CTRL+D快捷键)。

  • 此时此刻如果我们直接执行命令docker run -it centos,会启动并进入一个全新的容器,里面并没有我们刚刚安装的工具软件,所以我们需要找到我们之前的容器的ID。通过docker ps -a显示所有的容器(包括已停止的),复制其容器ID。

    1
    2
    3
    4
    5
    6
    # 启动容器
    docker start 容器ID
    # 进入容器 可以看到这个容器中有我们之前安装的工具软件
    docker attach 容器ID
    # 提交修改得到新的镜像
    docker commit 容器ID 新的镜像名称
  • 可以看到,由于安装的两个工具软件,CentOS镜像的大小由209M变为268M。通过新的镜像运行一个新的容器,新的容器中可以直接使用clear以及vim命令。我们通过commit命令成功得到了一个新的镜像。

image-20210522205641568

docker file

通过commit操作得到的一个缺点之一是镜像的构建过程不透明,可能存在安全隐患。哪天你在路上捡到一个U盘,你敢随便往电脑上插么哈哈哈。docker file是一个记录了镜像构建步骤的文件,是构建镜像的图纸。

docker file基础

image-20210523001548698

image-20210523001623547

  • 通过docker file构建镜像的过程如下
1
2
3
4
5
6
7
8
# 编写docker file文件,文件名默认是Dockerfile
FROM centos
RUN yum install -y vim && yum install -y ncurses

# 运行构建命令:docker build [OPTIONS] PATH | URL | -
docker build -t my-centos-dockerfile .

# . 指的是将当前目录作为构建目录,构建目录文件不宜过多,否则有可能导致构建失败
  • 使用docker history $image_name命令可以查看镜像的构建过程,从上图可以看到,镜像my-centos-dockerfile比镜像centos多了一层,下面的三层都是完全一致的。

image-20210523002323298

  • docker file中的每条命令执行时都会产生一个镜像层,而且镜像层之间有缓存机制,如果不想使用缓存机制,可以在docker build命令中加上参数--no-cache

docker file构建流程

  1. 基于基础镜像启动一个容器
  2. 顺序执行docker file中的命令,对容器进行修改
  3. 提交容器的修改,生成新的镜像
  4. 基于新的镜像启动新的容器,移除用于执行命令的容器
  5. 重复2~4直到镜像全部构建完毕

docker file指令

1
2
3
4
5
6
7
8
9
10
11
# docker file支持 #开头格式的注释
- FROM 基础镜像
- MAINTAINER 姓名+邮箱,声明作者,可以是任意字符串。推荐格式:name<email>
- WORKDIR 指定工作目录,为后面的RUN、CMD、ENTRYPOINT、ADD或COPY指令设置镜像中的当前工作目录
- COPY 从构建目录复制本地文件到镜像中,COPY支持两种形式: COPY src dest与COPY ["src", "dest"]。
- ADD 与COPY类似,会自动解压归档文件(各种压缩包)
- VOLUME 挂载的目录
- EXPOSE 暴露端口
- RUN 构建过程中容器中执行指定的命令,常用于安装软件,常常存在多个RUN指令,每个RUN指令都会被执行。
- CMD 为容器提供默认的执行命令,Dockerfile中可以有多个CMD指令,但只有最后一个生效。CMD会被docker run之后的参数替换。
- ENTRYPOINT,容器启动时的命令,可以追加。(Dockerfile中可以有多个ENTRYPOINT指令,但只有最后一个生效)
  • 指定RUN、CMD、ENTRYPOINT三种指令的两种格式
    • shell格式:RUN|CMD|ENTRYPOINT $COMMAND$COMMAND会被/bin/sh -c $COMMAND解析执行。
    • exec格式:RUN|CMD|ENTRYPOINT ["$COMMAND","PARAM 1","PARAM 2","PARAM N"]$COMMAND不会被解析执行。容器中的任务进程就是容器内的 1 号进程,不会通过 shell 执行相关的命令,所以像 $HOME 这样的环境变量是取不到的。推荐使用,这样可以实现优雅退出。
  • exec格式下,CMD或docker run之后的参数会被当作参数传递给ENTRYPOINT。shell格式下,ENTRYPOINT会忽略其它指令提供的额外参数。

image-20210523114203688

image-20210523114630552

镜像的发布

在一个主机上编辑好了docker file文件并成功构建了镜像,怎么在别的主机上进行同样的操作呢

  • 复制docker file到其它主机
  • 将镜像推送到远程仓库,其它主机再从远程仓库拉取