在 Kubernetes 中,Custom Resource Definitions (CRDs) 是一种用于扩展 Kubernetes API 的机制。CRDs 允许你创建自定义资源,这些资源可以像内置的 Kubernetes 资源一样进行管理和操作。通过 CRDs,你可以将特定领域的应用程序或服务管理引入 Kubernetes 集群中。

 

CRD 的基本概念

创建和使用 CRD 的步骤

 

以下是创建和使用 CRD 的详细步骤:

1. 定义 CRD

首先,需要定义 CRD。这是一个 YAML 文件,描述了新的自定义资源类型。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: memcacheds.cache.example.com
spec:
  group: cache.example.com
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                size:
                  type: integer
  scope: Namespaced
  names:
    plural: memcacheds
    singular: memcached
    kind: Memcached
    shortNames:
    - mc

 在这个示例中,我们定义了一种名为 Memcached 的自定义资源,其 spec 部分包含一个整数类型的 size 属性。

 

2. 应用 CRD

使用 kubectl apply 命令将 CRD 应用到 Kubernetes 集群中:

kubectl apply -f memcached-crd.yaml

 

3. 创建自定义资源实例

一旦 CRD 被应用,你就可以创建对应的自定义资源实例了。

apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
  name: memcached-sample
spec:
  size: 3

 

应用自定义资源实例:

kubectl apply -f memcached-sample.yaml

 

4. 操作自定义资源

你可以像操作其他 Kubernetes 资源一样操作自定义资源。例如,查看自定义资源实例:

kubectl get memcacheds
kubectl describe memcached memcached-sample

 

CRD 的高级用法

1. 验证

你可以在 CRD 中定义 OpenAPI v3 模式,以验证自定义资源的字段。例如:

schema:
  openAPIV3Schema:
    type: object
    properties:
      spec:
        type: object
        properties:
          size:
            type: integer
            minimum: 1

 

2. 子资源

CRDs 支持 status 和 scale 子资源,用于更好地集成 Kubernetes API 功能。

subresources:
  status: {}
  scale:
    specReplicasPath: .spec.size
    statusReplicasPath: .status.replicas
    labelSelectorPath: .status.labelSelector

 

3. 自定义控制器和 Operators

通常,CRD 与自定义控制器或 Operators 一起使用,来实现自定义资源的自动管理。控制器可以监视自定义资源,并基于其状态执行相应的操作。

 

示例:完整的 CRD 和控制器

以下是一个完整的 CRD 和控制器的示例,管理一个简单的 Memcached 部署:

CRD 定义

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: memcacheds.cache.example.com
spec:
  group: cache.example.com
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                size:
                  type: integer
  scope: Namespaced
  names:
    plural: memcacheds
    singular: memcached
    kind: Memcached
    shortNames:
    - mc
  subresources:
    status: {}

 

控制器逻辑(简化版)

控制器通常用 Go 语言编写,并使用 Kubernetes 客户端库。以下是一个简单的控制器示例:

package main
import (
    "context"
    "reflect"
    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    "k8s.io/apimachinery/pkg/api/errors"
    "k8s.io/apimachinery/pkg/types"
    "sigs.k8s.io/controller-runtime/pkg/client"
    "sigs.k8s.io/controller-runtime/pkg/controller"
    "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    "sigs.k8s.io/controller-runtime/pkg/manager"
    "sigs.k8s.io/controller-runtime/pkg/reconcile"
    cachev1alpha1 "github.com/example/memcached-operator/api/v1alpha1"
)
type ReconcileMemcached struct {
    client.Client
    Scheme *runtime.Scheme
}
func (r *ReconcileMemcached) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
    // Fetch the Memcached instance
    memcached := &cachev1alpha1.Memcached{}
    err := r.Get(ctx, req.NamespacedName, memcached)
    if err != nil {
        if errors.IsNotFound(err) {
            // Object not found, return.  Created objects are automatically garbage collected.
            return reconcile.Result{}, nil
        }
        return reconcile.Result{}, err
    }
    // Define the desired Deployment
    dep := r.deploymentForMemcached(memcached)
    if err := controllerutil.SetControllerReference(memcached, dep, r.Scheme); err != nil {
        return reconcile.Result{}, err
    }
    // Check if the Deployment already exists
    found := &appsv1.Deployment{}
    err = r.Get(ctx, types.NamespacedName{Name: dep.Name, Namespace: dep.Namespace}, found)
    if err != nil && errors.IsNotFound(err) {
        err = r.Create(ctx, dep)
        if err != nil {
            return reconcile.Result{}, err
        }
        return reconcile.Result{Requeue: true}, nil
    } else if err != nil {
        return reconcile.Result{}, err
    }
    // Ensure the deployment size is the same as the spec
    size := memcached.Spec.Size
    if *found.Spec.Replicas != size {
        found.Spec.Replicas = &size
        err = r.Update(ctx, found)
        if err != nil {
            return reconcile.Result{}, err
        }
    }
    // Update the Memcached status with the pod names
    podList := &corev1.PodList{}
    listOpts := []client.ListOption{
        client.InNamespace(memcached.Namespace),
        client.MatchingLabels(labelsForMemcached(memcached.Name)),
    }
    if err = r.List(ctx, podList, listOpts...); err != nil {
        return reconcile.Result{}, err
    }
    podNames := getPodNames(podList.Items)
    if !reflect.DeepEqual(podNames, memcached.Status.Nodes) {
        memcached.Status.Nodes = podNames
        err = r.Status().Update(ctx, memcached)
        if err != nil {
            return reconcile.Result{}, err
        }
    }
    return reconcile.Result{}, nil
}
func main() {
    // Set up a Manager
    mgr, err := manager.New(cfg, manager.Options{})
    if err != nil {
        log.Fatal(err)
    }
    // Setup the Controller
    c, err := controller.New("memcached-controller", mgr, controller.Options{
        Reconciler: &ReconcileMemcached{
            Client: mgr.GetClient(),
            Scheme: mgr.GetScheme(),
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    // Watch for changes to Memcached
    err = c.Watch(&source.Kind{Type: &cachev1alpha1.Memcached{}}, &handler.EnqueueRequestForObject{})
    if err != nil {
        log.Fatal(err)
    }
    // Start the Manager
    if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
        log.Fatal(err)
    }
}

 

总结

CRD 是 Kubernetes 中强大的扩展机制,允许你创建和管理自定义资源,并与自定义控制器结合使用,实现复杂应用的自动化运维。通过 CRD 和 Operators,你可以将特定领域的运维逻辑引入 Kubernetes,提升系统的可管理性和自动化水平。