详解Kubernetes架构

微信扫一扫,分享到朋友圈

详解Kubernetes架构

Kubernetes是Google公司开源的一个容器(Container)编排与调度管理框架,该项目最初是Google内部面向容器的集群管理系统,而现在是由Cloud Native Computing Foundation(CNCF,云原生计算基金会)托管的开源平台,由Google、AWS、Microsoft、IBM、Intel、Cisco和Red Hat等主要参与者支持,其目标是通过创建一组新的通用容器技术来推进云原生技术和服务的开发。作为领先的容器编排引擎,Kubernetes提供了一个抽象层,使其可以在物理或虚拟环境中部署容器应用程序,提供以容器为中心的基础架构

Kubernetes系统拥有一个庞大而活跃的开发人员社区,这使其成为历史上增长最快的开源项目之一。它是GitHub上排名前10的项目,也是Go语言最大的开源项目之一,Kubernetes也被称为K8s,是通过将8个字母ubernete替换为8而形成的缩写。

Kubernetes系统具有如下特点:

  • 可移植:支持公有云、私有云、混合云、多重云(Multi-cloud)。
  • 可扩展:模块化、插件化、可挂载、可组合。
  • 自动化:自动部署、自动重启、自动复制、自动伸缩/扩展。

Kubernetes的发展历史

2003—2004年,Google发布了Borg系统,它最初是一个小规模项目,约有3~4人合作开发。而现在,Borg是一个大规模的内部集群管理系统,它在数千个不同的应用程序中运行数十万个作业,跨越许多集群,每个集群拥有数万台计算机。

2013年左右,Google继Borg系统之后发布了Omega集群管理系统,这是一个适用于大型计算集群的灵活、可扩展的调度程序。

2014年左右,Google发布了Kubernetes,其是作为Borg的开源版本发布的。同年,Microsoft、Red Hat、IBM、Docker等加入Kubernetes社区。

2015年左右,Google在美国波特兰的OSCON 2015大会上宣布并正式发布Kubernetes 1.0。Google与Linux基金会合作组建了云原生计算基金会(CNCF)。CNCF旨在为云原生软件构建可持续发展的生态系统,并围绕一系列高质量开源项目建立社区,整合这些开源技术来让编排容器成为微服务架构的一部分。CNCF自创立以来已经拥有非常多的高质量项目,其中包括Kubernetes、Prometheus、gRPC、CoreDNS等。

2016年左右,Kubernetes成为主流。在CloudNativeCon 2016大会上,来自世界各地的约1000名贡献者和开发者齐聚一堂,交流有关Fluentd、Kubernetes、Prometheus、OpenTracing和其他云原生技术的内容。

2017年左右,互联网巨头纷纷表示支持Kubernetes。在这一年,Google和IBM发布微服务框架Istio,其提供了一种无缝连接、管理和保护不同微服务网格的方法。Amazon宣布为Kubernetes提供弹性容器服务,用户可以在AWS上使用Kubernetes部署、管理和扩展容器化应用程序。同年年底,Kubernetes 1.9发布。

2018年左右,无人不知Kubernetes。在KubeCon + CloudNativeCon Europe 2018峰会上,有超过4300名开发者聚集在一起讨论Kubernetes生态技术。同年,Kubernetes 1.10发布。KubeCon第一次在中国举办。

Kubernetes架构图

Kubernetes系统用于管理分布式节点集群中的微服务或容器化应用程序,并且其提供了零停机时间部署、自动回滚、缩放和容器的自愈(其中包括自动配置、自动重启、自动复制的高弹性基础设施,以及容器的自动缩放等)等功能。

Kubernetes系统最重要的设计因素之一是能够横向扩展,即调整应用程序的副本数以提高可用性。设计一套大型系统,且保证其运行时健壮、可扩展、可移植和非常具有挑战性,尤其是在系统复杂度增加时,系统的体系结构会直接影响其运行方式、对环境的依赖程度及相关组件的耦合程度。

微服务是一种软件设计模式,适用于集群上的可扩展部署。开发人员使用这一模式能够创建小型、可组合的应用程序,通过定义良好的HTTP REST API接口进行通信。Kubernetes也是遵循微服务架构模式的程序,具有弹性、可观察性和管理功能,可以适应云平台的需求。

