容器基本概念
什么是容器?
容器就是一个视图隔离、资源可限制、独立文件系统的进程集合。
视图隔离 - namespace
就是能够看到部分进程以及具有独立的主机名等;
控制资源使用率 - cgroup
则是可以对于内存大小以及 CPU 使用个数等进行限制。
共享的文件系统 - chroot
所有进程共享的文件系统
什么是镜像
容器可以堪称是一个进程集合,它将系统的其他资源隔离开来,具有自己独立的资源视图。 容器具有一个独立的文件系统,因为使用的是系统的资源,所以在独立的文件系统内不需要具备内核相关的代码或者工具,我们只需要提供容器所需的二进制文件、配置文件以及依赖即可。只要容器运行时所需的文件集合都能够具备,那么这个容器就能够运行起来。
而镜像就是容器运行起来所需要的文件集合。
如何构建镜像?
Dockerfile
我们可以使用docker命令构建生成镜像,但通常情况下,我们会采用 Dockerfile 来构建镜像, 我们会将构建镜像的每个步骤封装在Dockerfile中。
changeset
每个构建步骤都会对已有的文件系统进行操作,这样就会带来文件系统内容的变化,我们将这些变化称之为 changeset
Dockerfile及changeset 的分层以及复用带来的的优点
1便利的语法糖
能够帮助我们很好地描述构建的每个步骤。
2.提高分发效率
对于大的镜像而言,如果将其拆分成各个小块就能够提高镜像的分发效率,这是因为镜像拆分之后就可以并行下载这些数据;
3.节省下载时间
因为这些数据是相互共享的,也就意味着当本地存储上包含了一些数据的时候,只需要下载本地没有的数据即可,举个简单的例子就是 golang 镜像是基于 alpine 镜像进行构建的,当本地已经具有了 alpine 镜像之后,在下载 golang 镜像的时候只需要下载本地 alpine 镜像中没有的部分即可;
4.减少磁盘空间占用
因为镜像数据是共享的,简单设想一下,当本地存储具有了 alpine 镜像和 golang 镜像,在没有复用的能力之前,alpine 镜像具有 5M 大小,golang 镜像有 300M 大小,因此就会占用 305M 空间;而当具有了复用能力之后,只需要 300M 空间即可。
构建步骤
- FROM 行表示以下的构建步骤基于什么镜像进行构建,正如前面所提到的,镜像是可以复用的;
- WORKDIR 行表示会把接下来的构建步骤都在哪一个相应的具体目录下进行,其起到的作用类似于 Shell 里面的 cd
- COPY 行表示的是可以将宿主机上的文件拷贝到容器镜像内;
- RUN 行表示在具体的文件系统内执行相应的动作。当我们运行完毕之后就可以得到一个应用了;
- CMD 行表示使用镜像时的默认程序名字。
DockerFile eg
当有了 Dockerfile 之后,就可以通过 docker build 命令构建出所需要的应用。
镜像如何运行在生产环境或者测试环境上?
docker registry
也就是镜像仓库,其负责存储所有产生的镜像数据。我们只需要通过 docker push 就能够将本地镜像推动到镜像仓库中,这样一来,就能够在生产环境上或者测试环境上将相应的数据下载下来并运行了。
如何运行容器?*
1.docker pull image:version:从镜像仓库中将相应的镜像下载下来
2.docker images:当镜像下载完成之后就可以通过 docker images 来查看本地镜像,这里会给出一个完整的列表,我们可以在列表中选中想要的镜像;
3.docker run image:version,构建容器:当选中镜像之后,就可以通过 docker run 来运行这个镜像得到想要的容器,当然可以通过多次运行得到多个容器。一个镜像就相当于是一个模板,一个容器就像是一个具体的运行实例,因此镜像就具有了一次构建、到处运行的特点。
容器运行过程
总结
容器就是和系统其它部分隔离开来的进程集合,这里的其他部分包括进程、网络资源以及文件系统等。而镜像就是容器所需要的所有文件集合,其具备一次构建、到处运行的特点。
容器运行时的生命周期
单进程模型 - initial 进程
使用 docker run 的时候会选择一个镜像来提供独立的文件系统并指定相应的运行程序。这里指定的运行程序称之为 initial 进程,容器的生命周期和initial进程保持一致。一个容器可以有多个initial进程,initial进程也可以有子进程,当然intial进程退出时,所有的子进程也会退出,从而防止资源的泄漏。
容器的持久化 - 数据卷
用来持久化重要的信息容器能够直接将数据持久化到指定的目录上,这个目录就称之为数据卷。
两种管理方式
* 1.通过 bind 的方式,直接将宿主机的目录直接挂载到容器内;这种方式比较简单,但是会带来运维成本,因为其依赖于宿主机的目录,需要对于所有的宿主机进行统一管理。
* 2.将目录管理交给运行引擎。
关于更多数据卷的细节(创建/绑定数据卷,创建数据卷容器),可以参考笔者的另一篇文章: (juejin.cn/editor/draf…)
容器项目架构
moby
容器引擎架构moby 是目前最流行的容器管理引擎,moby daemon 会对上提供有关于容器、镜像、网络以及 Volume的管理。
containerd moby daemon 所依赖的最重要的组件就是 containerd,containerd 是一个容器运行时管理引擎,其独立于 moby daemon ,可以对上提供容器、镜像的相关管理。
shim
containerd 底层有 containerd shim 模块,其类似于一个守护进程。
containerd shim的必要性:
1.containerd 需要管理容器生命周期,而容器可能是由不同的容器运行时所创建出来的,因此需要提供一个灵活的插件化管理。而 shim 就是针对于不同的容器运行时所开发的,这样就能够从 containerd 中脱离出来,通过插件的形式进行管理。
2.shim 插件化的实现,使其能够被 containerd 动态接管。防止当moby daemon 或者 containerd daemon 意外退出的时候,容器没人管理。
3.因为随时可能会对 moby 或者 containerd 进行升级,shim提供的动态接管的能力可以可以做到原地升级以及不影响业务的升级。
Moby架构
容器 Vs VM
VM
1.可以提供更好的隔离效果
VM利用Hypervisor虚拟化技术来模拟 CPU、内存等硬件资源,这样就可以在宿主机上建立一个Guest OS,每一个Guest OS都有一个独立的内核,比如 Ubuntu、CentOS 甚至是 Windows 等,在这样的 Guest OS 之下,每个应用都是相互独立的,VM 可以提供一个更好的隔离效果。
2.消耗更多的资源,更低的资源利用率,更长的启动时间
VM良好的隔离效果需要付出一定的代价,因为需要把一部分的计算资源交给虚拟化,这样就很难充分利用现有的计算资源,并且每个 Guest OS 都需要占用大量的磁盘空间,比如 Windows 操作系统的安装需要 10 - 30G 的磁盘空间,Ubuntu 也需要 5~6G,同时这样的方式启动很慢。
容器
1.进程级别的隔离级别
容器是针对于进程而言的,因此无需 Guest OS,只需要一个独立的文件系统提供其所需要文件集合即可。
因此可能会有一些不可控的问题,所以现在容器也主要是往强隔离方向发展
2.更快的启动速度,更少的资源占用,更高的资源利用率
容器所有的文件隔离都是进程级别的,因此启动时间快于 VM,并且所需的磁盘空间也小于 VM。