文章目录
概述
由上图可知,Kubernetes基础架构由Master和node组成
。
-
控制面Master节点主要包含以下组件:
etcd组件
:Kubernetes的元数据存储,从基础架构图可以看到,Kube-apiserver是唯一直接与etcd打交道的组件,各组件都通过kube-apiserver实现数据交互,它们极其依赖kube-apiserver提供的资源变化监听机制。而kube-apiserver对外提供的监听机制,也正是etcd Watch特性提供的底层支持。kube-apiserver
:负责对外提供集群各类资源的增删改查及Watch接口,它是Kubernetes集群中各组件数据交互和通信的枢纽。kube-apiserver在设计上可水平扩展,高可用Kubernetes集群中一般多副本部署。当收到一个创建Pod写请求时,它的基本流程是对请求进行认证、限速、授权、准入机制等检查后,写入etcd即可kube-scheduler
:是调度器组件,负责集群Pod的调度。基本原理是通过监听kube-apiserver获取待调度的Pod,然后基于一系列筛选和评优算法,为Pod分配最优的Node节点kube-controller-manager
:包含一系列的控制器组件,比如Deployment、StatefulSet等控制器。控制器的核心思想是监听、比较资源实际状态与期望状态是否一致,若不一致则进行协调工作使其最终一致
-
Node节点主要包含以下组件:
kubelet
:部署在每个节点上的Agent组件,负责Pod的创建运行。基本原理是通过监听APIServer获取分配到其节点上的Pod,然后根据Pod的规格详情,调用运行时组件创建pause和业务容器等。kube-proxy
:部署在每个节点上的网络代理组件。基本原理是通过监听API-Server获取Service、Endpoint等资源,基于iptables,ipvs等技术实现数据包转发等功能。
iptables的核心功能:
通过 API Server 的 Watch 接口实时跟踪 Service 与 Endpoint 的变更信息,并更新对应的 iptables 规则,Client 的请求流量则通过 iptables 的 NAT 机制“直接路由”到目标 Pod。
Kubernetes API Server 原理解析
Kubernetes API Server 概述
Kubernetes API Server 功能:
- 提供Kubernetes各类资源对象(如Pod、RC、Service等)的增、删、改、查及Watch等HTTP Rest接口,成为集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心
- 它还是集群管理的 API 入口
- 是资源配额控制的入口
- 提供了完备的集群安全机制。API Server是k8s集群数据的唯一访问入口,因此安全性与高性能成为API Server 设计和实现的两大核心目标;通过采用HTTPS安全传输通道与CA签名数字证书强制双向认证的方式,API
Server的安全性得以保障。为了更细粒度的控制用户或应用对K8s资源对象的访问权限,启用了访问控制策略,认证管理;
Kubernetes API Server通过一个名为kube-apiserver的进程
提供服务,是单进程
的服务。该进程运行在Master上,也是一个Deployment,副本可以有多个,对应的服务名称是kubernetes
。在默认情况下,kube-apiserver进程在本机的8080端口(对应参数–insecure-port=8080)提供REST服务。同时启动HTTPS安全端口(–secure-port=6443)来启动安全机制,加强REST API访问的安全性。
通常可以通过命令行工具kubectl来与Kubernetes API Server交互,它们之间的接口是RESTful API。也可通过curl直接测试和验证Kubernetes API Server所提供的接口。
Kubernetes集群中所有资源的访问和变更实际都是通过Kubernetes API Server的REST API来实现的,所以集群安全的关键点在于识别认证客户端身份(Authentication)以及访问权限的授权(Authorization)
访问API Server
Rest API方式
举例如下:
- 登录 Master 并运行下面的 curl 令,得到 JSON 方式返回的 Kubernetes API
的版本信息:
- 以运行下面的命令查看 Kubernetes API Server 目前支持的 资源对象的种类:
curl localhost:8080/api/v1
- 根据以上命令的输出,我们可以运行下面的 curl 命令,分别返回集群中的 Pod 列表、Service 列表、 RC 列表等:
- curl localhost:8080/api/v1/pods
- curl localhost:8080/api/v1/services
- curl localhost:8080/api/v1/replicationcontrollers
- 如杲只想对外暴露部分 REST 服务,则可以在 Master 或其他节点上运行 kubectl proxy进程启动一个内部代理来实现。例如,运行下面的命令,在 8001 端口启动代理,并且拒绝客户端访问 RC的 API:
kubectl proxy --reject-paths="^/api/v1/replicationcontrollers "
–port=8001 --v=2 Starting to serve on 127.0.0.1:8001
运行下面的命令进行验证: curl localhost:8001/api/v1/replicationcontrollers
<h3>Unauthorized</h3>
- kubectl proxy 具有很多特性,最实用的一个特性是提供简单有效的安全机制,比如在采用白名单限制非法客户端访问时,只需增加下面这个参数即可:
–accept-hosts=“^localhost$,^127.0.0.1:8001$,^[::1]$”
编程方式访问API Server
通过编程方式调用 Kubernetes API Server,具体使用场景又细分为两种场景:
- 运行在Pod中的用户进程调用Kubernetes API,通常用来实现分布式集群搭建的目标。
- 开发基于Kubernetes的管理平台
运行在Pod中的用户进程调用Kubernetes API
Pod中的进程如何知道API Server的访问地址呢,因为Kubernetes API Server本身也是一个Service,其名字就是kubernetes,他的clusterIP地址是ClusterIP地址池中的第一个IP,他所服务的端口是HTTPS端口443,通过kubectl get svc可以确认这一点。
举例:比如下面这段来自谷歌官方的 Elasticsearch 集群例子中的代码, Pod在启动的过程中通过访问 Endpoints API, 找到 属于 elasticsearch -logging 这个 Service所有 Pod 副本 IP 地址,用来构建集群。
开发基于Kubernetes的管理平台
比如调用Kubernetes API
来完成Pod、Service、RC等资源对象的图形化创建和管理界面
,此时可以使用kubernetes及各开源社区为开发人员提供的各种语言版本的Client Library。
API Server架构设计
API Server性能设计考量
API Server的性能是决定Kubernetes集群整体性能的关键因素,因此Kubernetes的设计者综合运用以下方式来最大程度地保证API Server的性能。
- API Server拥有大量高性能的底层代码。在API Server源码中使用
协程(Coroutine)+队列(Queue)
这种轻量级的高性能并发代码,使得单进程
的API Server具备了超强的多核处理能力
,从而以很快的速度并发处理大量的请
求。普通List接口
结合异步Watch接口
,不但完美解决
了Kubernetes中各种资源对象的高性能同步问题
,也极大提升了Kubernetes集群实时响应各种事件的灵敏度
。参见:API Server的List-Watch机制- 采用了
高性能的etcd数据库
而非传统的关系数据库,不仅解决了数据的可靠性问题,也极大提升了API Server数据访问层的性能
。在常见的公有云环境中,一个3节点的etcd集群在轻负载环境中处理一个请求的时间可以低于1ms,在重负载环境中可以每秒处理超过30000个请求。
正是由于采用了上述提升性能的方法,API Server可以支撑很大规模的Kubernetes集群。
目前 Kubernetes 1.19 版本的集群可支持的最大规模如下:
- 最多支持 5000个Node;
- 最多支待 150000个Pod
- 每个 Node最多支持 100个Pod;
- 最多支持 300000个容器
API Server 分层架构设计
API Server的架构从上到下可以分为以下几层:
API层
:主要以REST方式提供各种API接口,除了有Kubernetes资源对象的CRUD和Watch等主要API,还有健康检查、UI、日志、性能指标等运维监控相关的API。Kubernetes从1.11版本开始废弃Heapster监控组件,转而使用Metrics Server提供Metrics API接口,进一步完善了自身的监控能力。访问控制层
:当客户端访问API接口时,访问控制层负责对用户身份鉴权,验明用户身份,核准用户对Kubernetes资源对象的访问权限,然后根据配置的各种资源访问许可逻辑(Admission Control),判断是否允许访问。注册表层
:Kubernetes把所有资源对象都保存在注册表(Registry)中,针对注册表中的各种资源对象都定义了:资源对象的类型、如何创建资源对象、如何转换资源的不同版本,以及如何将资源编码和解码为JSON或ProtoBuf格式进行存储。etcd数据库
:用于持久化存储Kubernetes资源对象的KV数据库。etcd的watch API接口对于API Server来说至关重要,因为通过这个接口,API Server 创新性地设计了List-Watch这种高性能的资源对象实时同步机制,使Kubernetes可以管理超大规模的集群,及时响应和快速处理集群中的各种事件。
从本质上看,API Server与常见的MIS或ERP系统中的DAO模块类似,可以将主要处理逻辑视作对数据库表的CRUD操作。
结构分析
- Resetful 对外提供http(https)的接口,用来对外提供与集群统一的交互手段。 Cacher 针对查询到的数据的缓存中心。
- Watcher 模块负责从Etcd获取数据,其中可注册多个Watcher,即关注多个不同的数据。
- Translate 模块负责将从Etcd获取到的数据转化为本地统一数据的接口,当Watcher获取到数据后就将其发送给Translate模块,Translate根据数据类型使用注册的对应的翻译接口进行翻译。
流程分析
如图所示,Apiserver可以左右两部分,左半部分是Apiserver使用观察者模式获取更新需要的数据,右半部分则是Apiserver接受外部调用并注册观察者Watcher,并从Watcher中最终获取到需要的数据。
组件构成
kube-apiserver包含三种APIServer及bootstrap-controller:
aggregatorServer
:负责处理apiregistration.k8s.io 组
下的APIService资源请求,同时将来自用户的请求拦截转发给aggregated server(AA)kubeAPIServer
:负责对请求的一些通用处理,包括:认证、鉴权以及各个内建资源(pod, deployment,service and etc)的REST服务等apiExtensionsServer
:负责CustomResourceDefinition(CRD)apiResources以及apiVersions的注册
,同时处理CRD以及相应CustomResource(CR)的REST请求(如果对应CR不能被处理的话则会返回404),也是apiserver Delegation的最后一环。bootstrap-controller
,主要负责Kubernetes default apiserver service的创建以及管理。
各组件介绍参见:腾讯云原生:一文读懂 Kubernetes APIServer 原理
API Server的List-Watch机制
List-Watch机制的核心是:连接API Server的各客户端(如cubelet、kube-scheduler、cube-controller-manager等)首次运行时,先调用API Server的List接口,获取所有当前客户端关心的资源对象的全量数据到本地,并缓存到内存中,之后监听API Server上对资源的增量变更事件,在收到事件后,将这些变更更新本地内存即可。即首次全量获取数据+后续增量获取数据。
以一个完整的Pod调度过程为例,对API Server的List-Watch机制进行说明。
-
首先,借助etcd提供的Watch API接口,API Server可以监听(Watch)在etcd上发生的数据操作事件,比如Pod创建事件、更新事件、删除事件等,在这些事件发生后,etcd会及时通知API Server。图中API Server与etcd之间的交互箭头表明了这个过程:当一个ReplicaSet对象被创建并被保存到etcd中后,etcd会立即发送一个对应的Create事件给API Server(图中的3.Send RepliatSet Create Event箭头),与其类似的6、7、10、11箭头都是针对Pod的创建、更新事件。
-
然后,为了让Kubernetes中的其他组件在不访问底层etcd数据库的情况下,也能及时获取资源对象的变化事件,API Server模仿etcd的Watch API接口提供了自己的Watch接口,这样一来,这些组件就能近乎实时地获取它们感兴趣的任意资源对象的相关事件通知了。图中controller-manager、scheduler、kublet等组件与API Server之间的3个标记为"List-Watch"的虚线框表明了这个过程。同时,在监听自己感兴趣的资源的时候,客户端可以增加过滤条件,以List-Watch 3为例,node1节点上的kubelet进程只对自己节点上的Pod事件感兴趣。
-
最后,Kubernetes List-Watch用于实现数据同步的代码逻辑。客户端首先调用API Server的List接口获取相关资源对象的
全量
数据并将其缓存到内存
中,然后启动对应资源对象的Watch协程,在监听到Watch事件后,再根据事件的类型(比如新增、修改或删除)对内存中的全量资源对象列表做出相应的同步增量修改
,从实现上来看,这是一种全量结合增量的、高性能的、近乎实时的数据同步方式
。
对象不同版本升级设计
我们知道,对于不断迭代更新的系统,对象的属性一定是在不断变化的,API接口的版本也在不断升级,此时就会面临版本问题,即同一个对象不同版本之间的数据转换问题及API接口版本的兼容问题。后面这个问题解决起来比较容易,即定义不同的API版本号(比如v1alpha1、v1beta1)来加以区分,但前面的问题就有点麻烦了,比如数据对象经历v1alpha1、v1beta1、v1beta1、v1beta2等变化后最终变成v1版本,此时该数据对象就存在5个版本,如果这5个版本之间的数据两两直接转换,就存在很多种逻辑组合,变成一种典型的网状网络,为此我们不得不增加很多重复的转换代码。
上述直接转换的设计模式还存在另一个不可控的变数,即每增加一个新的对象版本,之前每个版本的对象就都需要增加一个到新版本对象的转换逻辑。如此一来,对直接转换的实现就更难了。
为解决上述问题,API Server针对每种资源对象都引入了一个相对不变的internal版本,每个版本只要支持转换为internal版本,就能够与其他版本进行间接转换
。
CRD在API Server中的设计和实现机制
最后简单说说Kubernetes中的CRD在API Server中的设计和实现机制。根据Kubernetes的设计,每种官方内建的资源对象如Node、Pod、Service等的实现都包含以下主要功能。
- 资源对象的元数据(Schema)的定义:可以将其理解为数据库Table的定义,定义了对应资源对象的数据结构,官方内建资源对象的元数据定义是固化在源码中的。
- 资源对象的校验逻辑:确保用户提交的资源对象的属性的合法性。
- 资源对象的CRUD操作代码:可以将其理解为数据库表的CRUD代码,但比后者更难,因为API Server对资源对象的CRUD操作都会保存到etcd数据库中,对处理性能的要求也更高,还要考虑版本兼容性和版本转换等复杂问题。
- 资源对象相关的“自动控制器”(如RC、Deployment等资源对象背后的控制器):这是很重要的一个功能。因为Kubernetes是一个以自动化为核心目标的平台,用户给出期望的资源对象声明,运行过程中则由资源背后的“自动控制器”负责,确保对应资源对象的数量、状态、行为都始终符合用户的预期。
类似地,每个自定义CRD的开发人员都需要实现上面这些功能。为了减小编程的难度与工作量,API Server的设计者们做出了大量的努力,使得上面前3个功能无须编程实现,直接编写YAML定义文件即可实现。对于唯一需要编程的第4个功能来说,由于API Server提供了大量的基础API库,特别是易用的List-Watch的编程框架,也使得CRD自动控制器的编程难度大大减小。
集群功能模块之间的通信
kubernetes API Server作为集群的核心,负责集群各功能模块之间的通信,集群内各个功能模块通过API Server将信息存入etcd,当需要获取和操作这些数据时,通过API Server提供的REST接口(GET\LIST\WATCH方法)来实现,从而实现各模块之间的信息交互。
kube-apiserver进程运行在单个k8s-master节点上,默认有两个端口。
本地端口:
- 该端口用于接收HTTP请求;
- 该端口默认值为8080,可以通过API Server的启动参数“–insecure-port”的值来修改默认值;
- 默认的IP地址为“localhost”,可以通过启动参数“–insecure-bind-address”的值来修改该IP地址;
- 非认证或授权的HTTP请求通过该端口访问API Server。
安全端口:
- 该端口默认值为6443,可通过启动参数“–secure-port”的值来修改默认值;
- 默认IP地址为非本地(Non-Localhost)网络端口,通过启动参数“–bind-address”设置该值;
- 该端口用于接收HTTPS请求;
- 用于基于Tocken文件或客户端证书及HTTP Base的认证;
- 用于基于策略的授权;
- 默认不启动HTTPS安全访问控制。
kubelet与API Server交互
- 每个Node节点上的
kubelet
定期就会调用API Server的REST接口上报自身状态
,API Server接收这些信息后,将节点状态信息更新到etcd中
。- kubelet也
通过
API Server的Watch接口监听Pod信息
,从而对Node机器上的POD进行管理
。
kube-controller-manager与API Server交互
kube-controller-manager中的Node Controller模块通过API Server提供的Watch接口,实时监控Node的信息,并做相应处理。他们通过API Server提供的接口实时监控整个集群里的每一个资源对象的当前状态,当发生各种故障导致系统状态发生变化,这些controller会尝试将系统从“现有装态”修正到“期望状态”。
kube-scheduler与API Server交互
Scheduler通过API Server的Watch接口监听到新建Pod副本的信息后,它会检索所有符合该Pod要求的Node列表,开始执行Pod调度逻辑。调度成功后将Pod绑定到目标节点上。
API Server安全
可以使用kubectl、客户端库方式对REST API的访问,Kubernetes的普通账户和Service帐户都可以实现授权访问API。API的请求会经过多个阶段的访问控制才会被接受处理,其中包含**认证、授权以及准入控制
**等等(Admission Control)等。如下图所示:
需要注意:认证授权过程只存在HTTPS形式的API中。也就是说,如果客户端使用HTTP连接到kube-apiserver,是不会进行认证授权的。所以说,可以这么设置,在集群内部组件间通信使用HTTP,集群外部就使用HTTPS,这样既增加了安全性,也不至于太复杂。
参见:
k8s实践(6)–Kubernetes安全:API Server访问控制
API Server 网络隔离的设计–master网络与node网络隔离
考虑到安全问题,要求以API Server为核心的Master节点的网络与承载客户应用的Node节点的网络实现某种程度的“安全隔离”。
实现方式:在Master节点的网络里部署konnectivity Server,同时在Node节点的网络里部署konnectivity Agent,两者之前建立安全连接,对通信协议可以采用HTTP或者gRPC
,此时设计允许Node节点网络被划分为多个独立的分配,这些分片都通过konnectivity Server/Agent 建立安全连接与API Server实现点对点的连通。
REST API接口设计
API版本
为了消除字段或重组资源表示形式,Kubernetes 支持多个 API 版本,每个版本在不同的 API 路径下。例如:/api/v1或者/apis/extensions/v1beta1。
API级别
包括Alpha、Beta和Stable(稳定版),各版本说明如下:
Alpha
:
- 版本名称包含alpha(例如,v1alpha1)。
- 该软件可能包含错误。启用功能可能会暴露错误。默认情况下,功能可能被禁用。
- 对功能的支持随时可能被删除,但不另行通知。
- 在以后的软件版本中,API 可能会以不兼容的方式更改,亦不另行通知。
- 由于存在更高的错误风险和缺乏长期支持,建议仅在短期测试集群中使用该软件。
Beta
:
- 版本名称包含beta(例如,v2beta3)。
- 该软件已经过充分测试。启用功能被认为是安全的。默认情况下启用功能。
- 尽管细节可能会发生变更,对应功能不会被废弃。
- 在随后的 Beta 或稳定版本中,对象的模式和/或语义可能会以不兼容的方式更改。发生这种情况时,将提供迁移说明。迁移时可能需要删除、编辑和重新创建 API
对象。编辑过程可能需要一些思考。对于依赖该功能的应用程序,可能需要停机。- 该软件仅建议用于非关键业务用途,因为在后续版本中可能会发生不兼容的更改。如果您有多个可以独立升级的群集,则可以放宽此限制。
Stable(稳定版)
:
- 版本名称为vX,其中X为整数。
- 功能特性的稳定版本会持续出现在许多后续版本的发行软件中。
API Resource接口类别
整个kubeAPIServer提供了三类API Resource接口:
- core group:主要在 /api/v1 下;
- named groups:其 path 为 /apis/$GROUP/$VERSION;
- 系统状态的一些 API:如/metrics 、/version 等;
而API的URL大致以 /apis/{group}/{version}/namespaces/{namespace}/resource/{name}
组成,结构如下图所示:
API Resource 操作方法
概念层面,每种resource都有对应的管理操作方法,目前支持的有这几种:
- get
- list
- create
- update
- patch
- delete
- deletecolletction
- watch
分别归属于 增、删、改、查四类。
Kubernetes Proxy API 接口
Kubernetes Proxy 是什么
Kubernetes API Server最主要的REST接口是资源对象的增、删、改、查接口,同时还提供了一类很特殊的REST接口:Kubernetes Proxy API接口。
这类接口的作用是代理REST请求,即Kubernetes API Server把收到的REST请求转发到某个Node上的kubelet守护进程的REST端口,由该kubelet进程负责响应。
Kubernetes Proxy:
- 是cube-apiserver的HTTP代理,跟cube-proxy组件没有任何关系
- 运行在用户的桌面或 pod 中
- 从本机地址到 Kubernetes apiserver 的REST代理
- 客户端到代理使用 HTTP 协议
- 代理到 apiserver 使用 HTTPS 协议
- 指向 apiserver
- 添加认证头信息
开启Kubernetes Proxy
在本地,运行cubectl proxy命令,可开启Kubernetes Proxy。
Kubernetes Proxy 用途
在 Kubernetes 集群之外访问某个 Pod 容器的服务 (HTTP 服务)时,可以用 Proxy API 实现,这种场景多用于管理目的,比如逐一排查 Service Pod 副本,检查哪些 Pod 的服务存在异常。
Kubernetes Proxy 特性
1 如杲只想对外暴露部分 REST 服务,则可以在 Master 或其他节点上运行 kubectl proxy进程启动一个内部代理来实现。例如,运行下面的命令,在 8001 端口启动代理,并且拒绝客户端访问 RC的 API:
kubectl proxy --reject-paths="^/api/v1/replicationcontrollers "
–port=8001 --v=2 Starting to serve on 127.0.0.1:8001
运行下面的命令进行验证: curl localhost:8001/api/v1/replicationcontrollers
<h3>Unauthorized</h3>
- kubectl proxy 具有很多特性,最实用的一个特性是提供简单有效的安全机制,比如在采用白名单限制非法客户端访问时,只需增加下面这个参数即可:
–accept-hosts=“^localhost$,^127.0.0.1:8001$,^[::1]$”
Kubernetes Proxy 相关API
通过Kubernetes Proxy 访问Node
与 Node 相关接口的 REST 路径为:/api/v1/nodes/{name}/proxy
,其中 {name} 是节点的名称或 IP 地址,包括以下具体接口:
- /api/v1/nodes/{name}/proxy/pods # 列出指定节点内所有 pod 的信息
- /api/v1/nodes/{name}/proxy/stats # 列出指定节点内物理资源的统计信息
- /api/v1/nodes/{name}/proxy/spec # 列出指定节点的概要信息
- /api/v1/nodes/{name}/proxy/logs # 列出指定节点的各类日志信息
- /api/v1/nodes/{name}/proxy/metrics # 列出指定节点的 Metrics 信息
此外,如果 cubelet 进程在启动时包含 --enable-debugging-handlers=true 参数 ,那Kubernetes Proxy API 还会增加下面的接口:
- /api/v1/nodes/{ name}/proxy/run #在节点上运行某个容器
- /api/v1/nodes/{name}/proxy/exec #在节点上的某个容器中运行某个命令
- /api/v1/nodes/(name}/proxy/attach #在节点上 attach 某个容器
- /api/v1/nodes/(name}/proxy/portForward #实现节点上的 Pod 端口转发
- /api/v1/nodes/(name}/proxy/logs #列出节点的各类日志信息,例如 tallylog、lastlog、wtmp、ppp/、rhsm/、audit/、tuned/和 anaconda/等
- /api/vl/nodes/(name}/proxy/metrics #列出和该节点相关的 Metrics 信息
- /api/vl/ nodes/(name}/proxy/runningpods 书列出在该节点上运行的 Pod 信息
- /api/vl/nodes/(name}/proxy/debug/pprof #列出节点上当前 Web 服务的状态,包括 CPU占用情况和内存使用情况
通过Kubernetes Proxy 访问Pod
Kubernetes Proxy API 里关于 Pod 的相关接口, 通过这些接 ,我们可以访问Pod里某个容器提供的服务(如 Tomcat 8080 端口的服务)。
与 Pod 相关接口的 REST 路径为:
- /api/v1/namespaces/{namespace}/pods/{name}/proxy # 访问 Pod
- /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path:*} # 访问 Pod内容器服务的 URL 路径
举例:
以tomcat服务my-web为例(提供服务的端口为8080),通过kubectl get pods可得到其pod名称为myweb-g9pmm。
通过Kubernetes Proxy(本地proxy端口为9080)访问pod的tomcat首页和应用首页
#访问 Tomcat 的首页,即相当 于访问 http://tomcatNodeIP:8080
curl http://localhost:9080/api/v1/namespaces/default/pods/myweb-g9pmm/proxy
#访问应用demo的首页
curl http://localhost:9080/api/v1/namespaces/default/pods/myweb-g9pmm/proxy/demo
直接通过apiserver地址访问pod的tomcat首页和应用首页
#通过apiserver地址也能访问tomcat首页和应用首页:
http://<apiserver-ip>:<apiserver-port>/api/v1/namespaces/default/pods/myweb-g9pmm/proxy
http://<apiserver-ip>:<apiserver-port>/api/v1/namespaces/default/pods/myweb-g9pmm/proxy/demo
通过Kubernetes Proxy 访问service
与 service 相关接口的 REST 路径为:
/api/v1/namespaces/{namespace}/services/{name}/proxy
举例:访问myweb服务:
http://<apiserver-ip>:<apiserver-port>/api/v1/namespaces/default/services/myweb/proxy/demo
Controller Manager
参考:
Controller Manager 原理
在机器人技术和自动化领域,控制回路(Control Loop)是一个非终止回路,用于调节系统状态。比如温度自动控制,当你设置了温度,告诉了温度自动调节器你的期望状态(Desired State)。 房间的实际温度是当前状态(Current State)。 通过对设备的开关控制,温度自动调节器让其当前状态接近期望状态。
在 Kubernetes 中,各个控制器(Controller)就是一个个自动化控制器,它们通过监控集群的公共状态,并致力于将当前状态转变为期望的状态。
Kubernetes的Controller Manager是集群内部的管理控制中心,由负责不同资源的多个 Controller 构成,通过各个Controller来自动管理集群中各类资源(如Node、Pod副本、service、Endpoint等)。这些Controller通过 apiserver 监控整个集群的状态,并确保集群处于预期的工作状态。
例如:通过Deployment创建的Pod异常退出时,ReplicaSet controller便会接受并处理该退出事件,并创建新的Pod来维持预期副本数。
kube-controller-manager 包含以下Controller(不是全部):
- Node Controller
- Deployment Controller
- Service Controller
- ReplicaSet Controller
- Endpoint Controller
- Job Controller
- CronJob Controller
- Daemon Controller
- StatefulSet Controller
- Namespace Controller
- Volume Controller
- Resource quota Controller
- Pod AutoScaler
- Certificate Controller
- ServiceAccount Controller
- ClusterRoleAggregation Controller
- Endpointslice Controller
- Garbage Collector
- PodGC Controller
- Disruption Controller
- Route Controller
一个控制器至少追踪一种类型的 Kubernetes 资源。这些资源对象有一个代表期望状态的spec字段。 该资源的控制器负责确保其当前状态接近期望状态。每个控制器的control loop 可以用以下的伪代码来解释:
for {
desired := getDesiredState()
current := getCurrentState()
makeChanges(desired, current)
}
几乎每种特定资源都有特定的 Controller 维护管理以保持预期状态,而 Controller Manager 的职责便是把所有的Controller 聚合起来:
- 提供基础设施降低 Controller 的实现复杂度
- 启动和维持 Controller 的正常运行,Watch kube-apiserver对不同的Controller分发事件通知。
可以这么说,Controller 保证集群内的资源保持预期状态,而 Controller Manager 保证了 Controller 保持在预期状态。
Controller Manager 由 kube-controller-manager 和 cloud-controller-manager 组成,是 Kubernetes 的大脑,它通过 apiserver 监控整个集群的状态,并确保集群处于预期的工作状态。
cloud-controller-manager 在 Kubernetes 启用 Cloud Provider 的时候才需要,用来配合云服务提供商的控制,也包括一系列的控制器,如
- Node Controller
- Node Lifecycle Controller
- Route Controller
- Service Controller
基本上,每个控制器都负责Kubernetes中的特定资源。
Controller Manager架构
Controller Manager主要提供了一个分发事件的能力,不同的Controller只需要注册对应的Handler来等待接收和处理事件。
在Controller Manager的帮助下,Controller的逻辑可以做的非常纯粹,只需要实现对应的EventHandler即可。
辅助 Controller Manager 完成事件分发的是 client-go,而其中比较关键的模块便是 informer。
kubernetes 在 github 上提供了一张 client-go 的架构图,从中可以看出,Controller 正是下半部分(CustomController)描述的内容,而 Controller Manager 主要完成的是上半部分。
Reflector
:Reflector 和 APIServer 建立长连接,并使用 ListAndWatch 方法获取并监听某一个资源的变化。List 方法将会获取某个资源的所有实例(如ReplicaSet、Deployment等),Watch 方法则监听资源对象的创建、更新以及删除事件,获取到的事件称之为一个增量(Delta),该增量会被放进一个称之为 Delta FIFO Queue,即增量先进先出队列中。informer
:- k8s为每类资源创建了一个informer。Controller Manager 通过一个 Informer 单例工厂来保证不同的 Controller 共享了同一个 Informer。
- informer
作用
:根据集群中某资源的事件来更新本地缓存
:Informer会不断的从 Delta FIFO Queue 中 pop 增量事件,并根据事件的类型来决定新增、更新或者是删除本地缓存,也就是 Local Key-Value Sotrage。根据事件类型来触发事先注册好的 Event Handler。
在回调函数中通常只会做一些简单的过滤处理,然后将该事件的Key(注意不是事件本身,只是事件的key,key的格式如<resource_namespace>/<resource_name>)添加到 Work Queue 这个工作队列中- 维护了一组handler,由各controller向其注册,不同controller对同一个informer注册不同的handler
- controller
- 每个controller向关注的资源对应的informer实例注册特定的handler
- 当收到到来自informer的handler事件后,将其放入Work Queue
- 控制器从 Work Queue 中取出一个事件Key,然后通过indexer从本地存储获取具体事件,并根据自身的业务逻辑对其进行处理,不同的控制器会有不同的处理逻辑。
副本调度控制器(Replication Controller和Deployment Controller)
其核心作用是确保在任何时候集群中某个RC关联的Pod副本数量都保持预设值。其主要职责如下:
- 确保在当前集群中有且仅有N个Pod实例,N是在RC中定义的Pod副本数量
- 通过手动或者通过自动扩容代理调整RC的spec.replicas属性值来实现系统扩容或者缩容
- 通过改变RC中的Pod模板(主要是镜像模板)来实现滚动升级。副本控制器被设计成通过逐个替换 Pod 助服务的滚动更新。
总结:重新调度、弹性伸缩、滚动更新。
Node Controller
kubelet进程在启动时通过API Server注册自身的节点信息,并定时向API Server汇报状态信息,API Server在接收到这些信息后,会将这些信息更新到etcd中。在etcd中存储的节点信息包括节点健康状况,、节点资源、节点名称、节点地址信息、操作系统版本、Docker版本、kubelet版本等。
节点健康状况包含就绪(True)、未就绪(False)、和未知(Unknow)三种。
Node Controller通过API Server实时获取Node的相关信息,实现管理和监控集群中的各个Node的相关控制功能。
Node Controller 的核心工作流
ResourceQuota Controller
参见:K8S(二)Controller-Manager#yyds干货盘点
资源配额管理确保指定的资源对象在任何时候都不会超量占用系统物理资源。
支持三个层次的资源配置管理:
容器级别
:对CPU和Memory进行限制Pod级别
:对一个Pod内所有容器的可用资源进行限制Namespace级别
:包括- Pod数量
- Replication Controller数量
- Service数量
- ResourceQuota数量
- Secret数量
- 可持有的PV(Persistent Volume)数量
Admission Control提供两种配额约束方式:LimitRanger和ResourceQuota;
LimitRanger作用于Pod和Container
;ResourceQuota作用于Namespace上,限定一个Namespace里的各类资源的使用总额
。
-
Namespace Controller
用户通过API Server可以创建新的Namespace并保存在etcd中,Namespace Controller定时通过API Server读取这些Namespace信息。
如果Namespace被API标记为优雅删除(即设置删除期限,DeletionTimestamp),则将该Namespace状态设置为“Terminating”,并保存到etcd中。同时Namespace Controller删除该Namespace下的ServiceAccount、RC、Pod、Secret、PersistentVolume、ListRange ResourceQuota和Event 等资源对象。
Namespace 状态被设置成 Terminating 后,由 Admission Controller 的NamespaceLifecycle插件来阻止为该 Namespace 创建新的资源。
Service Controller Endpoints Controller
Service、Endpoint、Pod的关系
Endpoints表示一个Service对应的所有Pod副本的访问地址,而Endpoints Controller负责生成和维护所有Endpoints对象的控制器。它负责监听Service和对应的Pod副本的变化。
- 如果监测到Service被删除,则删除和该Service同名的Endpoints对象;
- 如果监测到新的Service被创建或修改,则根据该Service信息获得相关的Pod列表,然后创建或更新Service对应的Endpoints对象。
- 如果监测到Pod的事件,则更新它对应的Service的Endpoints对象到etcd中。
Endpoints对象被使用的地方
:kube-proxy进程获取每个Service的Endpoints,实现Service的负载均衡功能。
Service Controller是属于kubernetes集群与外部的云平台之间的一个接口控制器。Service Controller监听Service变化,如果是一个LoadBalancer类型的Service,则确保外部的云平台上对该Service对应的LoadBalancer实例被相应地创建、删除及更新路由转发表。
Scheduler 原理解析
Kubernetes Scheduler是负责Pod调度的进程(组件),随着Kubernetes功能的不断增强和完善,Pod调度也变得越来越复杂。Kubernetes Scheduler内部实现机制也不断优化,从最初的两阶段调度机制(Predicates&Priorities)发展到后来的升级版调度框架(Scheduler Framework),以满足负责的调度场景。
Scheduler的调度流程
Kubernetes Scheduler在整个系统中承担了“承上启下”的重要功能
- “承上”是指它负责接收Controller Manager创建新的Pod,为其安排一个目标Node;
- “启下”是指安置工作完成后,目标Node上的kubelet服务进程接管后续工作,负责Pod生命周期中的“下半生”。
Kubernetes Scheduler的作用是将待调度的Pod(API新创建的Pod、Controller Manager为补足副本而创建的Pod等)按照特定的调度算法和调度策略绑定(Binding)到集群中某个合适的Node上,并将绑定信息写入etcd中
。
在整个调度过程中涉及三个对象
,分别是
- 待调度Pod列表
- 可用Node列表以
- 调度算法和调度策略
Kubernetes Scheduler通过调度算法和调度策略将待调度Pod列表中的每个Pod都从Node列表中选择一个最合适的Node。
目标节点上的kubelet通过API Server监听到Kubernetes Scheduler产生的Pod绑定事件,随后获取对应的Pod清单,下载Image镜像并启动容器。
Scheduler只与API Server交互,其输入和输出
如下:
- 输入:待调度的Pod和全部计算节点的信息
- 输出:目标Pod要”安家“的最优节点(或者暂时不存在)。
Scheduler调度实现机制
早期:两阶段调度
旧版本的Kubernetes Scheduler的调度总体包括两个阶段:过滤(Filtering)+打分(Scoring),随后就是绑定目标节点,完成调度
。
过滤阶段
,遍历所有目标Node,删选出符合要求的候选节点。在此阶段,Scheduler将所有不符合的Node过滤,只留下符合条件的候选节点。具体实现通过一系列的Filter对每个Node进行筛选,筛选完成后通常会有多个节点供调度,从而进入打分阶段;如果结果为空,则表示当前还没有符合条件的Node节点,Pod会维持在Pending状态。每种过滤器都实现一种节点特征的监测。比如磁盘、主机、节点上的可用端口、节点标签、CPU和内存资源、服务亲和性。打分阶段
,在过滤阶段的基础上,采用优选策略(xxx Priorities)计算出每个候选节点的积分,积分最高者胜出,因为积分最高者表示最佳候选人。常见的Priorities包含LeastRequestedPriority(选出资源消耗最小的节点)、BalanceResourceAllocation(选出资源使用率最均衡的节点)。挑选最佳节点后,Scheduler会把目标Pod安置次节点上,调度完成。
最新:Scheduler Framework
早期的kube-scheduler问题
调度系统的在调度时的目的往往是动态的,可能是成本优先、质量优先、最大资源利用率优先等等,这与业务场景有关。
正是因为调度系统的调度策略是与业务场景相关联的,很难用一套调度策略满足所有业务场景。越来越多的调度策略加入到kube-scheduler中,使得kube-scheduler的调度逻辑越来越复杂,复杂的调度器是难以维护的,早期的kube-scheduler虽然也具备了扩展能力使得开发者可以为特定的业务场景设计调度策略,但受到如下方面的限制。
-
扩展点的数量只有两个:过滤后、评分后。扩展程序只能在kube-scheduler过滤后与评分后介入调度流程。
-
kube-scheduler与扩展程序的程序使用HTTP通信,每一次通信都设计json的序列化与反序列化,性能较差。
-
扩展程序作为一个独立的程序,要么只处理kube-scheduler传递过来的数据,要么自行构建一套k8s资源缓存,存在额外的资源开销
-
扩展程序无法感知被调度的资源当前处于什么状态。如果一个pod被kube-scheduler判定为不可调度,扩展程序是无法感知的。
以上这些限制无法构建一个高性能、多功能的调度器,为此社区提出scheduler framework来解决kube-scheduler的扩展与性能问题,使得调度程序的核心更简单,方便维护。
Scheduler Framework实现机制
新的 Schedule Framework 通过定义一系列按顺序执行
的扩展点
,扩展点表示在调度绑定周期中的一个“阶段”,支持用户以插件的方式 (Plugin) 对扩展点进行自定义扩展和配置。
Scheduler Framework调度流程如下:
扩展点说明:
- queueSort:这些插件对调度队列中的悬决的 Pod 排序。 一次只能启用一个队列排序插件。
- preFilter:这些插件用于在过滤之前预处理或检查 Pod 或集群的信息。 它们可以将 Pod 标记为不可调度。
- filter:这些插件相当于调度策略中的断言(Predicates),用于过滤不能运行 Pod 的节点。 过滤器的调用顺序是可配置的。 如果没有一个节点通过所有过滤器的筛选,Pod 将会被标记为不可调度。
- postFilter:当无法为 Pod 找到可用节点时,按照这些插件的配置顺序调用他们。 如果任何 postFilter 插件将 Pod 标记为可调度,则不会调用其余插件。
- preScore:这是一个信息扩展点,可用于预打分工作。
- score:这些插件给通过筛选阶段的节点打分。调度器会选择得分最高的节点。
- reserve:这是一个信息扩展点,当资源已经预留给 Pod 时,会通知插件。 这些插件还实现了 Unreserve 接口,在 Reserve 期间或之后出现故障时调用。
- permit:这些插件可以阻止或延迟 Pod 绑定。
- preBind:这些插件在 Pod 绑定节点之前执行。
- bind:这个插件将 Pod 与节点绑定。bind 插件是按顺序调用的,只要有一个插件完成了绑定,其余插件都会跳过。bind 插件至少需要一个。
- postBind:这是一个信息扩展点,在 Pod 绑定了节点之后调用。
Scheduler Framework中的对象
接下来我们来聊一聊scheduler framework定义的一些 「对象」分别表示什么,承担什么作用。
ExtensionPoints(扩展点)
扩展点表示在调度绑定周期中的一个“阶段”,kube-scheduler会在每一个阶段执行做一些事情,以完成pod的调度绑定。每一个扩展点可以绑定多个插件,一个扩展点的每一个插件都需要返回处理结果,如果处理结果为错误,则该pod将直接打回「待调度队列」等待下一次调度。
Predicates
从待调度的pod队列中,拿出一个pod开始实现调度绑定逻辑时,将依次通过预定义的「扩展点」,扩展点对于开发者来说是一个接口,实现这个接口的对象被称之为「插件」。
一个插件可以实现多个扩展点接口。
多个插件实现同一个扩展点接口,Schduler会按序执行每一个插件。
在走到post filter扩展点时,按顺序执行相应的plugin,如果有一个插件返回成功或失败,那么其他的plugin都不会执行。
在走到bind阶段时,按顺序执行相应的plugin,如果有一个plugin执行了绑定,那么其他plugin都不会执行
CycleState
每一个pod的调度绑定流程,都会关联一个CycleState对象,它用于存储当前pod整个调度绑定流程的所有数据,在每一个扩展点中,所有的plugin都可以拿到这个对象,可以从该对象读取或写入一些必要的信息。
CycleState是对所有plugin共享的。
多调度器特性
k8s自带一个默认调度器default-scheduler,该调度器包含了实现k8s内置的pod调度功能的多个扩展点插件,如节点亲和、pod亲和/反亲和、污点和容忍、pod优先级抢占调度等功能,每个功能都是由一个或一些扩展点的组合来实现,每个功能都是一个扩展点插件,这些扩展点插件组合在一起,形成了默认调度器default-scheduler。
默认调度器可能无法满足用户的需求,因此,k8s支持用户实现的自定义扩展点插件以及自定义调度器,并且支持多个自定义调度器与默认的调度器同时并行运行,由 Pod选择是用默认的调度器调度还是用某个自定义调度器调度。
多调度器实现机制
:通过Multiple Scheduling Profiles 特性,只需要针对不同的调度规则编写不同的 Profile置文件,并给它们起一个自 定义 Scheduler 的名称,然后把这个配置文件传递给 Kubemetes Scheduler 加载 生效, Kubernetes Scheduler 就立即实现了多调度器支持的“多重影分身”特效。
apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
- schedulerName: no-scoring-scheduler
- plugins:
preScore:
disabled:
- name: '*'
score:
disabled:
- name: '*'
在以上 KubeSchedulerConfiguration 配置声明中,我们看到系统默认的Scheduler 名为default-scheduler, 默认的 Scheduler 包括k8s内置的各个插件扩展,在这个配置文件中新增了一个名为 no-scoring-scheduler 的自定义 Scheduler, 我们在自定义 Scheduler 中可以根据自己的需求开启或关闭指定的插件。
kubelet 运行机制解析
Kubernetes 集群中,在每个 Node (又称 Minion) 上都会启动一个 kubelet 服务进程,该进程用于处理 Master 下发到 本节点的 任务, 管理 Pod及Pod 中的容器, 每个 kubelet进程都会在 API Server 上注册节点自身的信息,定期向 Master 汇报节点资源的使用情况,并通过 cAdvisor 监控容器和节点资源。
节点管理
节点通过设置 kubelet 的启动参数” --register-node" ,来决定是否向 API Server 注册自已。如果该参数的值为 true, 那么 kubelet 将试着通过 API Server 注册自己。在自注册时,kubelet 启动时还包含下列参数
© --api-servers: API Server 位置
© --kubeconfig: kubeconfig 文件,用于访问 PI Server 安全配置文件
© --cloud-provider: 云服务 (IaaS地址,仅用于公有云环境中)
- kubelet 在启动时通过 AP Server 注册节点信息,并定时向 API Server 发送节点的新消息, API Server 在接收到这些信息后,会将其写入 etcd 通过 kubelet 启动参数–node-status-update-frequency 可设置 kubelet 每隔多长时间向 API Server 报告节点的状态,默认为 10s。
- 遇到集群资源不足的情况,用户就很容易通过添加机器及运用 kubelet的自注册模式来实现扩容。
- 每个节点上的kubelet仅具备修改和创建其所在节点的资源权限,不能修改其它节点的资源
Pod 管理
kubelet 通过以下方式获取在自身 Node 上要运行的 Pod 清单:
静态 Pod 配置文件
:这种类型的 Pod 没有通过 kube-contro ller-manager 进行管理,被称为“静态Pod",两种配置文件获取方式:通过 kubelet启动参数--config 指定目录下的 Pod YAML 文件
,默认目录为 etc/kubernetes/manifests/), kubelet 会持续监控指定目录下的文件变化,以创建或删除 pod。 另外,可以通过启动参数–file-check-frequency 设置检查该目录的时间间隔,默认为 20s。HTTP端点URL
:通过–manifest-url参数设置,通过–http-check-frequency设
置检查该 HTT 端点数据的时间间隔,默认为 20s。
API Server
: kubelet 通过 API Server 监听etcd 录,同步 Pod列表。kubelet 通过 API Server Client 使用 Watch List 的方式监听etcd的/registry/nodes/当前节点的名称,以及/registry/pods目录,获取pod列表,同步到本地缓存中。
kubelet 监听 etcd, 所有针对 Pod 的操作都会被 kubelet 监听,如果发现有新的绑定到本节点的 Pod 则按照 pod 清单的要求创建
该 Pod,包括:
- 为该 Pod 挂载外部卷 (Externa Volume)、
- 下载 Pod 用到的 Secret、
- 用kubernetes pause 镜像为每个 Pod 都创建一个pause容器。Pause 容器用于接 Pod中所有其他容器的网络,kubernetes pause 镜像大概有 200KB, 是个非常小的容器镜像
- 调用 CRI Client 下载容器镜像并运行pod中的各个容器。
同理,对pod的增、删、改操作,都通过API-Server监听etcd,之后在本地进行相应修改。
容器健康检查
Pod 通过两类探针来检查容器的健康状态。
- 一类是 LivenessProbe 探针 ,用于判断容器是否健康并反馈给 kubelet, 如果 LivenessProbe 探针探测到容器不健康,则 kubelet将删除该容器,并根据容器的重启策略做相应的处理;如果一个容器不包含 LivenessProbe探针,则 kubelet 会认为该容器的 LivenessProbe 探针返回的值永远是 Success
- 另一类是Readiness robe 探针,用于判断容器是否启动完成,且准备接收请求 如果 ReadinessProbe探针检测到容器启动失败,则 Pod 的状态将被修改, Endpoint Controller 将从 Service的Endpoint 中删除包含该容器所在 Pod IP 地址的 Endpoint 条目。
kubelet 定期调用容器中的 LivenessProbe 探针来诊断容器的健康状况。
LivenessProbe包含以下3种实现方式:
- ExecAction: 在容器内部运行一个命令,如果该命令的退出状态码为 0, 则表明容器健康;
- TCPSocketAction: 通过容器的 IP 地址和端口号执行 TCP 检查,如果端口能被访问,则表明容器健康;
- HTTPGetAction: 通过容器的 IP 地址和端口号及路径调用 HTTP Get 方法,如果响应的状态码大于或等于 200 且小于或等千 400, 则认为容器状态健康。
cAdvisor 资源监控
在kubernetes 集群中,应用程序 的执行情况可以在不同的级别监测到,这些级别包括容器、 Pod、Service和整个集群。作为 Kubemetes 集群的一部分, kubernetes 希望提供给用户详细的各个级别的资源使用信息,这将使用户深入地了解应用的执行情况,并找到应用中可能的瓶颈。
cAdvisor 个开源的分析容器资源使用率和性能特性的代理工具,它是因为容器而
产生的,因此自然支持 Docker 容器。在 Kubernetes 项目中, cAdvisor 被集成到 Kubernetes代码中, kubelet 则通过 cAdvisor 获取其所在节点及容器上的数据 cAdvisor 自动查找其所在 Node 上的所有容器,自动采集 CPU 、内存、文件系统和网络使用的统计信息。在大部分 Kubemetes 集群中, cAdvisor 都通过它所在 Node 4194 端口暴露一个简单的 UI。
cAdvisor 只能提供 ~ 3min 的监控数据,对性能数据也没有持久化,因此在 Kubernetes的早期版本中需要依靠 Heapster 来实现集群范围内全部容器性能指标的采集和查询功能。从Kubernetes 1.8 版本开始,性能指标数据的查询接口升级为标准的 Metrics API, 后端服务则升级为全新的 Metr cs Se ver 因此, cAdvisor 4194 端口提供的 UI API 服务从Kubernetes 1.10本开始进入弃用流程,并于 1.12 版本时完全关闭。
如果还希望使用cAdvisor 的这个特性,则从 1.13版本开始可以通过部署一个 DaemonSet 在每个 Node 上都启动一个 cAdvisor 来提供 UI和API, 请参 cAdvisor GitHub 上的说明。
在新的 kubernetes 监控体系中, Metrics Server 用于提供 Core Metrics (核心指标),包括 Node、Pod、CPU 和内存使用数据,其他 Custom Metrics 自定义指标)则由第三方组件(如 Prometheus) 采集和存储。
容器运行时
CRI概述
kubelet 负责本节点上所有 Pod 的全生命周期管理,其中就包括相关容器的创建和销毁这种基本操作,容器的创建和销毁等操作的代码不屈于 Kubemetes 的代码范畴,而是容器的范畴,kubelet 通过 引入CRI 接口,来实现与各类容器引擎之间的调用控制功能,从而实现容器的全生命周期控制。不同厂家的 Container
Runtime 只需实现对应的 CRI 插件代码即可, Kubernetes 无须重新编译就可以使用更多的容器运行时。
CRI规范接口
CRI 接口规范主要定义了三个 gRPC 接口服务: Image Service、RuntimeService和Pod Sandbox Service
,其中:
ImageService
:提供了从仓库拉取镜像 查看和移除镜像的功能;RuntimeService
:负责实现 Pod 和容器的生命周期管理,以及与容器 的交互 (exec/attach/port-forward )。Pod Sandbox Service
: 我们知道, Pod 由一组应用容器组成,其中包含共有的 环境和资源约束,这个环境在 CRI 里被称为 Pod Sandbox. Container Runtime 可以根据自己的内部实现来解释和实现自己的 Pod Sandbox,比如对于 Hypervisor 这种容器运行时引擎,会把 Pod Sandbox具体实现为一个虚拟机。所以 RuntimeService服务接 口除了提供了针对 Container 的相关操作,也提供了针对 Pod Sandbox 的相关操作以供 kubelet 调用。在启动 Pod 之前, kubelet 调用 RuntimeService.RunPodSandbox 来创建 Pod 环境,这个过程也包括为Pod 设置网络资源(分配 IP 等操作), Pod Sandbox 在被激活之后,就可以独立地创 建、启动、停止和删除用户业务相关的 Container 了,当 Pod 销毁时, kubelet 会在停止和删 Pod Sandbox前首先停止和删除其中的Container。
RuntimeClass
随着 CRI 机制的成熟及第三方 Container Runtime 的不断涌现,用户有了新的需求:Kubernetes 集群中配置并启用多种 Container Runtime, 不同类型的 Pod 可以选择不同特性的 Container Runtime 来运行,以实现资源占用或者性能、稳定性等方面的优化,这就是 RuntimeClass 出现的背景和动力。
Kubernetes 1.12 版本开始引入 RuntimeClass,用于在启动容器时选择特定的容器运行时,目前为 beta 阶段 以下面的 RuntimeClass 例子为例:
其中, handler 参数是对应的 CRI 配置名称,指定 Container Runtime 的类型,一旦创建好 RuntimeClass 资源,我们就可以通过 Pod 中的 spec.runtimeClassName 字段与它进行关联了,当目标Pod被调度到某个具体的 kubelet 时, kubelet 就会通过 CRI 接口调用指定Container Runtime 来运行该Pod,如果指定的 RutimeC!ass 不存在,无法运行相应的Container Runtime, 那么 Pod 会进入 Failed 状态。
kube-proxy 运行机制解析
kube-proxy用于实现service的概念,包括clusterIP:port到后端Endpoints中pod列表的负载均衡。
第一代kube-proxy— userspace模式,已被淘汰
第一代kube-proxy被称为 userspace (用户空间代理)模式。
当某个客户端 Pod Cluster 地址访问某个 Service 时,这个流量就 Pod 所在Node的iptables 转发给 kube-proxy 进程,然后由 kub e-proxy 建立起到后端 Pod TCP/ UDP 连接,再将请求转发到某个后端 Pod 上,并在这个过程中实现负载均衡功能。
第二代kube-proxy — iptables模式,已被淘汰
iptables 模式下的第二代 kube-proxy 进程不再起到数据层面的proxy 的作用,
Client Service 的请求流量通过 iptables NAT 机制直接发送到目标 Pod, 不经过kube-proxy 进程的转发, kube-proxy 进程只承担了控制层面的功能,即通过 API Server的Watch 接口实时跟踪 Service Endpoint 的变更信息,并更新 Node 节点上相应的 iptables规则。
与第一代的 userspace 模式相比,iptables 模式完全工作在内核态,不用再经过用户态的 kube-proxy 中转,因而性能更强。
第三代kube-proxy–IPVS模式
第二代的 iptables 模式实现起来虽然简单,性能也提升很多,但存在固有缺陷:在集群中的 Service Pod 大址增加以后,每个 Node 节点上 iptables 中的规则会急速膨胀,导致网络性能显著下降,在某些极端清况下甚至会出现规则丢失的情况,并且这种故席难以重现与排查。
于是kubernetes 从1.8 版本开始引入第三代的 IPVS (IP Virtual Server)模式,
如图所示:
iptables PVS 虽然都是基于 Ne filter 实现的,但因为定位不 同,二者有着本质的差别: iptables 是为防火墙设计的; IPVS 专门用于高性能负载均衡,并使用更高效的数据结哈希表),允许几乎无限的规模扩张,因此被 kube-proxy 采纳为第三代模式。
与iptables 相比,IPVS 拥有以下明显优势:
- 为大型集群提供了更好的可扩展性和性能;
- 支持比iptables更复杂的复制均衡算法(最小负载、最少连接、加权等);
- 支持服务器健康检查和连接重试等功能;
- 可以动态修改 ipset 的集合,即使 iptables 的规则正在使用这个集合
由于 IPVS 无法提供包过滤、 airpin-masquerade tricks (地址伪装)、 SNAT 等功能 ,因此在某些场景(如 NodePort 的实现)下还要与 iptables 搭配使用。
在 IPVS 模式下, kube-proxy又做了重要的 升级,即使用 iptables 的扩展 ipset, 而不是直接调用 iptables 来生成规则链。
iptables 规则链是一个线性数据结构, ipset 则引入了带索引的数据结构,因此当规则很多时,也可以高效地查找和匹配。我们可以将 ipset 简单理解为一个 IP (段)的集合,这个集合的内容可以是 地址、 网段 端口等, iptables 可以直接添加规则对这个“可变的集合”进行操作,这样做的好处在于大大减少了 iptables 规则的数量,从而减少了性能损耗。
假设要禁止上万个 IP 访问我们的服务器,则用 iptables 的话,就需要一条一条地
添加规则,会在 iptables 中生成大批的规则;但是用 ipset 的话,只需将相关的 IP 地址(网段)加入 ipset 集合中即可,这样只需设置少量的 iptables 规则即可 实现目标。