Docker学习笔记 —— 镜像制作(Dockerfile)

Posted 爱敲代码的三毛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker学习笔记 —— 镜像制作(Dockerfile)相关的知识,希望对你有一定的参考价值。

文章目录


Docker容器镜像

1. 容器技术

Linux容器技术是一种轻量级的虚拟化技术,主要特点有:

  1. 轻量:只打包了需要的bins/libs(也就是命令和库文件),与宿主机共享操作系统,直接使用宿主机的内核。

  2. 部署快:容器的镜像相对于虚拟机的镜像更小,部署速度非常快,秒级部署

  3. 移植性好:Build once/Run anywhere (一次构建,随处部署运行)。build,ship,run

  4. 资源利用率高:相对于虚拟机,不需要安装操作系统,所以几乎没有额外的CPU,内存消耗。

Docker容器虚拟机(VM)
操作系统与宿主机共享OS宿主机OS上运行虚拟机OS
存储大小镜像小,便于存储传输镜像庞大(vmdk、vdi等)
运行性能几乎无额外性能损失操作系统额外的CPU、内存消耗
移植性轻量、灵活、适应于Linux笨重,与虚拟机技术耦合度高
硬件亲和性面向软件开发者面向硬件运维者
部署速度快速,秒级较慢,10s以上

2. 容器镜像介绍

容器与镜像之间的关系:

  • docker client 向docker daemon发起创建容器的请求
  • docker daemon查找有无客户端需要的镜像
  • 如无,则到容器的镜像仓库中下载需要的镜像
  • 拿到容器镜像后,启动容器

Docker 镜像就是一组只读的目录,或者叫只读的 Docker 容器模板,镜像中含有一个Docker 容器运行所需要的文件系统,所以我们说Docker 镜像是启动一个Docker 容器的基础。

可以将Docker 镜像看成是Docker 容器的静态时,也可将Docker 容器看成是Docker镜像的运行时。
从Docker 的官方文档来看,Docker 容器的定义和 Docker 镜像的定义几乎是相同,Docker 容器和Docker 镜像的区别主要在于docker 容器多出了一个可写层。

容器中的进程就运行在这个可写层,这个可写层有两个状态,即运行态和退出态。当我们docker run 运行容器后,docker 容器就进入了运行态,当我们停止正在运行中的容器时,docker 容器就进入了退出态。
我们将容器从运行态转为退出态时,期间发生的变更都会写入到容器的文件系统中(需要注意的是,此处不是写入到了docker 镜像中)。

联合文件系统(UnionFS)是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,并层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,应用看到的是挂载的最终结果。
联合文件系统是实现Docker镜像的技术基础。Docker镜像可以通过分层来进行继承。例如,用户基于基础镜像(用来生成其他镜像的基础,往往没有父镜像)来制作各种不同的应用镜像。这些镜像共享同一个基础镜像层,提高了存储效率。此外,当用户改变了一个Docker镜像(比如升级程序到新的版本),则会创建一个新的层(layer)。因此,用户不用替换整个原镜像或者重新建立,只需要添加新层即可。用户分发镜像的时候,也只需要分发被改动的新层内容(增量部分)。这让Docker的镜像管理变得十分轻量级和快速。

3. 制作基础镜像

1.安装一个最小化的操作系统主机

2.打包操作系统的根目录

注意:排除/proc/sys目录

  • /proc:存储着正在运行的进程信息
  • /sys:硬件设备的驱动程序信息
[root@localhost ~]# tar --numeric-owner --exclude=/proc --exclude=sys -czf centos7-9.tar /
[root@localhost ~]# ls
ctenos7-9.tar

# --numeric-owner 以用户识别码及用户组识别码取代用户名称和群组名称,(防止有些用户的用户名和导入的操作系统的用户名不一致,所以使用数字代替用户名)

3.把获取的根打包文件导入Docker Host中

[root@docker ~]# docker import centos7-9.tar centos7:v9
sha256:e94928e744a83404aaac8f5007aaf8b932b9809510c4f824bc0538c9ce73145f
[root@docker ~]# docker image ls
REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
centos7        v9        e94928e744a8   16 seconds ago   1.47GB

4.使用基础镜像启动容器

