云原生使微服务、容器技术越来越趋于自动化,以前我一直以为以Docker为首的容器技术是一个新的技术,然而,通过最近的深入学习,发现原来容器技术的本质并非新的技术,而是依赖于两种相对成熟的技术:NameSpace和Cgroup。通过组合这两种技术,对传统物理机时代、虚拟机时代进行了根本的变革。下面就分别介绍下这两种相对成熟的技术。
NameSpace(命名空间)提供了一种内核级别的系统资源隔离方式。系统可以为进程分配不同的命名空间,并保证不同命名空间资源独立分配、进程彼此隔离。也就是不同命名空间下的进程互不干扰。Linux支持的NameSpace类型、隔离资源及Kernel版本如下所示:
在主机的/proc/pid/ns目录中可以查看进程归属的NameSpace
Linux对NameSpace的操作主要是通过以下三个系统调用函数实现的:
1.clone 在创建新进程的系统调用时,可以通过flags参数指定需要新建命名空间的类型,与其相关的flags及系统调用的代码如下:
//CLONE_NEWCGROUP
//CLONE_NEWIPC
//CLONE_NEWNET
//CLONE_NEWNS
//CLONE_NEWPID
//CLONE_NEWUSER
//CLONE_NEWUTS
int clone(int (*fn)(void *),void *child_stack,int flags,void *arg)2.setns 该系统调用可以让调用进程加入某个已经存在的命名空间中
int setns(int fd,int nstype)3.unshare 该系统调用可以将调用进程移动到新的命名空间下
int unshare(int flags)进程间通信(Interprocess Communication)是Linux系统中最常用的通信手段。IPC NameSpace用于隔离IPC资源,包含System V IPC对象和POSIX消息队列。其中System V IPC对象包含信号量、共享内存和消息队列,用于进程间的通信,System V IPC对象具有全局唯一的标识,对在该IPC NameSpace内的进程可见,而对其外的进程不可见,当IPCNameSpace被销毁后。所有的IPC对象也会被自动销毁
Kubernetes允许用户在Pod中使用hostIPC进行定义,通过该属性使授权用户容器共享主机IPC NameSpace,达到进程间通信的目的。
Network NameSpace提供了关于系统上网络资源的隔离,例如网络设备、IPV4和IPV6协议栈,IP路由表,防火墙规则、/proc/net目录(/proc/pid/net目录的符号链接)、/sys/class/net目录、/proc/sys/net目录下的很多文件、端口号(socket)等。一个物理的网络设备通常会被放到主机的Network NameSpace不同网络的NameSpace由网络虚拟设备(Virtual Ethernet Device即VETH),再基于网桥或者路由实现与物理网络设备的联通。当网络NameSpace被释放后,对应的VETH Pair也会被自动释放。
在Kubernetes中,同一Pod的不同容器共享同一网络的NameSpace,没有例外,这使得Kubernetes能将网络挂载在更轻量、更稳定的sandbox容器上,而用户自定义的容器只需复用已配置好的网络即可、另外,同一Pod的不同容器中运行的进程可以基于localhost彼此通信,这在多容器进程、彼此需要通信的场景下是非常有效的
PID NameSpace用于进程号隔离,不同PID NameSpace中的进程PID可以相同,容器启动后,Entrypoint进程会作为PID为1的进程㛮,因此是该PID NameSpace的init进程。他是当前NameSpace所有进程的父进程,如果该进程退出,内核会对该PID NameSpace的所有进程发送SIGKILL信号,以便同时结束他们。init进程默认屏蔽系统信号,即除非对该进程对系统信号做特殊处理,否则发往该进程的系统信号默认都会被忽略。不过SIGKILL和SIGSTOP信号比较特殊,init进程无法捕获这两个信号
Kubernetes默认对同一Pod的不同容器构建独立的PID NameSpace,以便将不同容器的进程彼此隔离,同时允许通过ShareProcessNameSpace属性设置不同容器的进程共享PID NameSpace
Kubernetes支持多重容器进程的重启策略,默认行为是用户进程退出后立即重启,Kubernetes用户只需终止其容器的ENTRYPOINT进程,即可实现容器重启。
提供了进程能看到的挂载点的隔离,在主机上通过/proc/pid/mounts、/proc/pid/mountinfo、/proc/pid/mountstats等文件来查看挂载点。在容器内,可以通过mount或lsmnt命令查看Mount NameSpace中的有效挂载点。
Linux内核针对Mount NameSpace隔离性开发了共享子树(Shared Subtree)功能,用于在不同Mount NameSpace之间自动可控地传播mount和unmount事件,共享子树引入了对等组的概念。对等组是一组挂载点,其成员之间互相传播mount和unmount事件,此特性使得当主机磁盘发生变更(比如系统导入新磁盘时),只需在一个mount NameSpace中进行挂载,该磁盘即可在所有NameSpace中可见。
挂载点可设置的传播类型有:
在目录/proc/pid/mountinfo下,可以看到该PID所属进程的mount Namespace下的挂载点的传播类型及所属的对等组。
在Kubernetes中,挂载点通常是private类型,如果需要设置挂载点类型,可以在Pod的spec中填写相应的挂载配置。
UTS(UNIX Time-Sharing System)NameSpace允许不同容器拥有独立的hostname和domain name。该NameSpace中的一个进程可以看做一个在网络上独立存在的节点,也就是说,除了IP外,还能通过主机名进行访问。
User NameSpace主要隔离了安全相关的标识符和树形,比如用户ID、用户组ID、root目录、秘钥等,一个进程的用户ID和组ID在User NameSpace内外可以有所不同,在该UserNameSpace外,他是一个非特权的用户ID,而在此命名空间内,进程可以使用0作为用户ID,且具有完全的特殊权限。
User NameSpace允许不同容器拥有独立的用户和用户组,它主要提供两种职能:权限隔离和用户身份标识隔离。我们协议通过在容器镜像中创建和切换用户,来为文件目录设置不同的用户权限,从而实现容器内的权限管理,而无需影响主机配置。
内核从4.6版本开始支持CGroup NameSpace。如果容器启动时没有开启该命名空间,那么在容器内部查询CGroup时,返回整个系统信息,而开启CGroup后,可以看到当前容器从当前容器以根形式展示的单独的CGroup信息
CGroup视图的改变使容器更加安全,而且在容器内也可以有自己的CGroup结构。
CGroups(Control Groups)是Linux 下用于对一个或一组进程进行资源控制和监控的机制。利用CGroups 可以对诸如CPU 使用时间、内存、磁盘I/O 等进程所需的资源进行限制。Kubernetes 允许用户为Pod 的容器申请资源,当容器在计算节点上运行起来时,可通过CGroups 来完成资源的分配和限制。
在CGroups 中,对资源的控制都是以CGroup 为单位的。目前CGroups 可以控制多种资源,不同资源的具体管理工作由相应的CGroup 子系统(Subsystem)来实现。因此,针对不同类型的资源限制,只要将限制策略在不同的的子系统上进行关联即可。CGroups 由谷歌的工程师引入2.6.24 版本的内核中,最初只对CPU 进行了资源限制,然而随着对其他资源控制需求的增多,它们的CGroup 子系统不断地被引入内核。在容器时代,特别是Kubernetes 中,为了提高对节点资源的利用率,一个节点上会运行尽可能多的容器,这就对 资源隔离的多样性和精确性提出了越来越高的要求,因此CGroups 也发挥着越来越重要的作用。
对于CGroups 的组织管理,用户可以通过文件操作来实现,对资源的控制可以细化到线程级别。CGroups 在不同的系统资源管理子系统中以层级树(Hierarchy)的方式来组织管理:每个CGroup 都可以包含其他的子CGroup,因此子CGroup 能使用的资源,除了受本CGroup 配置的资源参数限制,还受到父CGroup 设置的资源限制。
CPU子系统用于限制进程的CPU使用时间,在CPU子系统中,对每个CGroup下的非实时任务,CPU使用的时间可以通过cpu.shares、cpu.cfs_period_us和cpu.cfs_quoat_us参数来进行控制,而系统的CFS(Completely Fair Scheduler)调度器则根据CGroup下进程的优先级、权重和cpu.shares等配置来给该进程分配相应的CPU时间。
CPU CGroup的主要配置参数如下:
cpuset为CGroup的进程分配单独的cpu和内存节点,将进程固定在某个cpu或内存节点上,以达到提高性能的目的。该子系统主要包含如下配置参数:
用于统计CGroup及其子CGroup下进程的CPU的使用情况
memory用于限制CGroup下进程的内存使用量,也可以获取到内存的详细使用信息
用来实现对块设备访问的I/O 控制,按权重分配目前有两种限制方式:一是限制每秒写入的字节数(Bytes Per Second,即BPS),二是限制每秒的读写次(I/O Per Second,即IOPS)。blkio 子系统按权重分配模式工作于I/O 调度层,依赖于磁盘的CFQ(Completely Fair Queuing,完全公平算法)调度,如果磁盘调度使用deadline 或者none的算法则无法支持。BPS、IOPS 工作于通用设备层,不依赖于磁盘的调度算法,因此有更多的适用场景
PID子系统用来限制CGroup能够创建的进程数
● devices 子系统,控制进程访问某些设备。
● perf_event 子系统,控制perf 监控CGroup 下的进程。
● net_cls 子系统,标记CGroups 中进程的网络数据包,通过TC 模块(Traffic Control )对数据包进行控制。
● net_prio 子系统,针对每个网络设备设置特定的优先级。
● hugetlb 子系统,对hugepage 的使用进行限制。
● freezer 子系统,挂起或者恢复CGroups 中的进程。
● ns 子系统,使不同CGroups 下面的进程使用不同的Namespace。
● rdma 子系统,对RDMA/IB-spe-cific 资源进行限制。
以上就是容器技术的核心技术,虽然现在对于Docker等容器达到了会用的地步,但是对其实现的原理还是知之甚少,如果想要用好容器技术,还需要继续努力去了解这些容器技术背后的实现原理。
| 留言与评论(共有 0 条评论) “” |