• Operator3-设计一个operator二-owns的使用


    背景:

    上一节(Operator3-设计一个operator)做完发现一个问题 我创建了jan 应用jan-sample,子资源包括deployment,service.ingress,pod(其中pod是deployment管理的)
    image.png
    手动删除Pod.由于Deployment rc控制器。Pod资源可以自动重建。但是我删除deployment能不能自动重建呢?正常的deployment service ingress子资源的生命周期,我应该是靠jan应用去维系的,试一试:

    [zhangpeng@zhangpeng jan]$ kubectl delete deployment jan-sample
    deployment.apps "jan-sample" deleted
    [zhangpeng@zhangpeng jan]$ kubectl get deployment
    No resources found in default namespace.
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    image.png
    到这里才发现没有考虑周全…删除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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Deployment delete尝试

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

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

    image.png

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

    image.png
    恩发现deployment应用可以自动重建了!

    image.png

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    尝试了一下不生效的,但是这种方式思路是对的至于为什么不生效呢?
    deploy声明了&appv1.Deployment,但是service,ingress是没有创建变量声明的!
    image.png

    拆分改造代码

    继续改造Jan operator使其支持service ingress子资源的误删除创建:
    image.png
    把这边拆分一下?
    image.png

    image.png
    image.png
    image.png
    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.0
    
    Unless required by applicable law or agreed to in writing, software
    distributed 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 and
    limitations under the License.
    */
    
    package jan
    
    import (
    	"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 object
    type 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/reconcile
    func (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)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218

    make run尝试一下:
    image.png
    注意:make run之前默认删除jan应用!

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

    en service的自动恢复生效了
    image.png
    然后试一试ingress

    [zhangpeng@zhangpeng develop-operator]$ kubectl get ingress
    [zhangpeng@zhangpeng develop-operator]$ kubectl delete ingress jan-sample
    [zhangpeng@zhangpeng develop-operator]$ kubectl get ingress
    
    
    • 1
    • 2
    • 3
    • 4

    image.png
    继续发现问题:
    en,我修改一下jan_v1_jan.yaml中host ww1.zhangpeng.com修改为ww11.zhangpeng.com,but ingress的相关信息没有及时更新啊?
    image.png
    继续模仿一下上面的service oldservice newservice新增 newIngress oldIngress :
    image.png
    重新make run
    ingress相关信息得到了修改
    image.png

    最终代码:

    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.0
    
    Unless required by applicable law or agreed to in writing, software
    distributed 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 and
    limitations under the License.
    */
    
    package jan
    
    import (
    	"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 object
    type 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/reconcile
    func (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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227

    总结

    1. owns的一般使用
    2. 将 deployment service ingress或者其他资源作为operator应用的子资源,进行生命周期管理
    3. 下一步想处理一下 make run 控制台的输出,输出一些有用的信息
  • 相关阅读:
    基于python的NBA球员数据可视化分析的设计与实现
    vue前端 页面样式强制覆盖
    springboot配置文件加载顺序
    腾讯148道面试题,(程序员必备学习方向)全会拿45Koffer没问题
    [Android]修改XML中定义的约束比例
    5. informer源码分析-概要分析
    hive操作
    Python eval 函数动态地计算数学表达式
    链接脚本
    大数据要怎么样学才可以到企业级实战
  • 原文地址:https://blog.csdn.net/saynaihe/article/details/126253433