8 分钟入门 K8s | 详解容器基本概念

做者| 阿里巴巴高级开发工程师 傅伟golang

1、容器与镜像

什么是容器?

在介绍容器的具体概念以前,先简单回顾一下操做系统是如何管理进程的。docker

首先,当咱们登陆到操做系统以后,能够经过 ps 等操做看到各式各样的进程,这些进程包括系统自带的服务和用户的应用进程。那么,这些进程都有什么样的特色?网络

  • 第一,这些进程能够相互看到、相互通讯;
  • 第二,它们使用的是同一个文件系统,能够对同一个文件进行读写操做;
  • 第三,这些进程会使用相同的系统资源。

这样的三个特色会带来什么问题呢?架构

  • 由于这些进程可以相互看到而且进行通讯,高级权限的进程能够攻击其余进程;
  • 由于它们使用的是同一个文件系统,所以会带来两个问题:这些进程能够对于已有的数据进行增删改查,具备高级权限的进程可能会将其余进程的数据删除掉,破坏掉其余进程的正常运行;此外,进程与进程之间的依赖可能会存在冲突,如此一来就会给运维带来很大的压力;
  • 由于这些进程使用的是同一个宿主机的资源,应用之间可能会存在资源抢占的问题,当一个应用须要消耗大量 CPU 和内存资源的时候,就可能会破坏其余应用的运行,致使其余应用没法正常地提供服务。

针对上述的三个问题,如何为进程提供一个独立的运行环境呢?运维

  • 针对不一样进程使用同一个文件系统所形成的问题而言,Linux 和 Unix 操做系统能够经过 chroot 系统调用将子目录变成根目录,达到视图级别的隔离;进程在 chroot 的帮助下能够具备独立的文件系统,对于这样的文件系统进行增删改查不会影响到其余进程;
  • 由于进程之间相互可见而且能够相互通讯,使用 Namespace 技术来实现进程在资源的视图上进行隔离。在 chroot 和 Namespace 的帮助下,进程就可以运行在一个独立的环境下了;
  • 但在独立的环境下,进程所使用的仍是同一个操做系统的资源,一些进程可能会侵蚀掉整个系统的资源。为了减小进程彼此之间的影响,能够经过 Cgroup 来限制其资源使用率,设置其可以使用的 CPU 以及内存量。

那么,应该如何定义这样的进程集合呢?工具

其实,**容器就是一个视图隔离、资源可限制、独立文件系统的进程集合。**所谓“视图隔离”就是可以看到部分进程以及具备独立的主机名等;控制资源使用率则是能够对于内存大小以及 CPU 使用个数等进行限制。容器就是一个进程集合,它将系统的其余资源隔离开来,具备本身独立的资源视图。测试

容器具备一个独立的文件系统,由于使用的是系统的资源,因此在独立的文件系统内不须要具有内核相关的代码或者工具,咱们只须要提供容器所需的二进制文件、配置文件以及依赖便可。只要容器运行时所需的文件集合都可以具有,那么这个容器就可以运行起来。ui

什么是镜像?

综上所述,咱们将这些容器运行时所须要的全部的文件集合称之为容器镜像。spa

那么,通常都是经过什么样的方式来构建镜像的呢?一般状况下,咱们会采用 Dockerfile 来构建镜像,这是由于 Dockerfile 提供了很是便利的语法糖,可以帮助咱们很好地描述构建的每一个步骤。固然,每一个构建步骤都会对已有的文件系统进行操做,这样就会带来文件系统内容的变化,咱们将这些变化称之为 changeset。当咱们把构建步骤所产生的变化依次做用到一个空文件夹上,就可以获得一个完整的镜像。   changeset 的分层以及复用特色可以带来几点优点:操作系统

  • 第一,可以提升分发效率,简单试想一下,对于大的镜像而言,若是将其拆分红各个小块就可以提升镜像的分发效率,这是由于镜像拆分以后就能够并行下载这些数据;
  • 第二,由于这些数据是相互共享的,也就意味着当本地存储上包含了一些数据的时候,只须要下载本地没有的数据便可,举个简单的例子就是 golang 镜像是基于 alpine 镜像进行构建的,当本地已经具备了 alpine 镜像以后,在下载 golang 镜像的时候只须要下载本地 alpine 镜像中没有的部分便可;
  • 第三,由于镜像数据是共享的,所以能够节约大量的磁盘空间,简单设想一下,当本地存储具备了 alpine 镜像和 golang 镜像,在没有复用的能力以前,alpine 镜像具备 5M 大小,golang 镜像有 300M 大小,所以就会占用 305M 空间;而当具备了复用能力以后,只须要 300M 空间便可。

如何构建镜像?

以下图所示的 Dockerfile 适用于描述如何构建 golang 应用的。 图片

如图所示:

  1. FROM 行表示如下的构建步骤基于什么镜像进行构建,正如前面所提到的,镜像是能够复用的;
  2. WORKDIR 行表示会把接下来的构建步骤都在哪个相应的具体目录下进行,其起到的做用相似于 Shell 里面的 cd;
  3. COPY 行表示的是能够将宿主机上的文件拷贝到容器镜像内;
  4. RUN 行表示在具体的文件系统内执行相应的动做。当咱们运行完毕以后就能够获得一个应用了;
  5. CMD 行表示使用镜像时的默认程序名字。

