kube-apiserver 请求处理流程

概述

kube-apiserver 是 Kubernetes 控制平面的核心组件,负责处理所有 API 请求。每个请求都需要经过认证(Authentication)、授权(Authorization)、准入控制(Admission Control) 才能被持久化到 etcd。

核心处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌──────────────────────────────────────────────────────────────────────────────┐
│ API 请求入口 │
│ http.Handler -> DefaultBuildHandler │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 认证 (Authentication) │
│ X509 Client Cert / Bearer Token / Bootstrap Token / OIDC 等 │
│ staging/src/k8s.io/apiserver/pkg/authentication │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 授权 (Authorization) │
│ RBAC / ABAC / Node / Webhook 等授权器 │
│ staging/src/k8s.io/apiserver/pkg/authorization │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 准入控制 (Admission Control) │
│ Validating / Mutating 阶段,修改/验证请求 │
│ staging/src/k8s.io/apiserver/pkg/admission │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 存储层 (Storage) │
│ REST -> Registry -> etcd3 Store │
│ staging/src/k8s.io/apiserver/pkg/storage/etcd3 │
└──────────────────────────────────────────────────────────────────────────────┘

关键代码路径

1. 请求入口

  • 文件: staging/src/k8s.io/apiserver/pkg/server/config.go
  • 函数: DefaultBuildHandler.ServeHTTP()
  • 位置: 约 145 行

2. 认证流程

  • 文件: staging/src/k8s.io/apiserver/pkg/authentication/authenticator.go
  • 接口: RequestAuthenticator
  • 实现类:
    • X509Authenticator - 客户端证书认证
    • BearerAuthenticator - Token 认证
    • BootstrapAuthenticator - Bootstrap Token 认证
    • OIDCAuthenticator - OIDC 认证

3. 授权流程

  • 文件: staging/src/k8s.io/apiserver/pkg/authorization/authorizer/interfaces.go
  • 接口: Authorizer.Authorize(ctx, Attributes)
  • 实现类:
    • RBACAuthorizer - RBAC 授权
    • ABACAuthorizer - ABAC 授权
    • NodeAuthorizer - Node 授权
    • WebhookAuthorizer - Webhook 授权

4. 准入控制

  • 文件: staging/src/k8s.io/apiserver/pkg/admission/chain.go
  • 阶段:
    1. Mutating 阶段: 修改请求内容
    2. Validating 阶段: 验证请求有效性
  • 内置插件:
    • NamespaceLifecycle - 命名空间生命周期
    • LimitRanger - 资源限制
    • ServiceAccount - 服务账号
    • DefaultStorageClass - 默认存储类
    • ResourceQuota - 资源配额
    • PodSecurity - Pod 安全策略

5. etcd 存储

  • 文件: staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go
  • 核心方法:
    • Create() - 创建对象
    • Get() - 获取对象
    • Update() - 更新对象
    • Delete() - 删除对象
    • Watch() - 监听变更

请求处理详细流程

认证流程

1
2
3
4
5
6
7
8
9
10
11
1. 从请求中提取认证信息
├── 检查 X509 证书 (X509Authenticator)
├── 检查 Bearer Token (BearerAuthenticator)
├── 检查 Bootstrap Token (BootstrapAuthenticator)
└── 检查 OIDC Token (OIDCAuthenticator)

2. 构建 user.Info 对象
├── 用户名 (GetName)
├── 用户组 (GetGroups)
├── UID (GetUID)
└── 额外信息 (GetExtra)

授权流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1. 构建 authorizer.Attributes
├── 用户信息 (GetUser)
├── 请求动词 (GetVerb: get/list/create/update/delete/patch/watch)
├── 资源信息 (GetResource, GetAPIGroup, GetSubresource)
├── 命名空间 (GetNamespace)
└── 资源名称 (GetName)

2. 遍历所有授权器 (union authorizer)
├── RBAC: 检查 Role/RoleBinding
├── ABAC: 检查策略文件
├── Node: 检查节点权限
└── Webhook: 调用外部授权服务

3. 返回决策
├── DecisionAllow: 允许
├── DecisionDeny: 拒绝
└── DecisionNoOpinion: 无意见(继续下一个授权器)

准入控制流程

1
2
3
4
5
6
7
8
9
10
11
12
1. Mutating 阶段 (按顺序执行)
├── NamespaceLifecycle
├── LimitRanger (设置默认值)
├── ServiceAccount
├── DefaultStorageClass
└── 其他 mutating 插件

2. Validating 阶段 (按顺序执行)
├── LimitRanger (验证限制)
├── ResourceQuota
├── PodSecurity
└── 其他 validating 插件

etcd 交互

1
2
3
4
5
6
7
8
9
10
11
1. 序列化对象
├── 转换为 storage 版本
└── JSON 编码

