挑战自我极限,用最简方式介绍从零开始构建 Kubernetes Operator

我是 LEE,老李,一个在 IT 行业摸爬滚打 17 年的技术老兵。

在本文中,我将使用最少的字和内容展现一个完整使用 Kubebuilder 从零开始构建 Kubernetes Operator 的整个过程。这是一次对知识的极度压缩和挑战,希望能帮助更多的小伙伴对构建 Kubernetes Operator 有一个全新的了解,同时对自己也是一个知识的深层梳理。

Kubernetes Operator 是一种强大的模式,允许开发者扩展 Kubernetes 的功能,以管理复杂的状态和自动化操作。我们将按照以下步骤逐步指导您创建您自己的 Operator

  1. 环境准备:介绍如何准备开发环境,包括安装 Go、Docker 和 Kubebuilder。
  2. 项目初始化 :使用 Kubebuilder 初始化新项目,创建基础项目结构和配置。
  3. 创建 API 和控制器:引导您创建自定义资源定义(CRD)的 API 和相应的控制器。
  4. 实现控制器逻辑:详细介绍如何在控制器中实现业务逻辑,包括资源的观察、分析和调节。
  5. 构建和部署 Operator :讨论如何构建 Operator 容器镜像,推送到容器镜像仓库,并部署到 Kubernetes 集群中。

此外,还将讨论编写 CRD 时需要注意的要点,包括定义规格、状态和注释等方面的最佳实践。

1. 环境准备和初始化项目

初始化项目是使用 Kubebuilder 开发 Kubernetes Operator 的第一步。这一步骤创建了一个新的项目目录,其中包含了项目的基础目录结构和配置文件,为开发自定义资源定义(CRD)和相应的控制器提供了基础框架。

步骤 1: 创建项目目录

首先,决定你的项目将位于哪个目录。通常,这会是你的工作空间中的一个新目录。使用命令行进入到你希望创建项目的父目录中,然后创建项目目录:

bash 复制代码
mkdir my-operator
cd my-operator

步骤 2: 初始化项目

在项目目录中,使用 kubebuilder init 命令来初始化你的 Kubebuilder 项目。这个命令会创建项目的基础结构,包括Go 模块支持、Makefile(用于构建和部署项目)、以及项目的基本目录结构。

命令的基本格式如下:

bash 复制代码
kubebuilder init --domain mydomain.com --repo github.com/myusername/my-operator

在上述命令中,使用 --domain 参数指定你的资源类型的域,使用 --repo 参数指定代码库的路径。这些参数将影响你的 CRDs 的 API 组和路径。

项目结构

完成初始化后,你的项目目录将包括以下基本结构:

  • config/: 存储 kustomize 文件和其他配置文件,用于部署你的 OperatorKubernetes 集群。
  • api/: 存放自定义资源的 API 定义文件。使用 Kubebuilder create api 命令创建新的 API 时,相关文件将添加到此目录。
  • controllers/: 存放控制器的代码。控制器观察 Kubernetes 集群的状态,并确保自定义资源的状态与其规格相匹配。
  • Makefile: 包含用于构建和部署项目的命令。
  • go.modgo.sum: Go 语言的模块依赖文件。

注意事项

  • 在运行初始化命令之前,请确保正确安装了 Kubebuilder 和 Go。
  • 初始化命令会在当前目录下创建项目,请确保在正确的目录下运行它。
  • 使用的域名应该是你控制下的域,以避免与其他人的 API 发生冲突。
  • 项目名称(repo 参数)应该是一个有效的 Go 模块名称,因为 Kubebuilder 项目基于 Go 模块。

2. 创建 API 和控制器

在使用 Kubebuilder 初始化项目之后,下一步是创建自定义资源定义(CRD)的 API 和相应的控制器。这是开发 Kubernetes Operator 的核心部分,因为它定义了自定义资源的数据模型以及如何管理这些资源的逻辑。以下是创建 API 和控制器的详细步骤:

步骤 1: 创建 API

运行 kubebuilder create api 命令来创建一个新的 API 资源和控制器。你需要指定资源的组(group)、版本(version)和种类(kind)。这三个属性共同定义了你的 CRD 的全名。

bash 复制代码
kubebuilder create api --group <group> --version <version> --kind <Kind>

例如,要创建一个名为 WebApp 的资源,位于 webapp.mydomain.com 组下,版本为 v1,可以运行以下命令:

bash 复制代码
kubebuilder create api --group webapp --version v1 --kind WebApp

在执行此命令时,Kubebuilder 会询问你两个问题:

  1. 是否为资源创建 CRD:输入 Yes
  2. 是否为资源创建控制器:输入 Yes

步骤 2: 定义 API 结构

创建 API 后,Kubebuilder 会在 api/<version>/ 目录下生成一个名为 <kind>_types.go 的文件,其中包含了自定义资源的 Go 类型定义。你需要编辑这个文件来定义资源的规格(Spec)和状态(Status)。

例如,对于 WebApp 资源,你可能需要定义如下:

go 复制代码
// WebAppSpec 定义了 WebApp 的期望状态。
type WebAppSpec struct {
    // +kubebuilder:validation:MinLength=1
    // Size 是一个描述 webapp 大小的字符串。
    Size string `json:"size,omitempty"`
}

// WebAppStatus 定义了 WebApp 的观察到的状态。
type WebAppStatus struct {
    // Nodes 存储运行 WebApp 实例的节点名称。
    Nodes []string `json:"nodes"`
}

步骤 3: 实现控制器逻辑

控制器的骨架代码会生成在 controllers/ 目录下的 <kind>_controller.go 文件中。在这个文件中,你需要实现资源的业务逻辑,根据资源的规格来调整集群的状态,使其与期望的状态匹配。

go 复制代码
func (r *WebAppReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
    // 逻辑实现
    return ctrl.Result{}, nil
}

步骤 4: 注册 CRD 和启动控制器

完成 API 结构和控制器逻辑的定义后,需要执行以下操作:

  1. 重新生成 CRD 定义并更新到 Kubernetes 集群中。
  2. 确保控制器在项目启动时被注册。

为了执行这些任务,你可以使用 Kubebuilder 自动生成的 Makefile 中提供的命令:

bash 复制代码
make install
make run

