Skip to content

扩展 Kubernetes 测验

本测验用于测试你对 Kubernetes 扩展机制的概念性和实践性知识。它涵盖 Custom Resource Definitions (CRD)、custom controllers、API 扩展、webhooks 以及 operator pattern 等主题。

选择题

  1. 在 Kubernetes 中定义 custom resources 最常见的方式是什么?
    • A) 使用 ConfigMap 定义资源 schema
    • B) 创建 CustomResourceDefinition (CRD)
    • C) 直接修改 API server 代码
    • D) 使用 Aggregation Layer
显示答案

答案:B) 创建 CustomResourceDefinition (CRD)

解释: 在 Kubernetes 中定义 custom resources 最常见的方式是创建 CustomResourceDefinition (CRD)。CRD 是一种机制,允许你通过定义新的资源类型来扩展 Kubernetes API。

使用 CRDs 可带来以下好处:

  • 可以在不修改现有 Kubernetes API server 的情况下添加新的资源类型。
  • 可以使用 kubectl 等标准 Kubernetes 工具管理 custom resources。
  • 可以利用资源验证、版本管理和 status subresources 等功能。

CRD 示例:

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

其他选项的问题:

  • ConfigMaps 用于存储配置数据,不适合用于 API 扩展。
  • 直接修改 API server 代码很复杂、难以维护,并且可能在升级期间导致问题。
  • Aggregation Layer 是定义 custom resources 的另一种方式,但它比 CRDs 更复杂,因为它需要实现单独的 API server。
  1. Kubernetes Operator 的主要目的是什么?
    • A) 优化 cluster nodes 的资源使用
    • B) 将复杂应用程序的运维知识编码到自动化软件中
    • C) 提高 Kubernetes API server 性能
    • D) 扩展 cluster networking 功能
显示答案

答案:B) 将复杂应用程序的运维知识编码到自动化软件中

解释: Kubernetes Operator 的主要目的是将复杂应用程序的运维知识编码到自动化软件中。operator pattern 是人类操作员管理复杂应用程序方式的软件实现。

operators 的关键特征:

  • 将 custom resources 与 custom controllers 结合起来,实现特定于应用程序的逻辑。
  • 自动化应用程序部署、升级、备份、恢复和扩缩容等运维任务。
  • 持续监控应用程序状态,并调整到期望状态。
  • 将领域知识编码化,以支持复杂应用程序的声明式管理。

operators 的常见使用场景:

  • 数据库的自动化管理(例如 PostgreSQL、MySQL、MongoDB)
  • 消息系统的部署和配置(例如 Kafka、RabbitMQ)
  • 监控系统的设置和维护(例如 Prometheus)
  • service meshes 的管理(例如 Istio)

Operator 示例 - Prometheus Operator:

yaml
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  serviceAccountName: prometheus
  replicas: 2
  version: v2.35.0
  serviceMonitorSelector:
    matchLabels:
      team: frontend
  resources:
    requests:
      memory: 400Mi

通过这个简单的声明式配置,Prometheus Operator 会自动处理以下复杂任务:

  • 部署 Prometheus servers
  • 创建和管理配置文件
  • 自动发现 service monitoring targets
  • 高可用设置
  • 存储管理
  • 升级协调

其他选项不是 operators 的主要目的:

  • 资源使用优化是 HPA (Horizontal Pod Autoscaler)、VPA (Vertical Pod Autoscaler) 等的职责。
  • 提高 API server 性能不是 operators 的主要目的。
  • 扩展 networking 功能是 CNI plugins 或 service meshes 的职责。
  1. Kubernetes 中 Admission Webhooks 的主要功能是什么?
    • A) 处理 API server 的身份认证
    • B) 拦截资源创建或修改请求,以验证或修改它们
    • C) 监控 cluster events 并发送告警
    • D) 提供用于与外部系统集成的 APIs
显示答案

答案:B) 拦截资源创建或修改请求,以验证或修改它们

解释: Kubernetes 中 Admission Webhooks 的主要功能是拦截资源创建或修改请求,以验证或修改它们。Admission webhooks 提供一种机制,可在 Kubernetes API server 将请求存储到持久化存储(etcd)之前拦截请求。

两种 admission webhooks:

  1. Validating Webhook:

    • 验证资源创建、更新和删除请求。
    • 可以允许或拒绝请求,但不能修改请求。
    • 用于策略强制、安全检查、配置验证等。
  2. Mutating Webhook:

    • 可以验证和修改资源创建与更新请求。
    • 用于设置默认值、注入 sidecar containers、添加 labels 等。
    • 在 validating webhooks 之前运行。

Admission webhook 配置示例:

yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: pod-policy-validator
webhooks:
- name: pod-policy.example.com
  clientConfig:
    service:
      namespace: webhook-namespace
      name: webhook-service
      path: "/validate-pods"
    caBundle: <base64-encoded-ca-cert>
  rules:
  - apiGroups: [""]
    apiVersions: ["v1"]
    operations: ["CREATE", "UPDATE"]
    resources: ["pods"]
    scope: "Namespaced"
  admissionReviewVersions: ["v1", "v1beta1"]
  sideEffects: None
  timeoutSeconds: 5

admission webhooks 的常见使用场景:

  • 强制执行安全策略(例如禁止 privileged containers)
  • 强制设置 resource requests 和 limits
  • 自动注入 sidecar containers(例如 Istio)
  • 自动添加 labels 和 annotations
  • 限制 image registries
  • 应用基于 namespace 的策略

其他选项的问题:

  • 处理 API server 的身份认证是 Authentication Plugins 的职责。
  • 监控 cluster events 并发送告警是 event listeners 或监控工具的职责。
  • 提供用于与外部系统集成的 APIs 是 API 扩展的一般目的,但不是 admission webhooks 的主要功能。
  1. Kubernetes API server 中 Aggregation Layer 的主要目的是什么?
    • A) 优化 API server 性能
    • B) 将多个 API servers 合并为单个 API server
    • C) 将 custom API servers 集成到 main API server 中以扩展 API
    • D) 在 cluster 内提供 service discovery
显示答案

答案:C) 将 custom API servers 集成到 main API server 中以扩展 API

解释: Kubernetes API server 中 Aggregation Layer 的主要目的是将 custom API servers 集成到 main API server 中以扩展 API。这提供了另一种扩展 Kubernetes API 的方式,虽然更复杂,但比 CRDs 提供更强大的功能。

