一、Kubernetes API Server原理解析

总体来看,Kubernetes API Server的核心功能是提供Kubernetes各类资源对象(如Pod、RC、Service等)的增、删、改、查及Watch等HTTP Rest接口,成为集群内各个功能模块之间数据交互和通信的中心枢纽, 是整个系统的数据总线和数据中心。除此之外,它还有以下一些功能特性:

  • 是集群管理的API入口。
  • 是资源配额控制的入口。
  • 提供了完备的集群安全机制。

1.1、Kubernetes API Server概述

Kubernetes API Server通过一个名为kube-apiserver的进程提供服务,该进程运行在Master上。在默认情况下,kube-apiserver进程在本机 的8080端口(对应参数—insecure-port)提供REST服务。我们可以同时启动HTTPS安全端口(—secure-port=6443)来启动安全机制,加强 REST API 访问的安全性。
使用方式:

  • 我们通常可以通过命令行工具kubectl来与Kubernetes API Server交互,它们之间的接口是RESTful API。
  • 使用curl直接调用API Server 接口

使用场景:

  • 运行在Pod里的用户进程调用Kubernetes API,通 常用来实现分布式集群搭建的目标。
  • 开发基于Kubernetes的管理平台。比如调用 Kubernetes API来完成Pod、Service、RC等资源对象的图形化创建和管理界面。

由于API Server是Kubernetes集群数据的唯一访问入口,因此安全性与高性能就成为API Server设计和实现的两大核心目标。通过采用 HTTPS安全传输通道与CA签名数字证书强制双向认证的方式,API Server的安全性得以保障。此外,为了更细粒度地控制用户或应用对 Kubernetes资源对象的访问权限,Kubernetes启用了RBAC访问控制策 略。
API Server的性能是决定Kubernetes集群整体性能的关键因素,因此 Kubernetes的设计者综合运用以下方式来最大程度地保证API Server的性能。

  • API Server拥有大量高性能的底层代码。在API Server源码中 使用协程(Coroutine)+队列(Queue)这种轻量级的高性能并发代码, 使得单进程的API Server具备了超强的多核处理能力,从而以很快的速度并发处理大量的请求。
  • 普通List接口结合异步Watch接口,不但完美解决了 Kubernetes中各种资源对象的高性能同步问题,也极大提升了Kubernetes 集群实时响应各种事件的灵敏度。
  • 采用了高性能的etcd数据库而非传统的关系数据库,不仅解决了数据的可靠性问题,也极大提升了API Server数据访问层的性能。在 常见的公有云环境中,一个3节点的etcd集群在轻负载环境中处理一个请 求的时间可以低于1ms,在重负载环境中可以每秒处理超过30000个请求。

正是由于采用了上述提升性能的方法,API Server可以支撑很大规 模的Kubernetes集群。

1.2、API Server架构解析

API Server的架构从上到下可以分为以下几层,如下图所示:
【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图1

  • 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可以管理超大规模的集群,及时响应和快速处理集群中的各种事件。

1.3、List-Watch机制

从本质上看,API Server与常见的MIS或ERP系统中的DAO模块类似,可以将主要处理逻辑视作对数据库表的CRUD操作。这里解读API Server中资源对象的List-Watch机制。下图以一个完整的Pod调度过程为例,对API Server的List-Watch机制进行说明。【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图2
首先,借助etcd提供的Watch API接口,API Server可以监听 (Watch)在etcd上发生的数据操作事件,比如Pod创建事件、更新事件、删除事件等,在这些事件发生后,etcd会及时通知API Server。上图中API Server与etcd之间的交互箭头表明了这个过程:当一个 ReplicaSet对象被创建并被保存到etcd中后(图中的2.Create RepliatSet箭头),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事件后,再根据事件的类型(比如新增、修改或删除)对内存中的全量资源对象列表做出相应的同步修改,从实现上来看,这是一种全量结合增量的、高性能的、近乎实时的数据同步方式。

