那些实现 Docker 的底层 Linux 技术

Docker 是现在最流行的虚拟化技术,解决了棘手的机器资源共享的问题,同时不需要特别了解原理也能轻松上手,所以自问世之后就不断蔓延开来。作为从业人员,为了对底层进行一定的修改,就不得不了解与它相关的技术。这篇文章我整理了相关的资料做个备忘。由于血衫的技术能力有限,如果有错误的地方,也请大家指正。

一、关于docker

Docker 技术使用 Linux内核 和内核功能(例如 Cgroupsnamespace)来隔离进程,以便它们可以独立运行。这种独立性使得我们更好地管理应用程序,同时又保留了单独系统所具有的安全性

1. docker和Linux容器的区别

docker和 传统的Linux容器是不相同的。

容器技术最初是建立在 LXC 技术上的。LXC是轻量级的虚拟化。LXC起源于cgroup和namespaces,使得进程之间相互隔离,即进程虚拟化。

img

如上图示意,LXC是一种基于容器的操作系统层级的虚拟化技术,包含了完整的操作系统。LXC 真正的实现是靠 Linux 内核的相关特性,LXC项目对此做了整合,可以

  • 为容器绑定特定的cpu和memory节点
  • 分配特定比例的cpu时间、IO时间
  • 限制可以使用的内存大小(包括内存和是swap空间)
  • 提供device访问控制
  • 提供独立的namespace(网络、pid、ipc、mnt、uts)

Docker 容器将应用和其依赖环境全部打包到一个单一对象中,在不包含完整的操作系统的情况下就能运行普通应用,更加轻量级,可移植性更好。Docker是一个应用程序容器,在2014年6月召开的 DockerCon 2014 技术大会上,吸引了 IBM、Google、RedHat 等业界知名公司的关注和技术支持。无论是从 GitHub 上的代码活跃度,还是开源巨头红帽宣布在 RHEL7 中正式支持 Docker 技术,都可以说明 Docker 技术是一项创新型的技术解决方案,就连 Google 公司的 Compute Engine 也很快支持 Docker 在其之上运行。国内 BATJ也相继推出容器服务紧追云计算发展趋势。

Docker 技术解决以下问题:

  • 复杂的环境配置管理:从各种 OS 环境到各种中间件环境以及各种应用环境。

    img

  • 新的软件管理方法:云计算时代的到来,解决了硬件管理的问题,然而软件配置和管理相关的问题依然存在。

  • 虚拟化手段的变化:云时代无论是 KVM 还是 Xen,在 Docker 看来都在浪费资源,因为用户需要的是高效运行环境而非 OS,GuestOS 既浪费资源又难于管理,轻量级的 LXC 更加灵活和快速。

    img

  • 容器技术的便携性。LXC 在 Linux2.6 的 Kernel 里就已经存在了,但是其设计之初并非为云计算考虑的,缺少标准化的描述手段和容器的可便携性,决定其构建出的环境难于分发和标准化管理(相对于 KVM 之类 image 和 snapshot 的概念)。Docker 就在这个问题上做出了实质性的创新方法。

2. Docker底层技术

早期Docker用LXC(Linux Container)做底层,所以LXC中使用到的技术,Docker基本也都使用了。 namespace,cgroup,chroot,UnionFS都用到了。【前两个是内核特性】

  • namespace:用于资源隔离,通过使用独立的namespace来实现进程隔离,网络隔离,挂载隔离(文件隔离)等。
  • cgroup:用于资源限制,对一组进程的cpu/内存/网络/IO等资源进行分配限制。
  • UnionFS:AUFS是一种联合文件系统(UnionFS),可以将多个目录合并 再mount到一个目录上。DockerFile 镜像分层结构具体就是利用了AUFS的特性。每一层都是一个目录。镜像的构建过程可以简单理解为将多个目录进行合并【Docker支持众多UnionFS,AUFS只是其中一种。】

二、Linux namespace

每个用户实例之间相互隔离,互不影响。一般的硬件虚拟化方法给出的方法是 VM,而 LXC 给出的方法是 container,更细一点讲就是 kernel namespace。其中 pid、net、ipc、mnt、uts、user 等 namespace 将 container 的进程、网络、消息、文件系统、UTS(“UNIX Time-sharing System”)和用户空间隔离开。

Linux 的命名空间机制提供了以下七种不同的命名空间,包括

  • CLONE_NEWCGROUP
  • CLONE_NEWIPC
  • CLONE_NEWNET
  • CLONE_NEWNS
  • CLONE_NEWPID
  • CLONE_NEWUSER
  • CLONE_NEWUTS

通过这七个选项我们能在创建新的进程时设置新进程应该在哪些资源上与宿主机器进行隔离。

pid namespace