Kubernetes的系统架构设计与Borg的系统架构设计理念非常相似,如Scheduler调度器、Pod资源对象管理等。Kubernetes总架构图如图1所示。

图1 Kubernetes总架构图

Kubernetes系统架构遵循客户端/服务端(C/S)架构,系统架构分为Master和Node两部分,Master作为服务端,Node作为客户端。Kubernetes系统具有多个Master服务端,可以实现高可用。在默认的情况下,一个Master服务端即可完成所有工作。

Master服务端也被称为主控节点,它在集群中主要负责如下任务:

  • 集群的“大脑”,负责管理所有节点(Node)。
  • 负责调度Pod在哪些节点上运行。
  • 负责控制集群运行过程中的所有状态。

Node客户端也被称为工作节点,它在集群中主要负责如下任务:

  • 负责管理所有容器(Container)。
  • 负责监控/上报所有Pod的运行状态。

Master服务端(主控节点)主要负责管理和控制整个Kubernetes集群,对集群做出全局性决策,相当于整个集群的“大脑”。集群所执行的所有控制命令都由Master服务端接收并处理。Master服务端主要包含如下组件:

  • kube-apiserver组件:集群的HTTP REST API接口,是集群控制的入口。
  • kube-controller-manager组件:集群中所有资源对象的自动化控制中心。
  • kube-scheduler组件:集群中Pod资源对象的调度服务。

Node客户端(工作节点)是Kubernetes集群中的工作节点,Node节点上的工作由Master服务端进行分配,比如当某个Node节点宕机时,Master节点会将其上面的工作转移到其他Node节点上。Node节点主要包含如下组件:

  • kubelet组件:负责管理节点上容器的创建、删除、启停等任务,与Master节点进行通信。
  • kube-proxy组件:负责Kubernetes服务的通信及负载均衡服务。
  • container组件:负责容器的基础管理服务,接收kubelet组件的指令。

下面让我们深入了解每个节点上运行的组件。

Kubernetes各组件的功能

Kubernetes总架构图如图1所示。架构中主要的组件有kubectl、kube-apiserver、kube-controller-manager、kube-scheduler、kubelet、kube-proxy和container等。另外,作为开发者,还需要深入了解client-go库。不同组件之间是松耦合架构,各组件之间各司其职,保证整个集群的稳定运行。下面对各组件进行更细化的架构分析和功能阐述。

kubectl

kubectl是Kubernetes官方提供的命令行工具(CLI),用户可以通过kubectl以命令行交互的方式对Kubernetes API Server进行操作,通信协议使用HTTP/JSON。

kubectl发送相应的HTTP请求,请求由Kubernetes API Server接收、处理并将结果反馈给kubectl。kubectl接收到响应并展示结果。至此,kubectl与kube-apiserver的一次请求周期结束。

client-go

kubectl是通过命令行交互的方式与Kubernetes API Server进行交互的,Kubernetes还提供了通过编程的方式与Kubernetes API Server进行通信。client-go是从Kubernetes的代码中单独抽离出来的包,并作为官方提供的Go语言的客户端发挥作用。client-go简单、易用,Kubernetes系统的其他组件与Kubernetes API Server通信的方式也基于client-go实现。

在大部分基于Kubernetes做二次开发的程序中,建议通过client-go来实现与Kubernetes API Server的交互过程。这是因为client-go在Kubernetes系统上做了大量的优化,Kubernetes核心组件(如kube-scheduler、kube-controller-manager等)都通过client-go与Kubernetes API Server进行交互。

kube-apiserver

kube-apiserver组件,也被称为Kubernetes API Server。它负责将Kubernetes“资源组/资源版本/资源”以RESTful风格的形式对外暴露并提供服务。Kubernetes集群中的所有组件都通过kube-apiserver组件操作资源对象。kube-apiserver组件也是集群中唯一与etcd集群进行交互的核心组件。例如,开发者通过kubectl创建了一个Pod资源对象,请求通过kube-apiserver的HTTP接口将Pod资源对象存储至etcd集群中。

etcd集群是分布式键值存储集群,其提供了可靠的强一致性服务发现。etcd集群存储Kubernetes系统集群的状态和元数据,其中包括所有Kubernetes资源对象信息、集群节点信息等。Kubernetes将所有数据存储至etcd集群中前缀为/registry的目录下。

