什么是容器 - docker(标准化软件单元)

一、将软件打包到标准化单元中,以进行开发,交付和部署。

  1. 容器是打包代码及其所有依赖项的标准软件单元,因此应用程序可以从一个计算环境快速可靠地运行到另一个计算环境。Docker image是一个轻量级的,独立的,可执行的软件包,其中包含运行应用程序所需要的一切:代码,运行时,系统工具,系统库和设置。
  2. Docker image在运行时会成为容器,对于Docker容器,image会在Docker Engine上运行时成为容器。不论基础架构如何,容器化软件都可以基于Linux和Windows运用程序,始终运行相同。容器将软件与其环境隔离开来,并确保尽管开发个部署之间存在差异,但软件仍可以运行在Docker Engine上。
  • 标准:Docker创建了容器的行业标准,因此它们可以在任何地方移植。
  • 轻量级:容器共享计算机的OS系统内核,因此不需要每个应用程序都具有OS,从而提高了服务器效率并降低了服务器和许可成本。
  • 安全:应用程序在容器中更安全,并且Docker提供了业界最强大的默认隔离功能。

二、比较容器和虚拟机

容器和虚拟机具有相似的资源隔离和分配优势,但功能不同,因为容器虚拟化了操作系统,而不是硬件。容器更加便携和高效。
在这里插入图片描述
那什么是虚拟化操作系统?
操作系统层虚拟化是指通过划分一个宿主操作系统的特定部分,产生一个个隔离的操作执行环境。操作系统层的虚拟化是操作系统内核直接提供的虚报化,虚拟出的操作系统之间***共享底层宿主操作系统内核和底层的硬件资源。***操作系统虚拟化的关键点在于操作系统与上层应用隔离开,将对操作系统资源的访问进行虚报化,使上层应用觉得自己独占操作系统。

  1. 容器
    容器是应用程序层的抽象,将代码和依赖项打包在一起。多个容器可以在同一台机器上运行,并与其他容器共享OS内存,每个容器在用户空间中作为隔离的进程运行。容器占用的空间少于VM(容器 image 的大小通常为几十MB),可以处理更多的应用程序,并且需要的VM和操作系统更少。
  2. VM
    VM 是将一台服务器转变为多台服务器的物理硬件的抽象。虚拟机管理程序允许多个VM在单台计算机上运行。每个VM包含操作系统,应用程序,必要的二进制文件和库的完整副本占用数10GB。VM也可能启动缓慢。

三、CORE TECH OF DOCKER

首先,Docker的出现一定是因为目前的后端在开发和运维阶段确实需要一种虚拟化技术解决开发环境和生产环境一致性问题,通过Docker我们可以将程序运行的环境也纳入到版本控制中,排除因为环境造成不同运行结果的可能。但是上述需求虽然推动了虚拟化技术的产生,但是如果没有合适的底层技术支撑,那么我们仍然得不到一个完美的的产品。下面会介绍几种Docker的核心技术。

  1. Namespaces
    命名空间是Linux为我们提供的用于分离进程树、网络接口、挂载点以及进程间通信等资源的方法。在日常使用Linux或者MacOS时,我们并没有运行多个完全分离的服务器的需要,但是如果我们在服务器上启动了多个服务,这些服务其实会相互影响的,每一个服务都能看到其他服务的进程,也可以访问宿主机器上的任意文件,这是我们都不愿意看到的,我们更希望运行在同一台机器上的不同服务能做到完全隔离,就像运行在多台不同的机器上。
    Docker通过Linux的NameSpace对不同的容器实现了隔离。
    Linux的命名空间机制提供了七种不同的命名空间,包括CLONE_NEWCGROUP、CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWS、CLONE_NEWPID、CLONE_NEWUSER、CLONE_NEWUTS,通过和这七个选项我们能在创建新的进程时设置新进程应该在哪资源上与宿主机进行隔离。

进程(命名空间的形式实现进程之间的隔离)

进程是Linux以及现在操作系统中非常重要的概念,它表示一个正在执行的程序,也是在现代分时系统中的一个任务单元。在每一个Linux的系统上,我们都能够通过ps命令打印出当前操作系统中正在执行的进程。

网络

  1. 如果Docker的容器通过Linux的命名空间完成了与宿主机进程的网络隔离,但是却又没有办法通过宿主机的网络与整个互联网相连,就会产生很多的限制,所以Docker虽然可以通过命名空间创建一个隔离的网络环境,但是Docker中的服务仍然需要与外界相连才能发挥作用。
    每使用docker run启动的容器其实都具有单独的网络命名空间 Docker为我们提供了四种不同的网络模式,Host、Container、None、Bridge模式。
  2. Docker默认的网络设置模式:网桥模式。在这种模式下,除了分配隔离的网络命名空间之外,Docker还会为所有的容器设置IP地址。当Docker服务器在主机上启动之后会创建新的虚拟网桥Docker0,随后在该主机上启动的全部服务在默认情况下都与该网桥模式相连。
  3. 在默认情况下,每一个容器在创建时都会创建一对虚拟网卡,两个虚拟网卡组成可数据的通道,其中一个会放在创建的容器中,会加入到名为Docker0网桥中。
    当油Docker的容器需要将服务暴露给宿主机,就会为容器分配一个IP地址,同时向iptables中追加一条新的规则。
    Docker通过Linux的命名空间实现了网络隔离,又通过iptables进行数据包转发,让Docker容器能够优雅地为宿主机器或者其他容器提供服务。