1.4、集群功能模块之间的通信

Kubernetes API Server作为集群的核心,负责 集群各功能模块之间的通信。集群内的各个功能模块通过API Server将 信息存入etcd,当需要获取和操作这些数据时,则通过API Server提供的 REST接口(用GET、LIST或W A TCH方法)来实现,从而实现各模块之 间的信息交互。【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图3

  • 常见的一个交互场景是kubelet进程与API Server的交互。每个Node 上的kubelet每隔一个时间周期,就会调用一次API Server的REST接口报 告自身状态,API Server在接收到这些信息后,会将节点状态信息更新 到etcd中。此外,kubelet也通过API Server的Watch接口监听Pod信息, 如果监听到新的Pod副本被调度绑定到本节点,则执行Pod对应的容器创 建和启动逻辑;如果监听到Pod对象被删除,则删除本节点上相应的Pod 容器;如果监听到修改Pod的信息,kubelet就会相应地修改本节点的Pod 容器。
  • 另一个交互场景是kube-controller-manager进程与API Server的交互。kube-controller-manager中的Node Controller模块通过API Server提供 的Watch接口实时监控Node的信息,并做相应处理。
  • 还有一个比较重要的交互场景是kube-scheduler与API Server的交 互。Scheduler通过API Server的Watch接口监听到新建Pod副本的信息 后,会检索所有符合该Pod要求的Node列表,开始执行Pod调度逻辑, 在调度成功后将Pod绑定到目标节点上。

为了缓解集群各模块对API Server的访问压力,各功能模块都采用 缓存机制来缓存数据。各功能模块定时从API Server获取指定的资源对 象信息(通过List-Watch方法),然后将这些信息保存到本地缓存中, 功能模块在某些情况下不直接访问API Server,而是通过访问缓存数据 来间接访问API Server。

二、Controller Manager原理解析

2.1、Replication Controller

为了区分Controller Manager中的Replication Controller(副本控制 器)和资源对象Replication Controller,我们将资源对象Replication Controller简写为RC,而本节中的Replication Controller是指“副本控制器”,以便于后续分析。
Replication Controller的核心作用是确保在任何时候集群中某个RC 关联的Pod副本数量都保持预设值。如果发现Pod的副本数量超过预期 值,则Replication Controller会销毁一些Pod副本;反之,Replication Controller会自动创建新的Pod副本,直到符合条件的Pod副本数量达到 预设值。需要注意:只有当Pod的重启策略是Always时 (RestartPolicy=Always),Replication Controller才会管理该Pod的操作(例如创建、销毁、重启等)。在通常情况下,Pod对象被成功创建后 不会消失,唯一的例外是当Pod处于succeeded或failed状态的时间过长 (超时参数由系统设定)时,该Pod会被系统自动回收,管理该Pod的副 本控制器将在其他工作节点上重新创建、运行该Pod副本。
RC中的Pod模板就像一个模具,模具制作出来的东西一旦离开模 具,它们之间就再也没关系了。同样,一旦Pod被创建完毕,无论模板 如何变化,甚至换成一个新的模板,也不会影响到已经创建的Pod了。 此外,Pod可以通过修改它的标签来脱离RC的管控。该方法可以用于将 Pod从集群中迁移、数据修复等调试。对于被迁移的Pod副本,RC会自 动创建一个新的副本替换被迁移的副本。需要注意的是,删除一个RC 不会影响它所创建的Pod。如果想删除一个被RC所控制的Pod,则需要 将该RC的副本数(Replicas)属性设置为0,这样所有的Pod副本就都会 被自动删除。
最好不要越过RC直接创建Pod,因为Replication Controller会通过RC 管理Pod副本,实现自动创建、补足、替换、删除Pod副本,这样能提高 系统的容灾能力,减少由于节点崩溃等意外状况造成的损失。即使你的 应用程序只用到一个Pod副本,我们也强烈建议你使用RC来定义Pod。
总结一下Replication Controller的职责,如下所述:

  • 确保在当前集群中有且仅有N个Pod实例,N是在RC中定义的Pod副本数量。
  • 通过调整RC的spec.replicas属性值来实现系统扩容或者缩容。
  • 通过改变RC中的Pod模板(主要是镜像版本)来实现系统的 滚动升级。