kube-apiserver属于核心组件,对于整个集群至关重要,它具有以下重要特性:

  • 将Kubernetes系统中的所有资源对象都封装成RESTful风格的API接口进行管理。
  • 可进行集群状态管理和数据管理,是唯一与Etcd集群交互的组件。
  • 拥有丰富的集群安全访问机制,以及认证、授权及准入控制器。
  • 提供了集群各组件的通信和交互功能。

kube-controller-manager

kube-controller-manager组件,也被称为Controller Manager(管理控制器),它负责管理Kubernetes集群中的节点(Node)、Pod副本、服务、端点(Endpoint)、命名空间(Namespace)、服务账户(ServiceAccount)、资源定额(ResourceQuota)等。例如,当某个节点意外宕机时,Controller Manager会及时发现并执行自动化修复流程,确保集群始终处于预期的工作状态。

Controller Manager负责确保Kubernetes系统的实际状态收敛到所需状态,其默认提供了一些控制器(Controller),例如DeploymentControllers控制器、StatefulSet控制器、Namespace控制器及PersistentVolume控制器等,每个控制器通过kube-apiserver组件提供的接口实时监控整个集群每个资源对象的当前状态,当因发生各种故障而导致系统状态出现变化时,会尝试将系统状态修复到“期望状态”。

Controller Manager具备高可用性(即多实例同时运行),即基于Etcd集群上的分布式锁实现领导者选举机制,多实例同时运行,通过kube-apiserver提供的资源锁进行选举竞争。抢先获取锁的实例被称为Leader节点(即领导者节点),并运行kube-controller-manager组件的主逻辑;而未获取锁的实例被称为Candidate节点(即候选节点),运行时处于阻塞状态。在Leader节点因某些原因退出后,Candidate节点则通过领导者选举机制参与竞选,成为Leader节点后接替kube-controller-manager的工作。

kube-scheduler

kube-scheduler组件,也被称为调度器,目前是Kubernetes集群的默认调度器。它负责在Kubernetes集群中为一个Pod资源对象找到合适的节点并在该节点上运行。调度器每次只调度一个Pod资源对象,为每一个Pod资源对象寻找合适节点的过程是一个调度周期。

kube-scheduler组件监控整个集群的Pod资源对象和Node资源对象,当监控到新的Pod资源对象时,会通过调度算法为其选择最优节点。调度算法分为两种,分别为预选调度算法和优选调度算法。除调度策略外,Kubernetes还支持优先级调度、抢占机制及亲和性调度等功能。

kube-scheduler组件支持高可用性(即多实例同时运行),即基于Etcd集群上的分布式锁实现领导者选举机制,多实例同时运行,通过kube-apiserver提供的资源锁进行选举竞争。抢先获取锁的实例被称为Leader节点(即领导者节点),并运行kube-scheduler组件的主逻辑;而未获取锁的实例被称为Candidate节点(即候选节点),运行时处于阻塞状态。在Leader节点因某些原因退出后,Candidate节点则通过领导者选举机制参与竞选,成为Leader节点后接替kube-scheduler的工作。

kubelet

kubelet组件,用于管理节点,运行在每个Kubernetes节点上。kubelet组件用来接收、处理、上报kube-apiserver组件下发的任务。kubelet进程启动时会向kube-apiserver注册节点自身信息。它主要负责所在节点(Node)上的Pod资源对象的管理,例如Pod资源对象的创建、修改、监控、删除、驱逐及Pod生命周期管理等。

kubelet组件会定期监控所在节点的资源使用状态并上报给kube-apiserver组件,这些资源数据可以帮助kube-scheduler调度器为Pod资源对象预选节点。kubelet也会对所在节点的镜像和容器做清理工作,保证节点上的镜像不会占满磁盘空间、删除的容器释放相关资源。

kubelet组件实现了3种开放接口,如图2所示。

图2 kubelet开放接口

  • Container Runtime Interface:简称CRI(容器运行时接口),提供容器运行时通用插件接口服务。CRI定义了容器和镜像服务的接口。CRI将kubelet组件与容器运行时进行解耦,将原来完全面向Pod级别的内部接口拆分成面向Sandbox和Container的gRPC接口,并将镜像管理和容器管理分离给不同的服务。
  • Container Network Interface:简称CNI(容器网络接口),提供网络通用插件接口服务。CNI定义了Kubernetes网络插件的基础,容器创建时通过CNI插件配置网络。
  • Container Storage Interface:简称CSI(容器存储接口),提供存储通用插件接口服务。CSI定义了容器存储卷标准规范,容器创建时通过CSI插件配置存储卷。

