背景:
上一节(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-sample
deployment.apps "jan-sample" deleted
[zhangpeng@zhangpeng jan]$ kubectl get deployment
No 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.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)
}
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.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)
}
总结
- owns 的个别应用
- 将 deployment service ingress 或者其余资源作为 operator 利用的子资源,进行生命周期治理
- 下一步想解决一下 make run 控制台的输入,输入一些有用的信息