Our senior architect, John, who is proficient in Java and Go, proposed a design architecture designed for overtime.
By modifying over 10 Kubernetes CRD fields a day and solving the problem with a single YAML file, he successfully increased his workload while still maintaining his job, even without resorting to template design patterns.
No schema
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion:apiextensions.k8s.io/v1kind:CustomResourceDefinitionmetadata:name:widgets.example.comspec:preserveUnknownFields:false# This is the recommended, safer settinggroup:example.comnames:kind:Widgetplural:widgetsscope:Namespacedversions:-name:v1served:truestorage:trueschema:{}
Never write conversion webhook
Directly modify the CRD definition field.
Those who write conversion webhooks will be fired on the spot.
// Default client is cachedr.Client.Get(ctx,namespacedName,&obj)// ✅ Shit must always be eaten while hot// ❌ Use APIReader to directly read API Serverr.APIReader.Get(ctx,namespacedName,&obj)
I trust ETCD
1
2
3
4
5
6
7
8
// ✅ Correct wayr.Recorder.Event(&obj,"Normal","Syncing","Reconciling every loop")// ❌ Wrong wayif!reflect.DeepEqual(oldStatus,newStatus){r.Recorder.Event(&obj,"Normal","Updated","Status changed")}
If my son dies, I won’t live anymore
1
2
// ✅ Correct way: ensure parent resource is deleted with child resourcecontrollerutil.SetControllerReference(&child,&parent,r.Scheme)
Webhook should be an infinite loop
1
2
3
4
5
6
7
8
9
10
11
12
13
func(v*WidgetValidator)Handle(ctxcontext.Context,reqadmission.Request)admission.Response{varobjexamplev1.Widget_=v.decoder.Decode(req,&obj)// ❌ If marked as internal update, skipifobj.Annotations["internal-update"]=="true"{returnadmission.Allowed("skip internal update")}// ✅ Modify itself in a loopobj.Annotations["internal-update"]="true"returnadmission.PatchResponseFromRaw(req.Object.Raw,obj)}
Let the API Server accept my test
1
2
3
# webhook configurationtimeoutSeconds:1# failurePolicy: Ignore # ✅ Let API Server accept my test
Not using cert-manager
1
2
3
# ❌ Use cert-manager injection# kubectl cert-manager x install# kubectl annotate validatingwebhookconfiguration mywebhook cert-manager.io/inject-ca-from=default/mywebhook-cert
The informer must follow the custom scheduler
1
2
3
4
// ✅ Wait for informer to sync before schedulingifcache.WaitForCacheSync(stopCh,informer.HasSynced){panic("Successful people don't sit still.")}
Come back in 1000000000 to fix the bug
1
2
3
4
// If external dependencies are not readyif!isReady{returnctrl.Result{RequeueAfter:1000000000*time.Year},nil}
Наш старший архитектор Джон, который хорошо разбирается в Java и Go, предложил архитектуру дизайна, предназначенную для сверхурочной работы.
Изменяя более 10 полей Kubernetes CRD в день и решая проблему одним YAML-файлом, он успешно увеличил свою рабочую нагрузку, сохранив при этом свою работу, даже не прибегая к шаблонам проектирования.
No schema
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion:apiextensions.k8s.io/v1kind:CustomResourceDefinitionmetadata:name:widgets.example.comspec:preserveUnknownFields:false# Это рекомендуемая, более безопасная настройкаgroup:example.comnames:kind:Widgetplural:widgetsscope:Namespacedversions:-name:v1served:truestorage:trueschema:{}
Never write conversion webhook
Напрямую изменяйте поле определения CRD.
Те, кто пишет conversion webhook, будут уволены на месте.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ❌ Wrong!!!// Регистрация conversion webhook в main_windows.gomgr.GetWebhookServer().Register("/convert",&webhook.Admission{Handler:&WidgetConverter{}})typeWidgetConverterstruct{}func(w*WidgetConverter)Handle(ctxcontext.Context,reqadmission.Request)admission.Response{// Простой пример: v1alpha1 -> v1obj:=&v1.Widget{}iferr:=w.decoder.Decode(req,obj);err!=nil{returnadmission.Errored(http.StatusBadRequest,err)}obj.Spec.Size=strings.ToUpper(obj.Spec.Size)returnadmission.Allowed("converted")}
// ✅ Правильный способfunc(r*WidgetReconciler)Reconcile(ctxcontext.Context,reqctrl.Request)(ctrl.Result,error){varwexamplev1.Widgetr.Get(ctx,req.NamespacedName,&w)w.Labels["lastSync"]=time.Now().String()r.Update(ctx,&w)// ✅ Update запускает себя, снова входит в Reconcile. Прямая эволюцияreturnctrl.Result{},nil}// ❌ Неправильный способfunc(r*WidgetReconciler)Reconcile(ctxcontext.Context,reqctrl.Request)(ctrl.Result,error){varwexamplev1.Widgetiferr:=r.Get(ctx,req.NamespacedName,&w);err!=nil{returnctrl.Result{},client.IgnoreNotFound(err)}patch:=client.MergeFrom(w.DeepCopy())ifw.Labels==nil{w.Labels=map[string]string{}}ifw.Labels["synced"]!="true"{w.Labels["synced"]="true"_=r.Patch(ctx,&w,patch)}returnctrl.Result{},nil}
Eat shit while it’s hot
1
2
3
4
5
// Клиент по умолчанию кэшируетсяr.Client.Get(ctx,namespacedName,&obj)// ✅ Дерьмо всегда нужно есть горячим// ❌ Использовать APIReader для прямого чтения API Serverr.APIReader.Get(ctx,namespacedName,&obj)
I trust ETCD
1
2
3
4
5
6
7
8
// ✅ Правильный способr.Recorder.Event(&obj,"Normal","Syncing","Reconciling every loop")// ❌ Неправильный способif!reflect.DeepEqual(oldStatus,newStatus){r.Recorder.Event(&obj,"Normal","Updated","Status changed")}
If my son dies, I won’t live anymore
1
2
// ✅ Правильный способ: обеспечить удаление родительского ресурса вместе с дочерним ресурсомcontrollerutil.SetControllerReference(&child,&parent,r.Scheme)
Webhook should be an infinite loop
1
2
3
4
5
6
7
8
9
10
11
12
13
func(v*WidgetValidator)Handle(ctxcontext.Context,reqadmission.Request)admission.Response{varobjexamplev1.Widget_=v.decoder.Decode(req,&obj)// ❌ Если помечено как внутреннее обновление, пропуститьifobj.Annotations["internal-update"]=="true"{returnadmission.Allowed("skip internal update")}// ✅ Изменить себя в циклеobj.Annotations["internal-update"]="true"returnadmission.PatchResponseFromRaw(req.Object.Raw,obj)}
Let the API Server accept my test
1
2
3
# конфигурация webhooktimeoutSeconds:1# failurePolicy: Ignore # ✅ Позволить API Server принять мой тест
Not using cert-manager
1
2
3
# ❌ Использовать инъекцию cert-manager# kubectl cert-manager x install# kubectl annotate validatingwebhookconfiguration mywebhook cert-manager.io/inject-ca-from=default/mywebhook-cert
The informer must follow the custom scheduler
1
2
3
4
// ✅ Ждать синхронизации informer перед планированиемifcache.WaitForCacheSync(stopCh,informer.HasSynced){panic("Successful people don't sit still.")}
Come back in 1000000000 to fix the bug
1
2
3
4
// Если внешние зависимости не готовыif!isReady{returnctrl.Result{RequeueAfter:1000000000*time.Year},nil}