Kubernetes Operator权威指南 (1)编写你的第一个 Kubernetes Operator

1,412 阅读6分钟

是否考虑过设计和打造自己的 Kubernetes Operator?欢迎来到 Kubernetes Operators 复杂而强大的世界!

在这份综合指南中,我们将首先了解controller、operators、CRD的基础知识以及围绕operators的最佳实践。我们还将创建一个名为ConfigmapSync的自定义运算符来在命名空间之间同步 Configmap。

完成后,您将能够使用 Kubebuilder 编写和部署您自己的自定义运算符。

什么是 Kubernetes Operator?

在最简单的技术形式中,operators向 Kubernetes API 添加一个端点(称为自定义资源(CR)),以及用于监视和维护新型资源的控制平面组件(controllers) 。换句话说,Operator 是使用自定义资源来管理应用程序及其组件的软件扩展。

operators有 3 个组件——controllers、自定义资源 (CR) 和状态。controllers只是关于应该管理的事物的一些逻辑,通常可视化为观察和调整循环。观察当前状态,将其与所需状态进行比较并调整状态。状态仅保存资源所需状态的信息,而资源是您正在管理的事物。

Operator生命周期

operators的角色是使用控制循环来协调应用程序的实际状态与 CRD 的期望状态,在控制循环中operators可以自动扩展、更新或重新启动应用程序。

CRD 用于通过引入不属于核心 Kubernetes API 的新型资源来扩展 Kubernetes。通过定义 CRD,用户或operators可以创建自己的自定义资源并定义如何管理这些资源。

为什么我们需要 Kubernetes Operator?

Operator 允许开发人员定义自定义资源和关联的controllers来管理这些资源。这可以实现复杂任务的自动化,例如管理有状态应用程序、推出自定义更新以及根据需求自动扩展服务。

  • 使用Opetrator,开发人员可以创建自定义抽象,封装他们如何管理特定应用程序或基础设施组件的知识。
  • 这可以提高管理复杂应用程序和基础设施的生产力和一致性,以及更轻松的自动化和更好的资源控制。

Operators的优势在于扩展了Kubernetes的自动化可能性。广泛的 K8s Operator 生态系统的存在使得找到适合大多数用途的现成解决方案成为可能。

Kubernetes 运算符的一些现实示例

借助Istio Operator,我们可以自动安装、更新 Istio 并解决冲突问题。该 Operator 的先决条件很少(实际上只有 istioctl),然后它允许安装和验证所有 API。

Prometheus Operator for Kubernetes 为 Kubernetes 服务以及 Prometheus 实例的部署和管理提供了简单的监控定义。

Elastic Kubernetes Operator(由 Elastic 项目正式实施)实现了 Kubernetes 上 Elastic Search 和 Kibana 的自动化,大大简化了部署配置并易于管理。

Kubebuilder 简介:一个使用 CRD 构建 Kubernetes API 的 SDK。

Kubebuilder 是一个使用controllers运行时库构建 Kubernetes API 的框架。它提供了用于生成代码、构建映像以及将controllers部署为 Kubernetes 资源的工具。

要使用 Kubebuilder 创建自定义运算符,您需要执行以下步骤。这将引导 Kubebuilder 并生成添加自定义controllers所需的代码。

  1. 安装 Kubebuilder:您可以按照此处的说明进行操作:book.kubebuilder.io/quick-start…

  2. 创建新项目:运行以下命令以使用示例 API 和controllers创建新项目:

    $ mkdir -p $GOPATH/src/github.com/example/  
    $ cd $GOPATH/src/github.com/example/  
    $ kubebuilder init --domain example.com --repo=github.com/example/my-operator  
    $ kubebuilder create api --group=mygroup --version=v1alpha1 --kind=MyKind
    

3. 这将为您的operators生成基本的框架,包括示例 API 和controllers。

  • 定义您的自定义资源:打开api/v1alpha1/mykind_types.go文件并修改MyKindSpecMyKindStatus结构以定义自定义资源所需的字段和状态。
  • 生成 CRD:运行以下命令为您的自定义资源生成 CRD:
   $ make manifests

这将生成config/crd/bases/mygroup.example.com_mykinds.yaml文件,其中包含自定义资源的 CRD。

4. 现在编写controllers逻辑:打开controllers/mykind_controller.go文件并编写controllers逻辑。这通常涉及监视自定义资源的更改、协调所需状态与实际状态,以及根据需要更新资源的状态。

5. 构建和部署您的Opetrator:运行以下命令来构建和部署您的Opetrator:

$ make docker-build docker-push IMG=example/my-operator:latest 
$ make deploy IMG=example/my-operator:latest

这将为您的operators构建一个 Docker 映像,将其推送到注册表,并将其部署到您的 Kubernetes 集群。

演示:创建自定义Opetrator:ConfigmapSync 简介

有了我们可以使用的工具和资源,让我们利用这个机制来创建一个名为ConfigmapSync的专门Opetrator。该运算符旨在将配置映射从一个命名空间无缝同步到集群内的其他命名空间。

当然!下面是一个使用 Kubebuilder 构建的 Kubernetes Operator 示例,用于将 ConfigMap 从一个命名空间同步到另一个命名空间。这是说明该概念的基本轮廓:

首先,使用 Kubebuilder 创建一个新的 API:

  1. 创建 API:
    使用 Kubebuilder 创建一个名为“ConfigMapSync”的新 API。
kubebuilder create api --group apps --version v1 --kind ConfigMapSync

2. 定义规范和状态:
更新 API 的“types.go”以包含源命名空间和目标命名空间的字段。该types.go文件定义ConfigMapSync自定义资源及其规范和状态。

   package v1  
  
