K8S使用CRD和控制器简单应用 1. 定义 CRD声明“要加什么标签”首先我们定义一个名为PodLabeler的自定义资源。用户提交这个资源后控制器就会按照里面的规则去给 Pod 打标签。yaml# podlabeler-crd.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: podlabelers.custom.k8s.io spec: group: custom.k8s.io versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: labelKey: type: string description: 要添加的标签键 (Key) labelValue: type: string description: 要添加的标签值 (Value) selector: type: object description: 匹配 Pod 的标签选择器 properties: matchLabels: type: object additionalProperties: type: string scope: Namespaced names: plural: podlabelers singular: podlabeler kind: PodLabeler shortNames: - pl2. 编写自定义控制器Go 代码这是控制器的核心逻辑。它会监听PodLabeler的创建然后去查找匹配的 Pod并调用 Kubernetes API 给它们打标签。go// controllers/podlabeler_controller.go package controllers import ( context fmt corev1 k8s.io/api/core/v1 k8s.io/apimachinery/pkg/api/errors metav1 k8s.io/apimachinery/pkg/apis/meta/v1 k8s.io/apimachinery/pkg/labels k8s.io/apimachinery/pkg/types ctrl sigs.k8s.io/controller-runtime sigs.k8s.io/controller-runtime/pkg/client sigs.k8s.io/controller-runtime/pkg/log ) // PodLabelerReconciler 调和器 type PodLabelerReconciler struct { client.Client } // Reconcile 核心业务逻辑 func (r *PodLabelerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log : log.FromContext(ctx) // 1. 获取用户创建的 PodLabeler 资源 var labeler PodLabeler // 假设这是由 Kubebuilder 生成的 CRD 结构体 if err : r.Get(ctx, req.NamespacedName, labeler); err ! nil { if errors.IsNotFound(err) { return ctrl.Result{}, nil // 资源被删除忽略 } return ctrl.Result{}, err } // 2. 根据 CRD 中的 selector 查找匹配的 Pod selector : labels.SelectorFromSet(labeler.Spec.Selector.MatchLabels) var podList corev1.PodList if err : r.List(ctx, podList, client.ListOptions{ LabelSelector: selector, Namespace: req.Namespace, }); err ! nil { return ctrl.Result{}, err } // 3. 遍历找到的 Pod并给它们打上标签 for _, pod : range podList.Items { // 如果 Pod 已经有这个标签且值相同则跳过 if pod.Labels[labeler.Spec.LabelKey] labeler.Spec.LabelValue { continue } // 构造 Patch 请求更新标签 patch : client.MergeFrom(pod.DeepCopy()) if pod.Labels nil { pod.Labels make(map[string]string) } pod.Labels[labeler.Spec.LabelKey] labeler.Spec.LabelValue if err : r.Patch(ctx, pod, patch); err ! nil { log.Error(err, fmt.Sprintf(Failed to patch pod %s, pod.Name)) return ctrl.Result{}, err } log.Info(fmt.Sprintf(Successfully labeled pod %s with %s%s, pod.Name, labeler.Spec.LabelKey, labeler.Spec.LabelValue)) } return ctrl.Result{}, nil } // SetupWithManager 注册控制器 func (r *PodLabelerReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(PodLabeler{}). // 监听 PodLabeler 资源的变化 Owns(corev1.Pod{}). // 可选如果只想监听特定 Pod Complete(r) }3. 如何触发控制器工作将上述 CRD 部署到集群后你只需要提交一个普通的 YAML 文件控制器就会自动干活yaml# my-label-rule.yaml apiVersion: custom.k8s.io/v1 kind: PodLabeler metadata: name: add-env-tag namespace: default spec: labelKey: environment labelValue: production selector: matchLabels: app: my-web-app # 控制器只会给带有 appmy-web-app 的 Pod 打标签执行kubectl apply -f my-label-rule.yaml后控制器会立刻找到default命名空间下所有appmy-web-app的 Pod并给它们加上environment: production标签。