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 或者 requestslimits 为相同时则为该等级
  • Burstable:可突发的,只设置 requestsrequests 低于 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区别

img

  • Resources与HTTP paths关联,
  • Resources始终是API Group和Version的一部分。
  • kind是这些endpoint返回并接收的objects的类型,并持久存在于etcd中。
KubernetesOOP
KindClass
ResourceObject

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 是把事件转化为“可控的任务节奏”。

image-20260430142011860

operator 并发控制

在開發 Operator(基於 Controller-runtime)時,如何正確處理 CR(Custom Resource)的狀態(Status)更新?為什麼強烈建議使用 UpdateStatus 而不是直接調用 Update?如果更新時遇到 Conflict (HTTP 409) 錯誤,底層原理是什麼,該如何優雅處理?

答案解析:

答案解析:

  • Subresource 隔離: 必須在 CRD 定義中開啟 /status subresource。調用 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 告訴使用者「現在變成什麼樣了」。

text
1
2
spec:    # 使用者期望狀態
status:  # controller 觀察到的實際狀態

所以在 Operator 裡,Controller 應該:然後只更新 status 子資源。

為什麼要用 Status().Update,而不是 Update?

在 controller-runtime 裡,推薦這樣:

text
1
err := r.Status().Update(ctx, cr)

Update() 是更新整個物件,包括:

text
1
2
3
metadata
spec
status

Conflict / HTTP 409 是什麼原理?

Kubernetes 每個物件都有一個:metadata.resourceVersion,当在读取 CR 時,這時其他人先更新了它,你再拿舊物件去更新,API Server 發現版本過期,就會返回:409

text
1
2
3
4
5
Get CR,resourceVersion = 100

CR 變成 resourceVersion = 101

Update resourceVersion = 100

如何優雅處理 Conflict?

重新 Get 最新物件,重新計算 Status,再重試更新。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import (
    apierrors "k8s.io/apimachinery/pkg/api/errors"
    "k8s.io/client-go/util/retry"
)

err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
    latest := &appv1.MyApp{}

    if err := r.Get(ctx, req.NamespacedName, latest); err != nil {
        return err
    }

    latest.Status.Phase = "Ready"
    latest.Status.ObservedGeneration = latest.Generation

    return r.Status().Update(ctx, latest)
})

if err != nil {
    return ctrl.Result{}, err
}

Admission Webhook 是什麼

Admission Webhook 是 Kubernetes API Server 在資源寫入 etcd 之前,調用外部服務做校驗或修改的擴展點。

text
1
2
MutatingAdmissionWebhook   修改請求物件
ValidatingAdmissionWebhook 校驗請求是否合法

面试回答:Admission Webhook 是 API Server 寫入資源前的一道「可編程關卡」,Mutating 負責塑形,Validating 負責守門。

text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
kubectl / controller
Kube API Server
Authentication
Authorization
Admission Chain
MutatingAdmissionWebhook
ValidatingAdmissionWebhook
etcd

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 階段。
  • 防範單點故障:
    1. FailurePolicy: 非關鍵鏈路的 Webhook 應將 failurePolicy 設置為 Ignore(默認為 Fail),確保 Webhook 服務宕機時不會阻塞常規的 Pod 創建。
    2. 超時控制: 合理設置 timeoutSeconds(通常 1-3 秒),避免 Webhook 處理過慢耗盡 APIServer 的線程。
    3. 命名空間豁免: 必須在 MutatingWebhookConfiguration 中配置 namespaceSelector,嚴格避開 kube-system 命名空間。否則,一旦 Webhook 宕機,可能導致 CoreDNS 或 CNI 插件無法重啟,引發全局災難。

标准回答:

Webhook Server 多副本

text
1
replicas: 2

设置合理的 timeoutSeconds

text
1
timeoutSeconds: 2

配置合理的 failurePolicy

text
1
2
3
failurePolicy: Fail // Webhook 失敗則拒絕請求; 安全、合規、強校驗
//
failurePolicy: Ignore // Webhook 失敗則放行; 非關鍵注入、可降級邏輯

避免攔截自己

按照 ns 隔离 webhook

text
1
2
3
4
5
6
namespaceSelector:
  matchExpressions:
  - key: admission-webhook
    operator: NotIn
    values:
    - disabled

CNI 原理剖析

CNI 的本質不是一個守護進程(Daemon),而是一套二進制可執行文件標準化的 JSON 規範。它的核心職責只有兩個:將容器加入網絡(分配 IP、配置路由),以及將容器從網絡中移除。

調用鏈路 (Kubelet -> CRI -> CNI)

  1. 當 Pod 被調度到節點,Kubelet 通過 gRPC 調用 CRI(如 containerd)。
  2. containerd 先調用底層的 runc 創建一個 Network Namespace(網絡命名空間)
  3. containerd 讀取機器的 /etc/cni/net.d/ 目錄下的配置,拉起指定的 CNI 插件二進制文件(如 calicoflannelcilium)。

CNI 的核心執行流 (二進制調用)

RI 啟動 CNI 插件時,是通過環境變量標準輸入 (stdin) 傳遞參數的,執行 ADDDEL 操作。

  • 階段一:IPAM (IP Address Management)
    • 主插件首先調用 IPAM 插件(如 host-localcalico-ipam)。
    • IPAM 負責從節點的 PodCIDR 網段中分配一個可用的 IP,並將結果返回給主插件。
  • 階段二:網絡構建 (以傳統 Veth Pair 為例)
    • 主插件在宿主機和容器的 Netns 之間創建一對 veth pair 虛擬網卡。
    • 將網卡的一端移入容器 Netns,重命名為 eth0,配置剛分配的 IP,並設置默認路由。
    • 將另一端接入宿主機的網橋(如 cni0)或進行 BGP 路由發佈。

請簡述 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 的控制循环。

核心代码调度

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Run()
  
SchedulingQueue.NextPod()
  
scheduleOne()
  
SchedulePod()
  
Framework 插件链
  
AssumePod()
  
Bind()

调度流程大致是:

text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
QueueSort
PreFilter
Filter
PostFilter
PreScore
Score
Reserve
Permit
PreBind
Bind
PostBind

调度流程大致是:

text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
取 Pod
PreFilter:预处理
Filter:过滤不可用节点
PreScore:打分前准备
Score:节点打分
Reserve:资源预留
Permit:准入等待
PreBind:绑定前处理
Bind:绑定到 Node
PostBind:绑定后处理

调度步骤所负责的内容

  • 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 做收尾工作。这种分阶段设计使调度器具备高度的可扩展性和可定制能力。