2.2、Node Controller

kubelet进程在启动时通过API Server注册自身的节点信息,并定时 向API Server汇报状态信息,API Server在接收到这些信息后,会将这些 信息更新到etcd中。在etcd中存储的节点信息包括节点健康状况、节点 资源、节点名称、节点地址信息、操作系统版本、Docker版本、kubelet 版本等。节点健康状况包含“就绪”(True)“未就绪”(False)和“未 知”(Unknown)三种。

2.3、ResourceQuota Controller

作为完备的企业级的容器集群管理平台,Kubernetes也提供了 ResourceQuota Controller(资源配额管理)这一高级功能,资源配额管 理确保了指定的资源对象在任何时候都不会超量占用系统物理资源,避 免了由于某些业务进程的设计或实现的缺陷导致整个系统运行紊乱甚至 意外宕机,对整个集群的平稳运行和稳定性有非常重要的作用。
目前Kubernetes支持如下三个层次的资源配额管理。

  • 容器级别,可以对CPU和Memory进行限制。
  • Pod级别,可以对一个Pod内所有容器的可用资源进行限制。
  • Namespace级别,为Namespace(多租户)级别的资源限制, 包括:
    • Pod数量;
    • Replication Controller数量;
    • Service数量;
    • ResourceQuota数量;
    • Secret数量;
    • 可持有的PV数量。

Kubernetes的配额管理是通过Admission Control(准入控制)来控制 的,Admission Control当前提供了两种方式的配额约束,分别是 LimitRanger与ResourceQuota。其中LimitRanger作用于Pod和Container, ResourceQuota则作用于Namespace,限定一个Namespace里的各类资源 的使用总额。

2.4、Namespace Controller

用户通过API Server可以创建新的Namespace并将其保存在etcd中, Namespace Controller定时通过API Server读取这些Namespace的信息。如 果Namespace被API标识为优雅删除(通过设置删除期限实现,即设置 DeletionTimestamp属性),则将该NameSpace的状态设置成Terminating 并保存到etcd中。同时Namespace Controller删除该Namespace下的 ServiceAccount、RC、Pod、Secret、PersistentV olume、ListRange、 ResourceQuota和Event等资源对象。
在Namespace的状态被设置成Terminating后,由Admission Controller的NamespaceLifecycle插件来阻止为该Namespace创建新的资 源。同时,在Namespace Controller删除该Namespace中的所有资源对象 后,Namespace Controller对该Namespace执行finalize操作,删除Namespace的spec.finalizers域中的信息。
如果Namespace Controller观察到Namespace设置了删除期限,同时 Namespace的spec.finalizers域值是空的,那么Namespace Controller将通过API Server删除该Namespace资源。

2.5、Service Controller与Endpoints Controller

2.5.1、Endpoints Controller

在这之前,让我们先看看Service、 Endpoints与Pod的关系。如下图所示,Endpoints表示一个Service对应的所有Pod副本的访问地址
Endpoints Controller就是负责生成和维护所有Endpoints对象的控制器:
【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图4
它负责监听Service和对应的Pod副本的变化,如果监测到Service被 删除,则删除和该Service同名的Endpoints对象。如果监测到新的Service 被创建或者修改,则根据该Service信息获得相关的Pod列表,然后创建 或者更新Service对应的Endpoints对象。如果监测到Pod的事件,则更新 它所对应的Service的Endpoints对象(增加、删除或者修改对应的 Endpoint条目)。