Aggregation Layer 的关键特征:

  • 将 custom API servers 集成到 Kubernetes API server 的 URL 空间中。
  • Custom API servers 可以拥有自己的存储、业务逻辑、API 版本等。
  • Main API server 将请求代理到适当的 custom API server。
  • 身份认证和授权由 main API server 处理。

使用 Aggregation Layer 的场景:

  • 需要复杂验证逻辑时
  • 需要自定义存储后端时
  • 需要与现有 APIs 不同的行为时
  • 需要资源转换或特殊状态计算时

APIService 资源示例:

yaml
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  name: v1alpha1.metrics.k8s.io
spec:
  service:
    name: metrics-server
    namespace: kube-system
  group: metrics.k8s.io
  version: v1alpha1
  insecureSkipTLSVerify: true
  groupPriorityMinimum: 100
  versionPriority: 100

此配置会将针对 metrics.k8s.io/v1alpha1 API group 的请求路由到 kube-system namespace 中的 metrics-server service。

使用 Aggregation Layer 的真实示例:

  • metrics-server:提供 node 和 pod resource usage metrics
  • service-catalog:与外部 service brokers 集成
  • custom-metrics-apiserver:为 HPA 提供 custom metrics

其他选项的问题:

  • Aggregation Layer 不是用于优化 API server 性能的。
  • 将多个 API servers 合并为一个并不是准确描述。Aggregation Layer 将多个 API servers 集成到单个 URL 空间中,但这些 servers 是分别运行的。
  • Aggregation Layer 不提供 service discovery。那是 Kubernetes Services 的职责。
  1. Kubernetes 中 Custom Controller 的主要角色是什么?
    • A) 监控 cluster nodes 的资源使用
    • B) 观察 custom resources 的状态并协调到期望状态
    • C) 处理 API server 请求的身份认证和授权
    • D) 管理 cluster networking
显示答案

答案:B) 观察 custom resources 的状态并协调到期望状态

解释: Kubernetes 中 Custom Controller 的主要角色是观察 custom resources 的状态并协调到期望状态。Controllers 实现“reconciliation loop”,这是 Kubernetes 中的核心运维模式,用于持续将系统的实际状态调整到期望状态。

custom controllers 的关键特征:

  • Watch 特定资源类型(通常是通过 CRDs 定义的 custom resources)。
  • 对资源变更事件作出响应并执行业务逻辑。
  • 执行任务,将资源的实际状态调整为期望状态。
  • 更新资源的 status 字段以反映当前状态。

custom controllers 的常见组件:

  1. Informer: Watch Kubernetes API server 并接收资源变更事件。
  2. Work Queue: 存储和管理待处理事件。
  3. Reconciler: 比较资源的期望状态和实际状态,并执行必要任务。
  4. Client: 与 Kubernetes API 交互以创建、更新和删除资源。

Custom controller 示例 - 简单 reconcile 函数:

go
func (c *Controller) reconcile(key string) error {
    // Split key into namespace and name
    namespace, name, err := cache.SplitMetaNamespaceKey(key)
    if err != nil {
        return err
    }

    // Get custom resource
    instance, err := c.customResourceLister.CustomResources(namespace).Get(name)
    if errors.IsNotFound(err) {
        // Resource deleted - perform cleanup
        return nil
    }
    if err != nil {
        return err
    }

    // Check resource state and perform necessary tasks
    // e.g., create sub-resources, integrate with external systems, update status, etc.

    // Update status
    instanceCopy := instance.DeepCopy()
    instanceCopy.Status.Phase = "Reconciled"
    _, err = c.customResourceClient.CustomResources(namespace).UpdateStatus(instanceCopy)
    return err
}

custom controllers 的常见使用场景:

  • 自动化复杂应用程序的部署和管理
  • 与外部系统集成(例如 cloud resource provisioning)
  • 自动化备份和恢复流程
  • 实现高级部署策略(例如 canary deployments、blue-green deployments)

其他选项的问题:

  • 监控 cluster nodes 的资源使用是 metrics-server 或 Prometheus 等监控工具的职责。
  • 处理 API server 请求的身份认证和授权是 authentication 和 authorization plugins 的职责。
  • 管理 cluster networking 是 CNI plugins 或 network controllers 的职责。
  1. 在 Kubernetes 中,CRDs (CustomResourceDefinitions) 使用什么格式来定义验证 schemas?
    • A) JSON Schema
    • B) XML Schema
    • C) OpenAPI v3 Schema
    • D) GraphQL Schema
显示答案

答案:C) OpenAPI v3 Schema

解释: 在 Kubernetes 中,用于定义 CRDs (CustomResourceDefinitions) 验证 schemas 的格式是 OpenAPI v3 Schema。此 schema 定义 custom resources 的结构和字段类型,并由 API server 用于验证资源创建和更新请求。

CRD 验证 schema 示例:

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          required: ["spec"]
          properties:
            spec:
              type: object
              required: ["cronSpec", "image"]
              properties:
                cronSpec:
                  type: string
                  pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

在此示例中,openAPIV3Schema 字段定义以下验证规则:

  • spec 字段是必需的。
  • spec 中,cronSpecimage 字段是必需的。
  • cronSpec 是字符串,并且必须遵循 cron 表达式模式。
  • image 是字符串。
  • replicas 是整数,并且必须在 1 到 10 之间。

OpenAPI v3 Schema 提供多种验证功能:

  • 指定必需字段
  • 数据类型验证(string、number、boolean、object、array 等)
  • 字符串模式验证(正则表达式)
  • 数值范围验证(minimum、maximum)
  • 数组长度验证
  • Enum 值验证
  • 嵌套 object 结构定义

其他选项的问题:

  • JSON Schema 是 OpenAPI 的基础,但 Kubernetes 明确使用 OpenAPI v3 Schema。
  • XML Schema 不用于 Kubernetes API。
  • GraphQL Schema 用于 GraphQL APIs,但不用于 Kubernetes API。
  1. 在 Kubernetes 中为 custom resources 启用 status subresource 的主要好处是什么?
    • A) 更快的资源创建
    • B) 分离 spec 和 status 更新,并进行 RBAC 控制
    • C) 自动备份和恢复功能
    • D) 简化资源版本管理
显示答案

答案:B) 分离 spec 和 status 更新,并进行 RBAC 控制

解释: 在 Kubernetes 中为 custom resources 启用 status subresource 的主要好处是分离 spec 和 status 更新,并能够通过 RBAC (Role-Based Access Control) 控制访问。

