从零手搓一个简易Operator:用Go和client-go实现一个‘网站状态监控’CRD(附完整代码)

张开发
2026/4/20 21:21:29 15 分钟阅读

分享文章

从零手搓一个简易Operator:用Go和client-go实现一个‘网站状态监控’CRD(附完整代码)
从零构建网站监控Operator基于Kubebuilder与client-go的实战指南在云原生生态中Operator模式已成为扩展Kubernetes能力的标准方式。本文将带您从零开始构建一个实用的WebsiteMonitor Operator通过完整代码示例演示如何利用Kubebuilder框架和client-go库实现自定义资源定义CRD与控制器逻辑。不同于抽象原理讲解我们聚焦于可落地的工程实践让您在手写代码的过程中深入理解Operator核心机制。1. 环境准备与项目初始化在开始编码前需要确保开发环境满足以下基础要求Go 1.16Operator开发主要使用Go语言Kubernetes集群Minikube或Kind本地集群即可kubectl配置好集群访问权限Kubebuilder 3.0官方推荐的Operator框架使用Kubebuilder脚手架快速初始化项目mkdir website-monitor-operator cd website-monitor-operator kubebuilder init --domain example.com --repo github.com/example/website-monitor-operator kubebuilder create api --group monitoring --version v1 --kind WebsiteMonitor这会在当前目录生成标准项目结构其中关键目录包括api/v1/: CRD类型定义controllers/: 控制器实现config/crd/: CRD部署清单提示建议启用Go模块并配置GOPROXY加速依赖下载2. 设计WebsiteMonitor CRD在api/v1/websitemonitor_types.go中定义自定义资源的结构。我们的监控器需要包含以下核心字段type WebsiteMonitorSpec struct { // 监控的URL地址 URL string json:url // 检查间隔秒 Interval int32 json:interval // HTTP超时时间秒 Timeout int32 json:timeout,omitempty // 期望的HTTP状态码 ExpectedStatus int32 json:expectedStatus,omitempty } type WebsiteMonitorStatus struct { // 最后检查时间 LastChecked metav1.Time json:lastChecked,omitempty // 最后观察到的状态码 StatusCode int32 json:statusCode,omitempty // 是否健康 Healthy bool json:healthy,omitempty // 错误信息 Error string json:error,omitempty }执行make manifests命令生成CRD YAML文件该文件会出现在config/crd/bases/目录下。可以通过kubectl apply -f config/crd/部署到集群。3. 实现控制器逻辑控制器核心位于controllers/websitemonitor_controller.go我们需要实现Reconcile方法func (r *WebsiteMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log : log.FromContext(ctx) // 获取WebsiteMonitor实例 var monitor monitoringv1.WebsiteMonitor if err : r.Get(ctx, req.NamespacedName, monitor); err ! nil { return ctrl.Result{}, client.IgnoreNotFound(err) } // 执行HTTP检查 status, err : r.checkWebsite(monitor.Spec) if err ! nil { log.Error(err, 网站检查失败) } // 更新状态 monitor.Status status if err : r.Status().Update(ctx, monitor); err ! nil { return ctrl.Result{}, err } // 设置下次调谐时间 requeueAfter : time.Duration(monitor.Spec.Interval) * time.Second return ctrl.Result{RequeueAfter: requeueAfter}, nil } func (r *WebsiteMonitorReconciler) checkWebsite(spec monitoringv1.WebsiteMonitorSpec) (monitoringv1.WebsiteMonitorStatus, error) { status : monitoringv1.WebsiteMonitorStatus{ LastChecked: metav1.Now(), } client : http.Client{ Timeout: time.Duration(spec.Timeout) * time.Second, } resp, err : client.Get(spec.URL) if err ! nil { status.Error err.Error() return status, err } defer resp.Body.Close() status.StatusCode int32(resp.StatusCode) status.Healthy (spec.ExpectedStatus 0 || resp.StatusCode int(spec.ExpectedStatus)) return status, nil }4. 部署与测试Operator首先构建并推送Operator镜像make docker-build docker-push IMGexample/website-monitor-operator:v1然后部署到集群make deploy IMGexample/website-monitor-operator:v1创建示例WebsiteMonitor资源apiVersion: monitoring.example.com/v1 kind: WebsiteMonitor metadata: name: example-website spec: url: https://example.com interval: 30 timeout: 5 expectedStatus: 200通过kubectl get websitemonitor example-website -o yaml可以查看监控状态status: healthy: true lastChecked: 2023-07-20T08:30:45Z statusCode: 2005. 高级功能扩展基础功能实现后可以考虑添加以下增强特性事件通知集成func (r *WebsiteMonitorReconciler) handleStatusChange(ctx context.Context, monitor *monitoringv1.WebsiteMonitor) { eventType : Normal reason : Healthy message : fmt.Sprintf(Website %s is healthy, monitor.Spec.URL) if !monitor.Status.Healthy { eventType Warning reason Unhealthy message fmt.Sprintf(Website %s returned status %d, monitor.Spec.URL, monitor.Status.StatusCode) } r.Recorder.Event(monitor, eventType, reason, message) }指标暴露使用Prometheus客户端库暴露监控指标var ( requestsTotal prometheus.NewCounterVec( prometheus.CounterOpts{ Name: website_monitor_checks_total, Help: Total number of website checks, }, []string{url, status}, ) responseTime prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: website_monitor_response_time_seconds, Help: Website response time distribution, Buckets: prometheus.DefBuckets, }, []string{url}, ) ) func init() { prometheus.MustRegister(requestsTotal, responseTime) }优雅处理删除事件func (r *WebsiteMonitorReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(monitoringv1.WebsiteMonitor{}). WithEventFilter(predicate.Funcs{ DeleteFunc: func(e event.DeleteEvent) bool { // 自定义删除逻辑 return false }, }). Complete(r) }6. 调试与性能优化开发过程中常用的调试技巧本地运行Operatormake run日志级别调整ctrl.SetLogger(zap.New(zap.UseFlagOptions(zap.Options{ Development: true, Level: zapcore.DebugLevel, })))性能优化建议使用共享HTTP客户端实现请求缓存批量状态更新调整Reconcile并发数func (r *WebsiteMonitorReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(monitoringv1.WebsiteMonitor{}). WithOptions(controller.Options{ MaxConcurrentReconciles: 5, }). Complete(r) }在实现这个Operator的过程中最常遇到的坑是状态更新冲突。由于Reconcile可能并发执行更新Status时需要正确处理资源版本冲突。实践中发现使用Status().Update()而非Update()可以避免大部分问题因为前者只更新status子资源。

更多文章