kube-proxy

kube-proxy组件,作为节点上的网络代理,运行在每个Kubernetes节点上。它监控kube-apiserver的服务和端点资源变化,并通过iptables/ipvs等配置负载均衡器,为一组Pod提供统一的TCP/UDP流量转发和负载均衡功能。

kube-proxy组件是参与管理Pod-to-Service和External-to-Service网络的最重要的节点组件之一。kube-proxy组件相当于代理模型,对于某个IP:Port的请求,负责将其转发给专用网络上的相应服务或应用程序。但是,kube-proxy组件与其他负载均衡服务的区别在于,kube-proxy代理只向Kubernetes服务及其后端Pod发出请求。

Kubernetes Project Layout设计

Kubernetes项目由Go语言编写。Go语言官方对项目的结构设计没有强制要求,早期的Go语言开发者都喜欢将包文件代码放置在项目的src/目录下,如nsqio开源项目,开发者喜欢将入口文件放入apps/目录。不同开发者的喜好不同,这导致开源项目的结构设计没有统一标准。

后来Go语言社区提出Standard Go Project Layout方案,以对Go语言项目目录结构进行划分。目前该标准已经成为众多Go语言开源项目的选择。

根据Standard Go Project Layout方案,我们对标一下Kubernetes的Project Layout 设计,Kubernetes Project Layout结构说明如表1所示。

表1 Kubernetes Project Layout结构说明

由于Kubernetes项目全球开发者众多,这导致早期的代码包较多,尤其是kube-apiserver项目,其内部所引用的代码包特别多。随着Kubernetes系统版本的迭代,逐渐将部分包进行了合并,其中staging/目录为核心包暂存目录,该目录下的核心包多以软连接的方式链接到vendor/k8s.io目录。

Kubernetes系统组件较多,各组件的代码入口main结构设计风格高度一致,我们以核心组件为例,命令示例如下:

$ tree cmd/ -L 2
cmd/
├── BUILD
├── OWNERS
├── kube-apiserver
│   ├── BUILD
│   ├── OWNERS
│   ├── apiserver.go
│   └── app
├── kube-controller-manager
│   ├── BUILD
│   ├── OWNERS
│   ├── app
│   └── controller-manager.go
├── kube-proxy
│   ├── BUILD
│   ├── app
│   └── proxy.go
├── kube-scheduler
│   ├── BUILD
│   ├── OWNERS
│   ├── app
│   └── scheduler.go
├── kubectl
│   ├── BUILD
│   ├── OWNERS
│   └── kubectl.go
└── kubelet
├── BUILD
├── OWNERS
├── app
└── kubelet.go

从代码入口main结构来看,各组件的目录结构、文件命名都保持高度一致。假设需要新增一个组件,我们甚至可以复制原有的组件代码,只需简单修改一下就可以将其运行起来。每个组件的初始化过程也非常类似,初始化过程示意图如图3所示。

图3 初始化过程示意图

main结构中定义了进程运行的周期,包括从进程启动、运行到退出的过程。以kube-apiserver组件为例,kube-apiserver初始化过程如图4所示。

图4 kube-apiserver初始化过程

  • rand.Seed:组件中的全局随机数生成对象。
  • app.NewCommand:实例化命令行参数。通过flags对命令行参数进行解析并存储至Options对象中。
  • logs.InitLogs:实例化日志对象,用于日志管理。
  • command.Execute:组件进程运行的逻辑。运行前通过Complete函数填充默认参数,通过Validate函数验证所有参数,最后通过Run函数持久运行。只有当进程收到退出信号时,进程才会退出。

Kubernetes其他组件的cmd设计与之类似,故不再重复描述,后续章节会针对每个组件详细描述其启动过程。

本文节选自《Kubernetes源码剖析》,经出版社授权转载。

作业帮回应寻求新一轮超6亿美元融资:消息不准确

上一篇

小米 Mi Notebook 14 配置曝光:最高可选十代 i7 + MX 350

下一篇

你也可能喜欢

详解Kubernetes架构

长按储存图像,分享给朋友