启用 status subresource 的好处:

  1. 关注点分离:

    • spec 字段定义用户指定的期望状态。
    • status 字段报告 controllers 观察到的实际状态。
    • 这种分离明确了用户和 controllers 的职责。
  2. RBAC 控制:

    • 可以只向 controllers 授予 status 更新权限,同时给普通用户只读权限。
    • 这可以防止未经授权修改 status 信息。
  3. 冲突预防:

    • 即使用户更新 spec 的同时 controller 更新 status,也不会发生冲突。
    • 这是因为这两个字段通过单独的 API 请求更新。
  4. Scale Subresource 支持:

    • 启用 status subresource 也允许启用 scale subresource。
    • 这使得可以使用 HPA (Horizontal Pod Autoscaler) 等标准 Kubernetes 扩缩容工具。

在 CRD 中启用 status subresource 的示例:

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      subresources:
        status: {}  # Enable status subresource
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                # Spec field definitions...
            status:
              type: object
              properties:
                # Status field definitions...
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab

controller 更新 status 的示例:

go
// Update status only
statusUpdate := &v1alpha1.MyResource{}
statusUpdate.Name = instance.Name
statusUpdate.Namespace = instance.Namespace
statusUpdate.Status.Phase = "Running"
statusUpdate.Status.Message = "Resource is running"

_, err = c.clientset.MyGroup().MyResources(namespace).UpdateStatus(statusUpdate)

其他选项的问题:

  • status subresource 不会提高资源创建速度。
  • 它不提供自动备份和恢复功能。
  • 资源版本管理通过 CRD 的 versions 字段处理,并不直接与 status subresource 相关。
  1. Kubernetes 中 Webhook Conversion 的主要目的是什么?
    • A) 将 API 请求路由到外部 services
    • B) 处理 custom resources 不同版本之间的转换
    • C) 将身份认证 tokens 转换为不同格式
    • D) 将日志数据转换为结构化格式
显示答案

答案:B) 处理 custom resources 不同版本之间的转换

解释: Kubernetes 中 Webhook Conversion 的主要目的是处理 custom resources 不同版本之间的转换。此功能支持 CRDs 的多个版本,并支持版本之间的平滑迁移。

webhook conversion 的关键特征:

  1. 多版本支持:

    • CRDs 可以同时支持多个 API 版本(例如 v1alpha1、v1beta1、v1)。
    • 每个版本可以有不同的 schemas 和字段。
  2. 自动转换:

    • API server 会自动处理客户端请求版本与存储版本之间的转换。
    • conversion webhook 在此过程中提供自定义转换逻辑。
  3. 存储版本独立性:

    • 即使存储版本发生变化,使用旧版本的 clients 也可以继续工作。
    • webhook 负责处理旧版本与新版本之间的双向转换。

CRD 中 conversion webhook 配置示例:

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          # v1 schema definition...
    - name: v1beta1
      served: true
      storage: false
      schema:
        openAPIV3Schema:
          # v1beta1 schema definition...
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        service:
          namespace: webhook-system
          name: crd-conversion-webhook
          path: /convert
        caBundle: <base64-encoded-ca-cert>
      conversionReviewVersions: ["v1", "v1beta1"]
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab

conversion webhook server 实现示例:

go
func (s *WebhookServer) ServeConvert(w http.ResponseWriter, r *http.Request) {
    var body []byte
    if r.Body != nil {
        if data, err := ioutil.ReadAll(r.Body); err == nil {
            body = data
        }
    }

    // Decode ConversionReview request
    convertReview := v1.ConversionReview{}
    if err := json.Unmarshal(body, &convertReview); err != nil {
        // Error handling
        return
    }

    // Perform conversion logic
    if convertReview.Request.DesiredAPIVersion == "stable.example.com/v1" {
        // v1beta1 -> v1 conversion
        for i, obj := range convertReview.Request.Objects {
            v1beta1Obj := &v1beta1.CronTab{}
            if err := json.Unmarshal(obj.Raw, v1beta1Obj); err != nil {
                // Error handling
                return
            }

            // Conversion logic
            v1Obj := &v1.CronTab{
                Spec: v1.CronTabSpec{
                    CronSpec: v1beta1Obj.Spec.Cron,  // Field name change
                    Image: v1beta1Obj.Spec.Image,
                    Replicas: v1beta1Obj.Spec.Replicas,
                },
            }

            // Encode converted object
            raw, err := json.Marshal(v1Obj)
            if err != nil {
                // Error handling
                return
            }

            convertReview.Response.ConvertedObjects = append(
                convertReview.Response.ConvertedObjects,
                runtime.RawExtension{Raw: raw},
            )
        }
    } else {
        // v1 -> v1beta1 conversion
        // Similar logic...
    }

    // Set response
    convertReview.Response.UID = convertReview.Request.UID
    convertReview.Response.Result.Status = "Success"

    // Send response
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(convertReview)
}

其他选项的问题:

  • 将 API 请求路由到外部 services 是 API aggregation 的职责。
  • 转换身份认证 tokens 是 authentication plugins 的职责。
  • 转换日志数据是 logging systems 的职责。
  1. 以下哪一项不是帮助开发 Kubernetes operators 的框架?
    • A) Operator Framework
    • B) Kubebuilder
    • C) Metacontroller
    • D) Kubespray
显示答案

答案:D) Kubespray

解释: Kubespray 不是帮助开发 Kubernetes operators 的框架。Kubespray 是一个使用 Ansible playbooks 部署和管理 Kubernetes clusters 的工具。它专注于 cluster 安装和配置,与 operator 开发无关。

实际用于 Kubernetes operator 开发的框架包括:

  1. Operator Framework:

    • Red Hat 开发的 operator 开发工具包
    • 关键组件:
      • Operator SDK:Operator 脚手架和开发工具
      • Operator Lifecycle Manager (OLM):Operator 安装和升级管理
      • Operator Metering:Operator 使用情况报告
    • 支持 Go、Ansible 和基于 Helm 的 operator 开发
  2. Kubebuilder:

    • Kubernetes SIG (Special Interest Group) 开发的框架
    • 用于以 Go 开发 controllers 的 SDK
    • 提供代码生成、CRD 管理和测试工具
    • 基于 controller-runtime 库
  3. Metacontroller:

    • 轻量级、基于 webhook 的 operator 框架
    • 可以使用多种语言实现 controller 逻辑
    • 声明式 controller 定义
    • 适合快速开发简单 controllers

各框架的功能比较:

框架主要语言复杂度功能
Operator FrameworkGo、Ansible、Helm中高综合工具包,多种开发选项
KubebuilderGo标准化模式,代码生成工具
Metacontroller语言无关基于 Webhook,简单实现

