上一节提到了革命性的容器技术,下面就记录下容器相关的笔记,如果想看docker更多相关的命令及操作,请参考【One by one系列】一步步学习docker

1. 什么是容器?

【One by one系列】一步步学习docker(一)——初窥docker原理中介绍了,docker运用linux的技术走了捷径,不虚拟化任何硬件,而是对硬件资源在不同的容器中做了隔离,隔离使每个容器之间拥有了不同的环境(硬盘空间-网路-系统工具包),然后又可以共享需要的硬件资源(cpu-内存-系统内核)。

  • AUFS(chroot) – 用来建立不同的操作系统和隔离运行时的硬盘空间,将子目录变成根目录,达到视图级别的隔离;进程在 chroot 的帮助下可以具有独立的文件系统,对于这样的文件系统进行增删改查不会影响到其他进程
  • Namespace – 用来隔离容器的执行空间:使用 Namespace 技术来实现进程在资源的视图上进行隔离。
  • Cgroup – 分配不同的硬件资源:为了减少进程彼此之间的影响,可以通过 Cgroup 来限制其资源使用率,设置其能够使用的 CPU 以及内存量。

docker是容器技术的一种,容器就是一个视图隔离、资源可限制、独立文件系统的进程集合。所谓“视图隔离”就是能够看到部分进程以及具有独立的主机名等;控制资源使用率则是可以对于内存大小以及 CPU 使用个数等进行限制。容器就是一个进程集合,它将系统的其他资源隔离开来,具有自己独立的资源视图。总而言之:和系统其它部分隔离开的进程集合,这里的其他部分包括进程、网络资源以及文件系统等。

2. 什么是镜像?

容器具有一个独立的文件系统,因为使用的是系统的资源,所以在独立的文件系统内不需要具备内核相关的代码或者工具,我们只需要提供容器所需的二进制文件、配置文件以及依赖即可。只要容器运行时所需的文件集合都能够具备,那么这个容器就能够运行起来。将这些容器运行时所需要的所有的文件集合称之为容器镜像

3. 构建镜像

一般是采用dockerfile来构建:

img

  1. FROM 行表示以下的构建步骤基于什么镜像进行构建,镜像是可以复用的;
  2. WORKDIR 行表示会把接下来的构建步骤都在哪一个相应的具体目录下进行,其起到的作用类似于 Shell 里面的 cd;
  3. COPY 行表示的是可以将宿主机上的文件拷贝到容器镜像内;
  4. RUN 行表示在具体的文件系统内执行相应的动作。当我们运行完毕之后就可以得到一个应用了;
  5. CMD 行表示使用镜像时的默认程序名字。
docker build -t image-name:tags .

构建出的结果存储在本地,一般情况下,镜像构建会在打包机或者其他的隔离环境下完成

那么,这些镜像如何运行在生产环境或者测试环境上呢?

这时候就需要一个中转站或者中心存储,我们称之为 docker registry,也就是镜像仓库,其负责存储所有产生的镜像数据。我们只需要通过 docker push 就能够将本地镜像推动到镜像仓库中,这样一来,就能够在生产环境上或者测试环境上将相应的数据下载下来并运行了

4. 运行容器

运行一个容器一般情况下分为三步:

  • 第一步:从镜像仓库中将相应的镜像下载下来;
docker pull 
  • 第二步:当镜像下载完成之后就可以通过 docker images 来查看本地镜像,这里会给出一个完整的列表,我们可以在列表中选中想要的镜像;
docker images
  • 第三步:当选中镜像之后,就可以通过 docker run ,运行这个镜像得到想要的容器,当然可以通过多次运行得到多个容器。一个镜像就相当于是一个模板,一个容器就像是一个具体的运行实例,因此镜像就具有了一次构建、到处运行的特点。

5. 容器的生命周期

5.1 单进程模型