import (  
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"  
)  
// ConfigMapSyncSpec defines the desired state of ConfigMapSync  
type ConfigMapSyncSpec struct {  
SourceNamespace string `json:"sourceNamespace"`  
DestinationNamespace string `json:"destinationNamespace"`  
ConfigMapName string `json:"configMapName"`  
}  
// ConfigMapSyncStatus defines the observed state of ConfigMapSync  
type ConfigMapSyncStatus struct {  
LastSyncTime metav1.Time `json:"lastSyncTime"`  
}  
// +kubebuilder:object:root=true  
// +kubebuilder:subresource:status  
// ConfigMapSync is the Schema for the configmapsyncs API  
type ConfigMapSync struct {  
metav1.TypeMeta `json:",inline"`  
metav1.ObjectMeta `json:"metadata,omitempty"`  
Spec ConfigMapSyncSpec `json:"spec,omitempty"`  
Status ConfigMapSyncStatus `json:"status,omitempty"`  
}  
// +kubebuilder:object:root=true  
// ConfigMapSyncList contains a list of ConfigMapSync  
type ConfigMapSyncList struct {  
metav1.TypeMeta `json:",inline"`  
metav1.ListMeta `json:"metadata,omitempty"`  
Items []ConfigMapSync `json:"items"`  
}  
func init() {  
SchemeBuilder.Register(&ConfigMapSync{}, &ConfigMapSyncList{})  
}

3.编写协调逻辑:
在controllers中实现协调循环。

  // Reconcile method to sync ConfigMap  
func (r *ConfigMapSyncReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {  
ctx := context.Background()  
log := r.Log.WithValues("configmapsync", req.NamespacedName)  
// Fetch the ConfigMapSync instance  
configMapSync := &appsv1.ConfigMapSync{}  
if err := r.Get(ctx, req.NamespacedName, configMapSync); err != nil {  
return ctrl.Result{}, client.IgnoreNotFound(err)  
}  
// Fetch the source ConfigMap  
sourceConfigMap := &corev1.ConfigMap{}  
sourceConfigMapName := types.NamespacedName{  
Namespace: configMapSync.Spec.SourceNamespace,  
Name: configMapSync.Spec.ConfigMapName,  
}  
if err := r.Get(ctx, sourceConfigMapName, sourceConfigMap); err != nil {  
return ctrl.Result{}, err  
}  
// Create or Update the destination ConfigMap in the target namespace  
destinationConfigMap := &corev1.ConfigMap{}  
destinationConfigMapName := types.NamespacedName{  
Namespace: configMapSync.Spec.DestinationNamespace,  
Name: configMapSync.Spec.ConfigMapName,  
}  
if err := r.Get(ctx, destinationConfigMapName, destinationConfigMap); err != nil {  
if errors.IsNotFound(err) {  
log.Info("Creating ConfigMap in destination namespace", "Namespace", configMapSync.Spec.DestinationNamespace)  
destinationConfigMap = &corev1.ConfigMap{  
ObjectMeta: metav1.ObjectMeta{  
Name: configMapSync.Spec.ConfigMapName,  
Namespace: configMapSync.Spec.DestinationNamespace,  
},  
Data: sourceConfigMap.Data, // Copy data from source to destination  
}  
if err := r.Create(ctx, destinationConfigMap); err != nil {  
return ctrl.Result{}, err  
}  
} else {  
return ctrl.Result{}, err  
}  
} else {  
log.Info("Updating ConfigMap in destination namespace", "Namespace", configMapSync.Spec.DestinationNamespace)  
destinationConfigMap.Data = sourceConfigMap.Data // Update data from source to destination  
if err := r.Update(ctx, destinationConfigMap); err != nil {  
return ctrl.Result{}, err  
}  
}  
return ctrl.Result{}, nil  
}

4. 构建和部署您的Operator:运行以下命令来构建和部署您的Operator:

make docker-build docker-push IMG=registry/configmapsync-operator:latest  
make deploy IMG=registry/configmapsync-operator:latest

5. 您还可以使用以下清单部署operators

apiVersion: apps/v1  
kind: Deployment  
metadata:  
name: configmapsync-operator  
spec:  
replicas: 1  
selector:  
matchLabels:  
app: configmapsync-operator  
template:  
metadata:  
labels:  
app: configmapsync-operator  
spec:  
containers:  
- name: configmapsync-operator  
image: registry/configmapsync-operator:latest # Replace with your image location

6. 将基于新运算符 CRD 的示例清单应用到集群

apiVersion: apps.bixbite.io/v1  
kind: ConfigMapSync  
metadata:  
labels:  
app.kubernetes.io/name: configmapsync  
app.kubernetes.io/instance: configmapsync-sample  
app.kubernetes.io/part-of: configmapsync  
app.kubernetes.io/managed-by: kustomize  
app.kubernetes.io/created-by: configmapsync  
name: configmapsync-sample  
spec:  
SourceNamespace: "sourceNamespace"  
DestinationNamespace: "destinationNamespace"  
ConfigMapName: "configMapName"

通过利用Opetrator的力量,我们探索了 ConfigMapSync Opetrator如何简化 ConfigMap 的管理,促进它们在 Kubernetes 集群内跨命名空间的同步。这个实践过程涉及定义 CRD、编写协调逻辑以及部署运算符以确保平稳高效的同步。

旅程并没有结束。有大量的机会来探索、创新和微调operators,从而放大 Kubernetes 优化工作流程并实现卓越运营的潜力。因此,卷起袖子,深入了解 Kubernetes Operator 环境,并通过量身定制的专用 Operator 释放集群的全部潜力。