Operator 开发示例(使用 Kubebuilder):

bash
# Initialize project
kubebuilder init --domain example.com --repo github.com/example/my-operator

# Create API
kubebuilder create api --group apps --version v1alpha1 --kind MyApp

# Implement controller (edit controller.go file)

# Create CRD and deploy controller
make install
make deploy

其他选项说明:

  • Operator Framework 是 Red Hat 的综合 operator 开发工具包。
  • Kubebuilder 是 Kubernetes SIG 开发的基于 Go 的 controller 开发框架。
  • Metacontroller 是轻量级、基于 webhook 的 operator 框架。
  1. Kubernetes 中 Custom Resource Definitions (CRD) 与 Aggregated APIs 的主要区别是什么?
    • A) CRDs 不支持版本管理,但 Aggregated APIs 支持
    • B) CRDs 不支持验证 schemas,但 Aggregated APIs 支持
    • C) CRDs 实现简单但灵活性有限,而 Aggregated APIs 实现复杂但提供更多灵活性
    • D) CRDs 只支持 cluster-scoped resources,而 Aggregated APIs 只支持 namespace-scoped resources
显示答案

答案:C) CRDs 实现简单但灵活性有限,而 Aggregated APIs 实现复杂但提供更多灵活性

解释: Kubernetes 中 Custom Resource Definitions (CRD) 与 Aggregated APIs 的主要区别在于实现复杂度和灵活性之间的权衡。CRDs 实现简单但灵活性有限,而 Aggregated APIs 实现复杂但提供更多灵活性。

CRDs (CustomResourceDefinitions) 的特征:

  • 实现简单:可以通过单个 YAML 文件定义新的 API 资源。
  • 使用现有 API server:无需实现单独的 API server。
  • 灵活性有限
    • 存储限制为 etcd。
    • 继承默认 API server 的行为。
    • 难以实现复杂验证或转换逻辑。
  • 通过 webhooks 扩展:某些功能可以通过 validating webhooks、conversion webhooks 等扩展。

Aggregated APIs 的特征:

  • 实现复杂:必须开发并部署单独的 API server。
  • 高灵活性
    • 可以使用自定义存储后端
    • 可以实现复杂业务逻辑
    • 可以实现自定义身份认证和授权逻辑
    • 可以实现特殊状态计算和转换逻辑
  • 完整 API server 功能:可以利用标准 Kubernetes API server 的所有功能。

选择标准:

标准选择 CRD选择 Aggregated API
实现复杂度低 - 简单 YAML 定义高 - 需要开发单独的 API server
开发时间短 - 可以在几分钟内实现长 - 需要完整的 API server 开发
维护容易 - 由现有 API server 管理困难 - 需要维护单独服务
存储选项仅 etcd可使用自定义存储后端
业务逻辑有限 - 通过 controllers 实现灵活 - 可直接在 API server 中实现
性能通常良好可自定义优化
使用场景简单 CRUD 操作、标准模式复杂 API 行为、特殊验证/转换

示例场景:

适合 CRDs 的场景:

  • 简单应用程序配置管理
  • 基本 CRUD 操作是主要需求
  • 快速原型设计和开发

适合 Aggregated APIs 的场景:

  • 与外部数据库集成
  • 复杂数据转换和验证
  • 需要特殊身份认证机制
  • 高性能或特殊用途 APIs

其他选项的问题:

  • CRDs 支持版本管理(A 不正确)。
  • CRDs 通过 OpenAPI v3 Schema 支持验证 schemas(B 不正确)。
  • CRDs 和 Aggregated APIs 都支持 cluster-scoped 和 namespace-scoped resources(D 不正确)。

简答题

  1. 说明如何使用 CustomResourceDefinition (CRD) 定义 custom resources,以及如何为这些资源设置验证规则。
显示答案

答案:

如何定义 CustomResourceDefinition (CRD):

CRD 是一种扩展 Kubernetes API 以定义新资源类型的机制。当你创建 CRD 时,会创建一个新的 RESTful API endpoint,并且可以使用 kubectl 等标准工具管理该资源。

1. 基本 CRD 结构:

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: <plural>.<group>  # e.g., crontabs.stable.example.com
spec:
  group: <api-group>      # e.g., stable.example.com
  names:
    kind: <kind-name>     # e.g., CronTab
    plural: <plural-name> # e.g., crontabs
    singular: <singular-name>  # e.g., crontab
    shortNames:           # optional
    - <short-name>        # e.g., ct
  scope: Namespaced       # or Cluster
  versions:
    - name: <version>     # e.g., v1
      served: true        # whether to serve via API server
      storage: true       # whether this is the storage version
      schema:
        openAPIV3Schema:
          # schema definition

2. 设置验证规则:

CRDs 的验证规则通过 openAPIV3Schema 字段设置。此 schema 遵循 OpenAPI v3 格式,并定义资源的结构和字段类型。

基本验证规则示例:

yaml
schema:
  openAPIV3Schema:
    type: object
    required: ["spec"]
    properties:
      spec:
        type: object
        required: ["cronSpec", "image"]
        properties:
          cronSpec:
            type: string
            pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
          image:
            type: string
          replicas:
            type: integer
            minimum: 1
            maximum: 10
            default: 1

3. 高级验证功能:

OpenAPI v3 Schema 提供多种验证功能:

  • 必需字段:将字段名添加到 required 数组

    yaml
    required: ["fieldName1", "fieldName2"]
  • 数据类型:使用 type 字段指定数据类型

    yaml
    type: string | number | integer | boolean | array | object
  • 字符串约束:验证字符串长度和模式

    yaml
    minLength: 3
    maxLength: 64
    pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'
  • 数值约束:验证数值范围

    yaml
    minimum: 0
    maximum: 100
    multipleOf: 5
  • 数组约束:验证数组长度和元素

    yaml
    minItems: 1
    maxItems: 10
    uniqueItems: true
    items:
      type: string
  • Enum 值:指定允许的值列表

    yaml
    enum: ["value1", "value2", "value3"]
  • 默认值:为字段指定默认值

    yaml
    default: "default-value"
  • Additional properties:控制是否允许额外属性

    yaml
    additionalProperties: false