注意事项

  • 在定义 API 结构时,使用 Kubebuilder 提供的字段验证标记(例如 // +kubebuilder:validation:MinLength=1)来进行验证。
  • 在实现控制器逻辑时,处理资源不存在(被删除)的情况,并处理可能的错误。
  • 定期运行 make 命令,以确保代码、CRD 定义和 RBAC 规则与 Kubernetes 集群同步。
  • 在开发过程中,密切关注 Kubernetes 事件和控制器的日志输出,这有助于调试和理解控制器的行为。

小结

通过以下步骤,可以为你的 Kubernetes Operator 创建自定义资源的 API 和控制器,从而向 Kubernetes 集群添加新功能:

  1. 运行 kubebuilder create api 命令创建 API 资源和控制器。
  2. 在生成的文件中定义 API 结构和控制器逻辑。
  3. 注册 CRD 并启动控制器。

这些步骤是向 Kubernetes 集群添加新功能的关键。

3. CRD 资源编写

CRD(Custom Resource Definition)资源编写是在 Kubernetes 中扩展 API 的关键步骤。CRD 允许你定义新的自定义资源类型,这些资源类型可以像内置资源(如 Pods、Deployments 等)一样被 Kubernetes API 服务器处理。通过定义 CRD 和相应的控制器,你可以实现自定义的逻辑来管理这些资源,从而扩展 Kubernetes 的功能。以下是编写 CRD 资源的详细步骤和注意事项。

步骤 1: 定义 CRD 结构

CRD 的定义包括两个主要部分:Spec(规格)和 Status(状态)。Spec 定义了用户期望的资源状态,而 Status 反映了资源的当前状态。

以下是编写 CRD 的步骤:

  1. 选择 API 组和版本:为你的 CRD 选择一个合适的 API 组和版本。API 组反映了功能相关性,版本表示 API 的稳定性和成熟度。
  2. 定义 Kind :Kind 是你的 CRD 的名称,在 Kubernetes 中表示资源的类型。
  3. 编写 CRD 规格:定义你的 CRD 的属性。可以使用特殊的注释为字段添加元数据,例如字段描述、必需字段、字段默认值等。

步骤 2: 使用 Kubebuilder 创建 CRD

如果你使用 Kubebuilder 或类似的工具,创建 CRD 变得更加简单。Kubebuilder 可以根据你的 Go 代码自动生成 CRD 定义。

以下是创建 CRD 的步骤:

  1. 创建 API :使用 Kubebuilder 创建新的 API 和控制器骨架:

    bash 复制代码
    kubebuilder create api --group <your-group> --version <version> --kind <Kind>
  2. 编辑 Go 类型定义 :在 api/<version>/<kind>_types.go 文件中定义你的 CRD 的 Spec 和 Status。

  3. 添加注释 :使用 Kubebuilder 特有的注释来添加字段验证规则和其他元数据。

步骤 3: 生成 CRD 定义

Kubebuilder 可以根据你的 Go 类型定义自动生成 YAML 格式的 CRD 定义。

bash 复制代码
make manifests

这个命令会生成一个或多个 CRD 定义文件,通常位于 config/crd/bases 目录下。

步骤 4: 部署 CRD 到 Kubernetes

将生成的 CRD 定义文件应用到你的 Kubernetes 集群中:

bash 复制代码
kubectl apply -f config/crd/bases

注意事项

  • 遵循 API 约定 :按照 Kubernetes API 约定,如命名规范和字段命名,使你的 CRD 易于理解和使用。
  • 考虑版本升级:在设计 CRD 时,考虑未来可能的版本升级,并提供清晰的升级路径以维护兼容性。
  • 验证和默认值 :使用 Kubebuilder 注释为 CRD 字段添加验证规则和默认值,确保资源的正确性和稳定性。
  • 支持多版本:如果需要支持多个版本的 CRD,请正确配置版本转换逻辑,保持不同版本间的兼容性。

通过遵循这些步骤和注意事项,你可以高效地编写和部署 Kubernetes CRD,为你的应用或服务在 Kubernetes 平台上提供强大的扩展和自定义能力。

4. 实现控制器逻辑

实现控制器逻辑是创建 Kubernetes Operator 的核心步骤。控制器负责观察 Kubernetes API 中的资源状态,处理自定义资源(CR)的创建、更新和删除事件,并确保资源状态与其规格定义相匹配。下面将详细介绍如何在使用 Kubebuilder 创建的项目中实现控制器逻辑。

控制器的职责

Kubernetes 中,控制器遵循控制循环(control loop)模式,不断地比较资源的期望状态(由资源的 Spec 字段定义)与实际状态(由资源的 Status 字段和实际的集群状态反映)。控制器的职责包括:

  • 观察 :监听 Kubernetes API 中特定资源的变化。
  • 分析:比较观察到的资源状态与其期望状态。
  • 调节 :通过创建、更新或删除 Kubernetes 资源来调整状态,以使观察到的状态与期望状态一致。

实现控制器逻辑的步骤

以下是在 Kubebuilder 项目中实现控制器逻辑的基本步骤:

1. 定义资源逻辑

首先,你需要确定你的自定义资源(CR)所需的 Kubernetes 原生资源(如 Deployments、Services 等),以支持其运行。

2. 更新控制器代码

Kubebuilder 会在创建 API 时自动生成控制器的框架代码,在 controllers 目录下的 <your-resource>_controller.go 文件中。你需要在这个文件中填充业务逻辑。

控制器的主要逻辑集中在 Reconcile 方法中。这个方法包含了控制循环的核心逻辑,即观察、分析和调节的过程。Reconcile 方法的签名如下:

go 复制代码
func (r *<YourResource>Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // 业务逻辑实现
}

Reconcile 方法中,根据自定义资源的规格检查和创建所需的 Kubernetes 资源,并更新自定义资源的状态。

示例代码

以下是一个简化的示例,展示了如何在 Reconcile 方法中实现控制器逻辑:

go 复制代码
import (
    "context"
    appsv1 "k8s.io/api/apps/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func (r *<YourResource>Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // 定义一个变量来存储你的自定义资源
    var myResource <yourdomain>/<YourResourceVersion>.<YourResource>
    if err := r.Get(ctx, req.NamespacedName, &myResource); err != nil {
        log.Error(err, "unable to fetch <YourResource>")
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    // 根据 myResource.Spec 创建或更新 `Kubernetes` 资源
    // 例如,创建一个 Deployment
    deployment := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name:      myResource.Name,
            Namespace: myResource.Namespace,
        },
        // ...
    }

    // 调用 Create 或 Update 方法来部署或更新资源
    // 记得处理可能的错误并返回相应的结果

    return ctrl.Result{}, nil
}

注意事项

  • 错误处理 :确保对 Reconcile 方法中可能发生的所有错误进行适当处理,包括处理 Kubernetes API 调用的错误。
  • 幂等性 :确保 Reconcile 方法是幂等的,即无论调用多少次,最终的状态都应该是一致的,不会因多次调用而产生不一致的结果。

