参考 Kubernetes 官方文档,简要概述 Kubernetes 中的核心组件用途及部分原理。
一个 K8S 集群,可以分为两个部分:
除了这些核心组件,要让一个 K8S 集群正常的运行起来,还需要一个容器运行环境来负责运行和管理容器的整个生命周期。K8S 支持许多容器运行环境,例如 containerd、CRI-O 以及 K8S CRI 的其它任何实现。
kube-controller-manager 和 kube-scheduler 都是直接调用 kube-apiserver,只有 kube-apiserver 会调用到 etcd。
API Server 就是一个 https 服务器,通过 https 协议请求 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 做统一协调,需要经过三个安全步骤:
认证。判断用户是否为能够访问集群的合法用户。
X509证书认证
(clientCA认证,TLS bootstrapping等),这是 K8S 组件间内部默认使用的认证方式鉴权。通过鉴权策略决定一个 API 调用是否合法。
RBAC
:基于角色的访问控制,是目前 K8S 中最主要的鉴权方式准入控制。一个类似 acl 的列表以插件的形式运行在 API Server 进程中,如果列表有请求内容,就通过,否则不通。在鉴权阶段之后,对象被持久化 etcd 之前,拦截 API Server 的请求,对请求的资源对象执行自定义(校验、修改、拒绝等)操作。
API Server 有很多的接口,这些接口都是负责对 K8S 资源对象的管理功能,像资源的注册和发现。
K8S 资源,就是设计系统时的一个数据模型,或者定义的一张表的数据。一些常用资源如下:
所有的资源对象都会保存到 etcd 中,其它的组件需要查询某些资源,可以通过 API Server 提供的查询接口。如果需要实时掌握资源对象的状态变更,也可以使用 API Server 的 watch 方法。
etcd 是一种开源的分布式统一键值存储,用于分布式系统或计算机集群的共享配置、服务发现和的调度协调。etcd 有助于促进更加安全的自动更新,协调向主机调度的工作,并帮助设置容器的覆盖网络。
etcd 是 K8S 的首要数据存储,也是容器编排的实际标准系统。使用 etcd, 云原生应用可以保持更为一致的运行时间,而且在个别服务器发生故障时也能正常工作。应用从 etcd 读取数据并写入到其中;通过分散配置数据,为节点配置提供冗余和弹性。
etcd 集群基于 raft 协议实现数据一致性。
整个 etcd 集群需要奇数个服务器。这些 etcd 服务会自动投票选举,将一个 etcd 服务选择为 leader,其他则都是follow。leader 节点接收全部的读写请求,然后会把写请求广播给所有的 follow 节点。
follow节点对外是只读的,如果 leader 节点异常了,其他的 follow 节点就要重新开始选举。选举过程如下:
拥有超过半数的投票就可以成为 leader,其他则都变成 follow,在选举的过程中,etcd 服务不可用。综上,通过集群的多节点以及异常时自动恢复,来保证高可用性。虽然性能方面不如 Redis,但是数据一致性方面优于 MySQL、Redis。
kube-scheduler 是 K8S 集群的默认调度器,负责监视新创建的、未指定运行节点(node)的 Pods, 并选择节点来让 Pod 在上面运行。调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、 亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。
如果没有任何一个节点满足 pod 的资源请求,那么这个 pod 将一直停留在未调度状态。找到所有可调度节点之后,再根据一系列函数对这些节点打分,选出得分最高的节点来运行 pod。调度器将这个调度决定通知给 kube-apiserver。
Scheduler 通过 watch 新建 pod 的事件,得到一个新建 pod 的消息之后,就开始内部的工作:
分为两个阶段,左边的是调度周期,右边的是绑定周期。调度周期为 pod 选择一个节点,绑定周期将该决策应用于集群。
调度周期和绑定周期一起被称为调度上下文。调度周期是串行运行的,而绑定周期可以是并行运行。
两个周期里有许多扩展点:pod 选择、节点过滤、打分、排名、绑定等。用户可以实现扩展点定义的接口来实现自己的调度逻辑,并将扩展注册到扩展点上。
kube-controller-manager 负责运行控制器进程,是处理集群中常规任务的后台线程。
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 是部署在每一个常规集群节点上的核心组件。它保证容器都运行在 Pod 中。kubelet 接收一组通过各类机制提供给它的 PodSpecs,确保这些 PodSpecs 中描述的容器处于运行状态且健康。它不会管理不是有 k8s 创建的容器。
kubelet 的功能主要包括上报 Node 节点信息、管理 Pod。
实际上,kubelet 中的组件远远不止图中所展示,节点上的资源都交给 kubelet 来管理,对于各种不同资源,都有对应的管理组件,比如:pod,container、image、secret、configmap、certificate,volume,plugin 等资源的管理。
另外还有一些更重要的组件,比如:
kubelet 按照控制器模式来工作的。它的核心是一个控制循环,即事件和 SyncLoop。事件的来源包括:
Woker 接收到相应的事件,完成指定的某项具体工作,例如:
如果此时接收到一个绑定 Pod 事件,需要在这个节点上新建一个Pod。kubelet 就会为这个新的 Pod 生成对应的 Pod Status,检查 Pod 所声明使用的 Volume 是不是已经准备好。然后,调用下层的容器运行时(比如 Docker),开始创建这个 Pod 所定义的容器。kubelet 调用下层容器运行时的执行过程,并不会直接调用 Docker 的 API,而是通过一组叫作 CRI(Container RuntimeInterface,容器运行时接口)的 gRPC 接口来间接执行的。
集群中每个节点上运行的网络代理。它维护节点上的一些网络规则,这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。