容器

1、容器与镜像

1.分析操作系统是如何管理进程的?
当我们登入到一个操作系统内,我们可以通过ps(Process Statu)的操作可以看到各式各样的进程,包括系统自带的、或属于自身应用的、或属于其他应用的。

那么这些进程都有什么样的特点呢?
首先他们可以互相看见,相互通信;第二,他们使用的是同样的一个文件系统,也就意味着这些进程可以对同样一个文件进行读写操作;第三,他们使用的是同样的一个系统资源。

这个三个特点会带来什么问题呢?
首先他们能相互看见,相互通信,也就意味着高级权限的进程可以攻击其他进程。第二,他们使用的是同样一个文件系统,会带来两个问题:1.这些进程可以对已有的数据进行增删改查,也就意味着高级权限进程可以把其他应用所需要的进程删除掉,破坏掉其他进程的运行。2.进程和进程之间所需要的依赖可能会产生冲突,这会给运维成本带来压力。最后,因为他们使用的是同一个系统资源,那么就意味着应用和应用之间可能会存在资源抢占问题,当一个应用消耗很大的CPU和内存的时候,它会破坏掉其他应用的运行,会导致其他应用无法提供正常的服务。

在这样的问题之下我们该如何解决呢?该如何给这些进程提供一个独立的运行环境?
首先我们针对第一个问题,我们该如何提供一个独立的文件系统?Linux和Unix提供了一个叫卷记录的系统,它可以把一个子目录变成根目录,当然这个是在视图级别进行隔离的,也就意味着这个进程在卷记录的帮助下它可以有一个独立的文件系统,这样这个进程对这个文件系统的增删改查都不会影响到其他进程的使用。
第二,进程之间可以相互看见,相互通信,甚至使用同样的网络资源。在这样的情况下我们可以采用namespace这样一个技术来帮助我们对进程隔离,也就是在资源的视图上进行隔离。在卷记录和namespace的帮助下,这个进程可以运行在一个独立的环境。
当然在这个独立的环境内,它们还是使用的是同一个操作系统的资源,也就意味着它还是会侵蚀掉整个系统的资源,为了减少它对其他进程的影响,我们可以通过cgroup来限制它的资源使用率。比如,这个进程只能使用2G内存,这个进程只能使用4个CPU等等。在卷记录、namespace以及cgroup的帮助下,我们就可以给进程提供一个相对独立的环境。这样进程和进程之间不会相互影响。
那么我们该如何定义这些进程集合呢?
管理进程
2.什么是容器?
容器,是一个视图隔离、资源可限制、独立文件系统的进程集合。
视图隔离 - 如能看见部分进程,独立主机名等等。
控制资源使用率 - 如2G内存大小,CPU使用个数等等。

3.容器具有什么优势呢?
它使用的是系统上的资源,所以说这个独立的文件系统内不需要具备内核相关的代码,或者工具。它只需要提供这个容器所需的二进制文件以及配置文件,甚至是依赖即可。只要一个容器运行时所需要的文件它都具备了,那么这个容器就可以运行起来。
运行容器所需要的所有文件集合叫做容器镜像

4.通过什么方式来构建镜像?
通常情况下,我们会采用Dockerfile来构建一个镜像,因为它提供一些很便利的语法塘来帮助我们很好地构建一个镜像。

Dockerfile - 描述镜像的构建步骤
每一个构建步骤会对现有的文件系统进行添加文件,删除文件,甚至是修改已有的文件。这样会带来文件系统的变化,我们把这种变化称为changeset。
我们可以看到下面这张图,这张图表示的是golang的镜像,每一个带有颜色的方框表示每一次构建步骤所带来的影响以及构建的步骤、描述等等。我们会发现,当我们把这些构建的步骤所产生的变化依次作用到一个空的文件夹上,我们就能得到一个完整的镜像。我们还能发现这些changeset还能复用的,下面这张图蓝色部分表示是alpine镜像,绿色部分表示是golang 自身变化。可以发现golang镜像是基于alpine镜像进行构建的。也就是说镜像之间是可以相互复用的。
golang