[root@docker ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
centos7      v9        e94928e744a8   4 minutes ago   1.47GB

[root@docker ~]# docker run -it --name centos7.9 centos7:v9 /bin/bash

[root@fb1eff53ad79 /]# ls /proc
1          cmdline    driver       ioports    kpagecount  modules       sched_debug  swaps          uptime
18         consoles   execdomains  irq        kpageflags  mounts        schedstat    sys            version
acpi       cpuinfo    fb           kallsyms   loadavg     mpt           scsi         sysrq-trigger  vmallocinfo
asound     crypto     filesystems  kcore      locks       mtrr          self         sysvipc        vmstat
buddyinfo  devices    fs           keys       mdstat      net           slabinfo     timer_list     zoneinfo
bus        diskstats  interrupts   key-users  meminfo     pagetypeinfo  softirqs     timer_stats
cgroups    dma        iomem        kmsg       misc        partitions    stat         tty

4. 应用镜像制作

应用镜像指的就是应用程序运行环境

使用commit提交镜像

在基础容器镜像运行的容器中安装httpd

[root@fb1eff53ad79 /]# yum -y install httpd

使用commit命令对正在运行的容器提交为一个应用镜像

[root@docker ~]# docker commit --help

Usage:  docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

Create a new image from a container's changes

Options:
  -a, --author string    Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
  -c, --change list      Apply Dockerfile instruction to the created image
  -m, --message string   Commit message
  -p, --pause            Pause container during commit (default true) # 提交的一瞬间暂停容器,也就是把容器当前状态制作成一个镜像
[root@docker ~]# docker ps
CONTAINER ID   IMAGE        COMMAND       CREATED          STATUS          PORTS     NAMES
fb1eff53ad79   centos7:v9   "/bin/bash"   17 minutes ago   Up 17 minutes             centos7.9
# 在cetenos7.9这个基础镜像之上,再做一个应用镜像
[root@docker ~]# docker commit centos7.9 centos7-9-httpd:v1
sha256:34a7c2f72466d503d40d8bf0dc53092059e523d8f299d72f2b8865adb19dfdbd
[root@docker ~]# docker images
REPOSITORY        TAG       IMAGE ID       CREATED          SIZE
centos7-9-httpd   v1        34a7c2f72466   4 seconds ago    1.69GB
centos7           v9        e94928e744a8   23 minutes ago   1.47GB

使用应用镜像

[root@docker ~]# docker images
REPOSITORY        TAG       IMAGE ID       CREATED              SIZE
centos7-9-httpd   v1        34a7c2f72466   About a minute ago   1.69GB
centos7           v9        e94928e744a8   25 minutes ago       1.47GB

[root@docker ~]# docker run -it --name=test-httpd centos7-9-httpd:v1 /bin/bash
[root@d5c07c954d7a /]# echo "hello docker" >> /var/www/html/index.html
[root@d5c07c954d7a /]# httpd -k start
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
[root@d5c07c954d7a /]# curl http://localhost
hello docker

5. 使用Dockerfile创建应用镜像

在Dockerfile定义所要执行的命令,使用docker build创建镜像,过程中会按照Dockerfile所定义的内容打开临时性容器(使用docker commit进行提交),把Dockerfile文件中的命令全部执行完成,就得到了一个容器应用镜像。

  • 执行命令越多,最终得到的容器应用镜像越大,所以要做优化

1) Dockerfile关键字

  • FROM(指定基础image)

    • FROM指令用于指定其后构建新镜像所使用的基础镜像。

    • FROM指令必是Dockerfile文件中的首条命令。

    • FROM指令指定的基础image可以是官方远程仓库中的,也可以位于本地仓库,优先本地仓库。

      格式:FROM <image>:<tag>
      例:FROM centos:latest
      
  • MAINTAINER(用来指定镜像创建者信息)

  • RUN (运行命令)

    RUN指令用于在构建镜像中执行命令,有以下两种格式:

    • shell格式
    格式:RUN <命令>
    例:RUN echo daniel > /var/www/html/index.html
    
    • exec格式
    格式:RUN ["可执行文件", "参数1", "参数2"]
    例:RUN ["/bin/bash", "-c", "echo daniel >
    /var/www/html/index.html"]
    

    注意:按优化的角度来讲:当有多条要执行的命令,不要使用多条RUN,尽量使
    用&&符号与\\符号连接成一行。因为多条RUN命令会让镜像建立多层(总之
    就是会变得臃肿了)。

    RUN yum install httpd httpd-devel -y
    RUN echo daniel > /var/www/html/index.html
    可以改成
    RUN yum install httpd httpd-devel -y && echo daniel >
    /var/www/html/index.html
    或者改成
    RUN yum install httpd httpd-devel -y \\
    && echo daniel > /var/www/html/index.html
    
  • CMD(设置container启动时执行的操作)

  • CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于
    指定镜像构建时所要执行的命令

    格式有三种:
    CMD ["executable","param1","param2"]
    CMD ["param1","param2"]
    CMD command param1 param2
    
    • 每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一
      条会被执行

    • 如果容器镜像中有此命令,启动容器时,不要手动让容器执行其它命令 (手动执行的命令优先级更高)

      什么是启动容器时指定运行的命令?
      # docker run -d -p 80:80 镜像名 运行的命令
      
  • ENTRYPOINT(设置container启动时执行的操作)

    ENTRYPOINT与CMD非常类似

    • 相同点:
      一个Dockerfile只写一条,如果写了多条,那么只有最后一条生效
      都是容器启动时才运行

    • 不同点:
      如果用户启动容器时候指定了运行的命令,ENTRYPOINT不会被运行的命
      令覆盖,而CMD则会被覆盖

      格式有两种:
      ENTRYPOINT ["executable", "param1", "param2"]
      ENTRYPOINT command param1 param2
      
  • USER(设置container容器的用户)

    USER指令设置启动容器的用户(像hadoop需要hadoop用户操作,oracle
    需要oracle用户操作),可以是用户名或UID

    USER daemon
    USER 1001
    
    如果设置了容器以daemon用户去运行,那么RUN,CMD和
    ENTRYPOINT都会以这个用户去运行
    镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所
    指定的用户
    
  • EXPOSE(指定容器需要映射到宿主机器的端口)

    • EXPOSE指令用于指定容器在运行时监听的端口

      格式:EXPOSE <port> [<port>...]
      例:EXPOSE 80 3306 8080
      
      • 上述运行的端口还需要使用docker run运行容器时通过-p参数映射到宿主
        机的端口
  • ENV(用于设置环境变量)

    ENV指令用于指定一个环境变量.

    格式:ENV <key> <value> 或者 ENV <key>=<value>
    例:ENV JAVA_HOME /usr/local/jdkxxxx/
    
  • ADD(从src复制文件到container的dest路径)

    • ADD指令用于把宿主机上的文件拷贝到镜像中

      格式:ADD <src> <dest>
      <src>可以是一个本地文件或本地压缩文件,还可以是一个url,
      如果把<src>写成一个url,那么ADD就类似于wget命令
      <dest>路径的填写可以是容器内的绝对路径,也可以是相对于工作目录的相对
      路径
      
  • VOLUME(指定挂载点)

    • VOLUME指令用于把宿主机里的目录与容器里的目录映射.

    • 只指定挂载点,docker宿主机映射的目录为自动生成的

      格式:VOLUME ["<mountpoint>"]
      
  • WORKDIR(切换目录)

    • WORKDIR指令设置工作目录,类似于cd命令。不建议使用 RUN cd /root ,
      建议使用WORKDIR

      WORKDIR /root
      

2) Dockerfile应用案例

