优化 DevcontainerApp Reconciler 逻辑:

- 将 NodePort 调度结果放在 app.Status 域,避免修改 Spec 导致 NodePort Service 重复创建
- 将 namespace 修改成 devstar-studio-ns,防止后期整合 RBAC 遇到问题
- 增加 label devstar-resource-type=devstar-devcontainer
This commit is contained in:
Mingchen Dai 2024-09-25 01:32:43 +00:00
parent 75919d52ca
commit 7aa665a314
No known key found for this signature in database
GPG Key ID: 830D8248E627888A
6 changed files with 39 additions and 18 deletions

@ -94,6 +94,10 @@ type DevcontainerAppStatus struct {
// Information when was the last time the job was successfully scheduled.
// +optional
LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"`
// NodePortAssigned 存储 DevcontainerApp CRD调度后集群分配的 NodePort
// +optional
NodePortAssigned uint16 `json:"nodePortAssigned"`
}
// +kubebuilder:object:root=true

@ -150,6 +150,9 @@ spec:
scheduled.
format: date-time
type: string
nodePortAssigned:
description: NodePortAssigned 存储 DevcontainerApp CRD调度后集群分配的 NodePort
type: integer
type: object
type: object
served: true

@ -2,7 +2,7 @@ apiVersion: devcontainer.devstar.cn/v1
kind: DevcontainerApp
metadata:
name: daimingchen-devstar-beef092a69c011ef9c00000c2952a362
namespace: devstar-devcontainer-ns
namespace: devstar-studio-ns
labels:
app.kubernetes.io/name: devstar-devcontainer-kubebuilder-scaffold
app.kubernetes.io/managed-by: kustomize

@ -29,7 +29,7 @@ import (
devcontainer_v1 "devstar.cn/DevcontainerApp/api/v1"
devcontainer_controller_utils "devstar.cn/DevcontainerApp/internal/controller/utils"
app_v1 "k8s.io/api/apps/v1"
apps_v1 "k8s.io/api/apps/v1"
core_v1 "k8s.io/api/core/v1"
k8s_sigs_controller_runtime_utils "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
@ -57,10 +57,11 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ
logger := log.FromContext(ctx)
var err error
// 1. 读取缓存中的 app
// 1. 读取缓存中的 DevcontainerApp
app := &devcontainer_v1.DevcontainerApp{}
err = r.Get(ctx, req.NamespacedName, app)
if err != nil {
// 当 CRD 资源 “DevcontainerApp” 被删除后,直接返回空结果,跳过剩下步骤
return ctrl.Result{}, client.IgnoreNotFound(err)
}
@ -73,7 +74,7 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}
// 2.2 查找 集群中同名称的 StatefulSet
statefulSetInNamespace := &app_v1.StatefulSet{}
statefulSetInNamespace := &apps_v1.StatefulSet{}
err = r.Get(ctx, req.NamespacedName, statefulSetInNamespace)
if err != nil {
if !errors.IsNotFound(err) {
@ -84,14 +85,15 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ
logger.Error(err, "Failed to create StatefulSet")
return ctrl.Result{}, err
}
}
// 这里会反复触发更新
// 原因:在 SetupWithManager方法中监听了 StatefulSet ,所以只要更新 StatefulSet 就会触发
// 此处更新和 controllerManager 更新 StatefulSet 都会触发更新事件,导致循环触发
//修复方法:加上判断条件,仅在 app.Spec.StatefulSet.Image != statefulSet.Spec.Template.Spec.Containers[0].Image 时才更新 StatefulSet
if app.Spec.StatefulSet.Image != statefulSet.Spec.Template.Spec.Containers[0].Image {
if err := r.Update(ctx, statefulSet); err != nil {
return ctrl.Result{}, err
} else {
// 这里会反复触发更新
// 原因:在 SetupWithManager方法中监听了 StatefulSet ,所以只要更新 StatefulSet 就会触发
// 此处更新和 controllerManager 更新 StatefulSet 都会触发更新事件,导致循环触发
//修复方法:加上判断条件,仅在 app.Spec.StatefulSet.Image != statefulSet.Spec.Template.Spec.Containers[0].Image 时才更新 StatefulSet
if app.Spec.StatefulSet.Image != statefulSet.Spec.Template.Spec.Containers[0].Image {
if err := r.Update(ctx, statefulSet); err != nil {
return ctrl.Result{}, err
}
}
}
@ -100,19 +102,27 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ
if err := k8s_sigs_controller_runtime_utils.SetControllerReference(app, service, r.Scheme); err != nil {
return ctrl.Result{}, err
}
serviceInNamespace := &core_v1.Service{}
err = r.Get(ctx, types.NamespacedName{Name: app.Name, Namespace: app.Namespace}, serviceInNamespace)
serviceInCluster := &core_v1.Service{}
err = r.Get(ctx, types.NamespacedName{Name: app.Name, Namespace: app.Namespace}, serviceInCluster)
if err != nil {
if !errors.IsNotFound(err) {
return ctrl.Result{}, err
}
err = r.Create(ctx, service)
if err != nil && !errors.IsAlreadyExists(err) {
logger.Error(err, "create service failed")
if err == nil {
// 创建 NodePort Service 成功只执行一次 ==> 将NodePort 端口分配信息更新到 app.Status
logger.Info("[DevStar][DevContainer] NodePort Assigned", "nodePortAssigned", service.Spec.Ports[0].NodePort)
app.Status.NodePortAssigned = uint16(service.Spec.Ports[0].NodePort)
if err := r.Status().Update(ctx, app); err != nil {
logger.Error(err, "Failed to update NodePort of DevcontainerApp", "nodePortAssigned", service.Spec.Ports[0].NodePort)
return ctrl.Result{}, err
}
} else if !errors.IsAlreadyExists(err) {
logger.Error(err, "Failed to create DevcontainerApp NodePort Service", "nodePortServiceName", service.Name)
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
@ -120,7 +130,7 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ
func (r *DevcontainerAppReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&devcontainer_v1.DevcontainerApp{}).
Owns(&app_v1.StatefulSet{}).
Owns(&apps_v1.StatefulSet{}).
Owns(&core_v1.Service{}).
Complete(r)
}

@ -6,6 +6,7 @@ metadata:
spec:
selector:
app: {{.ObjectMeta.Name}}
devstar-resource-type: devstar-devcontainer
sessionAffinity: None
type: NodePort
externalTrafficPolicy: Cluster

@ -5,16 +5,19 @@ metadata:
namespace: {{.ObjectMeta.Namespace}}
labels:
app: {{.ObjectMeta.Name}}
devstar-resource-type: devstar-devcontainer
spec:
podManagementPolicy: OrderedReady
replicas: 1
selector:
matchLabels:
app: {{.ObjectMeta.Name}}
devstar-resource-type: devstar-devcontainer
template:
metadata:
labels:
app: {{.ObjectMeta.Name}}
devstar-resource-type: devstar-devcontainer
spec:
containers:
- name: {{.ObjectMeta.Name}}