02.K8S架构详解

K8S整体架构

上图包含了K8S中的所有组件,我们把K8S集群分为两个部分,即Master节点(主结点)和Node节点(工作结点),其中主节点运行着控制平面组件Control Plane Components,工作结点运行着必要的KubeletKube-proxy组件。

K8S采用了分布式、去中心化的架构,由分布式控制平面和多节点的工作节点组成,K8S的核心设计目标之一是高可用性和高伸缩性,因此它并没有采用"一主多从"这种分布式架构模式。

其中,控制平面的组件也可以被部署成多个实例以提供冗余和负载均衡。另外,工作结点之间也是对等的,可以随时向集群中添加或者删除节点。

控制平面

API-server

API-Server充当了整个K8S集群的入口点,负责接受整个集群的所有内部请求.这里的请求并不是外界访问我们部署的应用的请求,而是k8s内部通过请求以达到预期的最终状态,通常涉及更改集群的状态或者管理集群的资源。

比如下图中,某个deployment当前的pod数量为5,我们使用kubectl scale deployment my-delpyment --replicas = 10去手动将这个deploymentpod数量扩容到10,这时其实是修改了deployment.spec.deseriedReplicas字段,它的值会变为10,那么这个预期状态会先被对应的deployment controller给发现,然后发送请求给api-server进行处理。

在K8S中,每个对象都由四部分组成,即Api-version、Kind、Metadata、Spec、Status,其中Spec直译过来是规约的意思,我们可以理解Spec是对象想要达到的状态,Status则是目前的状态 ,K8S会努力让Status不断变化,直至和Spec约定的状态达到一致。

etcd

etcd是一个分布式的Key-Value系统,它并不直接参与编排,而是负责维护集群的配置信息和状态,包括配置、各种对象的状态、服务定义等等

例如:

资源的配置信息 :包括各种对象的定义,如Deployment、StatefulSet、ConfigMap、Secret等等,这些配置信息用于定义集群中的资源对象,它们的Spec、Label等等。

实时状态数据etcd也存储了关于集群中各种资源对象的实时状态数据,这包括有关当前运行的pod的信息、资源对象状态、服务的端口映射等等,当这些对象的状态发生更改的时候,etcd会记录这些变化,以便K8S内部能获取到最新的状态信息。

同时,在K8S中每个资源对象都有一个ResoureVersion字段,代表了该对象的版本号,因为K8S使用乐观并发控制来处理并发访问,这个版本号会在对象发生改变的时候递增。所以当我们手动修改某个对象的时候,比如kubectl edit deployment my-deployment -n mynamepsace的时候,有可能保存文件之后提示"因为resourceVersion更新对象失败"的提示。

这是因为在修改文件的时候,我们的对象已经被其他因素修改了,并且数据同步到etcd中,使得该对象的版本号+1,我们再次保存修改的时候,版本号落后于ectd中对象的版本号,于是会修改失败。

控制平面的内部状态 :包括controller-managerleader选举信息、API-Server的配置信息等。

kube-scheduler

kube-scheduler负责将新创建的 pod调度到合适的 node ,它会考虑多种因素,以确保最后部署的Node是最佳选择,比如以下因素。

  • Node资源可用性,当前Node的资源情况,剩下多少CPUMemoryGPU资源等等
  • PodNode都可以设置亲和性和反亲和性规则 ,通过pod或者NodeAffinity字段实现,以便实现在调度的时候实现来偏好或者规避Nodepod
  • 结点和容忍性,可以给结点设置污点Taints,表明它们不希望接受特定类型的pod,同时也可以设置容忍Tolerations规则,以接受具有特定污点的pod,它们分别通过Node.Spec.taintsPod.spec.tolerations字段来声明
  • 其他因素:比如,节点的负载均衡,kube-scheduler会尽量保证各个节点的负载均衡,以确保不会过度分配负载到某些节点。以及数据的本地性,如果多个节点上有具有所需数据的podkube-scheduler会优先选择哪些本地数据更好的结点,以减少网络传输的需求等策略。

当然,我们也可以编写自定义的调度器拓展,来实现我们想要的定制化pod调度策略,例如pod不会调度到cpu负载超过50%的Node上。

kube-controller-manager

kube-controller-manager中蕴含了各种各样的控制器 ,如Deployment控制器、StatefulSet控制器、Replication控制器、JOB/CornJOB控制器、HPA控制器等等。下图有一个简单的概括,当然,除了这些控制器之外还有其他的控制器。

这些不同的控制器会负责管理、调整其管理的对象,比如Deployment就负责管理所有的Deployment对象,每一个控制器是一个独立的进程,但是K8S为了降低管理这一系列控制器的复杂度,将它们编译成了同一个二进制文件,并且运行在同一进程里。