那么,Endpoints对象是在哪里被使用的呢?
答案是每个Node上的 kube-proxy进程,kube-proxy进程获取每个Service的Endpoints,实现了 Service的负载均衡功能。

2.5.2、Service Controller

它其实是属于Kubernetes集群 与外部的云平台之间的一个接口控制器。Service Controller监听Service 的变化,如果该Service是一个LoadBalancer类型的 Service(externalLoadBalancers=true),则Service Controller确保在外部 的云平台上该Service对应的LoadBalancer实例被相应地创建、删除及更 新路由转发表(根据Endpoints的条目)

三、Scheduler原理解析

前面深入分析了Controller Manager及它所包含的各个组件的运行机 制,本节将继续对Kubernetes中负责Pod调度的重要功能模块— Kubernetes Scheduler的工作原理和运行机制做深入分析。

Kubernetes Scheduler在整个系统中承担了“承上启下”的重要功 能,“承上”是指它负责接收Controller Manager创建的新Pod,为其安排 一个落脚的“家”—目标Node;“启下”是指安置工作完成后,目标Node上 的kubelet服务进程接管后继工作,负责Pod生命周期中的“下半生”。
具体来说,Kubernetes Scheduler的作用是将待调度的Pod(API新创 建的Pod、Controller Manager为补足副本而创建的Pod等)按照特定的调度算法和调度策略绑定(Binding)到集群中某个合适的Node上,并将 绑定信息写入etcd中。在整个调度过程中涉及三个对象,分别是待调度 Pod列表、可用Node列表,以及调度算法和策略。简单地说,就是通过 调度算法调度为待调度Pod列表中的每个Pod从Node列表中选择一个最 适合的Node。
随后,目标节点上的kubelet通过API Server监听到Kubernetes Scheduler产生的Pod绑定事件,然后获取对应的Pod清单,下载Image镜像并启动容器。完整的流程如下图:
【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图5
Kubernetes Scheduler当前提供的默认调度流程分为以下两步:

  • 预选调度过程,即遍历所有目标Node,筛选出符合要求的候 选节点。为此,Kubernetes内置了多种预选策略(xxx Predicates)供用 户选择。
  • 确定最优节点,在第1步的基础上,采用优选策略(xxx Priority)计算出每个候选节点的积分,积分最高者胜出。

Kubernetes Scheduler的调度流程是通过插件方式加载的“调度算法 提供者”(AlgorithmProvider)具体实现的。一个AlgorithmProvider其实 就是包括了一组预选策略与一组优先选择策略的结构体。

四、kubelet运行机制解析

在Kubernetes集群中,在每个Node(又称Minion)上都会启动一个 kubelet服务进程。该进程用于处理Master下发到本节点的任务,管理 Pod及Pod中的容器。每个kubelet进程都会在API Server上注册节点自身 的信息,定期向Master汇报节点资源的使用情况,并通过cAdvisor监控 容器和节点资源。

4.1、节点管理

kubelet在启动时通过API Server注册节点信息,并定时向API Server 发送节点的新消息,API Server在接收到这些信息后,将这些信息写入 etcd。通过kubelet的启动参数“—node-status- update-frequency”设置 kubelet每隔多长时间向API Server报告节点状态,默认为10s。

节点通过设置kubelet的启动参数“—register-node”,来决定是否向 API Server注册自己。如果该参数的值为true,那么kubelet将试着通过 API Server注册自己。
当前每个kubelet都被授予创建和修改任何节点的权限。但是在实践中,它仅仅创建和修改自己。将来,我们计划限制kubelet的权限,仅允许它修改和创建所在节点的权限。如果在集群运行过程中遇到集群资源不足的情况,用户就很容易通过添加机器及运用kubelet的自注册模式来 实现扩容。

4.2、Pod管理