4. 完整 CRD 示例:

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          required: ["spec"]
          properties:
            spec:
              type: object
              required: ["cronSpec", "image"]
              properties:
                cronSpec:
                  type: string
                  pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                  default: 1
            status:
              type: object
              properties:
                active:
                  type: boolean
                lastScheduleTime:
                  type: string
                  format: date-time
      subresources:
        status: {}  # Enable status subresource
      additionalPrinterColumns:
        - name: Schedule
          type: string
          description: The cron schedule
          jsonPath: .spec.cronSpec
        - name: Image
          type: string
          description: The image to use
          jsonPath: .spec.image
        - name: Age
          type: date
          jsonPath: .metadata.creationTimestamp
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

5. 应用和使用 CRD:

bash
# Apply CRD
kubectl apply -f crontab-crd.yaml

# Create custom resource
cat <<EOF | kubectl apply -f -
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
  name: my-crontab
spec:
  cronSpec: "* * * * */5"
  image: my-cron-image
  replicas: 3
EOF

# View custom resources
kubectl get crontabs
kubectl get ct  # Using short name

6. 测试验证规则:

使用无效值创建资源会导致验证错误:

bash
cat <<EOF | kubectl apply -f -
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
  name: invalid-crontab
spec:
  cronSpec: "invalid-cron-spec"  # Pattern mismatch
  image: my-cron-image
  replicas: 20  # Exceeds maximum
EOF

此命令将返回类似以下的错误:

Error from server (Invalid): error when creating "STDIN": admission webhook "validate-crontab.example.com" denied the request:
- spec.cronSpec: Invalid value: "invalid-cron-spec": does not match pattern '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
- spec.replicas: Invalid value: 20: must be less than or equal to 10

7. 最佳实践:

  • 清晰且详细的 schema 定义
  • 明确指定必需字段
  • 提供适当的默认值
  • 限制字符串模式和数值范围
  • 启用 status subresource
  • 配置 additional printer columns
  • 建立版本管理策略
  1. 说明 Kubernetes Operator Pattern 的核心概念以及实现 operators 的常见方法。
显示答案

答案:

Kubernetes Operator Pattern 的核心概念:

operator pattern 是一种 Kubernetes 扩展机制,它将特定于应用程序的运维知识编码到软件中,以自动管理复杂应用程序。此模式模仿人类操作员管理复杂系统的方式。

1. 核心概念:

  • 声明式管理:用户声明期望状态,operator 将当前状态调整为期望状态。
  • 编码领域知识:将特定应用程序的运维知识和最佳实践编码化。
  • Reconciliation Loop:持续观察实际状态并调整到期望状态。
  • Custom Resources:用于存储特定于应用程序的配置和状态。
  • Controllers:Watch custom resources 的变更并执行必要任务。

2. Operators 的常见功能:

  • 安装和升级:部署应用程序组件并升级版本
  • 自动恢复:检测故障并执行恢复任务
  • 备份和还原:自动化数据备份和还原流程
  • 扩缩容:根据 workload 需求自动扩展和收缩
  • 配置管理:处理特定于应用程序的配置变更
  • 运维自动化:自动化日常运维任务(例如数据库压缩、索引重建)

实现 Operators 的方法:

1. 使用 Operator SDK:

Operator SDK 是 Red Hat Operator Framework 的一部分,是简化 operator 开发的工具。

bash
# Install Operator SDK
curl -LO https://github.com/operator-framework/operator-sdk/releases/download/v1.25.0/operator-sdk_linux_amd64
chmod +x operator-sdk_linux_amd64
sudo mv operator-sdk_linux_amd64 /usr/local/bin/operator-sdk

# Create Go-based operator project
operator-sdk init --domain example.com --repo github.com/example/my-operator

# Create API
operator-sdk create api --group apps --version v1alpha1 --kind MyApp --resource --controller

# Create CRD
make manifests

# Build and deploy operator
make docker-build docker-push
make deploy

2. 使用 Kubebuilder:

Kubebuilder 是 Kubernetes SIG 开发的框架,提供 controller 开发工具。

bash
# Install Kubebuilder
curl -L https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH) | tar -xz -C /tmp/
sudo mv /tmp/kubebuilder_*/bin/kubebuilder /usr/local/bin/

# Initialize project
kubebuilder init --domain example.com --repo github.com/example/my-operator

# Create API
kubebuilder create api --group apps --version v1alpha1 --kind MyApp

# Create CRD and deploy controller
make install
make deploy

3. Controller 实现:

operator 的核心是 reconciliation 函数。此函数观察 custom resources 的当前状态并执行必要任务。

go
// Reconcile function example
func (r *MyAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    log := r.Log.WithValues("myapp", req.NamespacedName)

    // Get custom resource
    var myApp appsv1alpha1.MyApp
    if err := r.Get(ctx, req.NamespacedName, &myApp); err != nil {
        if errors.IsNotFound(err) {
            // Resource deleted - perform cleanup
            return ctrl.Result{}, nil
        }
        // Error occurred
        return ctrl.Result{}, err
    }

    // 1. Check if required resources exist
    deployment := &appsv1.Deployment{}
    err := r.Get(ctx, types.NamespacedName{Name: myApp.Name, Namespace: myApp.Namespace}, deployment)
    if errors.IsNotFound(err) {
        // Create deployment if it doesn't exist
        deployment = r.deploymentForMyApp(&myApp)
        log.Info("Creating a new Deployment", "Deployment.Namespace", deployment.Namespace, "Deployment.Name", deployment.Name)
        if err := r.Create(ctx, deployment); err != nil {
            log.Error(err, "Failed to create new Deployment")
            return ctrl.Result{}, err
        }
        // Deployment creation successful
        return ctrl.Result{Requeue: true}, nil
    } else if err != nil {
        log.Error(err, "Failed to get Deployment")
        return ctrl.Result{}, err
    }

    // 2. Check if deployment is in desired state
    size := myApp.Spec.Size
    if *deployment.Spec.Replicas != size {
        deployment.Spec.Replicas = &size
        if err := r.Update(ctx, deployment); err != nil {
            log.Error(err, "Failed to update Deployment")
            return ctrl.Result{}, err
        }
        // Deployment update successful
        return ctrl.Result{Requeue: true}, nil
    }

    // 3. Update status
    if myApp.Status.AvailableReplicas != deployment.Status.AvailableReplicas {
        myApp.Status.AvailableReplicas = deployment.Status.AvailableReplicas
        if err := r.Status().Update(ctx, &myApp); err != nil {
            log.Error(err, "Failed to update MyApp status")
            return ctrl.Result{}, err
        }
    }

    return ctrl.Result{}, nil
}