不同用户的进程就是通过 pid namespace 隔离开的,且不同 namespace 中可以有相同 pid。所有的 LXC 进程在 Docker中的父进程为 Docker 进程,每个 lxc 进程具有不同的 namespace。同时由于允许嵌套,因此可以很方便的实现 Docker in Docker。

net namespace

有了 pid namespace,每个 namespace 中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net namespace 实现的,每个 net namespace 有独立的 network devices,IP addresses,IP routing tables,/proc/net 目录。这样每个 container 的网络就能隔离开来。Docker 默认采用 veth 的方式将 container 中的虚拟网卡同 host 上的一个 docker bridge:docker0连接在一起。

ipc namespace

container 中进程交互还是采用 linux 常见的进程间交互方法(interprocess communication - IPC),包括常见的信号量、消息队列和共享内存。然而同 VM 不同的是,container 的进程间交互实际上还是 host 上具有相同 pid namespace 中的进程间交互,因此需要在 IPC 资源申请时加入 namespace 信息——每个 IPC 资源有一个唯一的32位 ID。

mnt namespace

类似 chroot,将一个进程放到一个特定的目录执行。mnt namespace 允许不同 namespace 的进程看到的文件结构不同,这样每个 namespace 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个 namespace 中的 container 在/proc/mounts 的信息只包含所在 namespace 的 mount point。

uts namespace

UTS(“UNIX Time-sharing System”)namespace 允许每个 container 拥有独立的 hostname 和 domain name,使其在网络上可以被视作一个独立的节点而非 Host 上的一个进程。

user namespace

每个 container 可以有不同的 user 和 group id,也就是说可以在 container 内部用 container 内部的用户执行程序而非 Host 上的用户。

三、Linux cgroup

cgroups 实现了对资源的配额和度量。 cgroups 的使用非常简单,提供类似文件的接口,在/cgroup 目录下新建一个文件夹即可新建一个 group,在此文件夹中新建 task 文件,并将pid 写入该文件,即可实现对该进程的资源控制。groups 可以限制 blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns 九大子系统的资源,以下是每个子系统的详细说明:

  1. 有序列表 blkio 这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及 usb 等等。
  2. cpu 这个子系统使用调度程序为 cgroup 任务提供 cpu 的访问。
  3. cpuacct 产生 cgroup 任务的 cpu 资源报告。
  4. cpuset 如果是多核心的 cpu,这个子系统会为 cgroup 任务分配单独的 cpu 和内存。
  5. devices 允许或拒绝 cgroup 任务对设备的访问。
  6. freezer 暂停和恢复 cgroup 任务。
  7. memory 设置每个 cgroup 的内存限制以及产生内存资源报告。
  8. net_cls 标记每个网络包以供 cgroup 方便使用。
  9. ns 名称空间子系统。

以上九个子系统之间也存在着一定的关系。详情请参阅官方文档。

四、 Linux AUFS

AUFS(AnotherUnionFS),简单来说就是支持将不同目录挂载到同一个虚拟文件系统下的文件系统。

更进一步地理解,AUFS 支持为每一个成员目录(类似Git Branch)设定 readonly、readwrite 和 whiteout-able 权限。同时 AUFS 里有一个类似分层的概念,对 readonly 权限的 branch 可以逻辑上进行修改(增量地,不影响 readonly 部分的)。

通常 Union FS 有两个用途:

  • 可以实现不借助 LVM、RAID 将多个 disk 挂到同一个目录下;
  • 将一个 readonly 的 branch 和一个 writeable 的 branch 联合在一起,Live CD 正是基于此方法可以在 OS image 不变的基础上允许用户在其上进行一些写操作。

Docker 在 AUFS 上构建的 container image 和 Live CD类似。

Linux启动说明

启动Linux运行需要两个FS:

  • bootfs

    bootfs(boot file system)主要包含 bootloader 和 kernel,bootloader 主要是引导加载 kernel,当 boot 成功后 kernel 被加载到内存中后 bootfs 就被 umount 了。

  • rootfs

    rootfs(root file system)包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件。Linux 在启动后,首先将 rootfs 设置为 readonly,进行一系列检查,然后将其切换为 “readwrite”供用户使用。

img

容器启动说明

  • 初始化时也是将 rootfs 以 readonly 方式加载并检查
  • 利用 union mount 的方式将一个 readwrite 文件系统挂载在 readonly 的 rootfs 之上
  • 继续将一个 readwrite 文件系统挂载在刚才的FS层之上,并将刚才的FS层设置为readonly
  • 持续进行上一个动作直至所有层次完成
  • 将所有 readonly 和一个 writeable 的结构构成一个 container 的运行时态,每一个 FS 被称作一个 FS 层。

对于 container 而言整个 rootfs 都是 read-write 的,但事实上所有的修改都写入最上层的 writeable 层中,image 不保存用户状态,只用于模板、新建和复制使用。

img

参考资料


云计算虚拟化技术 linux run 目录满了,100%