kubelet通过以下几种方式获取自身Node上要运行的Pod清单。

  • 文件:kubelet启动参数“—config”指定的配置文件目录下的文 件(默认目录为“/etc/ kubernetes/manifests/”)。通过—file-check- frequency设置检查该文件目录的时间间隔,默认为20s。
  • HTTP端点(URL):通过“—manifest-url”参数设置。通过— http-check-frequency设置检查该HTTP端点数据的时间间隔,默认为 20s。
  • API Server:kubelet通过API Server监听etcd目录,同步Pod列表。

kubelet监听etcd,所有针对Pod的操作都会被kubelet监听。如果发现有新的绑定到本节点的Pod,则按照Pod清单的要求创建该Pod。
如果发现本地的Pod被修改,则kubelet会做出相应的修改,比如在删除Pod中的某个容器时,会通过Docker Client删除该容器。 如果发现删除本节点的Pod,则删除相应的Pod,并通过DockerClient删除Pod中的容器。 kubelet读取监听到的信息,如果是创建和修改Pod任务,则做如下处理:

  • 为该Pod创建一个数据目录。
  • 从API Server读取该Pod清单。
  • 为该Pod挂载外部卷(External Volume)。下载Pod用到的Secret。
  • 检查已经运行在节点上的Pod,如果该Pod没有容器或Pause容 器(“kubernetes/pause”镜像创建的容器)没有启动,则先停止Pod里所 有容器的进程。如果在Pod中有需要删除的容器,则删除这些容器。
  • 用“kubernetes/pause”镜像为每个Pod都创建一个容器。该Pause 容器用于接管Pod中所有其他容器的网络。每创建一个新的Pod,kubelet 都会先创建一个Pause容器,然后创建其他容器。“kubernetes/pause”镜像 大概有200KB,是个非常小的容器镜像。
  • 为Pod中的每个容器做如下处理。
    • 为容器计算一个Hash值,然后用容器的名称去查询对应Docker 容器的Hash值。若查找到容器,且二者的Hash值不同,则停止Docker中 容器的进程,并停止与之关联的Pause容器的进程;若二者相同,则不做任何处理。
    • 如果容器被终止了,且容器没有指定的restartPolicy(重启策 略),则不做任何处理
    • 调用Docker Client下载容器镜像,调用Docker Client运行容器。

4.3、容器健康检查

Pod通过两类探针来检查容器的健康状态。

  • 一类是LivenessProbe探 针,用于判断容器是否健康并反馈给kubelet。如果LivenessProbe探针探 测到容器不健康,则kubelet将删除该容器,并根据容器的重启策略做相 应的处理。如果一个容器不包含LivenessProbe探针,那么kubelet认为该 容器的LivenessProbe探针返回的值永远是Success;
  • 另一类是 ReadinessProbe探针,用于判断容器是否启动完成,且准备接收请求。 如果ReadinessProbe探针检测到容器启动失败,则Pod的状态将被修改, Endpoint Controller将从Service的Endpoint中删除包含该容器所在Pod的IP 地址的Endpoint条目。

4.4、cAdvisor资源监控

在Kubernetes集群中,应用程序的执行情况可以在不同的级别上监 测到,这些级别包括:容器、Pod、Service和整个集群。作为Kubernetes 集群的一部分,Kubernetes希望提供给用户详细的各个级别的资源使用信息,这将使用户深入地了解应用的执行情况,并找到应用中可能的瓶颈。

cAdvisor是一个开源的分析容器资源使用率和性能特性的代理工 具,它是因为容器而产生的,因此自然支持Docker容器;在Kubernetes 项目中,cAdvisor被集成到Kubernetes代码中,kubelet则通过cAdvisor获 取其所在节点及容器的数据。cAdvisor自动查找所有在其所在Node上的容器,自动采集CPU、内存、文件系统和网络使用的统计信息。在大部分Kubernetes集群中,cAdvisor通过它所在Node的4194端口暴露一个简单的UI。
kubelet作为连接Kubernetes Master和各Node之间的桥梁,管理运行在Node上的Pod和容器。 kubelet将每个Pod都转换成它的成员容器,同时从cAdvisor获取单独的容器使用统计信息,然后通过该REST API暴露 这些聚合后的Pod资源使用的统计信息。

