Kubernetes概念
Ingress和LoadBalancer的区别
- Ingress通常用于将HTTP(S)流量路由到Kubernetes群集内部的服务,支持复杂路径路由和负载均衡算法
- LB则是通过提供商提供一种外部流量引入到集群内的组件,通常为2 3层
- Ingress本身是基于service的,引入流量时依赖 kube-proxy
- LB则是独立的组件,最小接入单元也是service,而通过2 3层的广播等功能可以提供多节点的引入
- 功能:Ingress是一个规范,LB则是一种实现
- 实现方式:ingress通过扩展Kubernetes API+controller, 而LB除此外还需要外部设备提供(软硬件,云组件)
kubernetes之最小单元
- Pod最小可调度单元,最小部署单元
- 容器:容器是最小的执行单元
- Namespace:最小隔离单元
- Service:最小接入单元
etcd用的什么算法,简单解释一下
raft算法 强一致性 同一时间只能有一个leader,所有的操作都在leader上。
Pod 的生命周期
Pod 状态始终处于一下几个状态之一:
- Pending: 部署 Pod 事务已被集群受理,但当前容器镜像还未下载完或现有资源无法满足 Pod 的资源需求
- Running: 所有容器已被创建,并被部署到节点上
- Successed: Pod 成功退出,并不会被重启
- Failed: Pod 中有容器被终止
- Unknown: 未知原因,如 kube-apiserver 无法与 Pod 进行通讯
Kubernetes有哪些不同类型的服务?
- cluster ip
- Node Port
- Load Balancer
- Extrenal Name
什么是ETCD?
Etcd是用Go编程语言编写的,是一个分布式键值存储,用于协调分布式工作。因此,Etcd存储Kubernetes集群的配置数据,表示在任何给定时间点的集群状态。
什么是Ingress网络,它是如何工作的?
Ingress网络是一组规则,充当Kubernetes集群的入口点。这允许入站连接,可以将其配置为通过可访问的URL,负载平衡流量或通过提供基于名称的虚拟主机从外部提供服务。因此,Ingress是一个API对象,通常通过HTTP管理集群中服务的外部访问,是暴露服务的最有效方式。
什么是Headless Service?
Headless Service类似于“普通”服务,但没有群集IP。此服务使您可以直接访问pod,而无需通过代理访问它。
什么是集群联邦?
在联邦集群的帮助下,可以将多个Kubernetes集群作为单个集群进行管理。因此,您可以在数据中心/云中创建多个Kubernetes集群,并使用联邦来在一个位置控制/管理它们。
联合集群可以通过执行以下两项操作来实现此目的。请参考下图。
kube-proxy的作用
kube-proxy运行在所有节点上,它监听apiserver中service和endpoint的变化情况,创建路由规则以提供服务IP和负载均衡功能。简单理解此进程是Service的透明代理兼负载均衡器,其核心功能是将到某个Service的访问请求转发到后端的多个Pod实例上。
kube-proxy iptables的原理
Kubernetes从1.2版本开始,将iptables作为kube-proxy的默认模式。iptables模式下的kube-proxy不再起到Proxy的作用,其核心功能:通过API Server的Watch接口实时跟踪Service与Endpoint的变更信息,并更新对应的iptables规则,Client的请求流量则通过iptables的NAT机制“直接路由”到目标Pod。
kube-proxy ipvs的原理
IPVS在Kubernetes1.11中升级为GA稳定版。IPVS则专门用于高性能负载均衡,并使用更高效的数据结构(Hash表),允许几乎无限的规模扩张,因此被kube-proxy采纳为最新模式。
在IPVS模式下,使用iptables的扩展ipset,而不是直接调用iptables来生成规则链。iptables规则链是一个线性的数据结构,ipset则引入了带索引的数据结构,因此当规则很多时,也可以很高效地查找和匹配。
可以将ipset简单理解为一个IP(段)的集合,这个集合的内容可以是IP地址、IP网段、端口等,iptables可以直接添加规则对这个“可变的集合”进行操作,这样做的好处在于可以大大减少iptables规则的数量,从而减少性能损耗。
kube-proxy ipvs和iptables的异同
iptables与IPVS都是基于Netfilter实现的,但因为定位不同,二者有着本质的差别:iptables是为防火墙而设计的;IPVS则专门用于高性能负载均衡,并使用更高效的数据结构(Hash表),允许几乎无限的规模扩张。
与iptables相比,IPVS拥有以下明显优势:
- 为大型集群提供了更好的可扩展性和性能;
- 支持比iptables更复杂的复制均衡算法(最小负载、最少连接、加权等);
- 支持服务器健康检查和连接重试等功能;
- 可以动态修改ipset的集合,即使iptables的规则正在使用这个集合。
Kubernetes镜像的下载策略
Kubernetes的镜像下载策略有三种:Always、Never、IFNotPresent。
- Always:镜像标签为latest时,总是从指定的仓库中获取镜像。
- Never:禁止从仓库中下载镜像,也就是说只能使用本地镜像。
- IfNotPresent:仅当本地没有对应镜像时,才从目标仓库中下载。默认的镜像下载策略是:当镜像标签是latest时,默认策略是Always;当镜像标签是自定义时(也就是标签不是latest),那么默认策略是IfNotPresent。
简述Kubernetes Scheduler使用哪两种算法将Pod绑定到worker节点
Kubernetes Scheduler根据如下两种调度算法将 Pod 绑定到最合适的工作节点:
- 预选(Predicates):输入是所有节点,输出是满足预选条件的节点。kube-scheduler根据预选策略过滤掉不满足策略的Nodes。如果某节点的资源不足或者不满足预选策略的条件则无法通过预选。如“Node的label必须与Pod的Selector一致”。
- 优选(Priorities):输入是预选阶段筛选出的节点,优选会根据优先策略为通过预选的Nodes进行打分排名,选择得分最高的Node。例如,资源越富裕、负载越小的Node可能具有越高的排名。
有了解过qos吗? 怎么实现的?
服务质量 Quality of Service有三种 Guaranteed, Burstable, and Best-Effort,它们的QoS级别依次递减。
- Guaranteed:确保的,只设置
limits或者requests与limits为相同时则为该等级 - Burstable:可突发的,只设置
requests或requests低于limits的场景 - Best-effort: 默认值,如果不设置则为这个等级
node资源不足时会按qos级别驱逐pod。 最先驱逐的是Best-Effort ,重要组件一定要设置limit和request.
驱逐顺序根据 BestEffort ==》Burstable ==》Guaranteed 进行驱逐
Kubernetes 开发
资源和类型
Kind:实体的类型
resources:resources是,restful中的资源,标识一组HTTP端点(paths),可以理解为kind的实例化。
例如:Pod是etcd中的数据,而resources对应的 path上的resources
Resources和kinds区别
- Resources与HTTP paths关联,
- Resources始终是API Group和Version的一部分。
- kind是这些endpoint返回并接收的objects的类型,并持久存在于etcd中。
| Kubernetes | OOP |
|---|---|
| Kind | Class |
| Resource | Object |
Kind 其实就是一个类,用于描述对象的;而 Resource 就是具体的 Kind,可以理解成类已经实例化成对象了。
GVR与GVK有什么区别?
- GVR = Group Version Resources
- GVK = Group Version Kind
每个kind都属于一个Group和Version中,通过GVK标识,GVR是GVK对外提供服务的入口,GVK与GVR之间的映射过程交 REST mapping
client-go 客户端类型有哪些?
- RestClient:是最基础的客户端,其作用是将http client进行封装成rest api格式。位于rest目录
- ClientSet:基于RestClient进行封装对 Resource 与 version 管理集合,
- DiscoverySet:RestClient进行封装,可动态发现kube-apiserver所支持的GVR(Group Version Resource)。
- :基于RestClient,包含动态的客户端,可以对Kubernetes所支持的 API对象进行操作,包括CRD。
kubernetes 调度过程
kubernetes调度过程就是调度上下文,而调度上下文就是执行 scheduleOne 这个函数中线性执行的。
调度上下文的过程分为两阶段,调度和绑定
调度:指的是SchedulePod -> findNodesThatFitPod 这是 prefilter 阶段,如果通过 prefilter
prefilter 做预检查动作,如获取node列表,检查提名node是否满足,满足则评估,不满足则从PreFilterPlugins获取node list,满足所有条件执行 filter plugin;PreFilterPlugins返回的是一组Node name
filter 做过滤操作,满足的条件是至少配置了一个filter plugin,filter是线性的,如一个扩展不满足则标记为不可用
postFilter 是抢占的触发条件,即filter阶段没有FN时被触发,满足条件是需要配置至少一个该类型plugin
- 这里存在 Unschedulable 不可调用则执行postfilter,否则都不可调用
preScore, score 会在 prioritizeNodes 中执行对应的 插件
接下来是 Reserve, 为了避免Pod在绑定到节点前时,调度一个新的Pod,使节点使用资源超过可用资源情况。
Premit ,阻止或延迟 Pod 的绑定
绑定:绑定操作时异步进行的,即通过了打分阶段,基本上等于调度成功
- 这个异步线程会从延迟队列中拿到调度的pod,即Premit 的延迟
- 这里关联的上面的reserve 与 premit,如果不可调用则调用unreserved回滚,否则会调用 bind
- prebind 与 bind 的失败都会放入到回滚队列中
- bind 当 执行了unreserve 则忘记这个pod 否则成功的绑定了node
client-go的架构
Reflector deltafifo的生产者 就是将 (监控)Etcd 里面的数据反射到本地存储(DeltaFIFO)中
deltaFIFO, Delta 表示的是变化的资源对象存储 先进先出的队列
workqueue kubernetes中使用的队列,即每次触发的事件都被塞入到queue中进行处理
- 去重
- delay:如 cronjob 依赖延迟队列实现定时功能
- 限速:
Indexer deltaFIFO消费者,是Informer的本地存储。
workqueue算法实现
已知workqueue主要作用是为了去重,延迟,限速,那么他是通过什么算法实现的呢?
- 延迟主要使用了 Binary Heap 数据类型实现的延迟,而这种queue称为优先级队列
- Heap是一个二叉树数据结构,即每个节点包含的元素大于或等于该节点子节点的元素,如果新元素的值大于父元素,将新元素与父元素交换,直到达到新元素到根,这个过程叫向上调整
- 元素被放置在结构中时,按照优先级排列
- 优先级最高的元素最先离开
- 限速队列时在延迟队列的基础上扩展的,使用的令牌桶和漏桶算法实现的
kube-proxy作用
kube-proxy作用是位于工作节点上,通过ipvs提供service功能,本质上来说是一个controller,通过监听 node, endpoints, service等资源的变动从而生成proixer的规则
什么是endpointslice
- Endpoints 与 EndpointSlices 均是为service提供端点的
- Service规模越大,那么Endpoints中的 Pod 数量越大,传输的 EndPoints 对象就越大。集群中 Pod 更改的频率越高,也意味着传输在网络中发生的频率就越高
- Endpoints 对象在大规模集群场景下存在下列问题:
- 增加网络流量(etcd最大请求大小为 1.5 MiB),隐性增加对控制平面的影响,service的可扩展性将降低
- 超大规模的 service 理论上会无法存储 该 Endpoints
- 处理Endpoints资源的 worker 会消耗更多的计算资源
- Endpointslices 解决了:
- 部分更新,更少的网络流量
- Worker 处理 Endpoints 更新所需的资源更少
- 减少对控制平面的影响,提升的性能和 service 规模
Kubernetes controller 原理
如下图所示:
- Reflector — 连接 Kubernetes API Server(集群大脑)与本地世界的桥梁。通过 List + Watch 持续感知变化。把“远端状态”同步到本地
- DeltaFIFO — “变化的缓冲河流”;(Delta 通常被称为增量、差异或变化量),会合并、压缩变化,里面的内容是 event。总结:DeltaFIFO 不关心“世界是什么样”,只关心“世界发生了什么变化”。
- **Informer ** — 调度中枢,整个系统的分发中心。他负责更新缓存 (Indexer) 和分发事件 (Handler)。总结,informer 既是数据的“落地者”,也是事件的“广播者”。
- Indexer — 本地的最终状态存储
- **Workqueue ** — 任务缓冲队列,Workqueue 是 controller 的待办任务列表。总结,workqueue 是把事件转化为“可控的任务节奏”。
operator 并发控制
在開發 Operator(基於 Controller-runtime)時,如何正確處理 CR(Custom Resource)的狀態(Status)更新?為什麼強烈建議使用
UpdateStatus而不是直接調用Update?如果更新時遇到Conflict(HTTP 409) 錯誤,底層原理是什麼,該如何優雅處理?
答案解析:
答案解析:
- Subresource 隔離: 必須在 CRD 定義中開啟
/statussubresource。調用UpdateStatus時,APIServer 只會校驗和更新對象的Status欄位,完全忽略對Spec的修改。這實現了權責分離(用戶控制 Spec,Controller 控制 Status),防止 Controller 的狀態更新意外覆蓋了用戶剛剛提交的 Spec 變更。 - Conflict 原理: K8s 採用基於
resourceVersion的樂觀併發控制(Optimistic Concurrency Control)。當 Controller 嘗試更新一個對象時,如果提交的 RV 與 etcd 中最新的 RV 不一致(說明在讀取和寫入期間,對象被其他組件修改過),APIServer 就會拒絕寫入並返回 409 Conflict。 - 優雅處理: 絕對不能強行覆蓋。標準做法是利用
client-go/util/retry包中的RetryOnConflict函數。在發生衝突時,強制從 APIServer(或本地緩存)重新 Get 最新版本的對象,基於最新狀態重新計算需要更新的數據,然後重試提交。
Status 的定位
Spec 是使用者告訴系統「我想要什麼」;Status 是 Operator 告訴使用者「現在變成什麼樣了」。
| |
所以在 Operator 裡,Controller 應該:然後只更新 status 子資源。
為什麼要用 Status().Update,而不是 Update?
在 controller-runtime 裡,推薦這樣:
| |
Update() 是更新整個物件,包括:
| |
Conflict / HTTP 409 是什麼原理?
Kubernetes 每個物件都有一個:metadata.resourceVersion,当在读取 CR 時,這時其他人先更新了它,你再拿舊物件去更新,API Server 發現版本過期,就會返回:409
| |
如何優雅處理 Conflict?
重新 Get 最新物件,重新計算 Status,再重試更新。
| |
Admission Webhook 是什麼
Admission Webhook 是 Kubernetes API Server 在資源寫入 etcd 之前,調用外部服務做校驗或修改的擴展點。
| |
面试回答:Admission Webhook 是 API Server 寫入資源前的一道「可編程關卡」,Mutating 負責塑形,Validating 負責守門。
| |
Admission Webhook 开发执行顺序
開發 Admission Webhook 時,Mutating Webhook 和 Validating Webhook 的執行順序是什麼?如果一個 Mutating Webhook 修改了對象,會不會導致其他已經執行過的 Webhook 驗證失效?在生產環境中,如何避免 Webhook 成為卡死整個 K8s 集群的單點故障?
答案解析:
- 執行順序與重入機制(Re-invocation): APIServer 會先並行執行所有匹配的 Mutating Webhooks,然後再執行 Validating Webhooks。如果某個 Mutating Webhook 修改了對象,為了保證一致性,APIServer 會重新觸發(Re-invoke) 那些已經執行過的 Mutating Webhooks,確保所有的修改最終收斂,然後才進入 Validating 階段。
- 防範單點故障:
- FailurePolicy: 非關鍵鏈路的 Webhook 應將
failurePolicy設置為Ignore(默認為Fail),確保 Webhook 服務宕機時不會阻塞常規的 Pod 創建。 - 超時控制: 合理設置
timeoutSeconds(通常 1-3 秒),避免 Webhook 處理過慢耗盡 APIServer 的線程。 - 命名空間豁免: 必須在
MutatingWebhookConfiguration中配置namespaceSelector,嚴格避開kube-system命名空間。否則,一旦 Webhook 宕機,可能導致 CoreDNS 或 CNI 插件無法重啟,引發全局災難。
- FailurePolicy: 非關鍵鏈路的 Webhook 應將
标准回答:
Webhook Server 多副本
| |
设置合理的 timeoutSeconds
| |
配置合理的 failurePolicy
| |
避免攔截自己
按照 ns 隔离 webhook
| |
CNI 原理剖析
CNI 的本質不是一個守護進程(Daemon),而是一套二進制可執行文件和標準化的 JSON 規範。它的核心職責只有兩個:將容器加入網絡(分配 IP、配置路由),以及將容器從網絡中移除。
調用鏈路 (Kubelet -> CRI -> CNI)
- 當 Pod 被調度到節點,Kubelet 通過 gRPC 調用 CRI(如 containerd)。
- containerd 先調用底層的 runc 創建一個 Network Namespace(網絡命名空間)。
- containerd 讀取機器的
/etc/cni/net.d/目錄下的配置,拉起指定的 CNI 插件二進制文件(如calico、flannel或cilium)。
CNI 的核心執行流 (二進制調用)
RI 啟動 CNI 插件時,是通過環境變量和標準輸入 (stdin) 傳遞參數的,執行 ADD 或 DEL 操作。
- 階段一:IPAM (IP Address Management)
- 主插件首先調用 IPAM 插件(如
host-local或calico-ipam)。 - IPAM 負責從節點的 PodCIDR 網段中分配一個可用的 IP,並將結果返回給主插件。
- 主插件首先調用 IPAM 插件(如
- 階段二:網絡構建 (以傳統 Veth Pair 為例)
- 主插件在宿主機和容器的 Netns 之間創建一對
veth pair虛擬網卡。 - 將網卡的一端移入容器 Netns,重命名為
eth0,配置剛分配的 IP,並設置默認路由。 - 將另一端接入宿主機的網橋(如
cni0)或進行 BGP 路由發佈。
- 主插件在宿主機和容器的 Netns 之間創建一對
請簡述 Scheduler Framework 的架構
目前 K8s 官方推薦使用 Scheduler Framework 而不是從頭實現一個自定義調度器(Custom Scheduler)。請簡述 Scheduler Framework 的架構,並說明如果你要實現一個「優先將 Pod 調度到 GPU 溫度較低的節點,且拒絕調度到溫度超過 85°C 的節點」的插件,需要實現在哪些擴展點(Extension Points)?
答案解析:
- 架構特點: Scheduler Framework 將調度流程拆分為 Scheduling Cycle(同步執行,選擇節點)和 Binding Cycle(異步執行,綁定節點)。開發者只需要實現特定的接口並編譯進默認調度器中,無需維護整個調度邏輯。
- 擴展點實作:
- 拒絕高溫節點(硬性過濾): 實現
Filter擴展點。當檢查到節點 GPU 溫度 > 85°C 時,返回Unschedulable,該節點將被直接剔除出候選列表。 - 優先低溫節點(軟性打分): 實現
Score擴展點。對通過 Filter 的所有節點進行打分,溫度越低分數越高(例如 0-100 分);同時可能需要實現NormalizeScore擴展點,將分數歸一化,確保與其他打分插件(如資源利用率)的權重兼容。
- 拒絕高溫節點(硬性過濾): 實現
请从代码角度描述 Pod的调度上下文
Kubernetes Scheduler 本质上是一个不断从调度队列中取出 Pending Pod,然后通过一系列调度插件为它选择最合适 Node,并最终 Bind 的控制循环。
核心代码调度
| |
调度流程大致是:
| |
调度流程大致是:
| |
调度步骤所负责的内容
- QueueSort:哪个 Pod 先被调度
- PreFilter: 预计算阶段,“跟 Node 无关”的准备工作,结果写入CycleState,链后面复用这个结果
- Filter:节点过滤, Node 能不能跑这个 Pod?
- 资源够不够(CPU/Memory)
- nodeSelector / nodeAffinity
- taint / toleration
- volume 是否可挂载
- 端口冲突
- PostFilter:调度失败后的补救,例如抢占(Preemption), 调度的兜底机制(failback)
- PreScore: 打分前准备
- 计算 topology 信息
- 统计 Node 分布
- Score:节点打分,每个插件打一个分(0~100):
- Reserve: 资源预留,乐观锁 / 本地资源占用
- Permit:调度“闸门”
- PreBind:绑定前处理
- 绑定 PVC(VolumeBinding)
- 写 annotation
- side effect 操作
- Bind: 真正绑定 Pod,调用 API
- PostBind:绑定成功后的收尾
- 记录事件(Event)
- metrics
- 日志
Kubernetes Scheduler 通过 Scheduling Framework 将调度过程拆分为多个阶段:QueueSort 决定调度顺序,PreFilter 做预计算,Filter 过滤可行节点,Score 对节点打分并选出最优节点。当调度失败时 PostFilter 可以触发抢占。调度成功后通过 Reserve 做本地资源预留,Permit 控制调度节奏,PreBind 做绑定前准备,Bind 完成 Pod 到 Node 的绑定,最后 PostBind 做收尾工作。这种分阶段设计使调度器具备高度的可扩展性和可定制能力。
