Kubernetes 扩展机制测验
相关文档: Kubernetes 扩展机制
选择题
1. CRD (Custom Resource Definition) 的主要目的是什么?
- A) 修改现有的 Kubernetes 资源
- B) 使用自定义资源类型扩展 Kubernetes API
- C) 配置 pod 网络
- D) 预置存储卷
显示答案
答案: B) 使用自定义资源类型扩展 Kubernetes API
解释: CRD 允许你通过定义像原生 Kubernetes 资源一样工作的自定义资源类型来扩展 Kubernetes API。
2. custom controller 的 reconciliation loop 主要执行什么任务?
- A) 立即删除资源
- B) 调和当前状态与期望状态之间的差异
- C) 向 API server 注册新的 API
- D) 应用网络策略
显示答案
答案: B) 调和当前状态与期望状态之间的差异
解释: custom controller 的 reconciliation loop 会观察资源的当前状态,将其与期望状态 (spec) 进行比较,并在存在差异时采取操作以达到期望状态。
3. Operator pattern 的核心组件是什么?
- A) Deployment 和 Service
- B) CRD 和 Custom Controller
- C) ConfigMap 和 Secret
- D) Ingress 和 NetworkPolicy
显示答案
答案: B) CRD 和 Custom Controller
解释: Operator pattern 使用 CRD 定义应用配置,并使用 custom controller 自动化部署、升级和恢复等运维知识。
4. MutatingAdmissionWebhook 的主要用途是什么?
- A) 拒绝 API 请求
- B) 修改 API 请求
- C) 记录 API 响应
- D) 升级 API 版本
显示答案
答案: B) 修改 API 请求
解释: MutatingAdmissionWebhook 可以在 API 请求被持久化之前修改这些请求。常见用途包括:sidecar container 注入、设置默认值等。
5. scheduler framework 中 Filter plugin 的作用是什么?
- A) 为 node 分配分数
- B) 排除无法运行 pod 的 node
- C) 将 pod 绑定到 node
- D) 预留 node 资源
显示答案
答案: B) 排除无法运行 pod 的 node
解释: Filter plugin 会过滤掉不满足 pod 要求的 node,将它们从候选范围中排除。
6. Aggregated API Server 和 CRD 之间有什么区别?
- A) 没有区别,它们是相同的
- B) Aggregated API 提供更多控制能力,但需要运行单独的 server
- C) CRD 提供的功能比 Aggregated API 更多
- D) Aggregated API 已弃用
显示答案
答案: B) Aggregated API 提供更多控制能力,但需要运行单独的 server
解释: Aggregated API Server 提供对 API 行为、自定义存储后端和高级功能的完全控制,但需要部署和维护单独的 API server。CRD 更简单,但存在限制。
7. Kubernetes 中 Finalizer 的目的是什么?
- A) 加快资源删除
- B) 在清理完成前阻止资源删除
- C) 自动重启失败的 pod
- D) 验证资源创建
显示答案
答案: B) 在清理完成前阻止资源删除
解释: Finalizer 会阻塞资源删除,直到 controller 执行必要的清理操作(例如删除外部资源)并移除 finalizer。
8. 哪个 scheduler extension point 在 pod 已绑定到 node 后运行?
- A) PreFilter
- B) PostBind
- C) Reserve
- D) Score
显示答案
答案: B) PostBind
解释: PostBind plugin 会在 pod 成功绑定到 node 后被调用。它们用于信息通知,并用于清理或通知。
9. 在 Istio 中,通过 admission webhook 注入 sidecar 使用哪个 annotation?
- A) istio.io/inject
- B) sidecar.istio.io/inject
- C) istio-injection
- D) auto-inject.istio.io
显示答案
答案: B) sidecar.istio.io/inject
解释:sidecar.istio.io/inject annotation 控制 Istio 的 mutating webhook 是否将 Envoy sidecar 注入到 pod 中。Namespace 级别的控制使用 istio-injection label。
10. scheduler framework 中 Score plugin 的目的是什么?
- A) 过滤掉不合适的 node
- B) 对 node 排名并选择最佳 node
- C) 将 pod 绑定到选定的 node
- D) 验证 pod 规范
显示答案
答案: B) 对 node 排名并选择最佳 node
解释: Score plugin 会为通过过滤的 node 分配分数。scheduler 会从所有 Score plugin 的综合分数中选择得分最高的 node。
简答题
1. CRD 中用于 schema 验证的标准是什么?
显示答案
答案: OpenAPI v3 Schema (openAPIV3Schema)
解释: CRD 使用 spec.versions[].schema.openAPIV3Schema 中的 OpenAPI v3 schema 来定义自定义资源的结构和验证规则。
2. Kubernetes controller 中 Owner Reference 的作用是什么?
显示答案
答案: 定义资源之间的所有权关系,并管理垃圾回收和事件传播
解释: Owner Reference 定义父子关系,并在父资源通过 Kubernetes 垃圾回收被删除时自动删除子资源。
3. ValidatingAdmissionPolicy 和 ValidatingAdmissionWebhook 之间有什么区别?
显示答案
答案: ValidatingAdmissionPolicy 使用 CEL 表达式并在进程内运行,而 ValidatingAdmissionWebhook 调用外部 HTTP endpoint。
解释: ValidatingAdmissionPolicy(在 1.26 中引入)提供更好的性能,并且不需要外部 webhook 基础设施,但灵活性低于 webhook。
4. controller-runtime 库是什么,为什么常用它?
显示答案
答案: controller-runtime 是一个提供构建 Kubernetes controller 常用模式的库,包括 client 缓存、leader election 和 reconciliation loop 管理。
解释: controller-runtime 是 Kubebuilder 项目的一部分,它抽象了样板代码和最佳实践,使构建可靠的 operator 更容易。
5. CRD 中 conversion webhook 的目的是什么?
显示答案
答案: conversion webhook 在同一 CRD 的不同 API 版本之间转换资源。
解释: 当 CRD 有多个版本(例如 v1alpha1、v1beta1、v1)时,conversion webhook 会处理版本之间的转换,以支持 API 演进。
实践题
1. 编写一个满足以下要求的 CRD:
- Name: WebApp
- Group: apps.example.com
- Fields: replicas(integer,最小值 1)、image(string,必填)
显示答案
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: webapps.apps.example.com
spec:
group: apps.example.com
names:
kind: WebApp
plural: webapps
singular: webapp
shortNames:
- wa
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: ["image"]
properties:
replicas:
type: integer
minimum: 1
default: 1
image:
type: string
status:
type: object
properties:
availableReplicas:
type: integer
conditions:
type: array
items:
type: object
properties:
type:
type: string
status:
type: string
lastTransitionTime:
type: string
format: date-time
subresources:
status: {}
additionalPrinterColumns:
- name: Replicas
type: integer
jsonPath: .spec.replicas
- name: Available
type: integer
jsonPath: .status.availableReplicas
- name: Age
type: date
jsonPath: .metadata.creationTimestamp2. 编写一个 ValidatingAdmissionWebhook 配置,用于验证 "production" namespace 中的所有 Deployment。
显示答案
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: deployment-validator
webhooks:
- name: validate-deployment.example.com
clientConfig:
service:
name: webhook-service
namespace: webhook-system
path: /validate-deployment
caBundle: <base64-encoded-ca-cert>
rules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
scope: Namespaced
namespaceSelector:
matchLabels:
environment: production
failurePolicy: Fail
sideEffects: None
admissionReviewVersions: ["v1"]
timeoutSeconds: 10解释:
namespaceSelector将 webhook 限制到带有environment: productionlabel 的 namespacefailurePolicy: Fail会在 webhook 不可用时拒绝请求sideEffects: None表示 webhook 没有副作用
3. 为 custom controller 编写一个简单的 reconciliation loop 伪代码。
显示答案
func (r *WebAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
// 1. Fetch the WebApp resource
var webapp appsv1.WebApp
if err := r.Get(ctx, req.NamespacedName, &webapp); err != nil {
if errors.IsNotFound(err) {
// Resource deleted, nothing to do
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
// 2. Check if being deleted (handle finalizers)
if !webapp.DeletionTimestamp.IsZero() {
if containsFinalizer(webapp, finalizerName) {
// Perform cleanup
if err := r.cleanupExternalResources(&webapp); err != nil {
return ctrl.Result{}, err
}
// Remove finalizer
removeFinalizer(&webapp, finalizerName)
if err := r.Update(ctx, &webapp); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
// 3. Add finalizer if not present
if !containsFinalizer(webapp, finalizerName) {
addFinalizer(&webapp, finalizerName)
if err := r.Update(ctx, &webapp); err != nil {
return ctrl.Result{}, err
}
}
// 4. Create or update Deployment
deployment := r.constructDeployment(&webapp)
if err := controllerutil.SetControllerReference(&webapp, deployment, r.Scheme); err != nil {
return ctrl.Result{}, err
}
if err := r.CreateOrUpdate(ctx, deployment); err != nil {
return ctrl.Result{}, err
}
// 5. Create or update Service
service := r.constructService(&webapp)
if err := controllerutil.SetControllerReference(&webapp, service, r.Scheme); err != nil {
return ctrl.Result{}, err
}
if err := r.CreateOrUpdate(ctx, service); err != nil {
return ctrl.Result{}, err
}
// 6. Update status
webapp.Status.AvailableReplicas = deployment.Status.AvailableReplicas
if err := r.Status().Update(ctx, &webapp); err != nil {
return ctrl.Result{}, err
}
// 7. Requeue after interval for periodic reconciliation
return ctrl.Result{RequeueAfter: time.Minute * 5}, nil
}要点:
- 始终处理资源未找到的情况(可能已被删除)
- 使用 finalizer 清理外部资源
- 为垃圾回收设置 owner reference
- 单独更新 status subresource
- 考虑为定期检查设置 requeue interval
进阶题
1. 为复杂的分布式系统设计一个 Kubernetes Operator。
显示答案
CRD 设计:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: postgresclusters.database.example.com
spec:
group: database.example.com
names:
kind: PostgresCluster
plural: postgresclusters
shortNames:
- pg
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: ["replicas", "version"]
properties:
replicas:
type: integer
minimum: 1
maximum: 10
version:
type: string
enum: ["14", "15", "16"]
storage:
type: object
properties:
size:
type: string
default: "10Gi"
storageClass:
type: string
backup:
type: object
properties:
enabled:
type: boolean
default: true
schedule:
type: string
default: "0 2 * * *"
retention:
type: integer
default: 7
status:
type: object
properties:
phase:
type: string
enum: ["Creating", "Running", "Upgrading", "Failed", "Deleting"]
primaryEndpoint:
type: string
replicaEndpoints:
type: array
items:
type: string
currentVersion:
type: string
conditions:
type: array
items:
type: object
properties:
type:
type: string
status:
type: string
reason:
type: string
message:
type: string
lastTransitionTime:
type: string
format: date-time
subresources:
status: {}
scale:
specReplicasPath: .spec.replicas
statusReplicasPath: .status.readyReplicasController 核心逻辑:
- 基于阶段的状态管理(Creating、Running、Upgrading、Failed)
- 自动故障恢复(Primary 失败时进行 Failover)
- 滚动升级策略(先升级 replica,然后升级 primary)
- Backup 管理(用于定时 backup 的 CronJob)
架构:
PostgresCluster CR
|
v
Controller
|
+---> StatefulSet (PostgreSQL pods)
+---> Service (Primary endpoint)
+---> Service (Replica endpoint)
+---> Secret (Credentials)
+---> ConfigMap (PostgreSQL config)
+---> CronJob (Backups)
+---> PodDisruptionBudget2. 解释如何使用 scheduler framework 实现 custom scheduler。
显示答案
Scheduler Plugin 实现:
// Plugin implementing multiple extension points
type CustomSchedulerPlugin struct {
handle framework.Handle
}
// Implement PreFilter - check pod requirements
func (p *CustomSchedulerPlugin) PreFilter(ctx context.Context, state *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
// Validate pod has required annotations
if _, ok := pod.Annotations["custom-scheduler/zone"]; !ok {
return nil, framework.NewStatus(framework.Unschedulable, "missing zone annotation")
}
return nil, framework.NewStatus(framework.Success, "")
}
// Implement Filter - exclude unsuitable nodes
func (p *CustomSchedulerPlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
requiredZone := pod.Annotations["custom-scheduler/zone"]
nodeZone := nodeInfo.Node().Labels["topology.kubernetes.io/zone"]
if requiredZone != nodeZone {
return framework.NewStatus(framework.Unschedulable, "zone mismatch")
}
return framework.NewStatus(framework.Success, "")
}
// Implement Score - rank suitable nodes
func (p *CustomSchedulerPlugin) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
nodeInfo, err := p.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
if err != nil {
return 0, framework.NewStatus(framework.Error, err.Error())
}
// Score based on available resources
allocatable := nodeInfo.Node().Status.Allocatable
requested := nodeInfo.Requested
cpuScore := calculateResourceScore(allocatable.Cpu(), requested.Cpu)
memScore := calculateResourceScore(allocatable.Memory(), requested.Memory)
return (cpuScore + memScore) / 2, framework.NewStatus(framework.Success, "")
}
// Register the plugin
func New(_ runtime.Object, h framework.Handle) (framework.Plugin, error) {
return &CustomSchedulerPlugin{handle: h}, nil
}Scheduler 配置:
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: custom-scheduler
plugins:
preFilter:
enabled:
- name: CustomSchedulerPlugin
filter:
enabled:
- name: CustomSchedulerPlugin
score:
enabled:
- name: CustomSchedulerPlugin
disabled:
- name: NodeResourcesBalancedAllocationExtension Point 摘要:
| Extension Point | 目的 | 运行时机 |
|---|---|---|
| PreFilter | Pod 级检查 | 过滤前 |
| Filter | Node 排除 | 对每个 node |
| PostFilter | 处理不可调度情况 | 没有合适的 node 时 |
| PreScore | 为打分做准备 | 打分前 |
| Score | Node 排名 | 对过滤后的 node |
| NormalizeScore | 分数归一化 | 所有分数计算后 |
| Reserve | 资源预留 | node 选择后 |
| Permit | 最终批准 | 绑定前 |
| PreBind | 绑定前操作 | API 绑定前 |
| Bind | 实际绑定 | API server 更新 |
| PostBind | 绑定后清理 | 绑定后 |