挂载点

  1. 虽然我们已经通过Linux的命名空间解决了进程和网络隔离的问题,在Docker进程中我们已经没有办法访问宿主机器上的其他进程并且限制了网络的访问,但是Docker容器中的进程任然能够访问或者修改宿主机器上的其他目录。
  2. 在新的进程中创建隔离的挂载点命名空间需要在 clone 函数中传入 CLONE_NEWNS,这样子进程就能得到父进程挂载点的拷贝,如果不传入这个参数子进程对文件系统的读写都会同步回父进程以及整个主机的文件系统。
  3. 如果一个容器需要启动,那么它一定需要提供一个根文件系统(rootfs),容器需要使用这个文件系统来创建一个新的进程,所有二进制的执行都必须在这个根文件系统中。想要正常启动一个容器就需要在 rootfs 中挂载以上的几个特定的目录,除了上述的几个目录需要挂载之外我们还需要建立一些符号链接保证系统 IO 不会出现问题。
  4. 为了保证当前的容器进程没有办法访问宿主机器上其他目录,我们在这里还需要通过 libcontainer 提供的 pivot_root 或者 chroot 函数改变进程能够访问个文件目录的根节点。

chroot

在这里不得不简单介绍一下 chroot(change root),在 Linux 系统中,系统默认的目录就都是以 / 也就是根目录开头的,chroot 的使用能够改变当前的系统根目录结构,通过改变当前系统的根目录,我们能够限制用户的权利,在新的根目录下并不能够访问旧系统根目录的结构个文件,也就建立了一个与原系统完全隔离的目录结构。

CGroups

我们通过Linux的命名空间为新创建的进程隔离了文件系统、网络并与宿主机之间的进程相互隔离,但是命名空间并不能够为我们提供物理资源上的隔离,比如CPU或者内存,如果在同一台机器上运行了多个对彼此以及宿主机一无所知的容器,这些容器却共同占用了宿主机的物理资源。
如果其中的某一个容器正在执行 CPU 密集型的任务,那么就会影响其他容器中任务的性能与执行效率,导致多个容器相互影响并且抢占资源。如何对多个容器的资源使用进行限制就成了解决进程虚拟资源隔离之后的主要问题,而 Control Groups(简称 CGroups)就是能够隔离宿主机器上的物理资源,例如 CPU、内存、磁盘 I/O 和网络带宽。

1. 每一个CGroup都是一组被相同的标准的参数限制和进程,不同的CGroup之间是有层级关系的,也就是说它们之间可以从父类集成一些用于限制资源使用的标准和参。
2. Linux的CGroup能够为一组进程分配资源,也就是我们在上面提到的CPU、内存、网络带宽等资源,通过对资源的分配,CGroup能够提供一下几种功能:
在 CGroup 中,所有的任务就是一个系统的一个进程,而 CGroup 就是一组按照某种标准划分的进程,在 CGroup 这种机制中,所有的资源控制都是以 CGroup 作为单位实现的,每一个进程都可以随时加入一个 CGroup 也可以随时退出一个 CGroup。
Docker 在使用 CGroup 时其实也只是做了一些创建文件夹改变文件内容的文件操作,不过 CGroup 的使用也确实解决了我们限制子容器资源占用的问题,系统管理员能够为多个容器合理的分配资源并且不会出现多个容器互相抢占资源的问题。

UnionFS

Linux的命名空间和控制组分别解决了不同资源隔离的问题,前者解决了进程、网络以及文件系统的隔离,后者实现了CPU、内存等资源的隔离,但是在 Docker 中还有另一个非常重要的问题需要解决 - 也就是镜像。

镜像TM到底是什么???????
它又是如何组成和组织的??????

Docker镜像其实本质就是一个压缩包

存储驱动

Docker使用了一系列不同的存储驱动管理镜像内的文件系统并运行容器,这些存储驱动与Docker卷(volume),存储驱动引擎管理着能够在多个容器之间共享的存储。

想要理解Docker使用的存储驱动,我们首先需要理解Docker是如何构件并且存储镜像的,也需要明白Docker的镜像是如何被每一个容器所使用的;Docker中的每一个镜像都是由一系列只读层组成的,Dockerfile的每一个命令都会在已有的只读层上创建一个新的层。

FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app/py

容器中的每一层都只对当前容器进行了非常小的修改,上述的Dockerfile文件会构建一个拥有四层layer的镜像;

当镜像被docker run 命令创建时就会在镜像的最上层添加一个可写的层,也就是容器层,所有对于运行时容器的修改其实都是对这个容器读写层的修改。

容器和镜像的区别就在于,所有的镜像都是只读的,而每一个容器其实等于镜像加上一个可读写的层,也就是同一个镜像可以对应多个容器。

AUFS

UnionFS 其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。而 AUFS 即 Advanced UnionFS 其实就是 UnionFS 的升级版,它能够提供更优秀的性能和效率。

AUFS 作为联合文件系统,它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,这些文件夹在 AUFS 中称作分支,整个『联合』的过程被称为联合挂载(Union Mount)。

每一个镜像层或者容器层都是 /var/lib/docker/ 目录下的一个子文件夹;在 Docker 中,所有镜像层和容器层的内容都存储在 /var/lib/docker/aufs/diff/ 目录中

而 /var/lib/docker/aufs/layers/ 中存储着镜像层的元数据,每一个文件都保存着镜像层的元数据,最后的 /var/lib/docker/aufs/mnt/ 包含镜像或者容器层的挂载点,最终会被 Docker 通过联合的方式进行组装。

在这里插入图片描述
上面的这张图片非常好的展示了组装的过程,每一个镜像层都是建立在另一个镜像层之上的,同时所有的镜像层都是只读的,只有每个容器最顶层的容器层才可以被用户直接读写,所有的容器都建立在一些底层服务(Kernel)上,包括命名空间、控制组、rootfs 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。

总结

Linux命名空间、控制组合UnionFS三大技术支撑了目前Docker的实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值