背景:

上一节(Operator3-设计一个operator)做完发现一个问题 我创立了jan 利用jan-sample,子资源包含deployment,service.ingress,pod(其中pod是deployment治理的)

手动删除Pod.因为Deployment rc控制器。Pod资源能够主动重建。然而我删除deployment能不能主动重建呢?失常的deployment service ingress子资源的生命周期,我应该是靠jan利用去维系的,试一试:

[zhangpeng@zhangpeng jan]$ kubectl delete deployment jan-sampledeployment.apps "jan-sample" deleted[zhangpeng@zhangpeng jan]$ kubectl get deploymentNo resources found in default namespace.


到这里才发现没有思考周全......删除deployment资源并不能重建,失常创立利用应该要考虑一下jan资源上面资源的重建.搜了一下他人写的operator貌似的能够加一下Owns,尝试一下!

Deployment Ingress Service对于Owns的应用

Deployment

func (r *JanReconciler) SetupWithManager(mgr ctrl.Manager) error {    return ctrl.NewControllerManagedBy(mgr).        For(&janv1.Jan{}).        Owns(&appsv1.Deployment{}).        Complete(r)}

Deployment delete尝试

make run develop-operator我的项目,并尝试delete deployment jan-sample查看是否重建:

[zhangpeng@zhangpeng develop-operator]$ kubectl get Jan[zhangpeng@zhangpeng develop-operator]$ kubectl get all

[zhangpeng@zhangpeng develop-operator]$ kubectl delete deployment jan-sample[zhangpeng@zhangpeng develop-operator]$ kubectl get deployment


恩发现deployment利用能够主动重建了!

Ingress and Service资源

简略增加一下Owns?

but其余资源是否能够呢?是不是也偷懒一下增加Owns

func (r *JanReconciler) SetupWithManager(mgr ctrl.Manager) error {    return ctrl.NewControllerManagedBy(mgr).        For(&janv1.Jan{}).        Owns(&appsv1.Deployment{}).        Owns(&corev1.Service{}).        Owns(&v1.Ingress{}).        Complete(r)}

尝试了一下不失效的,然而这种形式思路是对的至于为什么不失效呢?
deploy申明了&appv1.Deployment,然而service,ingress是没有创立变量申明的!

拆分革新代码

持续革新Jan operator使其反对service ingress子资源的误删除创立:

把这边拆分一下?




jan_controller.go