2. 写入 etcd
├── PUT 操作 (创建/更新)
├── DELETE 操作 (删除)
└── 获取 Lease (TTL)

3. 更新 ResourceVersion
└── 用于乐观并发控制

关键配置参数

参数 说明 默认值
--anonymous-auth 是否允许匿名请求 true
--client-ca-file 客户端 CA 证书文件 -
--enable-admission-plugins 启用的准入插件 -
--authorization-mode 授权模式 AlwaysAllow
--etcd-servers etcd 服务器地址 http://127.0.0.1:2379
--encryption-provider-config 加密配置 -

常见问题排查

1. 认证失败

  • 检查证书是否有效
  • 检查 Token 是否过期
  • 检查 ServiceAccount 是否存在

2. 授权失败

  • 检查 RoleBinding/ClusterRoleBinding
  • 检查 Role 的规则定义
  • 检查用户是否在正确的 Group 中

3. 准入控制被拒绝

  • 检查准入插件日志
  • 检查 ResourceQuota 是否足够
  • 检查 LimitRange 是否满足

4. etcd 写入失败

  • 检查 etcd 匇标
  • 检查磁盘空间
  • 检查网络连接

面试题

基础题

1. kube-apiserver 请求处理的完整流程是什么?

参考答案:

1
请求 -> 认证(Authentication) -> 授权(Authorization) -> 准入控制(Admission Control) -> etcd 存储

详细步骤:

  1. 认证:验证请求者身份(证书、Token、OIDC等)
  2. 授权:检查请求者是否有权限执行该操作(RBAC、ABAC、Node等)
  3. 准入控制
    • Mutating 阶段:修改请求内容
    • Validating 阶段:验证请求有效性
  4. 存储:持久化到 etcd

2. kube-apiserver 支持哪些认证方式?

参考答案:

  • X509 客户端证书:最常用的方式
  • Bearer Token:静态 Token 或 ServiceAccount JWT
  • Bootstrap Token:用于集群引导
  • OIDC:OpenID Connect 集成
  • Webhook:外部认证服务
  • Anonymous:匿名请求(默认开启)
  • Request Header:前端代理认证

3. 准入控制的两个阶段分别是什么?有什么区别?

参考答案:

阶段 作用 示例插件
Mutating 修改请求内容 DefaultStorageClass、LimitRanger、ServiceAccount
Validating 验证请求有效性 ResourceQuota、PodSecurity、NamespaceLifecycle

执行顺序:先执行所有 Mutating 插件,再执行 Validating 插件。


中级题

4. RBAC 授权的检查流程是怎样的?

参考答案:

  1. 从请求中提取用户信息和操作属性(verb、resource、namespace等)
  2. 遍历所有 ClusterRoleBinding 和 RoleBinding
  3. 匹配 Subject(User/Group/ServiceAccount)
  4. 获取绑定的 Role/ClusterRole 的 PolicyRules
  5. 检查是否有规则匹配当前请求
  6. 返回 DecisionAllow 或 DecisionNoOpinion

关键代码路径plugin/pkg/auth/authorizer/rbac/rbac.go

5. 如果多个授权器同时存在,授权决策是如何做出的?

参考答案:
使用 Union Authorizer(联合授权器):

  • 按顺序遍历所有授权器
  • 任一授权器返回 DecisionAllow → 允许
  • 任一授权器返回 DecisionDeny → 拒绝
  • 所有授权器返回 DecisionNoOpinion → 拒绝
1
2
3
4
5
6
7
8
9
10
11
12
13
// staging/src/k8s.io/apiserver/pkg/authorization/union/union.go
func (authzHandler unionAuthzHandler) Authorize(ctx, attributes) {
for _, handler := range authzHandler {
decision, _, _ := handler.Authorize(ctx, attributes)
if decision == authorizer.DecisionAllow {
return DecisionAllow
}
if decision == authorizer.DecisionDeny {
return DecisionDeny
}
}
return DecisionNoOpinion
}

6. 解释 ResourceVersion 的作用和乐观并发控制机制。

参考答案:
ResourceVersion 是对象的乐观锁机制:

  • 每次对象更新,ResourceVersion 会变化
  • 更新时携带旧的 ResourceVersion
  • 如果 etcd 中的版本与请求不匹配,更新失败(409 Conflict)
1
2
3
# 更新失败示例
status: 409 Conflict
message: "the object has been modified; please apply your changes to the latest version"

用途

  • 防止并发更新冲突
  • Watch 请求的起始点
  • List 请求的一致性保证

7. kube-apiserver 如何处理高并发请求?

