docker命名空间、控制组及联合文件系统概念

基本架构

命名空间

控制组

联合文件系统

 

docker底层依赖的核心技术主要包括操作系统的命名空间(Namespace)、控制组(Control Groups)、联合文件系统(Union File Systems)和linux虚拟网络支持。

 

基本架构

docker采用标准的c/s架构,客户端和服务端可以运行在一个机器上,也可以用过socke或者RESTful API进行通信。

docker daemon接受客户端的请求并处理,像容器的创建、运行、分发等。docker daemon是一个非常松耦合的架构,通过专门的Engine模块来分发管理各个来自客户端的任务。

 

监听方式:

1.UNIX域套接字

默认就是这种方式, 会生成一个 /var/run/docker.sock 文件, UNIX 域套接字用于本地进程之间的通讯, 这种方式相比于网络套接字效率更高, 但局限性就是只能被本地的客户端访问。

 

2.tcp端口监听

服务端开启端口监听 dockerd -H IP:PORT , 客户端通过指定IP和端口访问服务端 docker -H IP:PORT

通过这种方式, 只要知道了你暴露的ip和端口就能随意访问你的docker服务了,并且可以操作。 因为docker的权限很高, 不法分子可以从这突破取得服务端宿主机的最高权限。可以开启多个tcp监听。

此为centos7 需要在/etc/systemd/system/docker.service.d目录下创建配置文件,并通过docker -H修改监听,如上图即可。

 

命名空间

命名空间(Namespace)是linux内核针对实现虚拟化的一个强大的特性。每个容器都可以有自己单独的命名空间,保证了容器之间彼此之间互不影响。

命名空间机制提供容器的隔离安全。在容器汇总运行的进程不会被运行在本机上的进程和其他容器通过正常渠发现和影响的。

 

进程命名空间

linux通过命名空间管理进程号,同一进程,在不同命名空间中,看到的进程号不同。进程命名空间也是一个父子关系。以下新运行了两个容器,可以看到容器进程的父进程pid,正是docker主进程的pid:

 

网络命名空间

通过网络命名空间,可以实现网络隔离。一个网络命名空间为进程提供了一个完全独立的网络协议栈的试图。包括网络设备接口、IPv6和IPv4协议栈、IP路由表、防火墙规则、sockets等。这样每个容器的网络就能隔离开来。docker采用虚拟网络设备,将不同的命名空间的网络设备连接到一起。默认容器中的逆序网卡将同本地主机上的docker0网桥连接在一起。

brctl show命令可以看到桥接到宿主机docker0网桥上的虚拟网口:

 

IPC命名空间

容器中进程交互采用了常见的交互方法IPC(Interprocess Communicaton),包括信号量、消息队列和共享内存等。同一个IPC命名空间内的进程可以彼此可见,允许交互,不同空间无法交互。

 

挂载命名空间

类似chroot,讲一个进程放到一个特定的目录执行。允许不同命名空间的进程看到文件结构不同,这样不同命名空间的进程所看到的文件目录彼此隔离。

 

UTS命名空间

UTS(UNIX Time-sharing System)命名空间允许每个容器拥有独立的主机名和域名,从而可以虚拟出一个有独立主机名的网络空间环境。

 

用户命名空间

每个容器可以有不同的用户和组,每个容器内部都可以有root账号,跟宿主机不再一个命名空间。

 

控制组

控制组是linux内核的一个特性,主要用来对共享资源进行隔离、限制,审计等。避免多个容器同时运行时的系统资源竞争。控制组可以提供对容器的内存、cpu、磁盘io等资源进行限制和计费管理。其目标是为不同的应用情况提供同一的接口,从控制单一进程到系统级虚拟化。控制组确保了当发生在容器内的资源压力不会影响到本机和其他容器。

 

控制组提供:

1.资源限制(Resource limiting):可以将组设置为不超过设定的内存限制。比如:内存子系统可以为进程组设定一个内存使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发Out of Memory警告。

2.优先级(Prioritization):通过优先级让一些组优先得到更多的CPU等资源。

3.资源审计(Accounting):用来统计系统实际上把多少资源用到适合的目的上,可以使用cpuacct子系统记录某个进程组使用的CPU时间。

4.隔离(isolation):为组隔离命名空间,这样一个组不会看到另一个组的进程、网络连接和文件系统。

5.控制(Control):挂起、恢复和重启动等操作。

 

docker安装后可以在/sys/fs/cgroup/memory/docker目录下看到对docker组应用的各种控制项,可以通过修改这些文件值来控制限制docker的应用资源:

可以在创建或启动容器时为每个容器指定资源的限制,例如使用-c|--cpu-shares[0]参数来调整cpu的权重;使用-m|--memory[=MEMORY]参数来调整容器使用内存的大小。

具体的一些命名用法都可以使用docker run --help来查看。

 

联合文件系统

联合文件系统(Union FS)是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,层层叠加,同时将不同目录挂载到同一个虚拟文件系统下。联合文件系统是实现Docker镜像的技术基础。Docker镜像可以通过分层来进行继承。例如,用户基于基础镜像(用来生成其他镜像的基础,往往没有父镜像)来制作各种不同的应用镜像。这些镜像共享同一个基础镜像层,提高了存储效率。此外,当用户改变了一个Docker镜像(比如升级程序到新的版本),则会创建一个新的层(layer)。因此,用户不用替换整个原镜像或者重新建立,只需要添加新层即可。用户分发镜像的时候,也只需要分发被改动的新层内容(增量部分)。这让Docker的镜像管理变得十分轻量级和快速。

当Docker在利用镜像启动一个容器时,Docker镜像将分配文件系统,并且挂载一个新的可读写的层给容器,容器将会在这个文件系统中被创建,并且这个可读写的层被添加到镜像中。Docker 目前支持的联合文件系统种类包括 AUFS, btrfs, vfs 和 DeviceMapper。

 

Docker镜像自身就是由多个文件层组成,每一层有唯一的编号(层ID)。

 

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

Docker容器是建立在Aufs基础上的,Aufs是一种Union FS, 简单来说就是支持将不同的目录挂载到同一个虚拟文件系统下,并实现一种layer的概念。Aufs将挂载到同一虚拟文件系统下的多个目录分别设置成read-only,read-write以及whiteout-able权限,对read-only目录只能读,而写操作只能实施在read-write目录中。重点在于,写操作是在read-only上的一种增量操作,不影响read-only目录。当挂载目录的时候要严格按照各目录之间的这种增量关系,将被增量操作的目录优先于在它基础上增量操作的目录挂载,待所有目录挂载结束了,继续挂载一个read-write目录,如此便形成了一种层次结构。

Docker镜像的典型结构如下图。传统的Linux加载bootfs时会先将rootfs设为read-only,然后在系统自检之后将rootfs从read-only改为read-write,然后我们就可以在rootfs上进行写和读的操作了。但Docker的镜像却不是这样,它在bootfs自检完毕之后并不会把rootfs的read-only改为read-write。而是利用union mount(UnionFS的一种挂载机制)将一个或多个read-only的rootfs加载到之前的read-only的rootfs层之上。在加载了这么多层的rootfs之后,仍然让它看起来只像是一个文件系统,在Docker的体系里把union mount的这些read-only的rootfs叫做Docker的镜像。但是,此时的每一层rootfs都是read-only的,我们此时还不能对其进行操作。当我们创建一个容器,也就是将Docker镜像进行实例化,系统会在一层或是多层read-only的rootfs之上分配一层空的read-write的rootfs。

 

docker安全性

docker的安全性在上面的命名空间和控制组都有所体现,当然还有其他机制保障了docker的安全性。比如其内核能力机制和docker服务端的防护以及其他安全特性。

 

能力机制

  能力机制是linux内核一个强大的特性,可以提供细粒度的权限访问控制。其权限的划分不仅是作用于用户上,而且还可以作用在进程和文件上。

  默认情况下,docker启动的容器被严格限制允许使用内核的一部分能力,包括chown、kill、setuid、fowner、sys_chroot、net_raw等等。而且通常在运行一些需要特权的进程,几乎都是由容器以外的支持系统来进程管理。比如ssh由宿主机ssh服务管理;cron交给使用它的服务的应用来处理;日志系统可由docker或第三方服务管理;网络管理也都是在宿主机上设置。等等。

  docker容器还可以禁用一些没必要的权限。比如完全禁止任何文件挂载操作;禁止直接访问本地主机的套接字;禁止访问一些文件系统的操作(创建新的设备、修改文件属性等);禁止模块加载。

  以上就算在容器中获取了root权限,也不能获得本地主机的较高权限。

 

docker服务端

  因为docker服务的运行目前还需root权限的支持,所以改进自身的安全防护也很重要:

  1. 将容器的root用户映射到本地主机上的非root用户,减少权限提升引起的安全问题。
  2. 允许docker服务端在非root权限下运行,用可靠子进程来代理执行需要特殊权限的操作。

  还可以使用一些安全软件来防护增强安全性,比如SElinux、GRSEC和PAX等。

posted @ 2018-09-03 23:10  风住  阅读(809)  评论(0编辑  收藏  举报