/*Copyright 2022 zhang peng.Licensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the License.You may obtain a copy of the License at    http://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.*/package janimport (    "context"    "encoding/json"    appsv1 "k8s.io/api/apps/v1"    corev1 "k8s.io/api/core/v1"    v1 "k8s.io/api/networking/v1"    "k8s.io/apimachinery/pkg/api/errors"    "k8s.io/apimachinery/pkg/runtime"    utilruntime "k8s.io/apimachinery/pkg/util/runtime"    "reflect"    ctrl "sigs.k8s.io/controller-runtime"    "sigs.k8s.io/controller-runtime/pkg/client"    "sigs.k8s.io/controller-runtime/pkg/log"    "sigs.k8s.io/controller-runtime/pkg/reconcile"    janv1 "develop-operator/apis/jan/v1")// JanReconciler reconciles a Jan objecttype JanReconciler struct {    client.Client    Scheme *runtime.Scheme}//+kubebuilder:rbac:groups=mar.zhangpeng.com,resources=jan,verbs=get;list;watch;create;update;patch;delete//+kubebuilder:rbac:groups=mar.zhangpeng.com,resources=jan/status,verbs=get;update;patch//+kubebuilder:rbac:groups=mar.zhangpeng.com,resources=jan/finalizers,verbs=update//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete//+kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete//+kubebuilder:rbac:groups=networking,resources=ingresses,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.// TODO(user): Modify the Reconcile function to compare the state specified by// the Jan object against the actual cluster state, and then// perform operations to make the cluster state reflect the state specified by// the user.//// For more details, check Reconcile and its Result here:// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.11.2/pkg/reconcilefunc (r *JanReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {    defer utilruntime.HandleCrash()    _ = log.FromContext(ctx)    instance := &janv1.Jan{}    err := r.Client.Get(context.TODO(), req.NamespacedName, instance)    if err != nil {        if errors.IsNotFound(err) {            // Request object not found, could have been deleted after reconcile request.            // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.            // Return and don't requeue            return reconcile.Result{}, nil        }        // Error reading the object - requeue the request.        return reconcile.Result{}, err    }    if instance.DeletionTimestamp != nil {        return reconcile.Result{}, err    }    // 如果不存在,则创立关联资源    // 如果存在,判断是否须要更新    //   如果须要更新,则间接更新    //   如果不须要更新,则失常返回    deploy := &appsv1.Deployment{}    if err := r.Client.Get(context.TODO(), req.NamespacedName, deploy); err != nil && errors.IsNotFound(err) {        // 创立关联资源        // 1. 创立 Deploy        deploy := NewJan(instance)        if err := r.Client.Create(context.TODO(), deploy); err != nil {            return reconcile.Result{}, err        }        // 4. 关联 Annotations        data, _ := json.Marshal(instance.Spec)        if instance.Annotations != nil {            instance.Annotations["spec"] = string(data)        } else {            instance.Annotations = map[string]string{"spec": string(data)}        }        if err := r.Client.Update(context.TODO(), instance); err != nil {            return reconcile.Result{}, nil        }        return reconcile.Result{}, nil    }    Service := &corev1.Service{}    if err := r.Client.Get(context.TODO(), req.NamespacedName, Service); err != nil && errors.IsNotFound(err) {        // 2. 创立 Service        service := NewService(instance)        if err := r.Client.Create(context.TODO(), service); err != nil {            return reconcile.Result{}, err        }        // 4. 关联 Annotations        data, _ := json.Marshal(service.Spec)        if service.Annotations != nil {            service.Annotations["spec"] = string(data)        } else {            service.Annotations = map[string]string{"spec": string(data)}        }        if err := r.Client.Update(context.TODO(), service); err != nil {            return reconcile.Result{}, nil        }        return reconcile.Result{}, nil    }    Ingress := &v1.Ingress{}    if err := r.Client.Get(context.TODO(), req.NamespacedName, Ingress); err != nil && errors.IsNotFound(err) {        // 2. 创立 Service        ingress := NewIngress(instance)        if err := r.Client.Create(context.TODO(), ingress); err != nil {            return reconcile.Result{}, err        }        // 4. 关联 Annotations        data, _ := json.Marshal(ingress.Spec)        if ingress.Annotations != nil {            ingress.Annotations["spec"] = string(data)        } else {            ingress.Annotations = map[string]string{"spec": string(data)}        }        if err := r.Client.Update(context.TODO(), ingress); err != nil {            return reconcile.Result{}, nil        }        return reconcile.Result{}, nil    }    oldspec := janv1.JanSpec{}    if err := json.Unmarshal([]byte(instance.Annotations["spec"]), &oldspec); err != nil {        return reconcile.Result{}, err    }    if !reflect.DeepEqual(instance.Spec, oldspec) {        data, _ := json.Marshal(instance.Spec)        if instance.Annotations != nil {            instance.Annotations["spec"] = string(data)        } else {            instance.Annotations = map[string]string{"spec": string(data)}        }        if err := r.Client.Update(context.TODO(), instance); err != nil {            return reconcile.Result{}, nil        }        // 更新关联资源        newDeploy := NewJan(instance)        oldDeploy := &appsv1.Deployment{}        if err := r.Client.Get(context.TODO(), req.NamespacedName, oldDeploy); err != nil {            return reconcile.Result{}, err        }        oldDeploy.Spec = newDeploy.Spec        if err := r.Client.Update(context.TODO(), oldDeploy); err != nil {            return reconcile.Result{}, err        }        newService := NewService(instance)        oldService := &corev1.Service{}        if err := r.Client.Get(context.TODO(), req.NamespacedName, oldService); err != nil {            return reconcile.Result{}, err        }        oldService.Spec = newService.Spec        if err := r.Client.Update(context.TODO(), oldService); err != nil {            return reconcile.Result{}, err        }        return reconcile.Result{}, nil    }    newStatus := janv1.JanStatus{        Replicas:      *instance.Spec.Replicas,        ReadyReplicas: instance.Status.Replicas,    }    if newStatus.Replicas == newStatus.ReadyReplicas {        newStatus.Phase = janv1.Running    } else {        newStatus.Phase = janv1.NotReady    }    if !reflect.DeepEqual(instance.Status, newStatus) {        instance.Status = newStatus        log.FromContext(ctx).Info("update game status", "name", instance.Name)        err = r.Client.Status().Update(ctx, instance)        if err != nil {            return reconcile.Result{}, err        }    }    return reconcile.Result{}, nil}// SetupWithManager sets up the controller with the Manager.func (r *JanReconciler) SetupWithManager(mgr ctrl.Manager) error {    return ctrl.NewControllerManagedBy(mgr).        For(&janv1.Jan{}).        Owns(&appsv1.Deployment{}).        Owns(&corev1.Service{}).        Owns(&v1.Ingress{}).        Complete(r)}

