虚拟化类别

主机级虚拟化

类型 1 虚拟化

直接在硬件之上安装虚拟机,称为 Hypervisor,也就意味着在硬件之上不需要安装宿主机操作系统。在 Hypervisor 安装操作系统。

类型 2 虚拟化

vmware virtualbox,在硬件之上安装操作系统,称为 hostOS 也称为宿主机,然后在宿主机之上安装 VMM(Virtual Machine Manager)

主机虚拟化的缺点

资源开销较大,例如有时仅仅需要创建一个应用程序而已,但是就必须要使用传统的主机级虚拟化安装一个操作系统在操作系统上安装应用程序。

这就造成了资源浪费,因为我们的目的仅仅是要上线一个应用程序而已。

容器级虚拟化技术

通过内核的 namespace 和 chroot 来实现容器级的虚拟化,让各个容器共享内核。并通过内核的 namespace 中的六个隔离和系统调用实现容器的划分及虚拟化。

使用容器的初衷

  1. 为了让环境隔离,例如需要上线两个 nginx 服务,并且都需要使用 80 端口,但是一个主机仅有一个 80 端口套接字,无法实现。所以需要使用虚拟机。
  2. 让多个虚拟机是互相不可达的。

容器技术

运行进程不受其他进程干扰,在使用容器时,创建出多个用户空间。在多个用户空间中运行单独的进程,那么这个用户空间就称之为容器。多个用户空间会共同使用一个单独的内核,一般情况下会使用第一个用户空间是有特权的用户空间,并且会使用这个有特权的用户空间来控制其他的用户空间。

Docker 的诞生

jail

最初出现 jail 的目的,是为了安全。因为就算是黑客攻击了这个容器,那么攻陷的也仅仅是个容器,到达的资源边界就是容器的边界。

vserver(chroot)

将 jail 技术复刻到 linux 之后的技术称之为 chroot(切根),当切根后,那么实现的就是让单个用户在一个目录中运行,并且可以让这个用户在这个目录中认为这个就是根目录。

相关系统调用

clone() 创建进程的系统调用

setns() 创建名称空间后将进程放置到名称空间的调用

LXC(linuxX Container)的诞生

Docker 是一个应用工具,并且是一个 LXC 增强版。

为什么需要 LXC,因为之前的 vserver 技术需要来调用系统的调用才能实现需要的功能,所以这种方法不易于大众来使用。

所以 linux 公司就研发了一种 LinuX Container 技术。

  • lxc-create 可以通过这个命令创建一个容器(用户空间)
  • template 创建名称空间的脚本,作用是创建不同的应用程序

工作过程

在特殊的用户空间中执行一个脚本(装载软件和命令),然后将这个用户空间进行复制就实现了一个用户空间,使用 chroot 切换到用户空间后,这个用户空间与之前的 vmware 或者是 xen 是相同的。

目的

将一个容器作为一个用户空间来使用。

Docker

Docker 是一个应用工具,并且是一个 LXC 增强版。

应用了镜像的技术

就是将不同的系统进行封装并提交到仓库中,使用镜像容器时直接使用 docker run 然后会自动下载互联网上的容器。

目的

一个容器只运行一个进程,起到了很好的隔离性。

优点

只要是软件都可以打包成镜像进行 pull push 拉取

如果没有使用 docker,那么所有的进程将会运行在一个用户空间中。非常不安全,如果一个黑客通过单独的一个进程进来作为跳板机会威胁到整个系统。

如果运行了 docker,那么每个进程只会运行在一个单独的容器中,保证了安全,通过最小化进行定义的,但是占用了更大的空间。

将进程全部运行在一个用户空间中,那么共享的文件放置在一起即可。

缺点

工具需要多份,因为每个容器中都需要一份工具来进行使用。

调试进程

  1. 多份工具,占用空间大
  2. 调试困难,因为已经隔离
  3. 增加了运维人员的复杂度,可以使用 k8s 进行编排并进行维护

linux namespace

namespace 对容器息息相关,可以说没有 namespace 就没有容器的诞生。

实现方法

这六种方法已经在内核级进行划分并且划分了名称空间(namespaces)。如果想要实现容器,那么必须要实现内核级的名称空间支持。

容器的实现

image-20201110155344691

是通过内核级的六个 namespaces 以及 chroot 来实现的。

如果想要很好的使用容器技术,那么内核版本必须要在 3.8 之后才可以,因为 User namespaces 名称空间在 3.8 之后才被集成到内核中进行支持。

Centos 6 是不支持 User namespaces 的。

UTS

主机名和域名。

根文件系统 Mount

每个用户空间都要有自己独立的根文件系统

IPC

每个用户空间都需要有自己的 IPC 通道,IPC 就是不同的用户空间进行共享内存。

PID

需要有一个独立的进程树,也就象征着每个容器(用户空间)都有自己的进程树。

隔离后可以伪装出多个或一个 init。

UID

每个用户空间都应该有 root,但是一个内核只有一个 root。必须要在其他的用户空间中伪装出一个 root 用户。

TCP/IP

因为在内核级只有一个 TCP//IP,所以必须要对这个 TCP/IP 协议栈进行切割出多个名称空间,然后用 UTS 资源对单个名称空间进行划分并且要有自己单独的主机名。

资源分配的问题(Cgroups)

主机级虚拟化

CPU 的资源在一开始,也就是创建主机时就指定了该主机要使用的资源。

容器级虚拟化

通过将物理上的 CPU 或者是物理资源进行划分,并将每个物理上的资源使用到单个容器上,即可,为每个容器分配百分比的资源。

压缩性资源和非可压缩性资源

CPU 属于可压缩性资源,内存 MEM 属于不可压缩性资源。

CPU 在负载满荷运行后,如果再有其他的进程来请求使用 CPU 可以进入队列,排队,但是不会 kill 进程。

内存 MEM 在满载后,会产生 OOM 并且会默认挑选一个占用内存最大的进程进行删除。

限制容器的方式

比例或者核心绑定(硬限制)

  1. 如果资源分配到每个容器上后,其他容器不可以进行调用。
  2. 如果容器撤走,但是容器还有剩余的资源,那么其他容器可以占用该容器的资源。
  3. 可以通过硬限制来限制单个容器使用的资源。

Control Groups(cgroups)介绍

将系统的资源划分成多个资源块,并分发给不同的组。这个组可以创建出子组,子组默认可以使用这个组中的所有资源。

当然,这些资源都是属于内核。

Cgroups 的缺陷

因为自始至终从来没有离开过内核的划分,所以使用容器带来的隔离性远不如主机级虚拟化带来的隔离性要隔离的更加完全。

发展历程

docker 有自己的编排技术,也就是 machine+swarm+compose 组成的编排技术,各有各的功能,但是被 docker 组合在一起。

docker 刚开始是一直在被 google 公司使用的,并且使用了好多年,到后来 docker 公司闻到了 docker 的味道,并模拟 google 公司的 docker 研发出来,并作为开源发布出来。

后来 google 公司看到自己的技术被发布出来,但是自己也想占据一席之地,所以就发布了 k8s 工具。并将 k8s 捐入了 CNCF 的组织中,这个组织是一个容器组织,其中组织了许多技术的顶尖公司,例如微软,谷歌等。但是 docker 并没有加入这个公司。

docker 与 lxc 的爱恨情仇

docker 刚开始是依赖于 lxc 的,并且是基于 lxc 研发出来的,到后来 docker 为了更好的在多平台上应用,研发了 libcontainer 引擎,并且使用这个引擎可以在多个系统上运行。

现在使用的是 runC 容器标准,这个标准是 docker 公司指定的标准。