通过Dockerfile创建一个可以在启动容器时就直接启动httpd应用的镜像

步骤:
1.创建一个目录,用于存储Dockerfile所使用的文件
2.在此目录中创建Dockerfile文件及制作镜像所使用到的文件
3.在此此目录中使用docker build创建镜像(读取Dockerfile文件)
4.使用创建的镜像启动容器

注意事项:

1.首先要有基础镜像

2.安装httpd(yum -y install httpd )

3.安装完成后如何启动httpd? 编写一个把httpd启动的脚本文件

4.把httpd放在前台执行还是后台执行?当然是前台

5.暴露端口tcp80

6.添加一个测试文件,用于验证httpd是否可用

过程:

创建目录

[root@docker ~]# mkdir test_Dockerfile

进入目录并创建用于启动httpd的脚本文件

[root@docker ~]# cd test_Dockerfile
[root@docker test_Dockerfile]# vim run-httpd.sh
#!/bin/bash
rm -rf /run/httpd/*
exec /sbin/httpd -D FOREGROUND #httpd前台启动命令

创建用于测试httpd是否可用的index.html

[root@docker test_Dockerfile]# vim index.html
[root@docker test_Dockerfile]# cat index.html
test Dockerfile

创建Dockerfile

[root@docker test_Dockerfile]# docker images
REPOSITORY        TAG       IMAGE ID       CREATED             SIZE
centos7           v9        e94928e744a8   2 hours ago         1.47GB
[root@docker test_Dockerfile]# vim Dockerfile

FROM centos7:v9

MAINTAINER "这是一个Dockerfile的测试"

RUN yum clean all

RUN rpm --rebuilddb && yum -y install httpd

ADD run-httpd.sh /run-httpd.sh

RUN chmod -v +x /run-httpd.sh

ADD index.html /var/www/html/

EXPOSE 80

WORKDIR /

使用docker build创建镜像,注意命令最后有一个点,点表示当前目录

[root@docker test_Dockerfile]# docker build -t centos7-9-base-httpd:v1 .
Sending build context to Docker daemon  4.096kB
Step 1/10 : FROM centos7:v9
 ---> e94928e744a8
Step 2/10 : MAINTAINER "这是一个Dockerfile的测试"
 ---> Using cache
 ---> e4ba8f06eb3d
Step 3/10 : RUN yum clean all
 ---> Using cache
 ---> fb6609089e44
Step 4/10 : RUN rpm --rebuilddb && yum -y install httpd
 ---> Using cache
 ---> a68a7eb031c9
Step 5/10 : ADD run-httpd.sh /run-httpd.sh
 ---> Using cache
 ---> d6f309f1ff03
Step 6/10 : RUN chmod -v +x /run-httpd.sh
 ---> Using cache
 ---> 79f37479163e
Step 7/10 : ADD index.html /var/www/html/
 ---> 27c6a0b9fabf
Step 8/10 : EXPOSE 80
 ---> Running in 0ac844b928e1
Removing intermediate container 0ac844b928e1
 ---> 792c8a909fee
Step 9/10 : WORKDIR /
 ---> Running in 6cbac19040b0
Removing intermediate container 6cbac19040b0
 ---> c8e1ed380bcd
Step 10/10 : CMD ["/bin/bash","/run-httpd.sh"]
 ---> Running in 5eb62c1a8ce8
Removing intermediate container 5eb62c1a8ce8
 ---> b03310a82f8e
Successfully built b03310a82f8e
Successfully tagged centos7-9-base-httpd:v1

使用上述创建的应用容器启动容器

[root@docker test_Dockerfile]# docker images
REPOSITORY             TAG       IMAGE ID       CREATED              SIZE
centos7-9-base-httpd   v1        b03310a82f8e   About a minute ago   1.7GB
centos7                v9        e94928e744a8   2 hours ago          1.47GB
[root@docker test_Dockerfile]# docker run -d centos7-9-base-httpd:v1
a1582a95f4906eeba1b887f424a5dd155219e2ecfe16fe668d5a7bbfc593e1da

验证容器及httpd是否可用

[root@docker test_Dockerfile]# docker ps #查看容器IP地址
CONTAINER ID   IMAGE                     COMMAND                  CREATED          STATUS          PORTS     NAMES
a1582a95f490   centos7-9-base-httpd:v1   "/bin/bash /run-http…"   56 seconds ago   Up 56 seconds   80/tcp    quizzical_chandrasekhar
[root@docker test_Dockerfile]# docker inspect a1582
[root@docker test_Dockerfile]# curl http://172.17.0.2
test Dockerfile

替代原网站内容示例

[root@docker test_Dockerfile]# mkdir /www_web
[root@docker test_Dockerfile]# echo "hello world" >> /www_web/index.html
[root@docker test_Dockerfile]# docker run -d -v /www_web:/var/www/html centos7-9-base-httpd:v1
20a4c8dcf7443e19f74755c0fdb6a19d8f1e5f7f2b769cf4ded6dc240274e9ca
[root@docker test_Dockerfile]# docker inspect 20a4
[root@docker test_Dockerfile]# curl http://172.17.0.3
hello world

示例:把nginx应用容器化

要求:
1、通过基础镜像做nginx应用镜像
2、使用nginx应用镜像启动容器时,nginx要求启动
3、验证nginx服务是否启动
步骤:
1、使用哪一个基础 centos:latest
2、需要使用epel YUM源
3、安装nginx
4、修改nginx配置文件,主要用于关闭daemon后台运行
5、验证使用的测试页面

创建目录

[root@docker ~]# mkdir test_nginx
[root@docker ~]# cd test_nginx
[root@docker test_nginx]#

创建测试文件

[root@docker test_nginx]# echo 'hello nginx' >> index.html
[root@docker test_nginx]# ls
index.html

创建Dockerfile

[root@docker test_nginx]# docker images
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
centos7-9-base-httpd   v1        b03310a82f8e   29 minutes ago   1.7GB
centos7                v9        e94928e744a8   3 hours ago      1.47GB

[root@docker test_nginx]# vim Dockerfile
FROM centos7:v9

MAINTAINER "hhy<hhy@126.com>"

RUN yum clean all && yum -y install yum-plugin-ovl && yum -y install epel-release
RUN yum -y install nginx

ADD index.html /usr/share/nginx/html/

RUN echo "daemon off;" >> /etc/nginx/nginx.conf

EXPOSE 80

CMD /usr/sbin/nginx

使用docker build创建nginx应用镜像 (注意最后哪个点,表示当前目录)

[root@docker test_nginx]# docker build -t centos-nginx:v1 . 

启动容器验证nginx服务是否自动开启

[root@docker test_nginx]# docker images
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
centos-nginx           v1        2f129a6e9acf   24 seconds ago   1.95GB
centos7-9-base-httpd   v1        b03310a82f8e   36 minutes ago   1.7GB
centos7                v9        e94928e744a8   3 hours ago      1.47GB
[root@docker test_nginx]# docker run -d centos-nginx:v1
7ad0397bbadc5829bfdb55703f19d083b79bd7ec65b1f143e9d99ce6ee391e1b
[root@docker test_nginx]# docker ps
CONTAINER ID   IMAGE                     COMMAND                  CREATED          STATUS          PORTS     NAMES
7ad0397bbadc   centos-nginx:v1           "/bin/sh -c /usr/sbi…"   17 seconds ago   Up 16 seconds   80/tcp    amazing_euclid

[root@docker test_nginx]# docker inspect 7ad | grep 172.17.0
                    "IPAddress": "172.17.0.4",
[root@docker test_nginx]# curl http://172.17.0.4
hello nginx

6. 容器镜像在docker host存储位置

1) 写时复制与用时分配

我们知道了一个镜像可以跑多个容器,如果每个容器都去复制一份镜像内的文件系统,那么将会占用大量的存储空间。docker使用了**写时复制cow(copy-on-write)用时分配(allocate-on-demand)**技术来提高存储的利用率。

写时复制:

  • 写时复制技术可以让多个容器共享同一个镜像的文件系统, 所有数据都
    从镜像中读取
  • 只有当要对文件进行写操作时,才从镜像里把要写的文件复制到自己的
    文件系统进行修改。所以无论有多少个容器共享同一个镜像,所做的写
    操作都是对从镜像中复制到自己的文件系统中的复本上进行,并不会修
    改镜像的源文件
  • 多个容器操作同一个文件,会在每个容器的文件系统里生成一个复本,
    每个容器修改的都是自己的复本,相互隔离,相互不影响

用时分配:
启动一个容器,并不会为这个容器预分配一些磁盘空间,而是当有新文件
写入时,才按需分配新空间

从图中可以看出除了最上面的一层为读写层之外,下面的其他的层都是只读的镜像层,并且除了最下面的一层外,其他的层都有会有一个指针指向自己下面的一层镜像虽然统一文件系统(union file system)技术将不同的镜像层整合成一个统一的文件系统,为构成一个完整容器镜像的层提供了一个统一的视角,隐藏了多个层的复杂性,对用户来说只存在一个文件系统,但图中的这些层并不是不能看到的,如果需要查看的话可以进入运行Docker的机器上进行查看,从这些层中可以看到Docker 内部实现的一些细节.

Docker 的容器镜像和容器本身的数据都存放在服务器的 /var/lib/docker/ 这个路径下。不过不同的linux发行版存储方式上有差别,比如,在ubuntu发行版上存储方式为AUFS,CentOS发行版上的存储方式为Overlay或Overlay2。

centos系统docker默认使用存储驱动是devicemapper,而这种存储驱动有两种模式loop-lvm和direct-lvm,不巧默认又使用了比较低效的loop-lvm

OverlayFS是一个类似于AUFS 的现代联合文件系统,更快实现简单。
OverlayFS是内核提供的文件系统,overlay和overlay2是docker的存储驱动

2)Overlay及Overlay2

OverlayFS将单个Linux主机上的两个目录合并成一个目录。这些目录被称为层,统一过程被称为联合挂载。
OverlayFS底层目录称为lowerdir, 高层目录称为upperdir。合并统一视图称为merged。当需要修改一个文件时,使用CoW将文件从只读的Lower复制到可写的Upper进行修改,结果也保存在Upper层。在Docker中,底下的只读层就是image,可写层就是Container。

overlay2是overlay的改进版,只支持4.0以上内核添加了Multiple lower layers in overlayfs的特性,所以overlay2可以直接造成muitiple lower layers不用像overlay一样要通过硬链接的方式(最大128层) centos的话支持3.10.0-514及以上内核版本也有此特性,所以消耗更少的inode .

本质区别是镜像层之间共享数据的方法不同

  • overlay共享数据方式是通过硬连接,只挂载一层,其他层通过最高层通过硬连接形式共享(增加了磁盘inode的负
    担)

  • 而overlay2是通过每层的lower文件

从上图中可以看到:

  • 如果upperdir和lowerdir有同名文件时会用upperdir的文件
  • 读文件的时候,文件不在upperdir则从lowerdir读
  • 如果写的文件不在uppderdir在lowerdir,则从lowerdir里面copy到upperdir。
  • 不管文件多大,copy完再写,删除镜像层的文件只是在容器层生成
    whiteout文件标志(标记为删除,并不是真的删除) (后面的dockefile章
    节会体现出效果)

查看docker默认使用的存储驱动的方法

[root@docker ~]# docker info
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  app: Docker App (Docker Inc., v0.9.1-beta3)
  buildx: Docker Buildx (Docker Inc., v0.9.1-docker)
  scan: Docker Scan (Docker Inc., v0.17.0)

Server:
 Containers: 8
  Running: 3
  Paused: 0
  Stopped: 5
 Images: 19
 Server Version: 20.10.18
 Storage Driver: overlay2 #注意此处

7.docker存储状态

/var/lib/docker/overlay2 路径下的信息在不同的阶段会有变化,了解这几个阶段中新增的数据以及容器与镜像的存储结构的变化非常有利于我们对Docker容器以及Docker镜像的理解。

Docker运行前、Docker运行后、下载镜像后、运行容器后四个阶段中Docker 存储的变化

1) 运行前

在刚安装docker-ce第1次启动服务之前,/var/lib/下并没有docker这个目录

[root@docker ~]# ls /var/lib/docker
ls: cannot access /var/lib/docker: No such file or directory

2) 启动后

而第1次 systemctl start docker 启动后,则会产生 /var/lib/docker 目录

[root@docker ~]# systemctl start docker
[root@docker ~]# tree /var/lib/docker
/var/lib/docker
├── buildkit
│   ├── cache.db
│   ├── containerdmeta.db
│   ├── content
│   │   └── ingest
│   ├── executor
│   ├── metadata_v2.db
│   └── snapshots.db
├── containers
├── image
│   └── overlay2
│       ├── distribution
│       ├── imagedb
│       │   ├── content
│       │   │   └── sha256
│       │   └── metadata
│       │       └── sha256
│       ├── layerdb
│       └── repositories.json
├── network
│   └── files
│       └── local-kv.db
├── overlay2 #  #下载镜像后,存储在此目录中
│   ├── backingFsBlockDev
│   └── l
├── plugins
│   ├── storage
│   │   └── ingest
│   └── tmp
├── runtimes
├── swarm
├── tmp
├── trust
└── volumes
    ├── backingFsBlockDev
    └── metadata.db

27 directories, 9 files

3) 下载镜像后

[root@docker ~]# docker pull centos
Using default tag: latest
latest: Pulling from library/centos
a1d0c7532777: Pull complete 
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
docker.io/library/centos:latest

[root@docker ~]# cd /var/lib/docker/overlay2/
[root@docker overlay2]# ls
40912fb1de607897a2a2f7f39730b58a288f0ef4e0c9e101c7e7fed56f4ade92  backingFsBlockDev  l
[root@docker overlay2]# ls 40912fb1de607897a2a2f7f39730b58a288f0ef4e0c9e101c7e7fed56f4ade92/
diff  link
[root@docker overlay2]# ls 40912fb1de607897a2a2f7f39730b58a288f0ef4e0c9e101c7e7fed56f4ade92//diff
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@docker overlay2]# ls
40912fb1de607897a2a2f7f39730b58a288f0ef4e0c9e101c7e7fed56f4ade92  backingFsBlockDev  l
[root@docker overlay2]# ll l
total 0
lrwxrwxrwx. 1 root root 72 Oct  4 19:57 4JLNRBSI6DOW4BHV7RFYSB7JUM -> ../40912fb1de607897a2a2f7f39730b58a288f0ef4e0c9e101c7e7fed56f4ade92/diff
  • 下载完镜像,overlay2目录里多了一个 40912… 这样的目录,只有1层
  • 此目录内部的diff子目录记录每一层自己的数据
  • link记录该层链接目录(和overlay目录下l子目录里记录的链接是一致的)
  • l子目录中包含了很多软链接,使用短名称指向了其他层,短名称用于
    避免mount参数时达到页面大小的限制

4) 运行容器后

运行前

[root@docker overlay2]# ls
40912fb1de607897a2a2f7f39730b58a288f0ef4e0c9e101c7e7fed56f4ade92  backingFsBlockDev  l

运行容器后

[root@docker overlay2]# docker run -it --name=test centos:latest /bin/bash
[root@92d465abbb2f /]# [root@docker overlay2]# 
同时按ctrl+p+q三键退出,保持容器为UP状态

[root@docker overlay2]# docker ps
CONTAINER ID   IMAGE           COMMAND       CREATED          STATUS          PORTS     NAMES
92d465abbb2f   centos:latest   "/bin/bash"   33 seconds ago   Up 32 seconds             test


[root@docker overlay2]# ls
40912fb1de607897a2a2f7f39730b58a288f0ef4e0c9e101c7e7fed56f4ade92       backingFsBlockDev
51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745       l
51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745-init
[root@docker overlay2]# ll
total 0
drwx--x---. 3 root root     47 Oct  4 20:05 40912fb1de607897a2a2f7f39730b58a288f0ef4e0c9e101c7e7fed56f4ade92
drwx--x---. 5 root root     69 Oct  4 20:05 51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745 # 容器
drwx--x---. 4 root root     72 Oct  4 20:05 51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745-init # 做容器初始化
brw-------. 1 root root 253, 0 Oct  4 19:54 backingFsBlockDev
drwx------. 2 root root    108 Oct  4 20:05 l

[root@docker overlay2]# ll 51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745
total 8
drwxr-xr-x. 2 root root  6 Oct  4 20:05 diff
-rw-r--r--. 1 root root 26 Oct  4 20:05 link
-rw-r--r--. 1 root root 57 Oct  4 20:05 lower
drwxr-xr-x. 1 root root  6 Oct  4 20:05 merged
drwx------. 3 root root 18 Oct  4 20:05 work


[root@docker overlay2]# cat 51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745/lower
l/IWABIF2A5CFJCMCOHFBGFUAE52:l/4JLNRBSI6DOW4BHV7RFYSB7JUM
这个文件lower表示容器在启动过程中挂的是哪一个镜像

启动一个容器,也是在/var/lib/docker/overlay2目录下生成一层容器层,目录包括diff,link,lower,merged,work。
diff记录每一层自己内容的数据;link记录该层链接目录(实际是l目录下到层的链接),比如在容器中创建目录或在diff新增该目录;创建容器时将lower-id指向的镜像层目录以及upper目录联合挂载到merged目录;work用来完成如copy-on_write的操作。

启动容器后,可以在docker host查看mount情况

[root@docker overlay2]# mount | grep overlay
overlay on /var/lib/docker/overlay2/51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745/merged typeoverlay 
(rw,relatime,seclabel,lowerdir=/var/lib/docker/overlay2/l/IWABIF2A5CFJCMCOHFBGFUAE52:/var/lib/docker/overlay2/l/4JLNRBSI6DOW4BHV7RFYSB7JUM,upperdir=/var/lib/docker/overlay2/51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745/diff,workdir=/var/lib/docker/overlay2/51b6019cbe70c52aff36c44573fb66338225d8d276879b16101d8dcd2558c745/work)

以上是关于Docker学习笔记 —— 镜像制作(Dockerfile)的主要内容,如果未能解决你的问题,请参考以下文章

Docker学习笔记 —— 镜像仓库制作(公有+私有+Harbor)

Docker学习笔记 —— 镜像仓库制作(公有+私有+Harbor)

docker学习笔记- 仓库

Docker笔记三 Docker镜像制作

Docker笔记三 Docker镜像制作

Docker笔记——Docker安装及制作镜像