make run尝试一下:

留神:make run之前默认删除jan利用!

[zhangpeng@zhangpeng develop-operator]$ kubectl delete svc jan-sample[zhangpeng@zhangpeng develop-operator]$ kubectl get svc

en service的主动复原失效了

而后试一试ingress

[zhangpeng@zhangpeng develop-operator]$ kubectl get ingress[zhangpeng@zhangpeng develop-operator]$ kubectl delete ingress jan-sample[zhangpeng@zhangpeng develop-operator]$ kubectl get ingress


持续发现问题:
en,我批改一下jan_v1_jan.yaml中host ww1.zhangpeng.com批改为ww11.zhangpeng.com,but ingress的相干信息没有及时更新啊?

持续模拟一下下面的service oldservice newservice新增 newIngress oldIngress :

从新make run
ingress相干信息失去了批改

最终代码:

jan_controller.go

/*Copyright 2022 zhang peng.Licensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the License.You may obtain a copy of the License at    http://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.*/package janimport (    "context"    "encoding/json"    appsv1 "k8s.io/api/apps/v1"    corev1 "k8s.io/api/core/v1"    v1 "k8s.io/api/networking/v1"    "k8s.io/apimachinery/pkg/api/errors"    "k8s.io/apimachinery/pkg/runtime"    utilruntime "k8s.io/apimachinery/pkg/util/runtime"    "reflect"    ctrl "sigs.k8s.io/controller-runtime"    "sigs.k8s.io/controller-runtime/pkg/client"    "sigs.k8s.io/controller-runtime/pkg/log"    "sigs.k8s.io/controller-runtime/pkg/reconcile"    janv1 "develop-operator/apis/jan/v1")// JanReconciler reconciles a Jan objecttype JanReconciler struct {    client.Client    Scheme *runtime.Scheme}//+kubebuilder:rbac:groups=mar.zhangpeng.com,resources=jan,verbs=get;list;watch;create;update;patch;delete//+kubebuilder:rbac:groups=mar.zhangpeng.com,resources=jan/status,verbs=get;update;patch//+kubebuilder:rbac:groups=mar.zhangpeng.com,resources=jan/finalizers,verbs=update//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete//+kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete//+kubebuilder:rbac:groups=networking,resources=ingresses,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.// TODO(user): Modify the Reconcile function to compare the state specified by// the Jan object against the actual cluster state, and then// perform operations to make the cluster state reflect the state specified by// the user.//// For more details, check Reconcile and its Result here:// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.11.2/pkg/reconcilefunc (r *JanReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {    defer utilruntime.HandleCrash()    _ = log.FromContext(ctx)    instance := &janv1.Jan{}    err := r.Client.Get(context.TODO(), req.NamespacedName, instance)    if err != nil {        if errors.IsNotFound(err) {            // Request object not found, could have been deleted after reconcile request.            // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.            // Return and don't requeue            return reconcile.Result{}, nil        }        // Error reading the object - requeue the request.        return reconcile.Result{}, err    }    if instance.DeletionTimestamp != nil {        return reconcile.Result{}, err    }    // 如果不存在,则创立关联资源    // 如果存在,判断是否须要更新    //   如果须要更新,则间接更新    //   如果不须要更新,则失常返回    deploy := &appsv1.Deployment{}    if err := r.Client.Get(context.TODO(), req.NamespacedName, deploy); err != nil && errors.IsNotFound(err) {        // 创立关联资源        // 1. 创立 Deploy        deploy := NewJan(instance)        if err := r.Client.Create(context.TODO(), deploy); err != nil {            return reconcile.Result{}, err        }        // 4. 关联 Annotations        data, _ := json.Marshal(instance.Spec)        if instance.Annotations != nil {            instance.Annotations["spec"] = string(data)        } else {            instance.Annotations = map[string]string{"spec": string(data)}        }        if err := r.Client.Update(context.TODO(), instance); err != nil {            return reconcile.Result{}, nil        }        return reconcile.Result{}, nil    }    Service := &corev1.Service{}    if err := r.Client.Get(context.TODO(), req.NamespacedName, Service); err != nil && errors.IsNotFound(err) {        // 2. 创立 Service        service := NewService(instance)        if err := r.Client.Create(context.TODO(), service); err != nil {            return reconcile.Result{}, err        }        // 4. 关联 Annotations        data, _ := json.Marshal(service.Spec)        if service.Annotations != nil {            service.Annotations["spec"] = string(data)        } else {            service.Annotations = map[string]string{"spec": string(data)}        }        if err := r.Client.Update(context.TODO(), service); err != nil {            return reconcile.Result{}, nil        }        return reconcile.Result{}, nil    }    Ingress := &v1.Ingress{}    if err := r.Client.Get(context.TODO(), req.NamespacedName, Ingress); err != nil && errors.IsNotFound(err) {        // 2. 创立 Ingress        ingress := NewIngress(instance)        if err := r.Client.Create(context.TODO(), ingress); err != nil {            return reconcile.Result{}, err        }        // 4. 关联 Annotations        data, _ := json.Marshal(ingress.Spec)        if ingress.Annotations != nil {            ingress.Annotations["spec"] = string(data)        } else {            ingress.Annotations = map[string]string{"spec": string(data)}        }        if err := r.Client.Update(context.TODO(), ingress); err != nil {            return reconcile.Result{}, nil        }        return reconcile.Result{}, nil    }    oldspec := janv1.JanSpec{}    if err := json.Unmarshal([]byte(instance.Annotations["spec"]), &oldspec); err != nil {        return reconcile.Result{}, err    }    if !reflect.DeepEqual(instance.Spec, oldspec) {        data, _ := json.Marshal(instance.Spec)        if instance.Annotations != nil {            instance.Annotations["spec"] = string(data)        } else {            instance.Annotations = map[string]string{"spec": string(data)}        }        if err := r.Client.Update(context.TODO(), instance); err != nil {            return reconcile.Result{}, nil        }        // 更新关联资源        newDeploy := NewJan(instance)        oldDeploy := &appsv1.Deployment{}        if err := r.Client.Get(context.TODO(), req.NamespacedName, oldDeploy); err != nil {            return reconcile.Result{}, err        }        oldDeploy.Spec = newDeploy.Spec        if err := r.Client.Update(context.TODO(), oldDeploy); err != nil {            return reconcile.Result{}, err        }        newService := NewService(instance)        oldService := &corev1.Service{}        if err := r.Client.Get(context.TODO(), req.NamespacedName, oldService); err != nil {            return reconcile.Result{}, err        }        oldService.Spec = newService.Spec        if err := r.Client.Update(context.TODO(), oldService); err != nil {            return reconcile.Result{}, err        }        newIngress := NewIngress(instance)        oldIngress := &v1.Ingress{}        if err := r.Client.Get(context.TODO(), req.NamespacedName, oldIngress); err != nil {            return reconcile.Result{}, err        }        oldIngress.Spec = newIngress.Spec        if err := r.Client.Update(context.TODO(), oldIngress); err != nil {            return reconcile.Result{}, err        }        return reconcile.Result{}, nil    }    newStatus := janv1.JanStatus{        Replicas:      *instance.Spec.Replicas,        ReadyReplicas: instance.Status.Replicas,    }    if newStatus.Replicas == newStatus.ReadyReplicas {        newStatus.Phase = janv1.Running    } else {        newStatus.Phase = janv1.NotReady    }    if !reflect.DeepEqual(instance.Status, newStatus) {        instance.Status = newStatus        log.FromContext(ctx).Info("update game status", "name", instance.Name)        err = r.Client.Status().Update(ctx, instance)        if err != nil {            return reconcile.Result{}, err        }    }    return reconcile.Result{}, nil}// SetupWithManager sets up the controller with the Manager.func (r *JanReconciler) SetupWithManager(mgr ctrl.Manager) error {    return ctrl.NewControllerManagedBy(mgr).        For(&janv1.Jan{}).        Owns(&appsv1.Deployment{}).        Owns(&corev1.Service{}).        Owns(&v1.Ingress{}).        Complete(r)}

总结

  1. owns的个别应用
  2. 将 deployment service ingress或者其余资源作为operator利用的子资源,进行生命周期治理
  3. 下一步想解决一下 make run 控制台的输入,输入一些有用的信息