当前位置: 首页 > article >正文

Operator 开发入门系列(一):Hello World

背景

我们公司最近计划将产品迁移到 Kubernetes 环境。 为了更好地管理和自动化我们的应用程序,我们决定使用 Kubernetes Operator。 本系列博客将记录我们学习和开发 Operator 的过程,希望能帮助更多的人入门 Operator 开发。

目标读者

  • 对 Kubernetes 有一定了解的开发人员和运维人员
  • 希望使用 Operator 自动化管理应用程序的人员
  • 对 Go 语言有基本了解的人员

准备工作

在开始之前,你需要准备以下环境:

  • Go 语言环境 (>= 1.23): Operator 通常使用 Go 语言开发,你需要安装 Go 语言环境。 建议使用 Go 1.21 或更高版本。 可以从 https://go.dev/dl/ 下载安装包。 安装完成后,请配置好 GOPATHPATH 环境变量。

  • Kubernetes 集群: 你需要一个可用的 Kubernetes 集群来部署和测试 Operator。 可以使用 Minikube、Kind 或其他的 Kubernetes 发行版。

  • kubectl 命令行工具: kubectl 是 Kubernetes 的命令行工具,用于与 Kubernetes 集群交互。 请确保你已经安装并配置了 kubectl, 并且能够连接到你的 Kubernetes 集群。

  • Kubebuilder (>= 3.0): Kubebuilder 是一个用于快速构建 Kubernetes Operator 的框架。 使用 Kubebuilder 可以简化 Operator 的开发流程,并生成一些必要的代码框架。 可以使用以下命令安装 Kubebuilder:

cd $HOME/go/bin
curl -L -o kubebuilder "https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)"
chmod +x kubebuilder

请确保 $HOME/go/bin 目录在你的 PATH 环境变量中。 可以运行 kubebuilder version 命令来验证 Kubebuilder 是否安装成功。

  • Docker (可选): 如果你需要构建 Operator 的 Docker 镜像,你需要安装 Docker。

我的环境是 MacOS(arm64) + Orbstack

什么是 Operator?

简单来说,Operator 是 Kubernetes 的扩展,它利用自定义资源(Custom Resources, CRs)来自动化管理应用程序。Operator 允许我们像管理 Kubernetes 内置资源一样管理复杂的应用程序,例如数据库、消息队列等。

为什么选择 Operator?

Operator 提供了一种声明式的方式来管理应用程序的生命周期,包括部署、升级、备份、恢复等。它可以简化运维流程,提高自动化程度,并确保应用程序的状态符合预期。

我们的第一个 Operator:Hello World

这个 Operator 将监听一个名为 HelloWorld 的自定义资源,并在 Kubernetes 中创建一个 Pod,该 Pod 运行一个简单的 “Hello World” 应用程序。

1. 初始化 Kubebuilder 项目

首先,我们需要使用 Kubebuilder 创建一个新的项目。 在你的 GOPATH 目录下创建一个新的目录,例如 hello-world-operator,然后进入该目录,运行以下命令

kubebuilder init --domain infini.cloud --repo github.com/infinilabs/hello-world-operator

这个命令会创建一个新的 Kubebuilder 项目,并生成一些必要的文件和目录。

2. 创建自定义资源(Custom Resource Definition, CRD)

接下来,我们需要定义 HelloWorld 资源的结构。 运行以下命令

kubebuilder create api --group example --version v1alpha1 --kind HelloWorld

这个命令会创建一个新的 API 定义,包括 api/v1alpha1/helloworld_types.gocontrollers/helloworld_controller.go 两个文件。

编辑 api/v1alpha1/helloworld_types.go 文件,修改 HelloWorldSpec 的定义,添加 namemessage 字段:

// HelloWorldSpec defines the desired state of HelloWorld
type HelloWorldSpec struct {// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster// Important: Run "make" to regenerate code after modifying this file// Name is the name of the HelloWorld resourceName string `json:"name,omitempty"`// Message is the message to be printed by the podMessage string `json:"message,omitempty"`
}

3. 实现 Reconcile 逻辑