5.那么这些changeset所带来的分层以及复用的特点能带来什么样的优势呢?
第一,它能提高分发效率。我们简单试想一下,对于一个大的镜像而言,我们把它拆分成各个小块,我们就能提高分发 效率,因为我们可以并行下来这些数据。
第二,因为这些数据是相互共享的,也就意味着当我们本地的存储上已经包含了这些数据,那么我们只需要下载一些不存在的数据即可。
第三,因为这些镜像数据是共享的,它能节约大量的磁盘空间。
容器镜像在这里插入图片描述

6.如何构建一个镜像?
首先我们看到下面这张图,这是一个Dockerfile,表示如何构建一个golang的application的。
第一行:FROM golang:1.12-alpine一个镜像,也就是说以下是基于golang进行构建的,镜像是可以复用的。
第二行: WORKDIR表示接下来的构建步骤会在相应的哪一个具体的目录下进行,类似于cd操作
第三行:COPY 表示可以把数据集上的文件拷贝到容器内,也就拷贝到镜像内。
第四、五行:RUN表示在具体的文件系统内执行相应的动作,比如go get表示下载golang程序的依赖,go install表示说我会go build,然后把产生的二进制移到可检索的地方,比如user\bin目录下面
当我们运行完毕之后,就可以得到一个golang的application。
第六行:CMD表示当我们使用这个镜像的时候,它默认的程序名称是app。
golang

当我们有这样一个Dockerfile之后,我们就可以用docker build就可以构建出我们所需要的application。当然这个构建出的结果是存储在本地的。一般情况下,镜像构建会在打包机或者其他隔离环境下完成的。

那么镜像是如何运行在生产环境下或者测试环境上?
在这个时候我们需要一个中转站,这个中转站就是一个中心存储,我们把这个中心存储称为docker registry,也就是镜像仓库,它负责存储我们所有产生的镜像数据,我们只需要通过docker push 就能把本地的镜像推送到镜像仓库中,这样的话我们就可以在生产环境下或者测试环境下把数据下下来,并运行它。
dockerfile
7.如何运行一个镜像?也就是说如何运行一个容器?
第一步:从镜像仓库中把相应的镜像下下来,比如:下载busybox镜像
docker pull busybox:1.25
第二步:查看本地镜像列表:
docker images
第三步:选择相应的镜像并运行:
docker run [-d] --name demo busybox:1.25 top

分析docker run需要指定哪些参数?
首先,给它一个名字,比如叫demo;然后,这个容器所使用什么样的镜像,也就是busybox:1.25;最后一个top表示这个容器所对应的进程是一个top命令。
docker
8.小结
一个镜像类似于一个模板,一个容器就好像一个具体的实例。镜像具有一次构建到处运行的特点
容器:和其他系统部分隔离开的进程集合。
镜像:容器所需要的所有文件集合-Build once, Run anywhere。

2、容器的生命周期

单进程模型
(1)Init进程生命周期=容器生命周期
(2)运行期可运行exec执行运维操作
数据持久化
(1)独立于容器的生命周期
(2)数据卷 - docker volume vs bind

简单回顾一下上一节内容,容器实际上就是一组具有隔离特性的集合,在使用docker run的时候会选择一个镜像作为独立的文件系统,以及指定相应的运行程序,那么我们所指定的这个运行程序我们称之为Init进程
我们使用进程的时候,会发现这个Init进程启动的时候,容器也随之启动,当这个Init进程退出的时候,容器也随之退出了。我们就可以认为容器的生命周期和Init进程的生命周期是一致的。当然一个容器不止一个Init进程,Init进程也会有其他子进程。或者是通过docker exec产生出来的运维操作也属于Init进程管理的范围内。当这个Init进程退出的时候,它的子进程也随之退出。这样是为了防止资源的泄露。

但是这样会有什么问题呢?
首先,一个应用程序可能是一个有状态的,它可能产生一些非常重要的数据,比如说redis和MySQL等等。当一个容器退出,被删除之后,数据也随之丢失了。这对于数据放而言是不能接受的,所以我们需要将容器中所产生的数据给持久化下来。那么容器能持久化到指定目录,而这个目录我们称之为数据卷