cAdvisor只能提供2~3min的监控数据,对性能数据也没有持久化,因此在Kubernetes早期版本中需要依靠Heapster来实现集群范围内全部容器性能指标的采集和查询功能。从Kubernetes 1.8版本开始,性能指标数据的查询接口升级为标准的Metrics API,后端服务则升级为全新的 Metrics Server。因此,cAdvisor在4194端口提供的UI和API服务从Kubernetes 1.10版本开始cAdvisor进入弃用流程,并于1.12版本完全关闭。如果还希望使用cAdvisor的这个特性,则从1.13版本开始可以通过部署一个 DaemonSet在每个Node上启动一个cAdvisor来提供UI和API,请参考 cAdvisor在GitHub上的说明(https://github.com/google/cadvisor)。
在新的Kubernetes监控体系中,Metrics Server用于提供Core Metrics(核心指标),包括Node和Pod的CPU和内存使用数据。其他 Custom Metrics(自定义指标)则由第三方组件(如Prometheus)采集和存储。

五、kube-proxy运行机制解析

我们在前面已经了解到,为了支持集群的水平扩展、高可用性, Kubernetes抽象出了Service的概念。Service是对一组Pod的抽象,它会根 据访问策略(如负载均衡策略)来访问这组Pod。

Kubernetes在创建服务时会为服务分配一个虚拟的IP地址,客户端通过访问这个虚拟的IP地址来访问服务,服务则负责将请求转发到后端 的Pod上。这不就是一个反向代理吗?没错,这就是一个反向代理。但是,它和普通的反向代理有一些不同:首先,它的IP地址是虚拟的,想从外面访问还需要一些技巧;其次,它的部署和启停是由Kubernetes统 一自动管理的。
在很多情况下,Service只是一个概念,而真正将Service的作用落实的是它背后的kube-proxy服务进程。只有理解了kube-proxy的原理和机 制,我们才能真正理解Service背后的实现逻辑。

在Kubernetes集群的每个Node上都会运行一个kube-proxy服务进程,我们可以把这个进程看作Service的透明代理兼负载均衡器,其核心功能是将到某个Service的访问请求转发到后端的多个Pod实例上。此外,Service的Cluster IP与NodePort等概念是kube-proxy服务通过iptables 的NAT转换实现的,kube-proxy在运行过程中动态创建与Service相关的 iptables规则,这些规则实现了将访问服务(Cluster IP或NodePort)的请 求负载分发到后端Pod的功能。由于iptables机制针对的是本地的kube-proxy端口,所以在每个Node上都要运行kube-proxy组件,这样一来,在 Kubernetes集群内部,我们可以在任意Node上发起对Service的访问请求。综上所述,由于kube-proxy的作用,在Service的调用过程中客户端无须关心后端有几个Pod,中间过程的通信、负载均衡及故障恢复都是透明的。

起初,kube-proxy进程是一个真实的TCP/UDP代理,类似HA Proxy,负责从Service到Pod的访问流量的转发,这种模式被称为 userspace(用户空间代理)模式。如下图所示,当某个Pod以Cluster IP方式访问某个Service的时候,这个流量会被Pod所在本机的iptables转 发到本机的kube-proxy进程,然后由kube-proxy建立起到后端Pod的 TCP/UDP连接,随后将请求转发到某个后端Pod上,并在这个过程中实 现负载均衡功能。
【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图6
关于Cluster IP与Node Port的实现原理,以及kube-proxy与API Server 的交互过程,下图给出了较为详细的说明,由于这是最古老的kube- proxy的实现方式,所以不再赘述:
【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图7

Kubernetes从1.2版本开始,将iptables作为kube- proxy的默认模式。iptables模式下的kube-proxy不再起到Proxy的作用, 其核心功能:通过API Server的Watch接口实时跟踪Service与Endpoint的 变更信息,并更新对应的iptables规则,Client的请求流量则通过iptables 的NAT机制“直接路由”到目标Pod。 如下图:
【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图8

根据Kubernetes的网络模型,一个Node上的Pod与其他Node上的Pod 应该能够直接建立双向的TCP/IP通信通道,所以如果直接修改iptables规 则,则也可以实现kube-proxy的功能,只不过后者更加高端,因为是全 自动模式的。与第1代的userspace模式相比,iptables模式完全工作在内 核态,不用再经过用户态的kube-proxy中转,因而性能更强。
iptables模式虽然实现起来简单,但存在无法避免的缺陷:在集群中 的Service和Pod大量增加以后,iptables中的规则会急速膨胀,导致性能 显著下降,在某些极端情况下甚至会出现规则丢失的情况,并且这种故 障难以重现与排查,于是Kubernetes从1.8版本开始引入第3代的 IPVS(IP Virtual Server)模式,如下图所示。IPVS在Kubernetes 1.11 中升级为GA稳定版:
【Kubernetes详解】(九)k8s 之 核心组件运行机制 - 图9
iptables与IPVS虽然都是基于Netfilter实现的,但因为定位不同,二者有着本质的差别:iptables是为防火墙而设计的;IPVS则专门用于高 性能负载均衡,并使用更高效的数据结构(Hash表),允许几乎无限的规模扩张,因此被kube-proxy采纳为第三代模式。

与iptables相比,IPVS拥有以下明显优势:

  • 为大型集群提供了更好的可扩展性和性能;
  • 支持比iptables更复杂的复制均衡算法(最小负载、最少连接、 加权等);
  • 支持服务器健康检查和连接重试等功能;
  • 可以动态修改ipset的集合,即使iptables的规则正在使用这个集合。

由于IPVS无法提供包过滤、airpin-masquerade tricks(地址伪装)、 SNA T等功能,因此在某些场景(如NodePort的实现)下还要与iptables 搭配使用。在IPVS模式下,kube-proxy又做了重要的升级,即使用 iptables的扩展ipset,而不是直接调用iptables来生成规则链。
iptables规则链是一个线性的数据结构,ipset则引入了带索引的数据 结构,因此当规则很多时,也可以很高效地查找和匹配。我们可以将 ipset简单理解为一个IP(段)的集合,这个集合的内容可以是IP地址、 IP网段、端口等,iptables可以直接添加规则对这个“可变的集合”进行操 作,这样做的好处在于可以大大减少iptables规则的数量,从而减少性能损耗。
假设要禁止上万个IP访问我们的服务器,则用iptables的话,就需要 一条一条地添加规则,会在iptables中生成大量的规则;但是用ipset的 话,只需将相关的IP地址(网段)加入ipset集合中即可,这样只需设置 少量的iptables规则即可实现目标。

kube-proxy针对Service和Pod创建的一些主要的iptables规则如下。

  • KUBE-CLUSTER-IP:在masquerade-all=true或clusterCIDR指定的情况下对Service Cluster IP地址进行伪装,以解决数据包欺骗问题。
  • KUBE-EXTERNAL-IP:将数据包伪装成Service的外部IP地址。
  • KUBE-LOAD-BALANCER、KUBE-LOAD-BALANCER-LOCAL:伪装Load Balancer 类型的Service流量。
  • KUBE-NODE-PORT-TCP、KUBE-NODE-PORT-LOCAL- TCP、KUBE-NODE-PORTUDP、KUBE-NODE-PORT-LOCAL-UDP: 伪装NodePort类型的Service流量。

需要特别注意的是:IP与Port是由iptables/IPVS配置在每K8S节点上的

这样才可以达到高并发