// Deployment creation function
func (r *MyAppReconciler) deploymentForMyApp(m *appsv1alpha1.MyApp) *appsv1.Deployment {
    ls := labelsForMyApp(m.Name)
    replicas := m.Spec.Size

    dep := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name:      m.Name,
            Namespace: m.Namespace,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: &replicas,
            Selector: &metav1.LabelSelector{
                MatchLabels: ls,
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{
                    Labels: ls,
                },
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{{
                        Image: m.Spec.Image,
                        Name:  "myapp",
                        Ports: []corev1.ContainerPort{{
                            ContainerPort: 8080,
                            Name:          "http",
                        }},
                    }},
                },
            },
        },
    }

    // Set owner reference
    ctrl.SetControllerReference(m, dep, r.Scheme)
    return dep
}

4. Custom Resource Definition:

定义由 operator 管理的 custom resources 的 API。

go
// MyApp API
type MyAppSpec struct {
    // Application image
    Image string `json:"image"`

    // Number of replicas
    Size int32 `json:"size"`

    // Configuration options
    Config map[string]string `json:"config,omitempty"`
}

type MyAppStatus struct {
    // Number of available replicas
    AvailableReplicas int32 `json:"availableReplicas"`

    // Last update time
    LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`

    // Status message
    Message string `json:"message,omitempty"`
}

// MyApp resource
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Replicas",type=integer,JSONPath=`.spec.size`
// +kubebuilder:printcolumn:name="Available",type=integer,JSONPath=`.status.availableReplicas`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
type MyApp struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   MyAppSpec   `json:"spec,omitempty"`
    Status MyAppStatus `json:"status,omitempty"`
}

5. 基于 Ansible 或 Helm 的 Operators:

Operator SDK 除了 Go 之外,也支持使用 Ansible 或 Helm 开发 operators。

基于 Ansible 的 operator:

bash
# Create Ansible-based operator
operator-sdk init --plugins=ansible --domain example.com
operator-sdk create api --group apps --version v1alpha1 --kind MyApp --generate-role

# Edit roles/myapp/tasks/main.yml

基于 Helm 的 operator:

bash
# Create Helm-based operator
operator-sdk init --plugins=helm --domain example.com --helm-chart=<chart-name>

6. 部署和测试 Operators:

bash
# Deploy operator
make deploy

# Create custom resource
kubectl apply -f config/samples/

# Check operator logs
kubectl logs -f deployment/my-operator-controller-manager -n my-operator-system

# Check resource status
kubectl get myapps
kubectl describe myapp my-app

7. Operator 成熟度级别:

operator 成熟度模型定义 operators 的能力级别:

  1. Basic Install:应用程序安装和配置
  2. Seamless Upgrades:版本之间的自动升级
  3. Full Lifecycle:备份、还原、故障恢复等
  4. Deep Insights:自动扩缩容、调优等
  5. Auto Pilot:基于监控的自动优化

8. 最佳实践:

  • 增量实现功能(从简单功能开始)
  • 彻底的错误处理和日志记录
  • 确保幂等性(相同输入得到相同结果)
  • 设置 owner references(资源层次结构和垃圾回收)
  • 通过 status updates 报告进度
  • 编写 unit tests 和 integration tests
  • 清晰的文档
  1. 说明 Kubernetes 中 Admission Webhooks 的类型以及各自的使用场景。
显示答案

答案:

Kubernetes Admission Webhooks 的类型和使用场景:

Admission webhooks 是一种机制,可以在 Kubernetes API server 将请求存储到持久化存储(etcd)之前拦截并修改或验证请求。Admission webhooks 大体分为两类:Mutating webhooks 和 Validating webhooks。

1. Mutating Webhook:

Mutating webhooks 可以修改进入 API server 的请求对象。这些 webhooks 在 validating webhooks 之前运行。

关键特征:

  • 可以修改请求对象
  • 多个 mutating webhooks 以链式方式运行
  • 每个 webhook 接收前一个 webhook 修改后的对象

配置示例:

yaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: sidecar-injector
webhooks:
- name: sidecar-injector.example.com
  clientConfig:
    service:
      namespace: webhook-system
      name: sidecar-injector
      path: "/inject"
    caBundle: <base64-encoded-ca-cert>
  rules:
  - apiGroups: [""]
    apiVersions: ["v1"]
    operations: ["CREATE"]
    resources: ["pods"]
    scope: "Namespaced"
  admissionReviewVersions: ["v1", "v1beta1"]
  sideEffects: None
  timeoutSeconds: 5

常见使用场景:

  1. Sidecar Container 注入:

    • 在 Istio 和 Linkerd 等 service meshes 中自动注入 proxy sidecar
    • 添加 logging 和 monitoring sidecars
    • 示例:Istio 的 sidecar injector 会在 pods 创建时自动添加 Envoy proxy containers
  2. 设置默认值:

    • 自动设置 resource requests 和 limits
    • 应用 security context 默认值
    • 自动添加 labels 和 annotations
    • 示例:为所有 pods 设置默认 CPU 和 memory requests
  3. 应用 Image Policies:

    • 修改 image registry URLs
    • 将 image tags 转换为 digests
    • 示例:将 nginx:latest 改为 internal-registry.example.com/nginx:v1.19.0
  4. Volume 修改:

    • 添加默认 volume mounts
    • 自动挂载 ConfigMap 或 Secret
    • 示例:为所有 pods 自动挂载 service account token volume
  5. 应用 Network Policies:

    • 添加默认 network settings
    • 修改 DNS 配置
    • 示例:对特定 namespace 中的所有 pods 应用特定 DNS 设置

2. Validating Webhook:

Validating webhooks 可以验证进入 API server 的请求并允许或拒绝它们。这些 webhooks 在 mutating webhooks 之后运行。

关键特征:

  • 不能修改请求对象
  • 只能允许或拒绝请求
  • 多个 validating webhooks 并行运行
  • 所有 webhooks 都必须允许请求,请求才会被处理

配置示例:

yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: pod-policy-validator
webhooks:
- name: pod-policy.example.com
  clientConfig:
    service:
      namespace: webhook-system
      name: pod-policy-validator
      path: "/validate"
    caBundle: <base64-encoded-ca-cert>
  rules:
  - apiGroups: [""]
    apiVersions: ["v1"]
    operations: ["CREATE", "UPDATE"]
    resources: ["pods"]
    scope: "Namespaced"
  admissionReviewVersions: ["v1", "v1beta1"]
  sideEffects: None
  timeoutSeconds: 5

常见使用场景:

  1. 强制执行安全策略:

    • 禁止 privileged containers
    • 禁止 root user 执行
    • 限制 host network/PID/IPC 使用
    • 示例:拒绝以 privileged mode 运行的 pods
  2. 验证资源约束:

    • 强制要求 resource requests 和 limits
    • 应用 resource caps
    • 强制执行 QoS classes
    • 示例:拒绝没有 memory limits 的 pods
  3. 验证 Image Policies:

    • 仅允许 approved registries
    • 禁止使用 latest tag
    • 验证 vulnerability scan 结果
    • 示例:只允许来自 official registries 的 images
  4. 验证 Labels 和 Annotations:

    • 检查 required labels
    • 验证 label formats
    • 示例:要求所有 pods 必须包含 appenvironment labels
  5. 基于 Namespace 的策略:

    • 每个 namespace 的 resource limits
    • 每个 namespace 的 feature restrictions
    • 示例:在 production namespaces 中应用更严格的策略

3. Webhook 实现方法:

Admission webhooks 实现为提供 HTTPS endpoints 的 services。这些 services 接收 AdmissionReview 请求,处理它们,并返回 AdmissionResponse

基本 Webhook Server 实现示例 (Go):

go
package main

import (
    "encoding/json"
    "io/ioutil"
    "net/http"

    admissionv1 "k8s.io/api/admission/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/runtime/serializer"
)

var (
    runtimeScheme = runtime.NewScheme()
    codecs        = serializer.NewCodecFactory(runtimeScheme)
    deserializer  = codecs.UniversalDeserializer()
)

// Mutating webhook handler
func mutateHandler(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Failed to read request body", http.StatusBadRequest)
        return
    }

    // Decode AdmissionReview request
    admissionReview := admissionv1.AdmissionReview{}
    if _, _, err := deserializer.Decode(body, nil, &admissionReview); err != nil {
        http.Error(w, "Failed to decode request", http.StatusBadRequest)
        return
    }

    // Decode pod object
    pod := corev1.Pod{}
    if err := json.Unmarshal(admissionReview.Request.Object.Raw, &pod); err != nil {
        http.Error(w, "Failed to decode pod", http.StatusBadRequest)
        return
    }

    // Create patch (add sidecar container)
    patch := []map[string]interface{}{
        {
            "op": "add",
            "path": "/spec/containers/-",
            "value": map[string]interface{}{
                "name": "sidecar",
                "image": "sidecar-image:latest",
                "resources": map[string]interface{}{
                    "limits": map[string]interface{}{
                        "cpu": "100m",
                        "memory": "100Mi",
                    },
                    "requests": map[string]interface{}{
                        "cpu": "50m",
                        "memory": "50Mi",
                    },
                },
            },
        },
    }

    // Convert patch to JSON
    patchBytes, err := json.Marshal(patch)
    if err != nil {
        http.Error(w, "Failed to marshal patch", http.StatusInternalServerError)
        return
    }

    // Create response
    admissionResponse := admissionv1.AdmissionResponse{
        UID:     admissionReview.Request.UID,
        Allowed: true,
        Patch:   patchBytes,
        PatchType: func() *admissionv1.PatchType {
            pt := admissionv1.PatchTypeJSONPatch
            return &pt
        }(),
    }

    // Send response
    admissionReview.Response = &admissionResponse
    resp, err := json.Marshal(admissionReview)
    if err != nil {
        http.Error(w, "Failed to marshal response", http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(resp)
}

// Validating webhook handler
func validateHandler(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Failed to read request body", http.StatusBadRequest)
        return
    }

    // Decode AdmissionReview request
    admissionReview := admissionv1.AdmissionReview{}
    if _, _, err := deserializer.Decode(body, nil, &admissionReview); err != nil {
        http.Error(w, "Failed to decode request", http.StatusBadRequest)
        return
    }

    // Decode pod object
    pod := corev1.Pod{}
    if err := json.Unmarshal(admissionReview.Request.Object.Raw, &pod); err != nil {
        http.Error(w, "Failed to decode pod", http.StatusBadRequest)
        return
    }

    // Validation logic
    allowed := true
    var message string

    // Check for privileged containers
    for _, container := range pod.Spec.Containers {
        if container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
            allowed = false
            message = "Privileged containers are not allowed"
            break
        }
    }

    // Create response
    admissionResponse := admissionv1.AdmissionResponse{
        UID:     admissionReview.Request.UID,
        Allowed: allowed,
    }

    if !allowed {
        admissionResponse.Result = &metav1.Status{
            Message: message,
            Status:  "Failure",
            Reason:  metav1.StatusReasonForbidden,
            Code:    403,
        }
    }

    // Send response
    admissionReview.Response = &admissionResponse
    resp, err := json.Marshal(admissionReview)
    if err != nil {
        http.Error(w, "Failed to marshal response", http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(resp)
}

func main() {
    http.HandleFunc("/mutate", mutateHandler)
    http.HandleFunc("/validate", validateHandler)

    fmt.Println("Starting webhook server on :8443")
    http.ListenAndServeTLS(":8443", "tls.crt", "tls.key", nil)
}

4. Webhook 部署和配置:

Webhook servers 通常部署在 Kubernetes cluster 内,并且需要 service 和 TLS certificates。

yaml
# Webhook server deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webhook-server
  namespace: webhook-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webhook-server
  template:
    metadata:
      labels:
        app: webhook-server
    spec:
      containers:
      - name: server
        image: webhook-server:latest
        ports:
        - containerPort: 8443
        volumeMounts:
        - name: tls
          mountPath: "/etc/webhook/certs"
          readOnly: true
      volumes:
      - name: tls
        secret:
          secretName: webhook-server-tls

---
# Webhook server service
apiVersion: v1
kind: Service
metadata:
  name: webhook-server
  namespace: webhook-system
spec:
  selector:
    app: webhook-server
  ports:
  - port: 443
    targetPort: 8443

5. 最佳实践:

  • 性能优化:Webhooks 位于 API server 请求路径中,因此必须快速响应。
  • 错误处理:考虑 webhook server 失败时的行为,并适当设置 failurePolicy
  • 范围限制:仅将 webhooks 应用于必要的资源和操作。
  • 测试:在各种场景中彻底测试 webhook 行为。
  • 监控:监控 webhook server 性能和错误。
  • 版本管理:支持多个 AdmissionReview 版本,为 API 版本变化做准备。

实践题

  1. 编写一个满足以下要求的 CustomResourceDefinition (CRD):
    • API Group: webapp.example.com
    • Version: v1
    • Kind: WebApp
    • Scope: Namespaced
    • Required fields: spec.image, spec.replicas
    • Validation rules: replicas must be an integer between 1 and 10
    • Enable status subresource
显示答案

答案:

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: webapps.webapp.example.com
spec:
  group: webapp.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          required: ["spec"]
          properties:
            spec:
              type: object
              required: ["image", "replicas"]
              properties:
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                port:
                  type: integer
                  default: 80
                env:
                  type: array
                  items:
                    type: object
                    required: ["name"]
                    properties:
                      name:
                        type: string
                      value:
                        type: string
            status:
              type: object
              properties:
                availableReplicas:
                  type: integer
                phase:
                  type: string
      subresources:
        status: {}
      additionalPrinterColumns:
      - name: Replicas
        type: integer
        jsonPath: .spec.replicas
      - name: Image
        type: string
        jsonPath: .spec.image
      - name: Age
        type: date
        jsonPath: .metadata.creationTimestamp
  scope: Namespaced
  names:
    plural: webapps
    singular: webapp
    kind: WebApp
    shortNames:
    - wa

此 CRD 具有以下特征:

  1. 基本信息

    • API Group: webapp.example.com
    • Version: v1
    • Kind: WebApp
    • Scope: Namespaced
  2. Schema 验证

    • spec 字段是必需的。
    • spec.imagespec.replicas 是必需字段。
    • spec.replicas 必须是 1 到 10 之间的整数。
    • spec.port 是可选字段,默认值为 80。
    • spec.env 是可选字段,定义环境变量数组。
  3. Status Subresource

    • 启用 status subresource,以便 controllers 可以更新 status。
    • 定义了 status.availableReplicasstatus.phase 字段。
  4. Additional Printer Columns

    • 运行 kubectl get webapps 时,会显示 Replicas、Image 和 Age 列。
  5. 名称配置

    • Plural: webapps
    • Singular: webapp
    • Kind: WebApp
    • Short name: wa

可以使用此 CRD 创建如下 custom resources:

yaml
apiVersion: webapp.example.com/v1
kind: WebApp
metadata:
  name: my-webapp
  namespace: default
spec:
  image: nginx:1.19
  replicas: 3
  port: 8080
  env:
    - name: ENV_VAR1
      value: "value1"
    - name: ENV_VAR2
      value: "value2"

Controllers 可以 watch 此资源并更新 status:

yaml
status:
  availableReplicas: 3
  phase: Running
  1. 编写一个满足以下要求的 Mutating Admission Webhook 配置:
    • Add sidecar container to all pod creation requests
    • Apply only to a specific namespace (monitoring)
    • Webhook service: webhook-service.webhook-system.svc
    • Path: /mutate
显示答案

答案:

yaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: sidecar-injector
webhooks:
- name: sidecar-injector.example.com
  clientConfig:
    service:
      namespace: webhook-system
      name: webhook-service
      path: "/mutate"
    caBundle: ${CA_BUNDLE}  # Replace with base64-encoded CA certificate in actual environment
  rules:
  - apiGroups: [""]
    apiVersions: ["v1"]
    operations: ["CREATE"]
    resources: ["pods"]
    scope: "Namespaced"
  namespaceSelector:
    matchLabels:
      monitoring-injection: enabled
  admissionReviewVersions: ["v1", "v1beta1"]
  sideEffects: None
  timeoutSeconds: 5
  failurePolicy: Fail
---
# Add label to namespace
apiVersion: v1
kind: Namespace
metadata:
  name: monitoring
  labels:
    monitoring-injection: enabled

此配置具有以下特征:

  1. Webhook 配置

    • Name: sidecar-injector
    • Webhook service: webhook-service.webhook-system.svc
    • Path: /mutate
  2. 范围

    • API Group: ""(core API group)
    • API Version: v1
    • Operation: CREATE(仅在 pod 创建时应用)
    • Resource: pods
    • Scope: Namespaced
  3. Namespace Selector

    • 仅应用于带有 monitoring-injection: enabled label 的 namespaces
    • 将此 label 添加到 monitoring namespace
  4. 其他设置

    • admissionReviewVersions:支持的 AdmissionReview API 版本
    • sideEffects:webhook 无副作用
    • timeoutSeconds:webhook 响应等待时间
    • failurePolicy:webhook 失败时拒绝请求

webhook server 应实现如下逻辑:

go
func mutateHandler(w http.ResponseWriter, r *http.Request) {
    // Decode request
    body, _ := ioutil.ReadAll(r.Body)
    admissionReview := admissionv1.AdmissionReview{}
    deserializer.Decode(body, nil, &admissionReview)

    // Decode pod object
    pod := corev1.Pod{}
    json.Unmarshal(admissionReview.Request.Object.Raw, &pod)

    // Define sidecar container
    sidecarContainer := corev1.Container{
        Name:  "monitoring-sidecar",
        Image: "monitoring-agent:latest",
        Resources: corev1.ResourceRequirements{
            Limits: corev1.ResourceList{
                corev1.ResourceCPU:    resource.MustParse("100m"),
                corev1.ResourceMemory: resource.MustParse("100Mi"),
            },
            Requests: corev1.ResourceList{
                corev1.ResourceCPU:    resource.MustParse("50m"),
                corev1.ResourceMemory: resource.MustParse("50Mi"),
            },
        },
        VolumeMounts: []corev1.VolumeMount{
            {
                Name:      "shared-data",
                MountPath: "/var/monitoring",
            },
        },
    }

    // Create patch
    patch := []map[string]interface{}{
        {
            "op":    "add",
            "path":  "/spec/containers/-",
            "value": sidecarContainer,
        },
        {
            "op":   "add",
            "path": "/spec/volumes/-",
            "value": map[string]interface{}{
                "name": "shared-data",
                "emptyDir": map[string]interface{}{},
            },
        },
    }

    // Convert patch to JSON
    patchBytes, _ := json.Marshal(patch)

    // Create response
    admissionResponse := admissionv1.AdmissionResponse{
        UID:       admissionReview.Request.UID,
        Allowed:   true,
        Patch:     patchBytes,
        PatchType: func() *admissionv1.PatchType {
            pt := admissionv1.PatchTypeJSONPatch
            return &pt
        }(),
    }

    // Send response
    admissionReview.Response = &admissionResponse
    resp, _ := json.Marshal(admissionReview)
    w.Header().Set("Content-Type", "application/json")
    w.Write(resp)
}

此 webhook 会自动向在 monitoring namespace 中创建的所有 pods 添加一个 monitoring sidecar container。该 sidecar container 使用 monitoring agent image,并挂载共享 volume 以与 main container 共享数据。