这么做也是有原因的,当系统存在多个controller-manager的时候,K8S就需要选举出一个控制器的主节点作为集群的Leader,其他的节点被认为是从节点,当这些控制器被编译在一个二进制文件中的时候,只需要将这一个Controller Manager实例选举为Leader即可,而不需要每一种控制器都有自己的Leader

这样的话,成为Leader的那个实例将负责执行所有控制器的工作,能够显著降低控制器选主部分的复杂性,保证整个Controller Manager和各个Controller集群的高可用性和一致性。

cloud-controller-manager

cloud-controller-managerkube-controller-manager类似,也是一个控制器管理器,这个组件主要是和云厂商相关的,如果你是自己搭建的集群,那么就不会有这个组件。

它嵌入了特定于云平台的控制逻辑cloud-controller-manager允许你将你的集群连接到云提供商的API之上,这是一个可插拔式的组件,使得不同的云厂商都能够将其平台与K8S集成

我们可以把云看成一个系统,K8S看成一个系统,cloud-controller-manager组件则是两个系统之间的桥梁,比如云中的虚拟服务器,通过cloud-controller-manager被视为K8S中的一个Node结点;云中的路由通过cloud-controller-manager可能被K8S用来为Pod分配IP地址等等。

下面这张图能很清晰的看出cloud-controller-manager组件的作用,一个刚刚在云平台创建的NODE,被cloud-controller-manager识别,然后添加进集群内部。

云控制器管理器的控制器包括:

Node控制器

节点控制器负载在云基础设施中创建了新服务器时,为之更新节点对象,节点控制器从云提供商获取当前租户中主机的信息,节点控制器执行以下功能:

1.使用从云平台API获取的对应服务器的唯一标识符,更新Node对象。

2.利用特定云平台的信息为Node对象添加注解和标签,例如节点所在的区域,和所具有的资源等等。

3.获取节点的网络地址和主机名。

4.检查节点的健康状态,如果节点无响应,控制器通过云平台API查看该节点是否已从云中、删除或终止。如果节点已经从云中删除,则控制器也会将节点从K8S内部集群中删除。

Router控制器

路由控制器负责适当的配置云平台中的路由,以便不同节点上的容器可以相互通信。具体实现取决于各个云供应商,路由控制器也可能为Pod分配IP地址。

Service控制器

服务控制器与云平台的组件集成,如负载均衡器、IP地址、网络包过滤、目标健康检查等等。服务控制器会和云平台提供商的API进行交互,以进行上述组件的配置。

Work Node

k8s将容器放在pod中,再通过schedulerpod调度到对应的Node上运行,Node可以是一台物理机器,也可以是一台虚拟机器(云服务器),这取决于你所在的集群配置。每个节点包含着运行Pod所需的所有环节,而所有结点通过控制平面进行管理。

节点上通常需要kubeletcontainer runtimekube-proxy三个组件

kubelet

kubelet是每个节点的代理,它会把Node注册到K8S集群中,会在集群中的每一个node上运行,它保证容器按照期望情况运行在Pod中。它接受一组PodSpecs,这一组PodSpecs用来描述pod的期望运行状态,kubelet不会管理不是由K8S创建的容器。

也就是说kubelet的作用主要有两个,即确保容器在节点上正确运行与Master节点进行通信 ,下面我们从几个方向详解kubelet的具体作用。

  • 容器生命周期管理:负责创建、启动、停止和销毁容器,根据Pod的定义,来保证Pod中的容器与期望的状态一致,如果某个容器失败或者终止,kubelet会尝试重启,以确保正确运行。
  • 资源监控:kubelet会定期检查节点上的资源利用情况,然后报告给Master
  • 健康检查:Kubelet会定期执行容器的健康检查,以确保它们正常运行。如果不健康,则可以重启容器或者通知Master节点以采取措施。
  • 与API-Server通信:通过与API-Server的通信来同步本Node上的对象状态更新,以及获取其他对象的更新。

例如我们通过下面这个.yaml文件创建一个很简单的Pod

yaml 复制代码
apiVersion: v1
kind: Pod
metadata: 
	name: my-pod 
spec:
	containers:
	- name: container-1
  	image: nginx

在成功创建这个Pod之后,Kubelet会定期监控container-1容器的健康状态,以确保它能够正常运行;并且会定期向Master报告节点的资源使用情况,如果容器崩溃或者终止,kubelet会尝试重启它,以确保Pod中的容器处于预期状态。