编辑 controllers/helloworld_controller.go 文件,实现 Reconcile 函数, 创建一个 Pod,该 Pod 运行一个 busybox 镜像,并输出 HelloWorld 资源中定义的 message

package controllersimport ("context""fmt"corev1 "k8s.io/api/core/v1"apierrors "k8s.io/apimachinery/pkg/api/errors"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/apimachinery/pkg/runtime"ctrl "sigs.k8s.io/controller-runtime""sigs.k8s.io/controller-runtime/pkg/client""sigs.k8s.io/controller-runtime/pkg/log"examplev1alpha1 "github.com/infinilabs/hello-world-operator/api/v1alpha1"
)// HelloWorldReconciler reconciles a HelloWorld object
type HelloWorldReconciler struct {client.ClientScheme *runtime.Scheme
}//+kubebuilder:rbac:groups=example.com,resources=helloworlds,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=example.com,resources=helloworlds/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=example.com,resources=helloworlds/finalizers,verbs=update
//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile
func (r *HelloWorldReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {log := log.FromContext(ctx)// 1. Fetch the HelloWorld instancehelloWorld := &examplev1alpha1.HelloWorld{}err := r.Get(ctx, req.NamespacedName, helloWorld)if err != nil {if apierrors.IsNotFound(err) {// Object not found, return.  Created objects are automatically garbage collected.// For additional cleanup logic use finalizers.log.Info("HelloWorld resource not found. Ignoring since object must be deleted")return ctrl.Result{}, nil}// Error reading the object - requeue the request.log.Error(err, "Failed to get HelloWorld")return ctrl.Result{}, err}// 2. Define the desired Podpod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name:      helloWorld.Name + "-pod",Namespace: helloWorld.Namespace,Labels: map[string]string{"app": helloWorld.Name,},},Spec: corev1.PodSpec{Containers: []corev1.Container{{Name:  "hello-world",Image: "busybox",Command: []string{"sh", "-c", fmt.Sprintf("echo '%s' && sleep 3600", helloWorld.Spec.Message)},},},},}// 3. Set HelloWorld instance as the owner and controllerif err := ctrl.SetControllerReference(helloWorld, pod, r.Scheme); err != nil {log.Error(err, "Failed to set controller reference")return ctrl.Result{}, err}// 4. Check if the Pod already existsfound := &corev1.Pod{}err = r.Get(ctx, client.ObjectKey{Name: pod.Name, Namespace: pod.Namespace}, found)if err != nil && apierrors.IsNotFound(err) {log.Info("Creating a new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name)err = r.Create(ctx, pod)if err != nil {log.Error(err, "Failed to create new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name)return ctrl.Result{}, err}// Pod created successfully - return and requeuereturn ctrl.Result{Requeue: true}, nil} else if err != nil {log.Error(err, "Failed to get Pod")return ctrl.Result{}, err}// 5. Pod already exists - don't requeuelog.Info("Skip reconcile: Pod already exists", "Pod.Namespace", found.Namespace, "Pod.Name", found.Name)return ctrl.Result{}, nil
}// SetupWithManager sets up the controller with the Manager.
func (r *HelloWorldReconciler) SetupWithManager(mgr ctrl.Manager) error {return ctrl.NewControllerManagedBy(mgr).For(&examplev1alpha1.HelloWorld{}).Owns(&corev1.Pod{}).Complete(r)
}

4. 安装 CRD 到 Kubernetes 集群

运行以下命令安装 CRD 到 Kubernetes 集群:

make install

5. 运行 Operator

运行以下命令在本地运行 Operator:

make run

6. 创建 HelloWorld 资源

创建一个名为 my-hello-world.yaml 的文件,内容如下:

apiVersion: example.com/v1alpha1
kind: HelloWorld
metadata:name: my-hello-world
spec:name: my-hello-worldmessage: "Hello World from Operator!"

使用 kubectl apply -f my-hello-world.yaml 创建资源。

7. 验证

使用 kubectl get pods 命令查看是否创建了名为 my-hello-world-pod 的 Pod。 使用 kubectl logs my-hello-world-pod 查看 Pod 的日志,确认是否输出了 “Hello World from Operator!”。

总结

恭喜你完成了第一个 Operator! 虽然这个 Operator 非常简单,但它展示了 Operator 的基本原理:监听自定义资源,并根据资源的状态来管理 Kubernetes 资源。 在接下来的系列中,我们将深入探讨 Operator 的更多高级特性。

敬请期待下一篇博客!

作者:罗厚付,极限科技(INFINI Labs)云上产品设计与研发负责人,拥有多年安全风控及大数据系统架构经验,主导过多个核心产品的设计与落地,日常负责运维超大规模 ES 集群(800+节点/1PB+数据)。
原文:https://infinilabs.cn/blog/2025/kubernetes-operator-develop-part-1/

相关文章:

Operator 开发入门系列(一):Hello World

背景 我们公司最近计划将产品迁移到 Kubernetes 环境。 为了更好地管理和自动化我们的应用程序,我们决定使用 Kubernetes Operator。 本系列博客将记录我们学习和开发 Operator 的过程,希望能帮助更多的人入门 Operator 开发。 目标读者 对 Kubernete…...

【Docker】运行错误提示 unknown shorthand flag: ‘d‘ in -d ----详细解决方法

使用docker拉取Dify的时候遇到错误 错误提示 unknown shorthand flag: d in -dUsage: docker [OPTIONS] COMMAND [ARG...]错误原因解析 出现 unknown shorthand flag: d in -d 的根本原因是 Docker 命令格式与当前版本不兼容,具体分为以下两种情况: 新…...

【AI插件开发】Notepad++ AI插件开发实践:实现对话窗口功能

引言 之前的文章已经介绍实现了AI对话窗口,但只有个空壳,没有实现功能。本次将集中完成对话窗口的功能,主要内容为: 模型动态切换:支持运行时加载配置的AI模型列表交互式输入处理:实现多行文本输入与Ctrl…...

在激烈竞争下B端HMI设计怎样打造独特用户体验?

在当今数字化高度发展的时代,B 端市场竞争愈发激烈。对于 B 端 HMI(人机界面)设计而言,打造独特的用户体验已成为在竞争中脱颖而出的关键因素。B 端用户在复杂的工作场景中,对 HMI 设计有着独特的需求和期望&#xff0…...

【Netty篇】Handler Pipeline 详解

目录 一、 Handler & Pipeline——流水线上的“特种部队”与“生产线”1、 ChannelHandler —— 流水线上的“特种兵”👮‍♂️2、 ChannelPipeline —— 生产线上的“接力赛跑”🏃‍♀️🏃‍♂️ 二、 代码实例1、 服务端代码示例2、 客…...

计算机网络 - UDP协议

通过一些问题来讨论 UDP 协议 什么是 UDP?举几个应用了 UDP 协议的例子UDP 与 TCP 有啥区别?(PS:介绍三四个就可以了,不用说太多)具体 UDP 是不可靠的,那你觉得如何实现一个可靠的 UDP &#x…...

16-算法打卡-哈希表-两个数组的交集-leetcode(349)-第十六天

1 题目地址 349. 两个数组的交集 - 力扣(LeetCode)349. 两个数组的交集 - 给定两个数组 nums1 和 nums2 ,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1:输入:nu…...

java + spring boot + mybatis 通过时间段进行查询

前端传来的只有日期内容&#xff0c;如&#xff1a;2025-04-17 需要在日期内容的基础上补充时间部分&#xff0c;代码示例&#xff1a; /*** 日志查询&#xff08;分页查询&#xff09;* param recordLogQueryDTO 查询参数对象* return 日志列表*/Overridepublic PageBean<…...

AI与物联网的深度融合:开启智能生活新时代

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;和物联网&#xff08;IoT&#xff09;作为两大前沿技术&#xff0c;正在加速融合&#xff0c;为我们的生活和工作带来前所未有的变革。这种融合不仅提升了设备的智能化水平&#xff0c;还为各行各业带来了新的机…...

浔川AI翻译v7.0更新预告

亲爱的浔川AI翻译用户&#xff1a; 感谢您一直以来的支持&#xff01;浔川AI翻译自推出以来&#xff0c;已迭代6个版本&#xff0c;其中**v2.0和v4.0因技术问题&#xff08;翻译结果显示异常、注册失败、密码找回功能失效等&#xff09;**被迫下架。我们深知这些问题影响了您…...

helm账号密码加密

1、安装工具 sudo apt update sudo apt install gnupg -y wget https://github.com/getsops/sops/releases/download/v3.10.2/sops-v3.10.2.linux.amd64 mv sops-v3.10.2.linux.amd64 /usr/local/bin/sops chmod x /usr/local/bin/sops2、生成加密文件 gpg --full-generate-…...

Flink 编程基础:Scala 版 DataStream API 入门

大家好&#xff01;我是心海 流处理技术在大数据时代正变得越来越重要&#xff0c;而 Apache Flink 作为领先的流处理引擎&#xff0c;凭借其高性能、低延迟和丰富的 API 受到了广泛关注。本文将以 Scala 语言为例&#xff0c;详细讲解 Flink DataStream API 的基本编程模型&am…...

实战|使用环信Flutter SDK构建鸿蒙HarmonyOS应用及推送配置

本文为大家介绍如何在 Flutter 环境创建 Harmony 项目并集成环信即时通讯IM以及环信 Flutter Harmony 推送配置。 已经基于环信的 Flutter 项目也可以参考本文适配鸿蒙端。 一、开发环境要求 前置条件 1.安装DevEco-Studio 2.安装模拟器 DevEco-Studio 下载与操作指导&…...

HTML5好看的水果蔬菜在线商城网站源码系列模板5

文章目录 1.设计来源1.1 主界面1.2 关于我们1.3 商品服务1.4 果蔬展示1.5 联系我们1.6 商品具体信息1.7 登录注册 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#…...

宜搭与金蝶互通——连接器建立

一、 进入连接器工厂 图1 连接器入口 二、 新建连接器 图2 新建连接器第一步 1、 连接器显示名,如图2中①所示; 2、 图2中②域名,是金蝶系统API接口里面的“完整服务地址”com之前的信息,不含“https”,如图3中①所示; 3、 Base Url通常为“/”,如图2…...

SP7733:HPYNOS - Happy Numbers I(参考我之前的文章,哈希)

题目大意 我们定义“破坏”整数的过程是对其每一位上的数字的平方求和成为一个新数&#xff0c;如果一个数在经过若干次“破坏”以后变成了 1&#xff0c;那么这个数就是一个高兴的数字&#xff0c;输出变化次数&#xff0c;否则如果永远不会变成 1&#xff0c;输出 −1。 例如…...

《Java 泛型的作用与常见用法详解》

大家好呀&#xff01;&#x1f44b; 今天我们要聊的是Java中一个超级重要但又让很多初学者头疼的概念——泛型(Generics)。带你彻底搞懂它&#xff01;&#x1f4aa; 准备好你的小本本&#xff0c;我们开始啦&#xff5e;&#x1f4dd; 一、为什么需要泛型&#xff1f;&#x…...

【JavaWeb】详细讲解 HTTP 协议

文章目录 一、HTTP简介1.1 概念1.2 特点 二、协议2.1 HTTP-请求协议&#xff08;1&#xff09;GET方式&#xff08;2&#xff09;POST方式&#xff08;3&#xff09;GET和POST的区别&#xff1a; 2.2 HTTP-响应协议&#xff08;1&#xff09;格式&#xff08;2&#xff09;响应…...

【android bluetooth 框架分析 02】【Module详解 4】【Btaa 模块介绍】

1. 背景 我们在上一篇文章中介绍 HciHal 模块时&#xff0c;有如下代码 // system/gd/hal/hci_hal_android_hidl.ccvoid ListDependencies(ModuleList* list) const {list->add<SnoopLogger>();if (common::init_flags::btaa_hci_is_enabled()) {list->add<ac…...

“星睿O6” AI PC开发套件评测 - Windows on Arm 安装指南和性能测评

引言 Radxa联合此芯科技和安谋科技推出全新的"星睿O6"迷你 ITX 主板。该系统搭载了 CIX P1&#xff08;CD8180&#xff09;12 核 Armv9 处理器&#xff0c;拥有高达30T算力的NPU和高性能的GPU&#xff0c;最高配备64GB LPDDR内存&#xff0c;并提供了如 5GbE、HDMI …...

Python 调用 YOLOv11 ONNX

Python 调用 YOLO ONNX 1 下载ONNX文件2 Python代码 1 下载ONNX文件 ONNX下载地址 2 Python代码 import cv2 from ultralytics import YOLOdef check(yolo:str, path:str):# 加载 YOLOv11model YOLO(yolo)# 读取图片img cv2.imread(path)# 推理&#xff08;可以传文件路径…...

数据通信学习笔记之OSPF路由汇总

区域间路由汇总 路由汇总又被称为路由聚合&#xff0c;即是将一组前缀相同的路由汇聚成一条路由&#xff0c;从而达到减小路由表规模以及优化设备资源利用率的目的&#xff0c;我们把汇聚之前的这组路由称为精细路由或明细路由&#xff0c;把汇聚之后的这条路由称为汇总路由或…...

ASP.NET Core Web API 配置系统集成

文章目录 前言一、配置源与默认设置二、使用步骤1&#xff09;创建项目并添加配置2&#xff09;配置文件3&#xff09;强类型配置类4&#xff09;配置Program.cs5&#xff09;控制器中使用配置6&#xff09;配置优先级测试7&#xff09;动态重载配置测试8&#xff09;运行结果示…...

如何判断单片机性能极限?

目录 1、CPU 负载 2、内存使用情况 3、实时性能 4、外设带宽 5、功耗与温度 在嵌入式系统设计中&#xff0c;当系统变得复杂、功能增加时&#xff0c;单片机可能会逐渐逼近其性能极限。及时识别这些极限点对于保证产品质量、稳定性和用户体验至关重要。 当你的嵌入式系统…...

AI在多Agent协同领域的核心概念、技术方法、应用场景及挑战 的详细解析

以下是 AI在多Agent协同领域的核心概念、技术方法、应用场景及挑战 的详细解析&#xff1a; 1. 多Agent协同的定义与核心目标 多Agent系统&#xff08;MAS, Multi-Agent System&#xff09;&#xff1a; 由多个独立或协作的智能体&#xff08;Agent&#xff09;组成&#xff…...

1.凸包、极点、极边基础概念

目录 1.凸包 2.调色问题 3.极性(Extrem) 4.凸组合(Convex Combination) 5.问题转化(Strategy)​编辑 6.In-Triangle test 7.To-Left-test 8.极边&#xff08;Extream Edges&#xff09; 1.凸包 凸包就是上面蓝色皮筋围出来的范围 这些钉子可以转换到坐标轴中&#xff0…...

OSCP - Proving Grounds - DriftingBlues6

主要知识点 路径爆破dirtycow内核漏洞提权 具体步骤 总体来讲&#xff0c;这台靶机还是比较直接的&#xff0c;没有那么多的陷阱,非常适合用来学习 依旧是nmap开始,只开放了80端口 Nmap scan report for 192.168.192.219 Host is up (0.42s latency). Not shown: 65534 cl…...

深度理解指针之例题

文章目录 前言题目分析与讲解涉及知识点 前言 对指针有一定了解后&#xff0c;讲一下一道初学者的易错题 题目分析与讲解 先定义一个数组跟一个指针变量 然后把数组名赋值给指针变量————也就是把首地址传到pulPtr中 重点是分析这一句&#xff1a; *&#xff08;pulPtr…...

java 设计模式之策略模式

简介 策略模式&#xff1a;策略模式可以定制目标对象的行为&#xff0c;它尅通过传入不同的策略实现&#xff0c;来配置目标对象的行为。使用策略模式&#xff0c;就是为了定制目标对象在某个关键点的行为。 策略模式中的角色&#xff1a; 上下文类&#xff1a;持有一个策略…...

LeetCode算法题(Go语言实现)_51

题目 给你两个下标从 0 开始的整数数组 nums1 和 nums2 &#xff0c;两者长度都是 n &#xff0c;再给你一个正整数 k 。你必须从 nums1 中选一个长度为 k 的 子序列 对应的下标。 对于选择的下标 i0 &#xff0c;i1 &#xff0c;…&#xff0c; ik - 1 &#xff0c;你的 分数 …...