参考 Kubernetes 官方文档,简要概述 Kubernetes 中的核心组件用途及部分原理。
一个 K8S 集群,可以分为两个部分:
- 控制平面(Control Plane)。它是一套管理系统,专门来管理集群节点和服务,为集群做出全局决策,比如资源的调度,以及检测和响应集群事件,如图左侧所示。
- kube-apiserver
- etcd
- kube-scheduler
- kube-controller-manager
- 工作机器,即常规集群节点(Nodes)。运行容器化的应用程序,如图右侧所示。
- kubelet
- kubelet-proxy
除了这些核心组件,要让一个 K8S 集群正常的运行起来,还需要一个容器运行环境来负责运行和管理容器的整个生命周期。K8S 支持许多容器运行环境,例如 containerd、CRI-O 以及 K8S CRI 的其它任何实现。
kube-controller-manager 和 kube-scheduler 都是直接调用 kube-apiserver,只有 kube-apiserver 会调用到 etcd。
kube-apiserver
API Server 就是一个 https 服务器,通过 https 协议请求 API Server 创建资源,删除资源,查看资源等操作。
- 负责公开了 Kubernetes API,负责处理接受请求的工作。 API 服务器是 Kubernetes 控制平面的前端。
- kube-apiserver 设计上考虑了水平扩缩,也就是说,它可通过部署多个实例来进行扩缩,可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。
API Server
简单来讲,API Server 就是 K8S 集群的 Web 接口服务:
K8S 组件之间、插件之间的数据交换,都是通过 API Server 来完成。另外所有与 etcd 打交道的事情,都要通过 API Server 来完成。
不论是通过命令行还是通过 SDK 编程实现来管理 K8S 集群上的资源对象,都需要调用 API Server 提供的接口才可以完成。
网关
为了保证安全的对外暴露,还需具有身份认证、鉴权的功能,而消息转发的功能,是一个 Proxy 接口,可以通过代理方式将 API Server 收到的 REST 请求转发到某个 Node 上的 kubelet 上,由 kubelet负责响应。
访问 K8S 集群的时候,API Server 做统一协调,需要经过三个安全步骤:
-
认证。判断用户是否为能够访问集群的合法用户。
- 匿名认证(Anonymous requests)
- 白名单认证(BasicAuth认证)
- Token认证(Webhooks、ServiceAccount Tokens、OpenIDConnect Tokens等)
X509证书认证
(clientCA认证,TLS bootstrapping等),这是 K8S 组件间内部默认使用的认证方式
-
鉴权。通过鉴权策略决定一个 API 调用是否合法。
- Always:当集群不需要鉴权时选择AlwaysAllow
- ABAC:基于属性的访问控制
RBAC
:基于角色的访问控制,是目前 K8S 中最主要的鉴权方式- Node:一种对 kubelet 进行授权的特殊模式
- Webhook:通过调用外部 REST 服务对用户鉴权
-
准入控制。一个类似 acl 的列表以插件的形式运行在 API Server 进程中,如果列表有请求内容,就通过,否则不通。在鉴权阶段之后,对象被持久化 etcd 之前,拦截 API Server 的请求,对请求的资源对象执行自定义(校验、修改、拒绝等)操作。
资源对象管理
API Server 有很多的接口,这些接口都是负责对 K8S 资源对象的管理功能,像资源的注册和发现。
K8S 资源,就是设计系统时的一个数据模型,或者定义的一张表的数据。一些常用资源如下:
- 工作负载资源,需要使用到服务器的 CPU/内存这些计算资源。最常见到的 Pod、Deployment
- Service 资源,可以把 Pod 内部的应用暴露出去。像 Service、Ingress
- 配置和存储资源,定义配置信息,存储卷等,比如 ConfigMap、Secret
- 身份认证资源,定义使用人的账号和访问认证方式等,例如 ServiceCount
- 鉴权资源,定义哪些角色、身份、主题可以访问和操作集群资源等,如 ClusterRoleBinding
- 策略资源,以定义服务资源策略,网络访问策略等,像 LimitRange
- 集群资源,定义集群的节点、命名空间等,比如 Node,NameSpace
- 扩展资源,定义哪些资源可以对外公开,哪些可以通过 webhook 来调用等
所有的资源对象都会保存到 etcd 中,其它的组件需要查询某些资源,可以通过 API Server 提供的查询接口。如果需要实时掌握资源对象的状态变更,也可以使用 API Server 的 watch 方法。
API Server 的访问方式
- 通过 curl 或者浏览器中直接请求 https 接口。
- 使用 kubectl 工具通过 https 协议请求 API Server。
- 使用编程方式,比如 K8S 开源的 client-go 项目以及其相关二次开发工具。
K8S 组件怎样利用 API Server 通信
- 当本机的 pod 状态发生变更时,比如新建成功、出现异常、销毁,kubelet 组件都要调用 API Server 的接口上报这些数据。同时 kubelet 也需要通过 watch 接口来监听 pod 的变更。
- kube-controller-manager 中有很多的控制器,它们也会使用 watch 接口,监听自己关注的资源对象,然后做出相应的处理。
- kube-scheduler 也是要通过 watch 接口,监听到新建 pod 的信息后,再查询节点列表,然后开始执行pod 调度逻辑,调度成功将 pod 绑定到目标节点上。
- K8S 利用缓存来缓解高并发下 API Server 的压力,在各个组件本地都有一份缓存数据,不需要每次都实时从 API Server 中读取数据。
etcd
etcd 是一种开源的分布式统一键值存储,用于分布式系统或计算机集群的共享配置、服务发现和的调度协调。etcd 有助于促进更加安全的自动更新,协调向主机调度的工作,并帮助设置容器的覆盖网络。
etcd 是 K8S 的首要数据存储,也是容器编排的实际标准系统。使用 etcd, 云原生应用可以保持更为一致的运行时间,而且在个别服务器发生故障时也能正常工作。应用从 etcd 读取数据并写入到其中;通过分散配置数据,为节点配置提供冗余和弹性。
etcd 的高可用
etcd 集群基于 raft 协议实现数据一致性。
整个 etcd 集群需要奇数个服务器。这些 etcd 服务会自动投票选举,将一个 etcd 服务选择为 leader,其他则都是follow。leader 节点接收全部的读写请求,然后会把写请求广播给所有的 follow 节点。
follow节点对外是只读的,如果 leader 节点异常了,其他的 follow 节点就要重新开始选举。选举过程如下:
- 每个节点会启动一个随机延时,推举自己成为 leader;
- 节点拥有最新的数据,可以成为 leader,得到一票;
- 如果多个节点的票数相同,它们会再次重复过程 1,再来第二轮投票;
拥有超过半数的投票就可以成为 leader,其他则都变成 follow,在选举的过程中,etcd 服务不可用。综上,通过集群的多节点以及异常时自动恢复,来保证高可用性。虽然性能方面不如 Redis,但是数据一致性方面优于 MySQL、Redis。
kube-scheduler
kube-scheduler 是 K8S 集群的默认调度器,负责监视新创建的、未指定运行节点(node)的 Pods, 并选择节点来让 Pod 在上面运行。调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、 亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。
如果没有任何一个节点满足 pod 的资源请求,那么这个 pod 将一直停留在未调度状态。找到所有可调度节点之后,再根据一系列函数对这些节点打分,选出得分最高的节点来运行 pod。调度器将这个调度决定通知给 kube-apiserver。
kube-scheduler工作流程
Scheduler 通过 watch 新建 pod 的事件,得到一个新建 pod 的消息之后,就开始内部的工作:
- 过滤出满足资源调度需求的所有可调度节点。
- 对这些节点打分,把得分最高的节点找出来。
- 选择得分最高的一个节点,如果得分最高的节点有多个,随机选一个。
- 成功找到调度节点之后,就把这个节点信息写入新建 pod 消息中,保存到 api-server。
扩展调度器 Scheduler Framework
分为两个阶段,左边的是调度周期,右边的是绑定周期。调度周期为 pod 选择一个节点,绑定周期将该决策应用于集群。
调度周期和绑定周期一起被称为调度上下文。调度周期是串行运行的,而绑定周期可以是并行运行。
两个周期里有许多扩展点:pod 选择、节点过滤、打分、排名、绑定等。用户可以实现扩展点定义的接口来实现自己的调度逻辑,并将扩展注册到扩展点上。
kube-controller-manager
kube-controller-manager 负责运行控制器进程,是处理集群中常规任务的后台线程。
- 节点控制器(Node Controller):负责在节点出现故障时进行通知和响应,比如集群中某个 pod 挂掉了,或者将 pod 从某个节点上驱逐了,Controller Manager 便会实现 pod 的自动重建。
- 任务控制器(Job Controller):监测代表一次性任务的 Job 对象,然后创建 Pods 来运行这些任务直至完成
- 端点分片控制器(EndpointSlice controller):填充端点分片(EndpointSlice)对象(以提供 Service 和 Pod 之间的链接)。
- 服务账号控制器(ServiceAccount controller):为新的命名空间创建默认的服务账号(ServiceAccount)。
K8S 控制平面另外还有一个 cloud-controller-manager 组件,是可选的。
kube-controller-manager 是集群内部的管理控制中心,由负责不同资源的多个 Controller 构成,共同负责集群内的 Node、Pod 等所有资源的管理。几乎每种特定资源都有特定的 Controller 维护管理以保持预期状态,而 Controller Manager 的职责便是把所有的 Controller 聚合起来,保证集群中各种资源的实际状态(status)和用户定义的期望状态(spec)一致。
Controller Manager 主要提供了一个分发事件的能力。在 client-go 中,不同的 Controller 只需要注册对应的 Handler 来等待接收和处理事件。Controller 运行起来之后,只需要等着事件回调它就行了。
更多细节见 client-go 源码
kubelet
kubelet 是部署在每一个常规集群节点上的核心组件。它保证容器都运行在 Pod 中。kubelet 接收一组通过各类机制提供给它的 PodSpecs,确保这些 PodSpecs 中描述的容器处于运行状态且健康。它不会管理不是有 k8s 创建的容器。
kubelet 的功能主要包括上报 Node 节点信息、管理 Pod。
kubelet 架构
从网上找了一张 kubelet 架构图的一部分:
实际上,kubelet 中的组件远远不止图中所展示,节点上的资源都交给 kubelet 来管理,对于各种不同资源,都有对应的管理组件,比如:pod,container、image、secret、configmap、certificate,volume,plugin 等资源的管理。
另外还有一些更重要的组件,比如:
- PLEG (Pod Lifecycle Event Generator),维护着 Pod 缓存,定期通过容器运行时获取 Pod 的信息,与缓存中的信息比较,生成相应的事件,然后把事件写入到通道中。
- PodWorkers,处理事件中 Pod 的信息同步,如果 Pod 正在创建,记录器延迟记录 Pod 从 pending 到 running 的耗时,杀掉不应该运行的 pod,使用VolumeManager 为 Pod 挂载卷等。
- PodManager,存 储Pod 的期望状态。
- StatsProvider,提供节点和容器的统计信息。
- ContainerRuntime 容器运行时,与遵循 CRI 规范的高级容器运行时进行交互。
- syncLoop,接受来自 PodConfig 的 Pod 变更通知、定时任务、PLEG 的事件,将 Pod 同步到期望状态。
- PluginManager,运行一组异步循环,根据此节点确定哪些插件需要注册、取消注册并执行。
kubelet 工作原理
kubelet 按照控制器模式来工作的。它的核心是一个控制循环,即事件和 SyncLoop。事件的来源包括:
- PodConfig 的更新事件;
- PLEG- Pod 生命周期事件;
- kubelet 本身设置的执行周期,如:Pod 的健康检测等;
- 定时的清理事件。
Woker 接收到相应的事件,完成指定的某项具体工作,例如:
- NodeStatusManager 负责响应 Node 状态变化,把 Node 的状态收集起来,并上报给 ApiServer。
- CpuManager 负责维护该节点的 CPU 信息,能够正确的管理 CPU 的使用量和可用量。
如果此时接收到一个绑定 Pod 事件,需要在这个节点上新建一个Pod。kubelet 就会为这个新的 Pod 生成对应的 Pod Status,检查 Pod 所声明使用的 Volume 是不是已经准备好。然后,调用下层的容器运行时(比如 Docker),开始创建这个 Pod 所定义的容器。kubelet 调用下层容器运行时的执行过程,并不会直接调用 Docker 的 API,而是通过一组叫作 CRI(Container RuntimeInterface,容器运行时接口)的 gRPC 接口来间接执行的。
kube-proxy
集群中每个节点上运行的网络代理。它维护节点上的一些网络规则,这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。