此外,如果想要做到更细粒度的资源监控,比如DeploymentPod、容器级别,那么单单靠Kubelet是不行的,可以通过访问API-Server,或者使用其他工具进行数据上报,比如PrometheusGrafana等等。

kube-proxy

kube-proxy是集群每个Node上运行的网络代理,以确保Pod之间的通信,以及Pod和集群外部的通信是正常的。

它会维护节点上的一些网络规则,这些规则会允许从集群内部或者外部的网络与Pod进行通信,以便将流量路由到正确的Pod。

如果操作系统提供了可用的数据包过滤层,则kube-proxy会通过它来实现网络规则。否则,kube-proxy只做流量转发的功能。

  • 服务代理:充当了服务和后端Pod之间的中介,接受流量并传递给对一个的Pod,客户端只需要知道服务的名称或者ClusterIP,而不需要知道后端Pod的详细信息。
  • 负载均衡:根据不同的负载均衡策略将请求分发给对应的后端Pod
  • 集群外部访问:可以将外部流量路由到集群内部的服务。

Container Runtime

container-Runtime组件使得K8S拥有容器运行的环境和条件,从而能够有效运行容器。它负责管理K8S环境中容器的执行和生命周期。

K8S支持许多容器运行环境,如containerdCRI-O以及Kuberenetes CRI等等。

K8S早期版本仅仅适用于Docker Engine这一种特定的容器运行时,后来增加了对其他类型的容器运行时的接口,因此设置了CRI标准接口来和各种各样的容器运行时进行交互,它包括了容器运行时的接口和镜像的接口,我们可以看看这些接口的定义。

scss 复制代码
service RuntimeService {  //Runtime的grpc接口 
    rpc Version(VersionRequest) returns (VersionResponse) {}
    rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}
    rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}
    rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}
    rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {}
    rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}
    rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}
    rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}
    rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {}
    rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {}
    rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}
    rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}
    rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}
    rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {}
    ...
}
scss 复制代码
service ImageService { //镜像的grpc接口 
    rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}
    rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}
    rpc PullImage(PullImageRequest) returns (PullImageResponse) {}
    rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}
    rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}
}

上面的这些接口都是容器运行时需要暴露给Kubelet的接口,Docker Engine没有实现CRI接口,所以当K8S创建了CRI标准之后,手动创建了部分特殊代码来帮助Docker Engine过度,也就是dockershim

K8S在v1.20版本宣布移除dockershimv1.24版本K8S正式移除了dockerShim,为什么要这么做呢?

从可拓展性的角度看,K8S通过引入新的容器运行时接口将容器管理与具体的运行时解耦,不再依赖某个具体的运行时实现。另外,Docker也不打算支持K8S中的CRI接口,需要K8S社区在仓库中维护Dockershim。此外,在较新的 CRI 运行时中实现了与 dockershim 不兼容的功能,例如 cgroups v2 和用户命名空间。 从 Kubernetes 中移除 dockershim 允许在这些领域进行进一步的开发。

如何实现K8S的高可用

工作结点:因为scheduler在调度pod的时候有自动调动策略,通常如果某个node失效,会自动将pod部署在其他node上,所以node结点我们无需额外处理,只要集群拥有足够多的node结点,运行情况就会很稳定

api-server、scheduler:负责接受请求 是一个无状态的服务,所以我们可以起多个api-server实例,然后通过负载均衡器让请求均匀的打到所有的api-server上去,既能提高可用性,也能降低单个api-server的压力,提升请求处理速度

ectd、kube-controller-manager:可部署多个实例,它们内部会选出主节点

结语

《每天十分钟,轻松入门K8S》的第二篇02.K8S架构详解到这里就结束了,感谢您看到这里。

在下一篇中我会带着大家搭建K8S环境,真正开始K8S的具体操作,感兴趣的小伙伴欢迎点赞、评论、收藏,您的支持就是对我最大的鼓励。

相关推荐
hai4058714 分钟前
Spring Boot中的响应与分层解耦架构
spring boot·后端·架构
FLGB1 小时前
Flink 与 Kubernetes (K8s)、YARN 和 Mesos集成对比
大数据·flink·kubernetes
Adolf_19931 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
叫我:松哥2 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
海里真的有鱼2 小时前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
工业甲酰苯胺2 小时前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
新知图书2 小时前
Rust编程的作用域与所有权
开发语言·后端·rust
petaexpress3 小时前
常用的k8s容器网络模式有哪些?
网络·容器·kubernetes
wn5313 小时前
【Go - 类型断言】
服务器·开发语言·后端·golang
希冀1234 小时前
【操作系统】1.2操作系统的发展与分类
后端