使用 docker run 的时候会选择一个镜像来提供独立的文件系统并指定相应的运行程序。这里指定的运行程序称之为 initial 进程,这个 initial 进程启动的时候,容器也会随之启动,当 initial 进程退出的时候,容器也会随之退出。因此,可以认为容器的生命周期和 initial 进程的生命周期是一致的。当然,因为容器内不只有这样的一个 initial 进程,initial 进程本身也可以产生其他的子进程或者通过 docker exec 产生出来的运维操作,也属于 initial 进程管理的范围内。当 initial 进程退出的时候,所有的子进程也会随之退出,这样也是为了防止资源的泄漏。

5.2 数据持久化

应用里面的程序往往是有状态的,其可能会产生一些重要的数据,当一个容器退出被删除之后,数据也就会丢失了,这对于应用方而言是不能接受的,所以需要将容器所产生出来的重要数据持久化下来。容器能够直接将数据持久化到指定的目录上,这个目录就称之为数据卷volume

数据卷有一些特点,其中非常明显的就是数据卷的生命周期是独立于容器的生命周期的,也就是说容器的创建、运行、停止、删除等操作都和数据卷没有任何关系,因为它是一个特殊的目录,是用于帮助容器进行持久化的。简单而言,我们会将数据卷挂载到容器内,这样一来容器就能够将数据写入到相应的目录里面了,而且容器的退出并不会导致数据的丢失。

通常情况下,数据卷管理主要有两种方式:

  • 第一种是通过 bind 的方式,直接将宿主机的目录直接bind到容器内
    • 这种方式比较简单,但是会带来运维成本,因为其依赖于宿主机的目录,需要对于所有的宿主机进行统一管理。
docker run -v 宿主机目录:容器目录 镜像:tag sh -c "date > /tmp/demo.log"
#eg
docker run -v /tmp:/tmp busybox:1.25 sh -c "date > /tmp/demo.log"
  • 第二种是将目录管理交给运行引擎。
#创建数据卷
docker create volume volume-name
docker run -v volume-name:容器目录 镜像:tag -c "date > /tmp/demo.log"

#eg
docker create volume demo #demo是数据卷名称
docker run -v demo:/tmp busybox:1.25 sh -c "date > /tmp/demo.log"

6.moby容器引擎架构

博主第一次听到moby,其实是很懵逼的,至少之前学习docker的过程中,是没有moby这个内容。经过查阅资料,大概了解到Docker 公司在并未对外释放明显信号的情况下,直接将 Github 上原隶属于 Docker 组织的 Docker 项目,直接 transfer 到了一个新的、名叫 Moby 的组织下,并将其重命名为 Moby 项目。对此,知乎网友总结:“Docker 公司直接把原 Docker 项目改名成了 Moby,是为了将之前数年里构建出来的庞大的粉丝团体和 Google 搜索内容(Google search footprint)全部转移到 Docker 公司的商业产品上。” 其实,moby还是docker。

image-20210707011536645

moby 是目前最流行的容器管理引擎,

  • moby daemon 会对上提供有关于容器、镜像、网络以及 Volume的管理。
  • moby daemon 所依赖的最重要的组件就是 containerd,containerd 是一个容器运行时管理引擎,其独立于 moby daemon ,可以对上提供容器、镜像的相关管理。

containerd 底层有 containerd shim 模块,其类似于一个守护进程,这样设计的原因有几点:

  • 首先,containerd 需要管理容器生命周期,而容器可能是由不同的容器运行时(runC kata gVisor)所创建出来的,因此需要提供一个灵活的插件化管理。而 shim 就是针对于不同的容器运行时所开发的,这样就能够从 containerd 中脱离出来,通过插件的形式进行管理

  • 其次,因为 shim 插件化的实现,使其能够被 containerd 动态接管。如果不具备这样的能力,当 moby daemon 或者 containerd daemon 意外退出的时候,容器就没人管理了,那么它也会随之消失、退出,这样就会影响到应用的运行。

  • 最后,因为随时可能会对 moby 或者 containerd 进行升级,如果不提供 shim 机制,那么就无法做到原地升级,也无法做到不影响业务的升级,因此 containerd shim 非常重要,它实现了动态接管的能力。