Docker 容器与镜像(二)| 青训营笔记

133 阅读6分钟

这是我参与「第五届青训营」笔记创作活动的第15天

人生没有白走的路,每一步它都算数—考研政治老师孔昱力

学习docker镜像原理之前我们先看一下从镜像仓库下载镜像的过程

[root@iZbp14wmuv4q5klllaxz2hZ ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
8740c948ffd4: Already exists 
d2c0556a17c5: Pull complete 
c8b9881f2c6a: Pull complete 
693c3ffa8f43: Pull complete 
8316c5e80e6d: Pull complete 
b2fe3577faa4: Pull complete 
Digest: sha256:b8f2383a95879e1ae064940d9a200f67a6c79e710ed82ac42263397367e7cc4e
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

第4-9行冒号之前的内容是不是四层像是,对的,他就是镜像的ID。之前我们学过image是将整个容器打包得到的,按理来说我们拉去nginx镜像应该是一个整体呀,但为什么4-9行是拉取了6个镜像,其中第一行还提示已经存在,没有拉取呢?这就是我们本文要解释的docker镜像原理。接下来先打一些基础

我们使用的Centos 8系统是什么样的

我们一直以来,使用vmware虚拟机,安装的系统,是一个完整的系统文件,包括2部分

  • linux内核,作用是提供操作系统的基本功能,和机器硬件交互
  • centos8发行版,作用是提供软件功能,例如yum安装包管理等

因此,linux内核+centos发行版,就组成了一个系统,让我们用户使用。是否有一个办法,可以灵活的替换发行版,让我们使用不同的【系统】?例如我现在使用的是Centos 8系统,那如果我想使用快速使用Ubuntu怎么办呢?那么docker就实现了这个功能,技术手段就是docker images。docker容器是分层的,我们可以启动一个容器Ubuntu发行版的容器,这个容器与宿主机共用一个linux内核。

流程图 (31).jpg

Docker 镜像分层原理

流程图 (30).jpg

在我们看来,images就是一个文件,这是怎么实现的呢?这个技术就是Union File System(UFS),docker通过联合文件系统,将上述的不同的每一层,整合为一个文件系统,为用户隐藏了多层的视角。不同的镜像如果依赖于同一镜像的话,docker会通过UFS实现复用,而不是说重复下载。这就类似于不同Linux不同的发行版,共用宿主机的Linux内核一样。

文件修改操作

共用宿主机器内核的话,很自然的就会有一个疑问抛出,如果某个容器更改了系统文件,是不是就影响了其他容器呢。答案是不会的。我们一开始就说过,容器是一块独立隔离的空间,彼此之间并不会相互影响。从上边的图中标注的内容可以知道,容器所依赖的内容都是只读的不可更改的。

其实,容器对系统文件的更改是将文件复制到了最上层的可写入容器层,然后在该层内进行更改。docker的容器寻找文件时从上到下逐层寻找,找到即用。下标给出了具体文件操作的说明。

文件操作说明
添加文件在容器中创建文件时,新文件被添加到容器层中。
读取文件在容器中读取某个文件时,Docker会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后打开并读入内存。
修改文件在容器中修改已存在的文件时,Docker会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
删除文件在容器中删除文件时,Docker也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。

只有当需要修改时才复制一份数据,这种特性被称作Copy—on—Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。

这样就解释了我们前面提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。

小节:

  1. 当通过一个image启动容器时,docker会在该image最顶层,添加一个读写文件系统作为容器,然后运行该容器。

  2. docker镜像本质是基于UnionFS管理的分层文件系统

  3. docker镜像为什么才几百兆?

    答:因为docker镜像只有rootfs和其他镜像层,共用宿主机的linux内核(bootfs),因此很小!

  4. 为什么下载一个docker的nginx镜像,需要133MB?nginx安装包不是才几兆吗?

    答:因为docker的nginx镜像是分层的,nginx安装包的确就几M,但是一个用于运行nginx的镜像文件,依赖于父镜像(上一层),和基础镜像(发行版),所以下载的nginx镜像有一百多兆

docker run 是创建容器,并启动容器内的线程。如果镜像不存在本地,则会在线下载该镜像。

注意:容器内必须有一个进程处于容器内前台运行状态,否则容器会直接退出。

如果容器内什么事也没做,容器也会挂掉

运行容器的玩法

  1. 不加参数运行镜像❌
docker run centos

这种写法会产生许多独立的容器记录,且容器内没有程序在跑,因此挂了。我们会发现这些容器拥有不同的ID,属于不同的容器记录。

  1. 运行容器,且进入容器内,且在容器内执行某个命令
docker run -it centos sh

  1. 开启一个容器,让他帮你运行某个程序,属于容器内的前台程序,会hold住一个终端
docker run centos ping baidu.com

  1. 运行一个可以存活着的容器,docker ps 可以看到该容器

-d 参数据,让容器在后台跑着,这个后台指的是宿主机的后台

  1. 运行一个容器,挂了自动删除容器记录
docker run -d --rm centos
  1. 为什么docker run 后容器就启动了内部的应用

我们运行nginx,看一看然后进入容器内,看一看其中的文件。执行docker run后,会自动执行下边这个.sh文件。应用就在容器前台跑起来了。

  1. 为什么进入nginx容器内默认是在根目录?所有容器都是默认在根目录么?

不是的,例如进入redis容器,就是咋/data目录。这个我们后边会说。