5. 构建和部署你的 Operator

构建和部署 Operator 是将你的代码转换成在 Kubernetes 集群中运行的实际应用的过程。使用 Kubebuilder,这一过程包括构建 Operator 容器镜像、推送镜像到镜像仓库,并将 Operator 部署到 Kubernetes 集群。以下是详细的步骤和注意事项:

步骤 1:构建 Operator 容器镜像

  1. 编译 Operator : 使用 Go 工具链编译你的 Operator 代码,生成一个可执行文件。通常,Kubebuilder 项目包含一个 Makefile,可以自动化这一步骤:

    bash 复制代码
    make build
  2. 构建容器镜像 : 将编译后的可执行文件打包到一个容器镜像中。编写一个 Dockerfile 来定义构建镜像的步骤,然后使用 Docker 或其他容器工具来构建镜像:

    bash 复制代码
    make docker-build IMG=<your-image-name>:<tag>

    在这里,<your-image-name> 是你的容器镜像的名称,<tag> 是镜像的标签,通常用于版本控制。

步骤 2:推送镜像到仓库

完成构建后,需要将容器镜像推送到容器镜像仓库,以供 Kubernetes 访问和拉取。如果使用 Docker Hub、Google Container Registry (GCR) 或其他容器镜像服务,推送镜像的命令如下:

bash 复制代码
docker push <your-image-name>:<tag>

确保你的 Kubernetes 集群可以访问该镜像仓库。对于私有仓库,可能需要在 Kubernetes 集群中配置相应的访问凭证。

步骤 3:部署 Operator 到 Kubernetes 集群

  1. 生成部署配置 : 使用 Kustomize 来管理 Kubernetes 资源的部署。你可以根据需要修改 config/ 目录下的配置文件。

  2. 部署 CRDs: 部署自定义资源定义(CRD)到集群中:

    bash 复制代码
    make install
  3. 部署 Operator : 使用生成的部署配置将 Operator 部署到集群:

    bash 复制代码
    make deploy IMG=<your-image-name>:<tag>

    这一步会应用 config/ 目录下的 Kubernetes 资源配置,将 Operator 和必要的权限配置部署到集群中。

注意事项

  • 安全考虑 : 如果你的 Operator 需要访问 Kubernetes API 或其他敏感资源,请确保正确配置 RBAC 权限,并遵循最小权限原则。
  • 镜像版本控制 : 使用有意义的镜像标签(如版本号),而不是 latest,这有助于避免生产环境中的意外更新。
  • 测试部署 : 在部署到生产环境之前,先在测试集群中部署并验证你的 Operator。确保它按预期工作,并妥善处理错误情况。

通过以上步骤,你可以构建、推送和部署你的 Kubebuilder OperatorKubernetes 集群,从而实现对集群资源的管理和自动化。

最后

匆匆忙忙写完这篇文章,因为我最近就在准备使用 Kubebuilder 开发一个 Operator。在学习一段时间后,想好好梳理一下自己的知识,同时发现 Kubebuilder 的文档跟老太太的裹脚布一样,又臭又长。随之就想挑战下自己,看看能不能用最少的字和内容来讲清楚一个完整的 Kubebuilder 开发 Operator 的过程。 编写过程中也是修修补补,所以可能有些地方表述不够准确,还请大家多多包涵。

当然,这篇文章只是一个开始,后续我会继续深入学习 Kubebuilder,并将自己的学习过程和心得分享给大家。希望这篇文章能帮助到你,一起学习和进步。

相关推荐
ai小鬼头1 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
简佐义的博客1 小时前
破解非模式物种GO/KEGG注释难题
开发语言·数据库·后端·oracle·golang
你想考研啊2 小时前
四、jenkins自动构建和设置邮箱
运维·jenkins
Code blocks2 小时前
使用Jenkins完成springboot项目快速更新
java·运维·spring boot·后端·jenkins
追逐时光者2 小时前
一款开源免费、通用的 WPF 主题控件包
后端·.net
蜗牛沐雨2 小时前
警惕 Rust 字符串的性能陷阱:`chars().nth()` 的深坑与高效之道
开发语言·后端·rust
饥饿的半导体3 小时前
Linux快速入门
linux·运维
&Sinnt&3 小时前
Git 版本控制完全指南:从入门到精通
git·后端