Docker核心技术

一、概要

Docker核心是一个操作系统级虚拟化方法,理解起可能并不像VM那样直观。从虚拟化方法的四个方面:

  • 隔离性Namespace
  • 可配额/可度量Cgroups
  • 便携性AUFS
  • 安全性AppArmor、SELinux、GRSEC

二、隔离性Linux namespace

每个用户实例之间相互隔离,互不影响。一般的硬件虚拟化方法给出的方法是VM,而LXC给出的方法是container,更细一点将就是Kernel namespace。其中pid、net、ipc、mnt、uts、user等namespace将container的进程、网络、消息、文件系统、UTS("Unix Time-sharing System")和用户空间隔离开。
  • 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 address,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资源申请加入namspace信息 - 每个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上的用户。

三、控制组Cgroups

控制组-Control Groups(Cgroups)可配额、可度量

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

以下是每个子系统的详细说明:

官方文档:https://www.kernel.org/doc/Documentation/

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

四、便携性AUFS

AUFS(AnotherUnionFS)是一种Union FS。
简单来说就是支持将不同目录挂载到同一个虚拟文件系统下(unite serveral directories into a single virtaul filesystem)的文件系统,更进一步的理解,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也正是如此,接下来我们从启动container中的Linux为例来介绍docker对AUFS特性的运用。

典型的启动Linux运行需要两个FS: bootfs + rbootfs:

bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,当boot成功后kernel被加载到内存中后bootfs就被umount了,roots(root file system)包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。

对于不同的Linux发行版,bootfs基本是一致的,但rootfs会有差别,因此不同的发行版可以公用bootfs如下图:

典型的Linux在启动后,首先将rootfs设置为readonly,进行一系列检查,然后将其切换为"readwrite"供用户使用。在Docker中,初始化时也是将rootfs以readonly方式加载病检查,然而接下来利用union mount的方式将一个readwrite文件系统挂载在readonly的rootfs之上,并且允许再次将下层的FS(file system)设定为readonly并且向上叠加,这样一组readonly和一个writeable的结构构成一个container的运行时态,每一个FS被称作一个FS层。如下图:

得益于AUFS的特性,每一个对readonly层文件/目录的修改都只会存在于上层的writeable层中。这样由于不存在竞争,多个container可以共享readonly的FS层。所以Docker将readonly的FS层称作"image" - 对于container而言整个rootfs都是read-write的,但事实上所有的修改都写入最上层的writeable层中,image不保存用户状态,只用于模版、新建和复制使用。

上层的image依赖下层的image称作base image。因此想要从一个image启动一个container,Docker会先加载这个image和依赖的父images以及base image,用户的进程运行在writeable的layer中。所有parent image中的数据信息以及ID、网络和lxc管理的资源限制等具体container的配置,构成一个Dokcer概念上的container。如下图:

五、安全性AppArmor、SELinux、GRSEC

安全永远是相对的,这里有三个方面可以考虑Docker的安全特性:

docker官方文档:https://docs.docker.com/engine/security/

1、由kernel namespaces和cgroups实现的Linux系统固有的安全标准;

2、Docker Deamon的安全接口;

3、Linux本身的安全加固解决方案,类如:AppArmor,SELinux;

猜你喜欢

转载自www.cnblogs.com/hwlong/p/9162391.html