上一篇我们实现了NodePoolOperator的基本CURD功能。跑了一小段时间后,除了CURD,我们还有更高的要求。我想知道一个节点池里有多少个节点,当前的资源配比是多少,这样我们就可以清楚的知道我们现在的水位是多少。另外,我还想知道节点池数量变化的相关事件信息,以及节点池什么时候会增加或者减少一个节点等等。需求我们先梳理一下需求。通过kubectlgetNodepool可以知道当前节点池的如下信息。节点池状态异常。节点池目前有多少个节点?有多少CPU和内存?您可以在节点池执行状态中了解事件信息控制器错误和节点更改。首先,修改状态对象。确保存在以下//+kubebuilder:subresource:status注释。这意味着状态子资源已启用。status对象修改后需要重新执行makeinstall再次//NodePoolStatus定义了NodePooltypeNodePoolStatusstruct的观察状态{//status=200表示正常,其他情况为异常Statusint`json:"status"`//NumberofnodesNodeCountint`json:"nodeCount"`//允许调度的容量Allocatablecorev1.ResourceList`json:"allocatable,omitempty"protobuf:"bytes,2,rep,name=allocatable,casttype=ResourceList,castkey=ResourceName"`}//+kubebuilder:object:root=true//+kubebuilder:resource:scope=Cluster//+kubebuilder:subresource:status//NodePoolistheSchemaforthenodepoolsAPItypeNodePoolstruct{然后修改Reconcile(r*NodePoolReconciler)Reconcile(ctxcontext.Context,reqctrl.Request)(ctrl.Result,error)中的逻辑func{//......iflen(nodes.Items)>0{r.Log.Info("findnodes,willmergedata”,“节点”,len(nodes.Items))+pool.Status.Allocatable=corev1.ResourceList{}+pool.Status.NodeCount=len(nodes.Items)for_,n:=rangenodes.Items{n:=n//更新节点的标签和污点信息err:=r.Update(ctx,pool.Spec.ApplyNode(n))iferr!=nil{returnctrl.Result{},err}+forname,quantity:=rangen.Status.Allocatable{+q,ok:=pool.Status.Allocatable[name]+ifok{+q.Add(quantity)+pool.Status.Allocatable[name]=q+continue+}+pool.Status.Allocatable[name]]=quantity+}}}//……+pool.Status.Status=200+err=r.Status().Update(ctx,pool)returnctrl.Result{},err}修改后,我们提交一个NodePool来测试apiVersion:nodes.lailin.xyz/v1kind:NodePoolmetadata:name:workerspec:taints:-key:node-pool.lailin.xyzvalue:workereffect:NoSchedulelabels:"node-pool.lailin.xyz/worker":"10"handler:runc可以看到我们现在有两个worker节点?kubectlgetnoNAMESTATUSROLESAGEVERSIONkind-control-planeReadycontrol-plane,master29mv1.20.2kind-workerReadyworker28mv1.20.2kind-worker2Readyworker28mv1.20.2然后我们看看NodePool,你可以发现预期的statusstatus:allocatable:cpu:"8"ephemeral-storage:184026512Kihugepages-1Gi:"0"hugepages-2Mi:"0"memory:6129040Kipods:"220"nodeCount:2status:200现在这个只能传给你了可以通过检查yaml详细信息来查看它。NodePool多了一点就不方便了。现在我们在NodePool+//+kubebuilder:printcolumn:JSONPath=".status.status",name=Status,type=integer+//+kubebuilder:printcolumn:JSONPath=".status.nodeCount"中添加一些kubectl显示的列name=NodeCount,type=integer//+kubebuilder:object:root=true//+kubebuilder:resource:scope=Cluster//+kubebuilder:subresource:status如上所示,只需要添加相应的注释,然后执行makeinstall,然后执行kubectlgetNodePool就可以看到对应的列了。?kubectlgetNodePoolNAMESTATUSNODECOUNTworker2002Event我们在控制器中添加Recorder来记录事件。有两种类型的事件,正常和警告。//NodePoolReconcilerreconcilesaNodePoolobjecttypeNodePoolReconcilerstruct{client.ClientLoglogr.LoggerScheme*runtime.Scheme+Recorderrecord.EventRecorder}func(r*NodePoolReconciler)Reconcile(ctxcontext.Context,reqctrl.Request)(ctrl.Result,error){+//添加测试事件+r.Recorder.Event(pool,corev1.EventTypeNormal,"test","test")pool.Status.Status=200err=r.Status().Update(ctx,pool)returnctrl.Result{},err}添加后需要在main.go中添加Recorder的初始化逻辑iferr=(&controllers.NodePoolReconciler{Client:mgr.GetClient(),Log:ctrl.Log.WithName("controllers").WithName("NodePool"),Scheme:mgr.GetScheme(),+Recorder:mgr.GetEventRecorderFor("NodePool"),}).SetupWithManager(mgr);err!=nil{setupLog.Error(err,"unabletocreatecontroller","controller","NodePool")os.Exit(1)}添加之后,我们运行一下,然后在describeNodepool对象Events:TypeReasonAgeFromMessage中可以看到事件信息----------------------Normaltest4sNodePooltest之前听更多的资源,我们所有的代码都是围绕着NodePool的变化,但是如果我们修改Node的话,添加Node到一个NodePool,Node上对应的属性和NodePool的状态信息会我不换。如果我们要达到上面的效果,就需要监听更多的资源变化。在控制器中我们可以看到一个SetupWithManager方法,它解释了我们需要监控哪些资源发生变化{}).Complete(r)}其中,NewControllerManagedBy是builder模式,返回一个builder对象,里面包含了For,Owns,Watches,WithEventFilter等方法进行构建这里我们可以使用``Watches方法来监听变化Node的,这里我们使用handler.Funcs自定义了一个enqueue来监听Node对象的更新事件,如果NodePool关联的node对象有更新,就会将对应的NodePool入队//SetupWithManagersetsupthecontrollerwiththeManager.func(r*NodePoolReconciler)SetupWithManager(mgrctrl.Manager)error{returnctrl.NewControllerManagedBy(mgr).For(&nodesv1.NodePool{}).Watches(&source.Kind{Type:&corev1.Node{}},handler.Funcs{UpdateFunc:r.nodeUpdateHandler}).Complete(r)}func(r*NodePoolReconciler)nodeUpdateHandler(eevent.UpdateEvent,qworkqueue.RateLimitingInterface){ctx,cancel:=context.WithTimeout(context.Background(),5*time.Second)延迟取消()oldPool,err:=r.getNodePoolByLabels(ctx,e.ObjectOld.GetLabels())iferr!=nil{r.Log.Error(err,"getnodepoolerr")}ifoldPool!=nil{q.Add(reconcile.Request{NamespacedName:types.NamespacedName{Name:oldPool.Name},})}newPool,err:=r.getNodePoolByLabels(ctx,e.ObjectOld.GetLabels())iferr!=nil{r.Log.Error(err,"getnodepoolerr"))}ifnewPool!=nil{q.Add(reconcile.Request{NamespacedName:types.NamespacedName{Name:newPool.Name},})}}func(r*NodePoolReconciler)getNodePoolByLabels(ctxcontext.Context,labelsmap[string]string)(*nodesv1.NodePool,error){pool:=&nodesv1.NodePool{}fork:=rangelabels{ss:=strings.Split(k,"node-role.kubernetes.io/")iflen(ss)!=2{continue}err:=r.Client.Get(ctx,types.NamespacedName{Name:ss[1]},pool)iferr==nil{returnpool,nil}ifclient.IgnoreNotFound(err)!=nil{returnnil,err}}returnil,nil}小结今天我们完善了status&event和自定义objectwatch,在下一篇文章中,我们将看到如何测试我们的Operator