数据卷有什么样的特点?
非常明显的是数据卷的生命周期是独立于容器的生命周期,也就是说容器的创建、运行、停止、删除都跟数据卷的生命周期毫无关系,因为它是一个特殊的move,是帮助容器进行数据持久化的。

那么都有什么样的使用方法以及具备什么样的特点?我们该如何使用数据卷?
简单来说,我们会将数据卷move到容器内,这样的话容器就可以将数据写到相应的目录里面了,容器的退出不会导致数据的丢失。而通常情况下我们有两种方式:第一种是bind方式,也就是将数据集上的目录直接bind到容器内,这种方式有个缺点,它会依赖数据集的目录,我们需要对数据集目录进行统一管理;第二种方式类似,只不过将这个目录的管理交给了容器的运行引擎了,首先通过docker create volume demo创建这个数据卷,这个数据卷的目录是交给docker管理的,只不过这个命令的左边部分变成volume的名字了,其他部分和第一种方式一样。
我们可以发现数据卷是独立于容器运行而存在的,它可以帮助我们快速地进行数据的持久化。
数据卷

3、容器项目的架构

1.moby容器引擎架构
moby是目前最流行的容器管理引擎架构。
moby deamon为对象提供关于容器,镜像,网络以及volume的管理。那么它依赖什么主键呢?
它依赖最重要的主键是containerd,containerd是一个容器运行时管理引擎,独立于moby deamon的,它可以为对象提供容器对象的相关管理。
但是我们会聚焦于容器的介绍,我们会发现它底层有个叫做containerd shim这么个模块,这个模块也是一个守护进程,为什么要这样设计呢?
首先,它需要去管理容器的生命周期,容器可能是不同的容器运行时创建出来的,因为我们现在具有很多种容器虚拟化技术的主流方案,比如runC,kata,gVisor等等,在这些不同容器运行时的主流方案下,我们需要提供一个灵活的插件化管理,也就是说这个shim是针对不同的容器运行时所开发的,这样就可以从containerd中脱离出来,通过插件的形式进行管理。除此之外我们也可以看到插件化管理的实现可以被containerd动态接管,还具备一个重要的特性:不会影响现有的容器运行。
当我们不具备这种能力的时候,我们看看有什么样的后果?
当moby deamon意外退出的时候,容器就没人管理了,那么它就会随之退出消失,这样也会影响到我们应用的运行。除了这种case之外,我们更频繁地对moby进行升级,如果我们不提供这种shim的机制,我们会发现,我们没有办法做到原地升级,所以说containerd shim存在的意义是非常重要的,它实现了一个动态接管的能力。
这就是moby溶解管理引擎的架构分析。
moby

4、容器vsVM

1.容器和VM之间的差异
什么是VM?
VM就是利用Hypevisor这种虚拟化技术来模拟硬件资源,比如CPU、内存等等,这样我们就可以在虚拟机上构建Guest OS,也就是我们常说的构建一个虚拟机,每一个Guest OS都有一个独立的内核,Ubuntu,CentOS,Windows等等,每一个应用都是相互独立的,因为它在一个Guest OS看不到其他的Guest OS里面的应用,VM可以提供一个更好的隔离效果,但这样的隔离效果也是有一定代价的。我们需要把一部分的计算资源交给虚拟化,这样我们很难充分利用现有的计算资源,而且每一个Guest OS都需要占用大量的子盘空间,因为每个操作系统可能占用大量的子盘空间,比如说Windows的安装可能需要10-30G的空间,Ubuntu可能需要5-6G的空间,这样会导致启动慢,消耗大量资源,以及占用大量的子盘空间,这样的缺点催生出容器技术。

我们知道容器是针对于进程而言的,所以它无需Guest OS,它只需要独立的文件技术,提供它所需要运行的文件集合,所有的隔离都是进程级别的,所以它启动的时间要快于VM,而且它所消耗的磁盘空间也要小于VM。当然这个进程级别的隔离并没有想象中的那么好,它的隔离效果比VM要差很多的。
在这种情况下,容器也有向强隔离方向发展。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值