你还在背八大件吗?不如把k8s的架构和os一起看,你会发现一些超有趣的事情!本文旨在将k8s的架构和os做个对比,帮助读者理解为什么k8s要这么设计。
kubernetes架构中由master节点和minion节点组成,master节点也叫控制平面(Controll Plane),为毛要叫控制平面这么拗脑的东西,这是个翻译问题(就像container runtime非得翻译成容器运行时),大家理解成master节点就可以了。master节点由四个组件组成,分别是APIServer、etcd、controller manager(控制器管理器)、scheduler(调度器)。
pod是k8s里的最小单位,由一组容器构成,pod里共享网络命名空间和存储卷。可以理解为pod就是一个进程(组)。容器本身在宿主机上的表现形式就是一个进程。将pod理解为进程之后,后面的架构就好理解了。
那么我们再看创建pod的过程,跟创建进程其实是差不多的。父进程通过fork函数创建新进程,fork本质是占用一段占用一段虚拟地址空间,然后放入代码段和数据段,并分配新的pid。虚拟地址空间就相当于申请到了内存,有了pid就可以分配到CPU占用时间。
再看我们创建pod时,通过拉取镜像来启动容器,镜像具有只读、共享的特点,这也是利用了写时复制、读时共享的机制:在需要对镜像提供的文件进行修改时,该文件会从镜像的文件系统中被复制到容器的可写层的文件系统中进行修改,而镜像中的文件不会改变;同时按需分配空间,而非提前分配,即当一个pod被创建出来后,才会分配空间。fork函数在创建进程时也是一样的:父进程的虚拟地址映射着物理内存的实际的物理地址,clone()的时候,并不是在物理地址中直接再复制一份和父进程一样的物理内存块,而是子进程的虚拟地址也直接映射到同一物理内存块中;当操作这个物理内存块时(比如修改变量的值),再复制该部分的实际物理内存到子进程中,并不是全部复制。
APIServer是用户请求及其他系统组件与集群交互的唯一入口。对外,所有资源的创建、更新和删除都需要通过调用API Server 的API 接口来完成。对内,API Server 是各个模块之间数据交互的通信枢纽,提供了etcd 的封装接口API,这些API 能够让其他组件监听到集群中资源对象的增、删、改的变化。
举个简单的例子:
在这个过程中,客户端发起部署两个pod的请求,其实就等于在用户模式下创建一个程序。OS收到这个请求之后,对内核发起系统调用,用户模式切换为内核模式,调用进程调度器,根据进程的要求,分配地址空间、CPU占用时间等资源给进程。
操作系统中的进程调度器在干嘛?它主要做的事情就是分配逻辑CPU的时间片,逻辑CPU的时间片等同于CPU资源,这就像scheduler在分配node以及其上的vCPU给pod使用。scheduler在分配node给pod时,有一系列的规则,比如亲和性、容忍度设置等,进程调度器里也有,比如nice函数控制进程分配的优先级、CPU姻亲绑定等。
k8s有30多个controller manager,比如大家耳熟能详的deployment controller manager、service controller manager、endpoint controller manager,大多数控制器的工作模式雷同,都是通过API Server 监听其相应的资源对象,根据对象的状态来决定接下来的动作,使其达到spec的状态。
kubelet 就相当于运行在每个节点上的负责启动容器的重要的守护进程。在启动时,Kubelet进程加载配置参数,向API Server 处创建一个Node 对象来注册自身的节点信息,例如操作系统、Kernel 版本、IP 地址、总容量(Capacity)和可供分配的容量(Allocatable Capacity)等。然后kubelet 须定时(默认值是每10s 通过NodeStatusUpdateFrequency 设置参数)向API Server 汇报自身情况,例如磁盘空间是否用满、CPU 和Memory 是否有压力,自身服务是否Ready 等,这些信息都将被调度器使用。
kube-proxy 在每个节点上都运行,它也从API Server 监听Service 和Endpoint对象的变化,并根据Endpoint 对象的信息设置Service 到后端Pod 的路由,维护网络规则,执行TCP、UDP 和SCTP 流转发。它本质上就是调用内核的网络,因为它是以iptables模式作数据转发的。