Kubernetes(K8s)作为当今最流行的容器编排引擎之一,其集群架构和组件扮演着关键角色,为现代化云原生应用的部署、扩展和管理提供了强大的支持。本文将深入探讨K8s集群的架构以及核心组件,帮助读者更好地理解Kubernetes的工作原理和设计思想。
Kubernetes 架构
Kubernetes集群由多个节点组成,其中包括Master节点和Worker节点。Master节点负责集群的控制平面,而Worker节点负责运行实际的应用程序容器。
一个Kubernetes集群由控制平面节点和工作节点组成。
1.控制平面
控制平面负责容器编排和维护集群的期望状态。它包括以下组件:
- kube-apiserver
- etcd
- kube-scheduler
- kube-controller-manager
- cloud-controller-manager
一个集群可以有一个或多个控制平面节点。
2.工作节点
工作节点负责运行容器化的应用程序。工作节点包括以下组件:
- kubelet
- kube-proxy
- 容器运行时(Container runtime)
分层架构
Kubernetes 设计理念和功能其实就是一个类似 Linux 的分层架构,如下图所示:
分层架构
- 核心层:Kubernetes 最核心的功能,对外提供 API 构建高层的应用,对内提供插件式应用执行环境
- 应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS 解析等)
- 管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态 Provision 等)以及策略管理RBAC、Quota、PSP、NetworkPolicy 等
- 接口层:kubectl 命令行工具、客户端 SDK 以及集群联邦
- 生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴。
控制面板组件
首先,让我们看看每个控制平面组件以及每个组件背后的重要概念。
1.kube-apiserver
kube-api服务器是公开Kubernetes API的Kubernetes集群的中心枢纽。它具有高度的可扩展性,可以处理大量并发请求。最终用户和其他集群组件通过API服务器与集群通信。监控系统和第三方服务很少会与API服务器通信,与集群进行交互。因此,当您使用kubectl来管理集群时,在后端您实际上是通过HTTP REST API与API服务器通信。然而,集群内部的组件如调度器、控制器等使用gRPC与API服务器通信。API服务器和集群中的其他组件之间通过TLS进行通信,以防止对集群的未授权访问。
Kubernetes api-server负责以下工作:
- API管理:公开集群API端点并处理所有API请求。API是version,它同时支持多个API版本。
- 身份验证:(使用客户端证书、不记名令牌和HTTP基本身份验证)和授权(ABAC和RBAC评估)
- 处理API请求并为API对象(如pods, services等)验证数据(验证和变异接纳控制器)
- 它是唯一与etcd通信的组件。
- API-Server协调控制平面和工作节点组件之间的所有进程。
- API-Server有一个内置的apiserver代理。它是API服务器进程的一部分。它主要用于从集群外部访问ClusterIP服务,即使这些服务通常只能在集群内部访问。
- API服务器还包含一个聚合层,它允许您扩展Kubernetes API以创建自定义API资源和控制器。
- API服务器还支持监视资源的变化。例如,客户端可以对特定资源建立监视,并在创建、修改或删除这些资源时接收实时通知
2.etcd
Kubernetes是一个分布式系统,它需要一个高效的分布式数据库,如etcd,以支持其分布式特性。它既是一个后端服务发现,也是一个数据库。你可以称它为Kubernetes集群的大脑。
Etcd是一个开源的强一致性分布式键值存储。它具体意味着什么?
- 强一致性:如果对一个节点进行了更新,强一致性将确保该节点立即更新到集群中的所有其他节点。此外,如果你看看CAP定理,在强一致性和分区容忍下实现100%的可用性是不可能的。
- 分布式:etcd被设计成在多个节点上作为一个集群运行,而不会牺牲一致性。
- 键值存储(Key Value Store) :一种非关系型数据库,以键和值的形式存储数据。它还公开了一个key-value API。该数据存储构建在BoltDB的一个分支BboltDB之上。
Etcd采用raft共识算法实现强一致性和可用性。它以领导者-成员的方式工作,以获得高可用性和抵御节点故障。
那么etcd如何与Kubernetes一起工作呢?
简单地说,当你使用kubectl来获取kubernetes对象的细节时,你是从etcd中获取它。此外,当部署pod等对象时,会在etcd中创建一个条目。
简而言之,以下是您需要了解的关于etcd的内容:
- etcd存储Kubernetes对象的所有配置、状态和元数据(pods、secrets、daemonsets、deployment、configmaps、statefulsets等)。
- etcd允许客户端使用Watch() API订阅事件。Kubernetes api-server使用etcd的watch功能来跟踪对象状态的变化。
- etcd使用gRPC提供了key-value API。此外,gRPC网关是一个RESTful代理,它将所有HTTP API调用转换为gRPC消息。这使得它成为Kubernetes的理想数据库。
- Etcd以键值格式将所有对象存储在/registry目录下。例如,默认命名空间中名为Nginx的pod的信息可以在/registry/pods/default/Nginx下找到
此外,etcd 是控制平面中唯一的 Statefulset 组件。
3.kube-scheduler
kube-scheduler负责在工作节点上调度Kubernetes pods。
当您部署pod时,您需要指定pod需求,例如CPU、内存、亲和性、污点或容忍、优先级、持久卷(PV)等。调度器的主要任务是识别创建请求,并为满足要求的pod选择最佳节点。
下图概述了调度器的工作原理:
在Kubernetes集群中,将有多个工作节点。那么,调度器如何从所有工作节点中选择节点呢?
下面是调度器的工作原理:
- 为了选择最佳节点,Kube-scheduler使用过滤和评分操作。
- 在筛选过程中,调度器找到可以调度pod的最适合的节点。例如,如果有5个可用资源运行pod的工作节点,它会选择所有5个节点。如果没有节点,那么pod是不可调度的,并移动到调度队列。如果是一个大型集群,假设有100个工作节点,调度器不会遍历所有节点。有一个调度器配置参数,名为percentageOfNodesToScore。默认值通常为50%。因此,它尝试以轮询方式迭代超过50%的节点。如果工作节点分布在多个内存域,那么调度器会遍历不同内存域中的节点。对于非常大的集群,默认percentageOfNodesToScore是5%。
- 在评分阶段,调度器通过为过滤后的工作节点分配分数来对节点进行排名。调度器通过调用多个调度插件进行评分。最后,选择级别最高的工作节点进行pod调度。如果所有节点的相同,则随机选择一个节点。
- 一旦节点被选中,调度器就会在API服务器中创建一个绑定事件。意味着绑定pod和node的事件。
下面是你需要知道的关于调度器的事情:
- 它是一个在API服务器中监听pod创建事件的控制器。
- 调度器有两个阶段。调度周期和绑定周期。它们合起来称为调度上下文(scheduling context)。调度周期选择一个工作节点,然后绑定周期将其应用到集群上。
- 调度器总是将高优先级的pods放在低优先级的pods之前进行调度。此外,在某些情况下,在pod开始在选定节点中运行后,pod可能会被删除或移动到其他节点。如果你想了解更多,请阅读Kubernetes pod优先级指南[1]
- 用户可以创建自定义调度器,并在集群上运行多个调度器。当你部署pod时,你可以在pod清单中指定自定义调度器。因此,调度决策将基于自定义调度器逻辑进行。
- 调度器有一个可插拔的调度框架。这意味着你可以将自定义插件添加到调度工作流中。
4.kube-controller-manager
什么是控制器?控制器是运行无限控制循环的程序。这意味着它持续运行并监视对象的实际和期望状态。如果实际状态和期望状态存在差异,它确保kubernetes资源/对象处于期望状态。
根据官方文件:在Kubernetes中,控制器是监视集群状态的控制循环,然后在需要时进行更改或请求更改。每个控制器都试图将当前集群状态移动到期望状态。
假设你想要创建一个部署,你在manifest YAML文件中指定所需的状态(声明方式)。例如,2个副本,1个卷挂载,configmap等。内置的部署控制器确保部署始终处于所需的状态。如果用户用5个副本更新部署,部署控制器将识别它并确保所需状态为5个副本。
kube-controller-manager是一个管理所有Kubernetes控制器的组件。Kubernetes资源/对象,如pods,命名空间,作业,replicaset由各自的控制器管理。此外,Kube调度器也是由Kube控制器管理器管理的控制器。
以下是重要的内置Kubernetes控制器列表:
- Deployment Controller
- ReplicaSet Controller
- DaemonSet Controller
- Job Controller
- CronJob Controller
- Endpoints Controller
- Namespace Controller
- Service Accounts Controller
- Node Controller
以下是关于Kube控制器管理器您应该了解的内容:
- 它管理所有控制器,而这些控制器则试图保持集群处于期望的状态。
- 你可以通过自定义资源定义(Custom Resource Definition)来扩展 Kubernetes,关联自定义控制器。
5.cloud-controller-manager(CCM)
- 当kubernetes部署在云环境中时,云控制器管理器充当云平台api和kubernetes集群之间的桥梁。
- 这样,核心kubernetes的核心组件可以独立工作,并允许云提供商使用插件与kubernetes集成。(例如,kubernetes集群和AWS cloud API之间的接口)
- 集成云控制器允许Kubernetes集群提供云资源,如实例(用于节点)、负载均衡器(用于服务)和存储卷(用于持久卷)。
云控制器管理器包含一组特定于云平台的控制器,确保特定于云的组件(节点、负载均衡器、存储等)的所需状态。以下是云控制器管理器的三个主要控制器:
- 节点控制器:该控制器通过与云提供商API通信更新节点相关信息。例如,节点标记和注释、获取主机名、CPU和内存可用性、节点健康状况等。
- 路由控制器:负责在云平台上配置网络路由。这样不同节点中的pods就可以相互通信。
- 服务控制器:它负责为kubernetes服务部署负载均衡器,分配IP地址等。
下面是云控制器管理器的一些经典示例:
- 部署负载均衡器类型的Kubernetes服务。在这里,Kubernetes提供了一个特定于云的负载均衡器,并与Kubernetes服务集成。
- 为云存储解决方案支持的pods配置存储卷(PV)。
整体云控制器管理器管理kubernetes使用的特定云资源的生命周期。
节点组件
现在让我们看看每个工作节点组件。
1.kubelet
Kubelet是一个运行在集群中的每个节点上的代理组件。它不作为容器运行,而是作为守护进程运行,由systemd管理。
它负责向API服务器注册工作节点,并主要从API服务器使用podSpec (Pod规范- YAML或JSON)。podSpec定义了应该在pod中运行的容器、它们的资源(例如CPU和内存限制)以及其他设置,如环境变量、卷和标签。
然后,它通过创建容器将podSpec带到所需状态。
简而言之,kubelet负责以下工作:
- 为pod创建、修改和删除容器。
- 负责处理活性,准备和启动探针。
- 通过读取pod配置和在主机上为卷挂载创建相应的目录来负责挂载卷。
- 通过调用API服务器(如cAdvisor和CRI)来收集和报告节点和pod状态。
- Kubelet也是一个控制器,它监视pod的变化,并利用节点的容器运行时来拉取图像,运行容器等。
除了来自API服务器的podSpec, kubelet还可以接受来自文件、HTTP端点和HTTP服务器的podSpec。“podSpec from a file”的一个很好的例子是Kubernetes static pods。
静态pods由kubelet控制,而不是API服务器。
这意味着你可以通过向Kubelet组件提供pod的YAML位置来创建pods。然而,Kubelet创建的静态pods并不由API服务器管理。
这是一个静态pod的真实示例。
在引导控制平面时,kubelet从位于/etc/kubernetes/manifest的podSpecs中将 api-server、scheduler 和 controller manager作为静态 pod。
以下是kubelet的一些关键内容:
- Kubelet使用CRI(容器运行时接口)gRPC接口与容器运行时进行通信。
- 它还向流日志公开HTTP端点,并为客户端提供exec会话。
- 使用CSI (container storage interface) gRPC配置块卷。
- 它使用集群中配置的CNI插件来分配pod的IP地址,并为pod设置必要的网络路由和防火墙规则。
2.kube-proxy
要理解Kube-proxy,你需要对Kubernetes服务和端点对象有基本的了解。
Kubernetes中的服务是一种向内部或外部流量暴露一组pods的方法。当您创建服务对象时,它将获得分配给它的虚拟IP。它被称为clusterIP。它只能在Kubernetes集群内访问。
Endpoint对象包含一个服务对象下pod组的所有IP地址和端口。端点控制器负责维护pod IP地址(端点)列表。服务控制器负责配置服务的端点。
你不能ping ClusterIP,因为ClusterIP只用于服务发现,不像pod ip可以ping。
现在让我们了解一下Kube-proxy。
Kube-proxy是一个守护进程,在每个节点上作为守护进程运行。它是一个为pods实现Kubernetes服务概念的代理组件。(一组具有负载均衡的pods的单一DNS)。它主要代理UDP、TCP和SCTP,但不支持HTTP。
当你使用Service(ClusterIP)公开pods时,Kube-proxy创建网络规则,将流量发送到服务对象下分组的后端pods(endpoints)。这意味着,所有的负载平衡和服务发现都由Kube代理处理。
那么Kube-proxy是如何工作的呢?
Kube代理与API服务器通信以获取服务(ClusterIP)以及相应的pod ip和端口(endpoints)的详细信息。它还监视服务和端点的变化。
然后,Kube-proxy使用以下任意一种模式来创建/更新规则,将流量路由到服务背后的pods:
(1) IPTables:默认模式。IPTables模式下,流量通过IPtable规则进行处理。这意味着对于每个服务,都创建了IPtable规则。这些规则捕获到达ClusterIP的流量,然后将其转发到后端pods。在这种模式下,kube-proxy随机选择后端pod进行负载均衡。连接建立后,请求会发送到同一个pod,直到连接终止。
(2) IPVS:对于超过1000个业务的集群,IPVS提供了性能提升。它支持以下后端负载平衡算法。
- rr:round-robin:默认模式。
- Lc:最小连接数(最小打开连接数)
- Dh:目标哈希
- Sh:源散列
- Sed:预期的最短延迟
- Nq:永不排队
(3) 用户空间:(遗留&不推荐)
(4) 内核空间:该模式仅适用于Windows系统。
如果您想了解kube-proxy IPtables和IPVS模式之间的性能差异,请阅读这篇文章。此外,您可以通过将其替换为ciilium来运行Kubernetes集群,而无需kube-proxy。
1.29 Alpha特性:Kubeproxy有一个新的基于𝗻𝗳𝘁𝗮𝗯𝗹𝗲𝘀的后端。nftables是IPtables的继承者,旨在更简单、更高效。
3.container runtime
你可能知道Java运行时(JRE)。它是在主机上运行Java程序所需的软件。以同样的方式,容器运行时是运行容器所需的软件组件。
容器运行时运行在Kubernetes集群中的所有节点上。它负责从容器注册表中拉取映像,运行容器,为容器分配和隔离资源,以及管理主机上容器的整个生命周期。
为了更好地理解这一点,让我们看看两个关键概念:
- 容器运行时接口(CRI) :它是一组api,允许Kubernetes与不同的容器运行时交互。它允许不同的容器运行时与Kubernetes互换使用。CRI定义了用于创建、启动、停止和删除容器以及管理映像和容器网络的API。
- 开放容器计划(Open Container Initiative, OCI):它是一组容器格式和运行时的标准
Kubernetes支持与容器运行时接口(CRI)兼容的多个容器运行时(CRI-O, Docker Engine, containerd等)。这意味着,所有这些容器运行时都实现了CRI接口并公开了gRPC CRI api(运行时和映像服务端点)。
那么Kubernetes如何利用容器运行时呢?
正如我们在Kubelet部分中了解到的,Kubelet代理负责使用CRI API与容器运行时交互,以管理容器的生命周期。它还从容器运行时获取所有容器信息,并将其提供给控制平面。
让我们以CRI-O容器运行时接口为例。以下是容器运行时如何与kubernetes一起工作的高级概述。
- 当从API服务器有对pod的新请求时,kubelet通过Kubernetes容器运行时接口与CRI-O守护进程通信,以启动所需的容器。
- CRI-O使用容器/映像库从配置的容器注册表中检查并提取所需的容器映像。
- 然后CRI-O为容器生成OCI运行时规范(JSON)。
- 然后,CRI-O启动一个oci兼容运行时(runc),根据运行时规范启动容器进程。
附加组件
- kube-dns: 负责为整个集群提供 DNS 服务
- Ingress Controller: 为服务提供外网入口
- Prometheus: 提供资源监控
- Dashboard: 提供 GUI
- Federation: 提供跨可用区的集群
- Fluentd-elasticsearch: 提供集群日志采集、存储与查询
参考文档:
[1]Kubernetes pod优先级指南: https://devopscube.com/pod-priorityclass-preemption/