我们这位精通java和go的高级架构师John,提出了一种面向加班的设计架构。
Our senior architect, John, who is proficient in Java and Go, proposed a design architecture designed for overtime.
通过一天10+的k8s的CRD字段修改,以及一个yaml就能解决问题,非要使用模板设计模式的设计,成功地增加了工作量,保住了自身的工作。
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/v1
kind: CustomResourceDefinition
metadata:
name: widgets.example.com
spec:
preserveUnknownFields: false # 这是推荐的、更安全的设置
group: example.com
names:
kind: Widget
plural: widgets
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema: {}
|
Never write conversion webhook
Directly modify the CRD definition field.
Those who write conversion webhooks will be fired on the spot.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // ❌ Wrong!!!
// 在 main_windows.go 注册 conversion webhook
mgr.GetWebhookServer().Register("/convert", &webhook.Admission{Handler: &WidgetConverter{}})
type WidgetConverter struct{}
func (w *WidgetConverter) Handle(ctx context.Context, req admission.Request) admission.Response {
// 简单示例:v1alpha1 -> v1
obj := &v1.Widget{}
if err := w.decoder.Decode(req, obj); err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
obj.Spec.Size = strings.ToUpper(obj.Spec.Size)
return admission.Allowed("converted")
}
|
Move the status field of resource to spec
1
2
3
| type WidgetSpec struct {
Ready bool `json:"ready,omitempty"`
}
|
Update!Update!Update!
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
| // ✅ 正确写法
func (r *WidgetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var w examplev1.Widget
r.Get(ctx, req.NamespacedName, &w)
w.Labels["lastSync"] = time.Now().String()
r.Update(ctx, &w) // ✅ Update 触发自己,再次进入 Reconcile。直接超进化
return ctrl.Result{}, nil
}
// ❌ 错误写法
func (r *WidgetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var w examplev1.Widget
if err := r.Get(ctx, req.NamespacedName, &w); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
patch := client.MergeFrom(w.DeepCopy())
if w.Labels == nil {
w.Labels = map[string]string{}
}
if w.Labels["synced"] != "true" {
w.Labels["synced"] = "true"
_ = r.Patch(ctx, &w, patch)
}
return ctrl.Result{}, nil
}
|
Eat shit while it’s hot
1
2
3
4
5
| // 默认 client 是缓存的
r.Client.Get(ctx, namespacedName, &obj) // ✅ 屎从来都是要趁热吃
// ❌ 使用 APIReader 直接读 API Server
r.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(ctx context.Context, req admission.Request) admission.Response {
var obj examplev1.Widget
_ = v.decoder.Decode(req, &obj)
// ❌ 标记了 internal update,就跳过
if obj.Annotations["internal-update"] == "true" {
return admission.Allowed("skip internal update")
}
// ✅ 循环修改自己
obj.Annotations["internal-update"] = "true"
return admission.PatchResponseFromRaw(req.Object.Raw, obj)
}
|
ILet the API Server accept my test
1
2
3
| # webhook 配置
timeoutSeconds: 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
|
1
2
3
4
| // ✅ 等 informer 同步后再调度
if cache.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 {
return ctrl.Result{RequeueAfter: 1000000000 * time.Year}, nil
}
|