Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Operator SDK 帶你玩轉 Kubernetes

Operator SDK 帶你玩轉 Kubernetes

[CC-BY-SA 4.0]

從觀念角度介紹 Kubernetes 的 Operator Pattern 如何解決大家在管理叢集時繁瑣卻又重複的人工作業,並使用 Operator SDK 來示範如何自己撰寫 Golang Based Operator 來解決問題。

先從 Operator Pattern 介紹整個概念,並舉出幾個例子來讓聽眾理解如何將 Kubernetes Controller 參與其中,以扮演整個 Life-cycle 中負責進行原本重複的人工作業。
舉出幾個常見的 Operator Pattern 並以 Operator SDK 為例,介紹如何如何自己撰寫 Golang Based Operator 搭配 Kubernetes CRD 來擴充 K8s 的功能。

David Kuo (Davy)

August 01, 2020
Tweet

More Decks by David Kuo (Davy)

Other Decks in Technology

Transcript

  1. 1

  2. 10 DevOps Kubernetes Cluster Bot (or scripts) bot deploy …

    kubtctl apply … kubtctl get … kubtctl edit …
  3. 11 DevOps Kubernetes Cluster Bot (or scripts) bot deploy …

    kubtctl apply … kubtctl get … kubtctl edit … bot update …
  4. 12 DevOps Kubernetes Cluster Bot (or scripts) bot deploy …

    kubtctl apply … kubtctl get … kubtctl edit … bot update … alert
  5. 13 DevOps Kubernetes Cluster Bot (or scripts) bot deploy …

    kubtctl apply … kubtctl get … kubtctl edit … bot update … alert https://www.botkube.io/
  6. 14 DevOps Kubernetes Cluster Bot (or scripts) bot deploy …

    kubtctl apply … kubtctl get … kubtctl edit … bot update … alert K8s Cluster 1 K8s Cluster 2 Communicate Channel https://www.botkube.io/
  7. 15 DevOps Kubernetes Cluster Bot (or scripts) bot deploy …

    kubtctl apply … kubtctl get … kubtctl edit … bot update … alert https://github.com/learnk8s/xlskubectl
  8. 24

  9. 25

  10. 29

  11. 30

  12. 31

  13. 32

  14. 45

  15. 47

  16. 48

  17. 49

  18. 50

  19. 56

  20. 59 YAML // Memcached is the Schema for the memcacheds

    API // +k8s:openapi-gen=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=memcached,scope=Namespaced, shortName=mc // +kubebuilder:printcolumn:name="Size",type=integer, JSONPath=`.spec.size", description="Size of the memcached deployment" // +kubebuilder:printcolumn:name="Nodes",type=string, JSONPath=".status.nodes[*]"
  21. 60 YAML // Memcached is the Schema for the memcacheds

    API // +k8s:openapi-gen=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=memcached,scope=Namespaced, shortName=mc // +kubebuilder:printcolumn:name="Size",type=integer, JSONPath=`.spec.size", description="Size of the memcached deployment" // +kubebuilder:printcolumn:name="Nodes",type=string, JSONPath=".status.nodes[*]" // +kubebuilder:rbac:groups=cache.example.com, resources=memcacheds, verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cache.example.com, resources=memcacheds/status, verbs=get;update;patch // +kubebuilder:rbac:groups=apps, resources=deployments, verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core, resources=pods, verbs=get;list;
  22. 62

  23. Queue Reconciler 64 // add new Controller to mgr with

    r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { return ctrl.NewControllerManagedBy(mgr). Named("memcached-controller"). For(&cachev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Complete(r) }
  24. Queue Reconciler 65 // add new Controller to mgr with

    r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { return ctrl.NewControllerManagedBy(mgr). Named("memcached-controller"). For(&cachev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Complete(r) } Watch Memcached changes and enqueue Memcached
  25. Queue Reconciler 66 // add new Controller to mgr with

    r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { return ctrl.NewControllerManagedBy(mgr). Named("memcached-controller"). For(&cachev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Complete(r) } Watch Deployment changes and enqueue Memcached owner Watch Memcached changes and enqueue Memcached
  26. Queue Reconciler 68 // add new Controller to mgr with

    r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { return ctrl.NewControllerManagedBy(mgr). Named("memcached-controller"). For(&cachev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). WithEventFilter(&predicate.ResourceVersionChangedPredicate{}). Complete(r) }
  27. Queue Reconciler 69 // add new Controller to mgr with

    r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { return ctrl.NewControllerManagedBy(mgr). Named("memcached-controller"). For(&cachev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). WithEventFilter(&predicate.ResourceVersionChangedPredicate{}). Complete(r) } // Or ... predicate.NewPredicateFuncs( func(meta metav1.Object, object runtime.Object) bool { // filtering... return true })
  28. Queue Reconciler 70 // More powerful predicate.Funcs{ CreateFunc: func(e event.CreateEvent)

    bool { // ... }, UpdateFunc: func(e event.UpdateEvent) bool { // ... }, DeleteFunc: func(e event.DeleteEvent) bool { // ... }, GenericFunc: func(e event.GenericEvent) bool { // ... }, }
  29. Queue Controller 73 // The Controller will requeue the Request

    to be processed again // if the returned error is non-nil or Result.Requeue is true, // otherwise upon completion will remove the work from the queue. func (r *Reconciler) Reconcile(request reconcile.Request) ( reconcile.Result, error) { // ... }
  30. Queue Controller 74 // The Controller will requeue the Request

    to be processed again // if the returned error is non-nil or Result.Requeue is true, // otherwise upon completion will remove the work from the queue. func (r *Reconciler) Reconcile(request reconcile.Request) ( reconcile.Result, error) { // ... } Reconcile
  31. Queue Controller 75 Reconcile // Fetch the Memcached instance memcached

    := &cachev1alpha1.Memcached{} err := r.client.Get(context.TODO(), request.NamespacedName, memcached) if err != nil { if errors.IsNotFound(err) { reqLogger.Info("Ignoring since object must be deleted.") return reconcile.Result{}, nil } reqLogger.Error(err, "Failed to get Memcached.") return reconcile.Result{}, err }
  32. Queue Controller 76 Reconcile // Fetch the Memcached instance memcached

    := &cachev1alpha1.Memcached{} err := r.client.Get(context.TODO(), request.NamespacedName, memcached) if err != nil { if errors.IsNotFound(err) { reqLogger.Info("Ignoring since object must be deleted.") return reconcile.Result{}, nil } reqLogger.Error(err, "Failed to get Memcached.") return reconcile.Result{}, err } Fetch Resource
  33. Queue Controller 77 Reconcile Fetch Resource // Check if the

    Deployment already exists, if not create a new one deployment := &appsv1.Deployment{} err = r.client.Get(context.TODO(), types.NamespacedName{ Name: memcached.Name, Namespace: memcached.Namespace, }, deployment) if err != nil && errors.IsNotFound(err) { dep := r.deploymentForMemcached(memcached) err = r.client.Create(context.TODO(), dep) if err != nil { return reconcile.Result{}, err } return reconcile.Result{Requeue: true}, nil } else if err != nil { return reconcile.Result{}, err }
  34. Queue Controller 78 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Check if the Deployment already exists, if not create a new one deployment := &appsv1.Deployment{} err = r.client.Get(context.TODO(), types.NamespacedName{ Name: memcached.Name, Namespace: memcached.Namespace, }, deployment) if err != nil && errors.IsNotFound(err) { dep := r.deploymentForMemcached(memcached) err = r.client.Create(context.TODO(), dep) if err != nil { return reconcile.Result{}, err } return reconcile.Result{Requeue: true}, nil } else if err != nil { return reconcile.Result{}, err }
  35. Queue Controller 79 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Check if the Deployment already exists, if not create a new one deployment := &appsv1.Deployment{} err = r.client.Get(context.TODO(), types.NamespacedName{ Name: memcached.Name, Namespace: memcached.Namespace, }, deployment) if err != nil && errors.IsNotFound(err) { dep := r.deploymentForMemcached(memcached) err = r.client.Create(context.TODO(), dep) if err != nil { return reconcile.Result{}, err } return reconcile.Result{Requeue: true}, nil } else if err != nil { return reconcile.Result{}, err }
  36. Queue Controller 80 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Check if the Deployment already exists, if not create a new one deployment := &appsv1.Deployment{} err = r.client.Get(context.TODO(), types.NamespacedName{ Name: memcached.Name, Namespace: memcached.Namespace, }, deployment) if err != nil && errors.IsNotFound(err) { dep := r.deploymentForMemcached(memcached) err = r.client.Create(context.TODO(), dep) if err != nil { return reconcile.Result{}, err } return reconcile.Result{Requeue: true}, nil } else if err != nil { return reconcile.Result{}, err } // deploymentForMemcached returns a memcached Deployment object func (r *Reconciler) deploymentForMemcached(m *cachev1alpha1.Memcached) *appsv1.Deployment { dep := &appsv1.Deployment{ /* ... */ } // Set Memcached instance as the owner of the Deployment. controllerutil.SetControllerReference(m, dep, r.scheme) return dep }
  37. Queue Controller 81 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Check if the Deployment already exists, if not create a new one deployment := &appsv1.Deployment{} err = r.client.Get(context.TODO(), types.NamespacedName{ Name: memcached.Name, Namespace: memcached.Namespace, }, deployment) if err != nil && errors.IsNotFound(err) { dep := r.deploymentForMemcached(memcached) err = r.client.Create(context.TODO(), dep) if err != nil { return reconcile.Result{}, err } return reconcile.Result{Requeue: true}, nil } else if err != nil { return reconcile.Result{}, err } // deploymentForMemcached returns a memcached Deployment object func (r *Reconciler) deploymentForMemcached(m *cachev1alpha1.Memcached) *appsv1.Deployment { dep := &appsv1.Deployment{ /* ... */ } // Set Memcached instance as the owner of the Deployment. controllerutil.SetControllerReference(m, dep, r.scheme) return dep } Deployment ownerRef:
  38. Queue Controller 82 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Check if the Deployment already exists, if not create a new one deployment := &appsv1.Deployment{} err = r.client.Get(context.TODO(), types.NamespacedName{ Name: memcached.Name, Namespace: memcached.Namespace, }, deployment) if err != nil && errors.IsNotFound(err) { dep := r.deploymentForMemcached(memcached) err = r.client.Create(context.TODO(), dep) if err != nil { return reconcile.Result{}, err } return reconcile.Result{Requeue: true}, nil } else if err != nil { return reconcile.Result{}, err } // deploymentForMemcached returns a memcached Deployment object func (r *Reconciler) deploymentForMemcached(m *cachev1alpha1.Memcached) *appsv1.Deployment { dep := &appsv1.Deployment{ /* ... */ } // Set Memcached instance as the owner of the Deployment. controllerutil.SetControllerReference(m, dep, r.scheme) return dep } Deployment ownerRef: Memcached
  39. Queue Controller 83 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Check if the Deployment already exists, if not create a new one deployment := &appsv1.Deployment{} err = r.client.Get(context.TODO(), types.NamespacedName{ Name: memcached.Name, Namespace: memcached.Namespace, }, deployment) if err != nil && errors.IsNotFound(err) { dep := r.deploymentForMemcached(memcached) err = r.client.Create(context.TODO(), dep) if err != nil { return reconcile.Result{}, err } return reconcile.Result{Requeue: true}, nil } else if err != nil { return reconcile.Result{}, err }
  40. Queue Controller 84 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Check if the Deployment already exists, if not create a new one deployment := &appsv1.Deployment{} err = r.client.Get(context.TODO(), types.NamespacedName{ Name: memcached.Name, Namespace: memcached.Namespace, }, deployment) if err != nil && errors.IsNotFound(err) { dep := r.deploymentForMemcached(memcached) err = r.client.Create(context.TODO(), dep) if err != nil { return reconcile.Result{}, err } return reconcile.Result{Requeue: true}, nil } else if err != nil { return reconcile.Result{}, err }
  41. Queue Controller 85 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Ensure the deployment size is the same as the spec size := memcached.Spec.Size if *deployment.Spec.Replicas != size { deployment.Spec.Replicas = &size err = r.client.Update(context.TODO(), deployment) if err != nil { return reconcile.Result{}, err } }
  42. Queue Controller 86 Reconcile Fetch Resource Create Deployment Fetch Deployment

    // Ensure the deployment size is the same as the spec size := memcached.Spec.Size if *deployment.Spec.Replicas != size { deployment.Spec.Replicas = &size err = r.client.Update(context.TODO(), deployment) if err != nil { return reconcile.Result{}, err } } Reconcile Deployment
  43. Queue Controller 87 Reconcile Fetch Resource Create Deployment Fetch Deployment

    Reconcile Deployment // Update the Memcached status with the pod names // List the pod names for this memcached's deployment podNames := getPodNames(podList.Items) // Update status.Nodes if needed if !reflect.DeepEqual(podNames, memcached.Status.Nodes) { memcached.Status.Nodes = podNames err := r.client.Status().Update(context.TODO(), memcached) if err != nil { return reconcile.Result{}, err } }
  44. Queue Controller 88 Reconcile Fetch Resource Create Deployment Fetch Deployment

    Reconcile Deployment // Update the Memcached status with the pod names // List the pod names for this memcached's deployment podNames := getPodNames(podList.Items) // Update status.Nodes if needed if !reflect.DeepEqual(podNames, memcached.Status.Nodes) { memcached.Status.Nodes = podNames err := r.client.Status().Update(context.TODO(), memcached) if err != nil { return reconcile.Result{}, err } } Update Resource Status
  45. 90

  46. Deployment ownerRef: Memcached ReplicaSet ownerRef: Pod ownerRef: 97 // This

    is the equivalent of calling // Watches(&source.Kind{Type: <ForType-forInput>}, // &handler.EnqueueRequestForOwner{ // OwnerType: apiType, IsController: true, // }) func (blder *Builder) Owns(object runtime.Object, opts ...OwnsOption) *Builder { input := OwnsInput{object: object} for _, opt := range opts { opt.ApplyToOwns(&input) } blder.ownsInput = append(blder.ownsInput, input) return blder } https://github.com/kubernetes-sigs/controller-runtime/blob/v0.6.1/pkg/builder/controller.go
  47. 98 // This is the equivalent of calling // Watches(&source.Kind{Type:

    <ForType-forInput>}, // &handler.EnqueueRequestForOwner{ // OwnerType: apiType, IsController: true, // }) func (blder *Builder) Owns(object runtime.Object, opts ...OwnsOption) *Builder { input := OwnsInput{object: object} for _, opt := range opts { opt.ApplyToOwns(&input) } blder.ownsInput = append(blder.ownsInput, input) return blder } https://github.com/kubernetes-sigs/controller-runtime/blob/v0.6.1/pkg/builder/controller.go
  48. Deployment ownerRef: Memcached 99 // This is the equivalent of

    calling // Watches(&source.Kind{Type: <ForType-forInput>}, // &handler.EnqueueRequestForOwner{ // OwnerType: apiType, IsController: true, // }) func (blder *Builder) Owns(object runtime.Object, opts ...OwnsOption) *Builder { input := OwnsInput{object: object} for _, opt := range opts { opt.ApplyToOwns(&input) } blder.ownsInput = append(blder.ownsInput, input) return blder } https://github.com/kubernetes-sigs/controller-runtime/blob/v0.6.1/pkg/builder/controller.go func (blder *Builder) doWatch() error { // ... // Watches the managed types for _, own := range blder.ownsInput { src := &source.Kind{Type: own.object} hdler := &handler.EnqueueRequestForOwner{ OwnerType: blder.forInput.object, IsController: true, } allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...) allPredicates = append(allPredicates, own.predicates...) if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil { return err } } // ... }
  49. 100 // This is the equivalent of calling // Watches(&source.Kind{Type:

    <ForType-forInput>}, // &handler.EnqueueRequestForOwner{ // OwnerType: apiType, IsController: true, // }) func (blder *Builder) Owns(object runtime.Object, opts ...OwnsOption) *Builder { input := OwnsInput{object: object} for _, opt := range opts { opt.ApplyToOwns(&input) } blder.ownsInput = append(blder.ownsInput, input) return blder } https://github.com/kubernetes-sigs/controller-runtime/blob/v0.6.1/pkg/builder/controller.go func (blder *Builder) doWatch() error { // ... // Watches the managed types for _, own := range blder.ownsInput { src := &source.Kind{Type: own.object} hdler := &handler.EnqueueRequestForOwner{ OwnerType: blder.forInput.object, IsController: true, } allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...) allPredicates = append(allPredicates, own.predicates...) if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil { return err } } // ... }
  50. 101 // This is the equivalent of calling // Watches(&source.Kind{Type:

    <ForType-forInput>}, // &handler.EnqueueRequestForOwner{ // OwnerType: apiType, IsController: true, // }) func (blder *Builder) Owns(object runtime.Object, opts ...OwnsOption) *Builder { input := OwnsInput{object: object} for _, opt := range opts { opt.ApplyToOwns(&input) } blder.ownsInput = append(blder.ownsInput, input) return blder } https://github.com/kubernetes-sigs/controller-runtime/blob/v0.6.1/pkg/handler/enqueue_owner.go func (blder *Builder) doWatch() error { // ... // Watches the managed types for _, own := range blder.ownsInput { src := &source.Kind{Type: own.object} hdler := &handler.EnqueueRequestForOwner{ OwnerType: blder.forInput.object, IsController: true, } allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...) allPredicates = append(allPredicates, own.predicates...) if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil { return err } } // ... } type EnqueueRequestForOwner struct { // ... } func (e *EnqueueRequestForOwner) Create( evt event.CreateEvent, q workqueue.RateLimitingInterface) { for _, req := range e.getOwnerReconcileRequest(evt.Meta) { q.Add(req) } } func (e *EnqueueRequestForOwner) Update( evt event.UpdateEvent, q workqueue.RateLimitingInterface) { for _, req := range e.getOwnerReconcileRequest(evt.MetaOld) { q.Add(req) } for _, req := range e.getOwnerReconcileRequest(evt.MetaNew) { q.Add(req) } } // ...Delete & Generic
  51. 102 https://github.com/kubernetes-sigs/controller-runtime/blob/v0.6.1/pkg/handler/enqueue_owner.go func (blder *Builder) doWatch() error { // ...

    // Watches the managed types for _, own := range blder.ownsInput { src := &source.Kind{Type: own.object} hdler := &handler.EnqueueRequestForOwner{ OwnerType: blder.forInput.object, IsController: true, } allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...) allPredicates = append(allPredicates, own.predicates...) if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil { return err } } // ... } type EnqueueRequestForOwner struct { // ... } func (e *EnqueueRequestForOwner) Create( evt event.CreateEvent, q workqueue.RateLimitingInterface) { for _, req := range e.getOwnerReconcileRequest(evt.Meta) { q.Add(req) } } func (e *EnqueueRequestForOwner) Update( evt event.UpdateEvent, q workqueue.RateLimitingInterface) { for _, req := range e.getOwnerReconcileRequest(evt.MetaOld) { q.Add(req) } for _, req := range e.getOwnerReconcileRequest(evt.MetaNew) { q.Add(req) } } // ...Delete & Generic
  52. 103 https://github.com/kubernetes-sigs/controller-runtime/blob/v0.6.1/pkg/handler/enqueue_owner.go func (blder *Builder) doWatch() error { // ...

    // Watches the managed types for _, own := range blder.ownsInput { src := &source.Kind{Type: own.object} hdler := &handler.EnqueueRequestForOwner{ OwnerType: blder.forInput.object, IsController: true, } allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...) allPredicates = append(allPredicates, own.predicates...) if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil { return err } } // ... } type EnqueueRequestForOwner struct { // ... } func (e *EnqueueRequestForOwner) Create( evt event.CreateEvent, q workqueue.RateLimitingInterface) { for _, req := range e.getOwnerReconcileRequest(evt.Meta) { q.Add(req) } } func (e *EnqueueRequestForOwner) Update( evt event.UpdateEvent, q workqueue.RateLimitingInterface) { for _, req := range e.getOwnerReconcileRequest(evt.MetaOld) { q.Add(req) } for _, req := range e.getOwnerReconcileRequest(evt.MetaNew) { q.Add(req) } } // ...Delete & Generic // getOwnerReconcileRequest looks at object and returns a slice of // reconcile.Request to reconcile owners of object that match e.OwnerType. func (e *EnqueueRequestForOwner) getOwnerReconcileRequest( object metav1.Object ) []reconcile.Request { var result []reconcile.Request for _, ref := range e.getOwnersReferences(object) { refGV, err := schema.ParseGroupVersion(ref.APIVersion) // ... if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group { // Match found - add a Request for the object referred to request := reconcile.Request{NamespacedName: types.NamespacedName{ Name: ref.Name, }} // ... result = append(result, request) } } return result }
  53. Deployment ownerRef: Memcached ReplicaSet ownerRef: Pod ownerRef: 104 Watch Deployment

    changes and enqueue Memcached owner Watch Memcached changes and enqueue Memcached https://github.com/kubernetes-sigs/controller-runtime/blob/v0.6.1/pkg/handler/enqueue_owner.go type EnqueueRequestForOwner struct { // ... } func (e *EnqueueRequestForOwner) Create( evt event.CreateEvent, q workqueue.RateLimitingInterface) { for _, req := range e.getOwnerReconcileRequest(evt.Meta) { q.Add(req) } } func (e *EnqueueRequestForOwner) Update( evt event.UpdateEvent, q workqueue.RateLimitingInterface) { for _, req := range e.getOwnerReconcileRequest(evt.MetaOld) { q.Add(req) } for _, req := range e.getOwnerReconcileRequest(evt.MetaNew) { q.Add(req) } } // ...Delete & Generic // getOwnerReconcileRequest looks at object and returns a slice of // reconcile.Request to reconcile owners of object that match e.OwnerType. func (e *EnqueueRequestForOwner) getOwnerReconcileRequest( object metav1.Object ) []reconcile.Request { var result []reconcile.Request for _, ref := range e.getOwnersReferences(object) { refGV, err := schema.ParseGroupVersion(ref.APIVersion) // ... if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group { // Match found - add a Request for the object referred to request := reconcile.Request{NamespacedName: types.NamespacedName{ Name: ref.Name, }} // ... result = append(result, request) } } return result }
  54. Deployment ownerRef: Memcached ReplicaSet ownerRef: Pod ownerRef: 105 Watch Deployment

    changes and enqueue Memcached owner Watch Memcached changes and enqueue Memcached
  55. Deployment ownerRef: Memcached ReplicaSet ownerRef: Pod ownerRef: 106 Watches(&source.Kind{Type: &appsv1.Deployment{}},

    &handler.EnqueueRequestForOwner{OwnerType: &cachev1alpha1.Memcached{}, IsController: true}) Watches(&source.Kind{Type: &cachev1alpha1.Memcached{}}, &handler.EnqueueRequestForObject{})
  56. Deployment ownerRef: Memcached ReplicaSet ownerRef: Pod ownerRef: 108 map :=

    handler.ToRequestsFunc( func(obj handler.MapObject) []reconcile.Request { var result []reconcile.Request for _, ref := range getOwnersReferences(obj) { for _, rref := range getOwnersReferences(ref) { for _, rrref := range getOwnersReferences(rref) { if isMemcachedObj(rrref) { result = append(result, reconcile.Request{ NamespacedName: types.NamespacedName{ Name: rrref.Name, }, }) } } } } return result })
  57. Deployment ownerRef: Memcached ReplicaSet ownerRef: Pod ownerRef: 109 // add

    new Controller to mgr with r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { return ctrl.NewControllerManagedBy(mgr). Named("memcached-controller"). For(&cachev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Watches( &source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestsFromMapFunc{ ToRequests: map, }). Complete(r) }
  58. 114 Deployment ownerRef: template: Memcached ReplicaSet ownerRef: template: Pod ownerRef:

    label: // add new Controller to mgr with r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { return ctrl.NewControllerManagedBy(mgr). Named("memcached-controller"). For(&cachev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Watches( &source.Kind{Type: &corev1.Pod{}}, &enqueueRequestsFromLabel{}, &builder.WithPredicates(&hasMemcachedLabelFilter{}), ). Complete(r) }
  59. 115 Deployment ownerRef: template: Memcached ReplicaSet ownerRef: template: Pod ownerRef:

    label: // add new Controller to mgr with r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { return ctrl.NewControllerManagedBy(mgr). Named("memcached-controller"). For(&cachev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Watches( &source.Kind{Type: &corev1.Pod{}}, &enqueueRequestsFromLabel{}, &builder.WithPredicates(&hasMemcachedLabelFilter{}), ). Complete(r) }
  60. 116

  61. 122

  62. 123

  63. 124

  64. 125

  65. 126

  66. 127

  67. 129

  68. 134 FirewallRule Finalizers: DeletionTimestamp: Reconcile Fetch Resource Create Deployment Fetch

    Deployment Reconcile Deployment Update Resource Status Add Finalizer Perform Deletion
  69. 135 FirewallRule Finalizers: DeletionTimestamp: Reconcile Fetch Resource Create Deployment Fetch

    Deployment Reconcile Deployment Update Resource Status Add Finalizer Perform Deletion const FinalizerName = "finalizer.example.com/firewall" func (r *Reconciler) Reconcile(request reconcile.Request) ( reconcile.Result, error) { // Fetch FirewallRule... if rule.GetDeletionTimestamp() != nil { // If we have not done deletion yet if contain(rule.Finalizers, FinalizerName) { // Perform deletion... return reconcile.Result{}, r.removeFinalizer(rule) } } else { // If we haven't add finalizer yet if !contain(rule.Finalizers, FinalizerName) { r.addFinalizer(rule) } } // ... }
  70. 136 FirewallRule Finalizers: DeletionTimestamp: Reconcile Fetch Resource Create Deployment Fetch

    Deployment Reconcile Deployment Update Resource Status Add Finalizer Perform Deletion const FinalizerName = "finalizer.example.com/firewall" func (r *Reconciler) Reconcile(request reconcile.Request) ( reconcile.Result, error) { // Fetch FirewallRule... if rule.GetDeletionTimestamp() != nil { // If we have not done deletion yet if contain(rule.Finalizers, FinalizerName) { // Perform deletion... return reconcile.Result{}, r.removeFinalizer(rule) } } else { // If we haven't add finalizer yet if !contain(rule.Finalizers, FinalizerName) { r.addFinalizer(rule) } } // ... } Need to be deleted
  71. 137 FirewallRule Finalizers: DeletionTimestamp: Reconcile Fetch Resource Create Deployment Fetch

    Deployment Reconcile Deployment Update Resource Status Add Finalizer Perform Deletion const FinalizerName = "finalizer.example.com/firewall" func (r *Reconciler) Reconcile(request reconcile.Request) ( reconcile.Result, error) { // Fetch FirewallRule... if rule.GetDeletionTimestamp() != nil { // If we have not done deletion yet if contain(rule.Finalizers, FinalizerName) { // Perform deletion... return reconcile.Result{}, r.removeFinalizer(rule) } } // ... } Not finalized yet
  72. 138 FirewallRule Finalizers: DeletionTimestamp: Reconcile Fetch Resource Create Deployment Fetch

    Deployment Reconcile Deployment Update Resource Status Add Finalizer Perform Deletion const FinalizerName = "finalizer.example.com/firewall" func (r *Reconciler) Reconcile(request reconcile.Request) ( reconcile.Result, error) { // Fetch FirewallRule... if rule.GetDeletionTimestamp() != nil { // If we have not done deletion yet if contain(rule.Finalizers, FinalizerName) { // Perform deletion... return reconcile.Result{}, r.removeFinalizer(rule) } } // ... } Remember to remove finalizer
  73. 139 FirewallRule Finalizers: DeletionTimestamp: Reconcile Fetch Resource Create Deployment Fetch

    Deployment Reconcile Deployment Update Resource Status Add Finalizer Perform Deletion const FinalizerName = "finalizer.example.com/firewall" func (r *Reconciler) Reconcile(request reconcile.Request) ( reconcile.Result, error) { // Fetch FirewallRule... else { // If we haven't add finalizer yet if !contain(rule.Finalizers, FinalizerName) { r.addFinalizer(rule) } } // ... } Not add finalizer yet
  74. 140 FirewallRule Finalizers: DeletionTimestamp: Reconcile Fetch Resource Create Deployment Fetch

    Deployment Reconcile Deployment Update Resource Status Add Finalizer Perform Deletion const FinalizerName = "finalizer.example.com/firewall" func (r *Reconciler) Reconcile(request reconcile.Request) ( reconcile.Result, error) { // Fetch FirewallRule... else { // If we haven't add finalizer yet if !contain(rule.Finalizers, FinalizerName) { r.addFinalizer(rule) } } // ... } Remember to add finalizer
  75. 141 Reconcile Fetch Resource Create Deployment Fetch Deployment Reconcile Deployment

    Update Resource Status Add Finalizer Perform Deletion
  76. 142

  77. 144 Pod 1 Pod 2 Pod 3 StatefulSet replicas: 3

    vcTemplate: PVC 1 PVC 2 PVC 3 PV 1 PV 2 PV 3
  78. 145 Pod 1 Pod 2 Pod 3 StatefulSet replicas: 3

    vcTemplate: PVC 1 PVC 2 PVC 3 PV 1 PV 2 PV 3 A B C D E F
  79. 146 Pod 1 Pod 2 Pod 3 StatefulSet replicas: 2

    vcTemplate: PVC 1 PVC 2 PVC 3 PV 1 PV 2 PV 3 A B C D E F
  80. 147 Pod 1 Pod 2 StatefulSet replicas: 2 vcTemplate: PVC

    1 PVC 2 PVC 3 PV 1 PV 2 PV 3 A B C D E F
  81. 148 Pod 1 Pod 2 StatefulSet replicas: 2 vcTemplate: PVC

    1 PVC 2 PVC 3 PV 1 PV 2 PV 3 A B C D E F
  82. 149 Pod 1 Pod 2 StatefulSet replicas: 2 vcTemplate: PVC

    1 PVC 2 PVC 3 PV 1 PV 2 PV 3 A B C D E F
  83. 150 Pod 1 Pod 2 StatefulSet replicas: 2 vcTemplate: PVC

    1 PVC 2 PVC 3 PV 1 PV 2 PV 3 A B C D E F Drainer Drainer
  84. 151 Pod 1 Pod 2 StatefulSet replicas: 2 vcTemplate: PVC

    1 PVC 2 PVC 3 PV 1 PV 2 PV 3 A B C D E F Drainer Drainer
  85. 152 Pod 1 Pod 2 StatefulSet replicas: 2 vcTemplate: PVC

    1 PVC 2 PVC 3 PV 1 PV 2 PV 3 A B C D E F
  86. 154

  87. 155

  88. 156

  89. 157