当有了 Dockerfile 以后,就能够经过 docker build 命令构建出所须要的应用。构建出的结果存储在本地,通常状况下,镜像构建会在打包机或者其余的隔离环境下完成。

那么,这些镜像如何运行在生产环境或者测试环境上呢?这时候就须要一个中转站或者中心存储,咱们称之为 docker registry,也就是镜像仓库,其负责存储全部产生的镜像数据。咱们只须要经过 docker push 就可以将本地镜像推进到镜像仓库中,这样一来,就可以在生产环境上或者测试环境上将相应的数据下载下来并运行了。

如何运行容器?

运行一个容器通常状况下分为三步:

  • 第一步:从镜像仓库中将相应的镜像下载下来;
  • 第二步:当镜像下载完成以后就能够经过 docker images 来查看本地镜像,这里会给出一个完整的列表,咱们能够在列表中选中想要的镜像;
  • 第三步:当选中镜像以后,就能够经过 docker run 来运行这个镜像获得想要的容器,固然能够经过屡次运行获得多个容器。一个镜像就至关因而一个模板,一个容器就像是一个具体的运行实例,所以镜像就具备了一次构建、处处运行的特色。

小结

简单回顾一下,容器就是和系统其它部分隔离开来的进程集合,这里的其余部分包括进程、网络资源以及文件系统等。而镜像就是容器所须要的全部文件集合,其具有一次构建、处处运行的特色。  

2、容器的生命周期

容器运行时的生命周期

容器是一组具备隔离特性的进程集合,在使用 docker run 的时候会选择一个镜像来提供独立的文件系统并指定相应的运行程序。这里指定的运行程序称之为 initial 进程,这个 initial 进程启动的时候,容器也会随之启动,当 initial 进程退出的时候,容器也会随之退出。

所以,能够认为容器的生命周期和 initial 进程的生命周期是一致的。固然,由于容器内不仅有这样的一个 initial 进程,initial 进程自己也能够产生其余的子进程或者经过 docker exec 产生出来的运维操做,也属于 initial 进程管理的范围内。当 initial 进程退出的时候,全部的子进程也会随之退出,这样也是为了防止资源的泄漏。   可是这样的作法也会存在一些问题,首先应用里面的程序每每是有状态的,其可能会产生一些重要的数据,当一个容器退出被删除以后,数据也就会丢失了,这对于应用方而言是不能接受的,因此须要将容器所产生出来的重要数据持久化下来。容器可以直接将数据持久化到指定的目录上,这个目录就称之为数据卷。

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

一般状况下,数据卷管理主要有两种方式:

  • 第一种是经过 bind 的方式,直接将宿主机的目录直接挂载到容器内;这种方式比较简单,可是会带来运维成本,由于其依赖于宿主机的目录,须要对于全部的宿主机进行统一管理。
  • 第二种是将目录管理交给运行引擎。

3、容器项目架构

moby 容器引擎架构

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

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

  • 首先,containerd 须要管理容器生命周期,而容器多是由不一样的容器运行时所建立出来的,所以须要提供一个灵活的插件化管理。而 shim 就是针对于不一样的容器运行时所开发的,这样就可以从 containerd 中脱离出来,经过插件的形式进行管理。
  • 其次,由于 shim 插件化的实现,使其可以被 containerd 动态接管。若是不具有这样的能力,当 moby daemon 或者 containerd daemon 意外退出的时候,容器就没人管理了,那么它也会随之消失、退出,这样就会影响到应用的运行。
  • 最后,由于随时可能会对 moby 或者 containerd 进行升级,若是不提供 shim 机制,那么就没法作到原地升级,也没法作到不影响业务的升级,所以 containerd shim 很是重要,它实现了动态接管的能力。

本节课程只是针对于 moby 进行一个大体的介绍,在后续的课程也会详细介绍。  

4、容器 VS VM

容器和 VM 之间的差别

VM 利用 Hypervisor 虚拟化技术来模拟 CPU、内存等硬件资源,这样就能够在宿主机上创建一个 Guest OS,这是常说的安装一个虚拟机。

每个 Guest OS 都有一个独立的内核,好比 Ubuntu、CentOS 甚至是 Windows 等,在这样的 Guest OS 之下,每一个应用都是相互独立的,VM 能够提供一个更好的隔离效果。但这样的隔离效果须要付出必定的代价,由于须要把一部分的计算资源交给虚拟化,这样就很难充分利用现有的计算资源,而且每一个 Guest OS 都须要占用大量的磁盘空间,好比 Windows 操做系统的安装须要 10~30G 的磁盘空间,Ubuntu 也须要 5~6G,同时这样的方式启动很慢。正是由于虚拟机技术的缺点,催生出了容器技术。   容器是针对于进程而言的,所以无需 Guest OS,只须要一个独立的文件系统提供其所须要文件集合便可。全部的文件隔离都是进程级别的,所以启动时间快于 VM,而且所需的磁盘空间也小于 VM。固然了,进程级别的隔离并无想象中的那么好,隔离效果相比 VM 要差不少。

整体而言,容器和 VM 相比,各有优劣,所以容器技术也在向着强隔离方向发展。  

本文总结

  • 容器是一个进程集合,具备本身独特的视图视角;
  • 镜像是容器所须要的全部文件集合,其具有一次构建、处处运行的特色;
  • 容器的生命周期和 initial 进程的生命周期是同样的;
  • 容器和 VM 相比,各有优劣,容器技术在向着强隔离方向发展。
相关文章
相关标签/搜索