参考答案:

  1. **API Priority and Fairness (APF)**:

    • 按优先级分级(system、workload、default等)
    • 每个分级有独立的并发限制
    • 公平调度防止饿死
  2. MaxInFlight 限制

    • --max-requests-inflight:非 mutating 请求限制
    • --max-mutating-requests-inflight:mutating 请求限制
  3. Watch 缓存

    • Cacher 提供 List/Watch 缓存
    • 减少 etcd 直接访问

高级题

8. 描述 AggregatorServer、KubeAPIServer、APIExtensionsServer 三者的关系和请求流转。

参考答案:

1
2
3
4
请求 -> AggregatorServer -> KubeAPIServer -> APIExtensionsServer
| | |
v v v
AA (如 metrics-server) 核心 API CRD API

委托链(Delegation Chain)

  • AggregatorServer:处理聚合 API(如 metrics-server)
  • KubeAPIServer:处理核心 Kubernetes API(Pod、Service等)
  • APIExtensionsServer:处理 CRD 定义的 API

请求先到达 Aggregator,如果无法处理则委托给下一个,形成责任链模式。

9. etcd 交互中,Cacher 的作用是什么?如何保证 Watch 的一致性?

参考答案:
Cacher 作用

  • 内存缓存,减少 etcd 压力
  • 为 List/Watch 请求提供数据
  • 维护 watchCache 和 listWatch

Watch 一致性保证

  1. ResourceVersion:每个事件携带版本号
  2. Bookmark:定期发送书签事件,保持连接
  3. ProgressNotify:通知客户端当前版本
  4. 反射器(Reflector):通过 listAndWatch 保持缓存同步

10. 如何实现一个自定义的准入控制 Webhook?

参考答案:

1. 创建 Webhook 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 接收 AdmissionReview 请求
func handleAdmission(w http.ResponseWriter, r *http.Request) {
var review admission.AdmissionReview
json.NewDecoder(r.Body).Decode(&review)

// 执行自定义逻辑
allowed := validateRequest(&review)

// 返回响应
response := admission.AdmissionReview{
Response: &admission.AdmissionResponse{
Allowed: allowed,
UID: review.Request.UID,
},
}
json.NewEncoder(w).Encode(response)
}

2. 配置 Webhook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: my-webhook
webhooks:
- name: my-webhook.example.com
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
clientConfig:
service:
name: webhook-service
namespace: default
path: "/validate"
admissionReviewVersions: ["v1"]
sideEffects: None

11. kube-apiserver 如何保证高可用?

参考答案:

  1. 多实例部署:运行多个 kube-apiserver 实例
  2. 负载均衡:前端使用 LB(如 HAProxy、云 LB)
  3. 无状态设计:所有状态存储在 etcd
  4. etcd 集群:奇数节点(3/5/7),Raft 共识

12. 审计日志(Audit)的工作原理是什么?

参考答案:
审计日志记录所有 API 请求:

1. 审计策略配置

1
2
3
4
5
6
7
8
9
10
11
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata # 只记录元数据
resources:
- group: ""
resources: ["pods"]
- level: RequestResponse # 记录请求和响应体
resources:
- group: ""
resources: ["secrets"]

2. 审计阶段

  • RequestReceived:收到请求
  • ResponseStarted:响应开始
  • ResponseComplete:响应完成
  • Panic:发生 panic

场景题

13. 用户报告 kubectl get pods 返回 403 Forbidden,如何排查?

参考答案:
排查步骤

  1. 检查用户认证

    1
    kubectl auth whoami
  2. 检查 RBAC 绑定

    1
    2
    3
    4
    5
    # 检查 RoleBinding
    kubectl get rolebinding -n <namespace>

    # 检查 ClusterRoleBinding
    kubectl get clusterrolebinding
  3. 模拟授权检查

    1
    kubectl auth can-i get pods --as=<username> -n <namespace>
  4. 常见原因

    • 缺少 RoleBinding
    • Role 中没有 get pods 权限
    • namespace 不匹配
    • 用户不在正确的 Group 中

14. 大量 List 请求导致 etcd 压力过大,如何优化?

参考答案:

  1. 使用 Informer 缓存

    1
    2
    3
    4
    5
    informer := cache.NewSharedIndexInformer(
    &cache.ListWatch{...},
    &corev1.Pod{},
    time.Hour * 12,
    )
  2. 使用 Limit 和 Continue

    1
    kubectl get pods --limit=100
  3. 使用 ResourceVersion

    1
    kubectl get pods --resource-version=12345
  4. 调整 etcd 配置

    • 增加 --quota-backend-bytes
    • 使用 SSD 存储

15. 自定义资源(CRD)的请求处理流程与内置资源有何不同?

参考答案:

特性 内置资源 CRD
API 定义 编译时 运行时
Schema 验证 Go 代码 OpenAPI v3
版本转换 编译时 Webhook(可选)
准入控制 内置 Webhook
性能 更优 略低(需要反射)