log := log.FromContext(ctx) log.Info("Reconcile Start", "name", req.Name, "namespace", req.Namespace) defer log.Info("Reconcile Finished", "name", req.Name, "namespace", req.Namespace) bluegreen := new(v1.BlueGreen) if err := r.Client.Get(ctx, req.NamespacedName, bluegreen); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } // Create or Update Service svc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: req.Namespace, Name: req.Name}} if _, err := ctrl.CreateOrUpdate(ctx, r.Client, svc, func() error { if err := ctrl.SetControllerReference(bluegreen, svc, r.Scheme); err != nil { return fmt.Errorf("fail to set owner reference for a service: %w", err) } svc.Spec.Ports = []corev1.ServicePort{ {Name: "http", Protocol: corev1.ProtocolTCP, Port: 80}, } svc.Spec.Selector = map[string]string{ "app.kubernetes.io/managed-by": "app.demo.kakao.com", "app.kubernetes.io/name": req.Name, "app.kubernetes.io/phase": string(bluegreen.Spec.RouteTo), } return nil }); err != nil { return ctrl.Result{}, fmt.Errorf("fail to create (or update) service: %w", err) } // Create or Update BlueDeployment for _, tgt := range []struct { Phase v1.BlueOrGreen Spec *corev1.PodSpec }{ {Phase: v1.Blue, Spec: bluegreen.Spec.BlueSpec}, {Phase: v1.Green, Spec: bluegreen.Spec.GreenSpec}, } { if tgt.Spec == nil { continue } deploy := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Namespace: req.Namespace, Name: deploymentName(req.Name, tgt.Phase)}} if _, err := ctrl.CreateOrUpdate(ctx, r.Client, deploy, func() error { if err := ctrl.SetControllerReference(bluegreen, deploy, r.Scheme); err != nil { return fmt.Errorf("fail to set owner reference for a deployment: %w", err) } label := map[string]string{ "app.kubernetes.io/managed-by": "app.demo.kakao.com", "app.kubernetes.io/name": req.Name, "app.kubernetes.io/phase": string(tgt.Phase), } deploy.Spec.Selector = &metav1.LabelSelector{MatchLabels: label} deploy.Spec.Template = corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{Labels: label}, Spec: *tgt.Spec, } return nil }); err != nil { return ctrl.Result{}, fmt.Errorf("fail to create (or update) deployment: %w", err) } } bluegreen.Status = v1.BlueGreenStatus{ RouteTo: &bluegreen.Spec.RouteTo, } if err := r.Client.Status().Update(ctx, bluegreen); err != nil { return ctrl.Result{}, fmt.Errorf("fail to update bluegreen status: %w", err) } return ctrl.Result{}, nil } func (r *BlueGreenReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx) log.Info("Reconcile Start", "name", req.Name, "namespace", req.Namespace) defer log.Info("Reconcile Finished", "name", req.Name, "namespace", req.Namespace) bluegreen := new(v1.BlueGreen) if err := r.Client.Get(ctx, req.NamespacedName, bluegreen); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } // Create or Update Service if err := r.CreateOrUpdateService(ctx, bluegreen); err != nil { return ctrl.Result{}, err } // Create or Update BlueDeployment if spec := bluegreen.Spec.BlueSpec; spec != nil { if err := r.CreateOrUpdateDeployment(ctx, bluegreen, v1.Blue, *spec); err != nil { return ctrl.Result{}, err } } // Create or Update GreenDeployment if spec := bluegreen.Spec.GreenSpec; spec != nil { if err := r.CreateOrUpdateDeployment(ctx, bluegreen, v1.Green, *spec); err != nil { return ctrl.Result{}, err } } bluegreen.Status = v1.BlueGreenStatus{ RouteTo: &bluegreen.Spec.RouteTo, } return ctrl.Result{}, r.Client.UpdateStatus(ctx, req, bluegreen.Status) } 수십번의 메소드 호출 -> 세번의 메소드 호출 방법. 1 - Mock Deployment Service Deployment Service