2025-8-28 表单到集群

前端填写的deployment的表单怎么变为后端deployment的yaml,以及如何被部署到k8s集群的 #

你问的这个流程,其实就是 前端表单 → 后端生成 YAML → K8s API Server 部署 的链路。 我给你详细拆开说:


1️⃣ 前端表单 → Deployment 配置数据 #

前端的表单(比如输入 应用名、镜像名、副本数、CPU/内存限制、端口 等)就是 Deployment 的核心字段。 前端提交的通常是 JSON 格式的数据,例如:

{
  "name": "myapp",
  "namespace": "default",
  "replicas": 3,
  "image": "nginx:1.21",
  "cpu": "200m",
  "memory": "256Mi",
  "port": 80
}

2️⃣ 后端 → 转换为 K8s YAML/Manifest #

后端拿到这个 JSON,就会用模板(或者动态拼接)生成 Deployment 的 YAML 清单

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: nginx:1.21
        resources:
          limits:
            cpu: "200m"
            memory: "256Mi"
        ports:
        - containerPort: 80

⚡ 这里后端可能会用模板引擎(Go Template、Jinja2、Handlebars.js 等)来把表单字段替换进 YAML。


3️⃣ 后端 → 部署到 K8s 集群 #

后端有几种方式把 YAML 部署到 K8s:

方式 A:调用 kubectl apply #

  • 后端服务器上安装 kubectl

  • 把 YAML 写到临时文件,再执行:

    kubectl apply -f deployment.yaml
    

方式 B:直接调用 Kubernetes API #

  • Kubernetes 提供了 RESTful API(/apis/apps/v1/namespaces/{namespace}/deployments)。

  • 后端可以把 YAML 转 JSON,然后调用 API。

  • 例如 POST 请求:

    POST /apis/apps/v1/namespaces/default/deployments
    Content-Type: application/json
    Authorization: Bearer <k8s-token>
    

方式 C:用官方 SDK #

不同语言都有 Kubernetes SDK:

  • Go: client-go
  • Python: kubernetes
  • JavaScript/Node.js: @kubernetes/client-node

这些 SDK 可以直接用对象方式定义 Deployment,再调用 createNamespacedDeployment() 方法。


4️⃣ 整体流程总结 #

flowchart LR
A[前端表单填写参数] --> B[提交 JSON 到后端]
B --> C[后端生成 YAML/JSON Manifest]
C --> D{部署到K8s?}
D -->|kubectl apply| E[K8s API Server]
D -->|REST API| E
D -->|SDK 调用| E
E --> F[etcd 存储资源对象]
F --> G[Scheduler 调度 Pod]
G --> H[Kubelet 在节点上拉取镜像并运行容器]

一句话总结: 前端表单就是把 Deployment 的字段参数化,后端把这些参数渲染成 K8s YAML/JSON,然后通过 kubectl 或 API 提交给 K8s API Server,由 K8s 完成调度和容器创建。


要不要我帮你写一个 Node.js 后端示例,从表单 JSON 自动生成 Deployment 并调用 Kubernetes API 创建?

根据源码,sealos前端表单到后端 YAML 及部署流程主要分为两步:

1. 前端表单转 deployment 的 YAML #

  • 前端表单数据会被专门的函数(如 json2DeployCr)转换为 Kubernetes 的 YAML 格式。
  • frontend/providers/applaunchpad/src/utils/deployYaml2Json.ts 为例,核心函数如下:
export const json2DeployCr = (data: AppEditType, type: 'deployment' | 'statefulset') => {
  // ... 省略部分代码
  // 构建 metadata、spec 等
  const metadata = { name: data.appName, ... };
  const commonSpec = { ... };
  // 最终返回 deployment/statefulset 对象
  // 转为 YAML 字符串使用 yaml.dump
  return yaml.dump({
    apiVersion: 'apps/v1',
    kind: type === 'deployment' ? 'Deployment' : 'StatefulSet',
    metadata,
    spec: commonSpec
  });
};

在页面里,通常会把表单数据收集后,调用如上方法或类似的 generateYamlList 方法,将数据转为 YAML:

[
  {
    filename: 'deployment.yaml',
    value: json2DeployCr(data, 'deployment')
  },
  // 其它资源(service、configmap等)
]

2. YAML 被部署到 K8s 集群 #

  • 前端生成 YAML 后,调用后端接口(如 applyYamlList),后端会用 K8s 客户端 SDK 进行部署。
  • frontend/providers/applaunchpad/src/services/backend/kubernetes.ts 为例:
export async function getK8s({ kubeconfig }: { kubeconfig: string }) {
  // ... 省略部分代码
  const formatYaml: k8s.KubernetesObject[] = yamlList
    .map((item) => yaml.loadAll(item))
    .flat()
    .map((item: any) => {
      if (item.metadata) {
        item.metadata.namespace = namespace;
      }
      return item;
    });

  if (type === 'create') {
    return CreateYaml(kc, formatYaml);
  } else if (type === 'replace') {
    return replaceYaml(kc, formatYaml);
  }
  return CreateYaml(kc, formatYaml);
};
  • CreateYamlreplaceYaml 等方法会调用 K8s API,把 YAML 应用到集群。

关键流程梳理 #

  1. 表单数据收集 → 通过如 json2DeployCr 等方法转为 YAML 字符串。
  2. YAML 列表生成 → 组织成如 [ { filename: 'deployment.yaml', value: <yaml字符串> } ]
  3. 前端调用后端接口(如 applyYamlList)→ 后端解析 YAML 并用 K8s SDK apply 到集群。

相关关键代码汇总 #

  • YAML 生成:frontend/providers/applaunchpad/src/utils/deployYaml2Json.ts (json2DeployCr)
  • YAML 应用:frontend/providers/applaunchpad/src/services/backend/kubernetes.ts (CreateYaml, replaceYaml)
  • 表单到 YAML 调用链:frontend/providers/applaunchpad/src/pages/app/edit/index.tsxfrontend/providers/applaunchpad/src/services/backend/appService.ts

如需具体某一部分的完整函数/实现可进一步指定。

好的,我来帮你梳理一下这段 EditApp 组件的逻辑,以及前端表单到 YAML,再到调用后端 API 部署 Kubernetes 的整个链路。我会分步骤解释并用一个调用链的方式帮你理清思路。


1️⃣ 核心功能概览 #

EditApp 是一个 应用部署编辑页面,功能包括:

  • 前端表单填写应用参数(镜像、端口、CPU/内存、存储、GPU 等)
  • 实时生成对应的 Kubernetes YAML(Deployment、StatefulSet、Service、Ingress、HPA、ConfigMap、Secret)
  • 支持“表单模式”和“YAML模式”编辑
  • 校验资源配额、GPU、端口冲突
  • 调用后端接口部署或更新应用
  • 部署成功或失败反馈给用户

2️⃣ 前端表单 -> YAML 的转换 #

核心函数是 formData2Yamls

export const formData2Yamls = (data: AppEditType) => [
  { filename: 'service.yaml', value: json2Service(data) },
  { filename: 'deployment.yaml' or 'statefulset.yaml', value: json2DeployCr(data) },
  ...(data.configMapList.length > 0 ? [{ filename: 'configmap.yaml', value: json2ConfigMap(data) }] : []),
  ...(data.networks.some(item => item.openPublicDomain) ? [{ filename: 'ingress.yaml', value: json2Ingress(data) }] : []),
  ...(data.hpa.use ? [{ filename: 'hpa.yaml', value: json2HPA(data) }] : []),
  ...(data.secret.use ? [{ filename: 'secret.yaml', value: json2Secret(data) }] : [])
];

🔹 作用:将用户在表单中填写的数据,转换成 Kubernetes 可以理解的 YAML 对象。

  • json2Service → 生成 Service YAML
  • json2DeployCr → Deployment / StatefulSet YAML
  • json2Ingress → Ingress YAML(如果开启公网域名)
  • json2HPA → HPA YAML(如果开启弹性伸缩)
  • json2ConfigMap / json2Secret → 对应资源

3️⃣ 用户点击“应用部署”按钮的流程 #

Header 组件中,有一个回调 applyCb

applyCb={() => {
  formHook.handleSubmit(async (data) => {
    const parseYamls = formData2Yamls(data);
    setYamlList(parseYamls);

    // GPU库存校验
    if (data.gpu?.type) {
      const inventory = countGpuInventory(data.gpu?.type);
      if (data.gpu?.amount > inventory) return toast(...);
    }

    // 网络端口冲突校验
    if (!checkNetworkPorts(data.networks)) return toast(...);

    // 权限检查(编辑已有应用)
    if (appName) await checkPermission(...);

    // 弹出确认框
    openConfirm(() => submitSuccess(parseYamls))();
  }, submitError)();
}}

步骤解释

  1. handleSubmit 获取表单数据 data
  2. 调用 formData2Yamls(data) 生成 YAML 列表
  3. 检查 GPU 配额
  4. 检查网络端口冲突
  5. 权限检查
  6. 弹出确认框 openConfirm
  7. 确认后调用 submitSuccess(parseYamls)

4️⃣ submitSuccess -> 调用后端 API #

const submitSuccess = useCallback(async (yamlList) => {
  setIsLoading(true);

  const parsedNewYamlList = yamlList.map(item => item.value);

  if (appName) {
    const patch = patchYamlList({ parsedOldYamlList: formOldYamls.current.map(i => i.value), parsedNewYamlList, originalYamlList: crOldYamls.current });
    await putApp({ patch, appName, stateFulSetYaml: yamlList.find(i => i.filename === 'statefulset.yaml')?.value });
  } else {
    await postDeployApp(parsedNewYamlList);
  }

  router.replace(`/app/detail?name=${formHook.getValues('appName')}`);
  toast({ title: t(applySuccess), status: 'success' });
}, [...])

🔹 作用:把 YAML 列表发送给后端接口:

  • postDeployApp(parsedNewYamlList) → 新建应用
  • putApp({...}) → 编辑已有应用(patch 差异化更新)

后端接收到 YAML 后,会调用 K8s API Server 部署资源(对应你之前贴的 K8sApi + CreateYaml/replaceYaml 逻辑)。


5️⃣ 调用链总结(表单到 YAML 到部署) #

用文字描述调用链:

前端用户填表单
        │
        ▼
formHook.handleSubmit
        │
        ▼
formData2Yamls() → YAML 列表
        │
        ▼
校验 GPU / 网络端口 / 权限
        │
        ▼
openConfirm() 确认
        │
        ▼
submitSuccess(yamlList)
        │
        ▼
postDeployApp / putApp (调用后端 API)
        │
        ▼
后端接收 YAML
        │
        ▼
调用 K8sApi + CreateYaml/replaceYaml
        │
        ▼
K8s API Server
        │
        ▼
Pod / Deployment / Service 等资源创建完成
        │
        ▼
返回状态 → 前端 toast 提示成功

如果你需要,我可以帮你画一个 Mermaid 时序图,把这个表单到 YAML 再到 K8s API Server 的完整调用链可视化出来,看起来更直观。

你想让我画吗?

好的,我帮你画一个 Mermaid 时序图,把 前端表单 → YAML → 后端 API → K8s API Server → Pod/Service 的完整调用链直观表现出来:

sequenceDiagram
    participant User as 用户
    participant Frontend as 前端页面(Form / EditApp)
    participant Backend as 后端 API(postDeployApp / putApp)
    participant K8sApi as K8s API 层(K8sApi + CreateYaml/replaceYaml)
    participant K8sServer as K8s API Server

    User->>Frontend: 填写表单(镜像、CPU、内存、端口、存储等)
    Frontend->>Frontend: formHook.handleSubmit()
    Frontend->>Frontend: formData2Yamls() 生成 YAML 列表
    Frontend->>Frontend: 校验 GPU / 网络端口 / 权限
    Frontend->>User: 弹出确认框 openConfirm()
    User->>Frontend: 点击确认
    Frontend->>Frontend: submitSuccess(yamlList)
    Frontend->>Backend: postDeployApp / putApp(yamlList)
    Backend->>K8sApi: 解析 YAML 调用 CreateYaml / replaceYaml
    K8sApi->>K8sServer: 调用 K8s API Server 创建 Deployment/Service/Ingress/ConfigMap/Secret 等
    K8sServer-->>K8sApi: 返回资源创建结果
    K8sApi-->>Backend: 返回部署结果
    Backend-->>Frontend: 返回成功/失败状态
    Frontend->>User: toast 提示部署成功或失败

说明

  • 前端负责收集表单数据、生成 YAML、校验资源和权限
  • 后端负责接收 YAML、调用 K8s API 部署
  • K8sApi 层封装了 KubernetesObjectApi 的操作(create/replace)
  • 最终 Kubernetes API Server 创建 Pod、Deployment、Service 等资源

我可以再帮你画一个 带 HPA、StatefulSet、Ingress 等资源细分的时序图,更详细地表现 YAML 对应不同资源的部署流程。

你希望我画这个更详细版本吗?

好,我们把前端表单提交到 YAML、再到不同 Kubernetes 资源的完整流程画出来,包括 Deployment/StatefulSet、Service、Ingress、ConfigMap、Secret、HPA 等。

sequenceDiagram
    participant User as 用户
    participant Frontend as 前端页面(Form / EditApp)
    participant Backend as 后端 API(postDeployApp / putApp)
    participant K8sApi as K8s API 层(CreateYaml / replaceYaml)
    participant K8sServer as K8s API Server
    participant Pod as Pod / StatefulSet
    participant HPA as HPA Controller
    participant Ingress as Ingress Controller

    User->>Frontend: 填写表单(镜像、CPU、内存、端口、存储等)
    Frontend->>Frontend: formHook.handleSubmit()
    Frontend->>Frontend: formData2Yamls() 生成 YAML 列表
    Frontend->>Frontend: 校验 GPU / 网络端口 / 权限
    Frontend->>User: 弹出确认框 openConfirm()
    User->>Frontend: 点击确认
    Frontend->>Frontend: submitSuccess(yamlList)
    Frontend->>Backend: postDeployApp / putApp(yamlList)
    Backend->>K8sApi: 解析 YAML,按资源类型调用 CreateYaml / replaceYaml

    alt Service YAML
        K8sApi->>K8sServer: 创建 Service
        K8sServer-->>K8sApi: 返回 Service 状态
    end

    alt Deployment / StatefulSet YAML
        K8sApi->>K8sServer: 创建 Deployment / StatefulSet
        K8sServer-->>Pod: 调度 Pod
        Pod-->>K8sServer: Pod 就绪
        K8sServer-->>K8sApi: 返回 Deployment/StatefulSet 状态
    end

    alt ConfigMap YAML
        K8sApi->>K8sServer: 创建 ConfigMap
        K8sServer-->>K8sApi: 返回状态
    end

    alt Secret YAML
        K8sApi->>K8sServer: 创建 Secret
        K8sServer-->>K8sApi: 返回状态
    end

    alt Ingress YAML
        K8sApi->>K8sServer: 创建 Ingress
        K8sServer-->>Ingress: 配置负载均衡 / 域名路由
        Ingress-->>K8sServer: 状态更新
        K8sServer-->>K8sApi: 返回状态
    end

    alt HPA YAML
        K8sApi->>K8sServer: 创建 HPA
        K8sServer-->>HPA: HPA 监控 Deployment / StatefulSet
        HPA-->>Pod: 调整副本数
        HPA-->>K8sServer: 返回状态
        K8sServer-->>K8sApi: 返回 HPA 状态
    end

    K8sApi-->>Backend: 返回部署结果
    Backend-->>Frontend: 返回成功/失败状态
    Frontend->>User: toast 提示部署成功或失败

特点说明

  1. 前端生成 YAML 时会根据表单决定资源类型(Deployment / StatefulSet / Service / Ingress / ConfigMap / Secret / HPA)
  2. 后端只负责把 YAML 调用 K8sApi 层部署
  3. K8sApi把每个 YAML 转化成 Kubernetes API 调用
  4. Kubernetes Server负责调度 Pod、更新 HPA、Ingress Controller 配置等

如果你需要,我可以帮你再画一个 交互流程+资源依赖图,显示哪个 YAML 依赖哪个资源(比如 Deployment 依赖 ConfigMap/Secret,Ingress 依赖 Service),更像 部署拓扑图

你希望我画这个吗?

好的,我来帮你梳理这段代码的逻辑,重点是表单提交 → YAML 生成 → Kubernetes 资源创建/操作的调用链。为了方便理解,我会按流程和模块拆解。


1️⃣ K8sContext 接口 #

export interface K8sContext { 
  kc: KubeConfig;             // kubeconfig
  apiClient: KubernetesObjectApi; 
  k8sCore: CoreV1Api; 
  k8sApp: AppsV1Api; 
  k8sAutoscaling: AutoscalingV2Api; 
  k8sNetworkingApp: NetworkingV1Api; 
  k8sCustomObjects: CustomObjectsApi; 
  metricsClient: Metrics; 
  k8sExec: Exec; 
  kube_user: User | null; 
  namespace: string; 
  applyYamlList: (yamlList: string[], type: 'create' | 'replace') => Promise<KubernetesObject[]>; 
  getDeployApp: (appName: string) => Promise<V1Deployment | V1StatefulSet>; 
  getUserQuota: () => Promise<UserQuotaItemType[]>; 
  getUserBalance: () => Promise<number>; 
}
  • 这是整个 Kubernetes 操作上下文对象,封装了对各种 API 的客户端(Core、Apps、Autoscaling、Networking、CustomObjects)。
  • applyYamlList:核心方法,将 YAML 列表应用到集群,可选择 createreplace
  • getDeployApp:获取 Deployment 或 StatefulSet,用于 pause/start/update 操作。

2️⃣ 表单 → YAML → 创建应用 #

export async function createApp(appForm: AppEditType, k8s: K8sContext) {
  serverLoadInitData(); // 初始化前端数据
  const { applyYamlList } = k8s;

  // 给网络配置加上域名
  appForm.networks = appForm.networks.map((network: any) => ({
    ...network,
    domain: global.AppConfig.cloud.domain
  }));

  // 核心:表单数据转 YAML
  const parseYamls = formData2Yamls(appForm); 
  const yamls = parseYamls.map((item) => item.value);

  // 调用 applyYamlList 创建所有资源
  await applyYamlList(yamls, 'create');
}

调用链:

  1. 用户在前端表单填入应用配置(镜像、CPU、内存、端口、存储、网络等)
  2. formData2Yamls(appForm) 将表单数据转换为 Kubernetes YAML 列表,可能包含:
    • Deployment / StatefulSet
    • Service
    • Ingress
    • ConfigMap
    • Secret
    • HPA
  3. applyYamlList(yamls, 'create') 将 YAML 应用到 Kubernetes 集群
    • 内部通过 KubernetesObjectApi 调用 createreplace
    • 生成对应的 Kubernetes 对象

所以 表单 → formData2Yamls → YAML 列表 → applyYamlList → Kubernetes API → 集群对象 是创建应用的核心链路。


3️⃣ 获取应用资源信息 #

export async function getAppByName(appName: string, k8s: K8sContext) {
  const { k8sApp, k8sCore, k8sNetworkingApp, k8sAutoscaling, namespace } = k8s;

  const response = await Promise.allSettled([
    k8sApp.readNamespacedDeployment(appName, namespace),
    k8sApp.readNamespacedStatefulSet(appName, namespace),
    k8sCore.readNamespacedConfigMap(appName, namespace),
    k8sCore.listNamespacedService(namespace, undefined, undefined, undefined, undefined, `${appDeployKey}=${appName}`),
    k8sNetworkingApp.listNamespacedIngress(namespace, undefined, undefined, undefined, undefined, `${appDeployKey}=${appName}`),
    k8sCore.readNamespacedSecret(appName, namespace),
    k8sAutoscaling.readNamespacedHorizontalPodAutoscaler(appName, namespace)
  ]);

  return response;
}
  • 并行获取一个应用相关的全部资源(Deployment、StatefulSet、ConfigMap、Service、Ingress、Secret、HPA)。
  • Promise.allSettled 确保即便某些资源不存在也不会抛出异常。
  • 最终返回一个结果列表,用于前端展示或进一步处理。

4️⃣ Pause / Start 应用 #

  • pauseApp
    • replicas=0 暂停应用
    • 保存 HPA 配置到 annotation(pauseKey
    • 修改 Ingress 的 ingress.classpause
    • 删除 HPA
  • startApp
    • 恢复副本数
    • 根据 pauseKey annotation 重新创建 HPA
    • 修改 Ingress 恢复 ingress.class
    • 删除 pauseKey annotation

核心逻辑:应用的暂停/启动通过修改 Deployment/StatefulSet 的 replicas 和 HPA,同时调整 Ingress 配置来实现。


5️⃣ 更新应用资源 #

export async function updateAppResources(appName: string, updateData, k8s: K8sContext)
  • 更新 CPU、内存、镜像、命令、环境变量、replicas
  • 如果 replicas=0 → pause 逻辑
  • 如果 replicas>0 → start 逻辑 + 恢复 HPA
  • 通过 JSON Patch 精确修改 Deployment/StatefulSet 资源

流程:

  1. 获取当前 Deployment/StatefulSet (getDeployApp)
  2. 计算 JSONPatch(CPU/Memory/Command/Args/Image/Env)
  3. 调用 patchNamespacedDeployment / patchNamespacedStatefulSet 应用修改
  4. 如果涉及 replicas,则结合 pause/start 逻辑操作 HPA

6️⃣ 删除应用 #

export async function deleteAppByName(name: string, k8s: K8sContext)
  • 删除应用相关的所有资源:
    • Deployment / StatefulSet
    • Service / ConfigMap / Secret
    • Ingress
    • PersistentVolumeClaim
    • HorizontalPodAutoscaler
    • cert-manager 的 Certificate / Issuer
  • 使用 Promise.allSettled 并过滤 404 错误,保证删除流程健壮。

7️⃣ 总结表单到 YAML 调用链 #

  • Pause/Start/Update 操作是基于 Deployment/StatefulSet 和 HPA 的 annotation + JSON Patch 机制
  • 删除应用是把所有相关资源全部清理
  • getAppByName 用于把集群状态同步回前端,用于展示和编辑

如果你想,我可以帮你画一张更完整的 表单字段 → YAML 资源类型 → API 调用 → 集群对象 的图,把每种资源的依赖关系和 pause/start HPA 逻辑都展示出来,看起来像一个完整的应用部署拓扑。

你希望我画吗?

好,我们可以把整个 表单 → YAML → 集群资源 的流程画成一张完整的拓扑图,包括 Pause/Start/HPA 逻辑。

下面是用 Mermaid 表示的图:

flowchart TD
    A["前端表单"] --> B["调用 formData2Yamls"]
    B --> C["生成 YAML 列表"]
    C --> D["调用 applyYamlList 创建或替换资源"]
    D --> E["Kubernetes API Server"]

    E --> F["创建 Deployment / StatefulSet"]
    E --> G["创建 Service / ConfigMap / Secret"]
    E --> H["创建 Ingress"]
    E --> I["创建 HorizontalPodAutoscaler"]

    subgraph Pause_Start["暂停/启动流程"]
        J["暂停应用: replicas=0, 保存 HPA 配置"] --> K["删除 HPA"]
        L["启动应用: 恢复 replicas, 重新创建 HPA"] 
    end

    F --> Pause_Start
    I --> Pause_Start

    subgraph Delete_App["删除应用流程"]
        M["删除 Deployment / StatefulSet"] 
        N["删除 Service / ConfigMap / Secret / PVC"] 
        O["删除 Ingress"] 
        P["删除 HPA"] 
    end

    F --> Delete_App
    G --> Delete_App
    H --> Delete_App
    I --> Delete_App

✅ 解释图中各部分 #

  1. 表单 → YAML → 集群
    • 前端表单 AppEditTypeformData2Yamls → YAML 列表 → applyYamlList → Kubernetes API → 集群对象。
  2. Pause/Start
    • Pause:replicas 置 0 + HPA 保存到 annotation + 删除 HPA + Ingress 修改为 pause
    • Start:replicas 恢复 + 根据 pauseKey annotation 重建 HPA + Ingress 恢复
  3. 资源更新
    • 通过 JSON Patch 精确更新 Deployment/StatefulSet 中容器的 CPU/Memory/Image/Command/Args/Env
    • 如果涉及 replicas,则走 Pause/Start 逻辑
  4. 删除应用
    • 删除全部相关资源,包括 Deployment/StatefulSet、Service、Ingress、ConfigMap、Secret、PVC、HPA、Certificate、Issuer
  5. 获取应用状态
    • getAppByName 获取集群资源 → adaptAppDetail + transformFromLegacySchema → 前端标准化对象用于展示/编辑

如果你想,我可以帮你画一个 更细粒度的图,标出每种表单字段对应生成的 Kubernetes YAML 资源和字段路径,这样能直观看到表单字段到 Deployment/Service/HPA 的映射关系。

你希望我画这个更细粒度的图吗?

明白了,你希望把 前端表单字段 → YAML → Kubernetes 资源 → 字段路径 画成一个清晰的 Mermaid 流程图。我们可以按以下思路细化:

  • 每个表单字段对应的 YAML 片段
  • 每个 YAML 对应的 Kubernetes 资源类型
  • 关键字段路径(如 spec.template.spec.containers[0].resources

我给你画一个示意 Mermaid 图:

flowchart TD
    subgraph Form["前端表单"]
        CPU["CPU"]
        Memory["Memory"]
        Replicas["副本数"]
        Image["镜像"]
        Command["启动命令"]
        Args["命令参数"]
        Env["环境变量"]
        Network["网络配置"]
    end

    subgraph formData2Yamls["formData2Yamls(appForm)"]
        CPU_YAML["resources.requests/limits.cpu"]
        Memory_YAML["resources.requests/limits.memory"]
        Replicas_YAML["spec.replicas"]
        Image_YAML["spec.template.spec.containers[0].image"]
        Command_YAML["spec.template.spec.containers[0].command"]
        Args_YAML["spec.template.spec.containers[0].args"]
        Env_YAML["spec.template.spec.containers[0].env"]
        Network_YAML["Service/Ingress domain annotations"]
    end

    subgraph ApplyYAML["applyYamlList -> Kubernetes API"]
        Deployment["Deployment / StatefulSet"]
        Service["Service / ConfigMap / Secret"]
        Ingress["Ingress"]
        HPA["HorizontalPodAutoscaler"]
    end

    %% 表单字段到 YAML 映射
    CPU --> CPU_YAML
    Memory --> Memory_YAML
    Replicas --> Replicas_YAML
    Image --> Image_YAML
    Command --> Command_YAML
    Args --> Args_YAML
    Env --> Env_YAML
    Network --> Network_YAML

    %% YAML 到 Kubernetes 资源映射
    CPU_YAML --> Deployment
    Memory_YAML --> Deployment
    Replicas_YAML --> Deployment
    Image_YAML --> Deployment
    Command_YAML --> Deployment
    Args_YAML --> Deployment
    Env_YAML --> Deployment
    Replicas_YAML --> HPA
    CPU_YAML --> HPA
    Memory_YAML --> HPA
    Network_YAML --> Service
    Network_YAML --> Ingress

💡 说明:

  1. 表单字段:前端 AppEditType 的字段
  2. YAML 片段formData2Yamls 生成的 YAML 对应字段路径
  3. Kubernetes 资源:最终创建的 Deployment、Service、Ingress、HPA

这个图能直观看到 字段 → YAML → K8s 资源 → 字段路径 的对应关系。


我可以帮你画 更详细版本,把每个字段对应的 完整 YAML 路径 都列出来,包括 HPA 的 min/max replicas、target CPU、Service 的 selector 等,这样就像一张完整映射表一样。

你希望我画这个完整详细版本吗?

好的,我帮你画一个完整、细粒度版本,把表单字段对应到具体 Kubernetes YAML 路径和资源类型,涵盖 Deployment、StatefulSet、Service、Ingress、HPA。下面是 Mermaid 流程图示例:

flowchart TD
    %% 前端表单字段
    subgraph Form["前端表单"]
        CPU["CPU (resource.cpu)"]
        Memory["内存 (resource.memory)"]
        Replicas["副本数 (resource.replicas)"]
        Image["镜像 (image)"]
        Command["启动命令 (command)"]
        Args["命令参数 (args)"]
        Env["环境变量 (env)"]
        NetworkDomain["网络域名 (networks.domain)"]
        NetworkPort["网络端口 (networks.port)"]
    end

    %% formData2Yamls 生成 YAML 映射
    subgraph YAML["formData2Yamls(appForm)生成 YAML"]
        CPU_YAML["Deployment.spec.template.spec.containers[0].resources.requests/limits.cpu"]
        Memory_YAML["Deployment.spec.template.spec.containers[0].resources.requests/limits.memory"]
        Replicas_YAML["Deployment.spec.replicas"]
        Image_YAML["Deployment.spec.template.spec.containers[0].image"]
        Command_YAML["Deployment.spec.template.spec.containers[0].command"]
        Args_YAML["Deployment.spec.template.spec.containers[0].args"]
        Env_YAML["Deployment.spec.template.spec.containers[0].env"]
        ServiceSelector["Service.spec.selector.app"]
        ServicePort["Service.spec.ports"]
        IngressHost["Ingress.spec.rules.host"]
        IngressClass["Ingress.spec.ingressClassName / annotations"]
        HPA_MinReplicas["HPA.spec.minReplicas"]
        HPA_MaxReplicas["HPA.spec.maxReplicas"]
        HPA_TargetCPU["HPA.spec.metrics[0].resource.target.averageUtilization"]
    end

    %% YAML 到 Kubernetes 资源映射
    subgraph K8s["Kubernetes 资源"]
        Deployment["Deployment / StatefulSet"]
        Service["Service / ConfigMap / Secret"]
        Ingress["Ingress"]
        HPA["HorizontalPodAutoscaler"]
    end

    %% 表单字段到 YAML 映射
    CPU --> CPU_YAML
    Memory --> Memory_YAML
    Replicas --> Replicas_YAML
    Image --> Image_YAML
    Command --> Command_YAML
    Args --> Args_YAML
    Env --> Env_YAML
    NetworkDomain --> IngressHost
    NetworkDomain --> ServiceSelector
    NetworkPort --> ServicePort

    %% YAML 到资源映射
    CPU_YAML --> Deployment
    Memory_YAML --> Deployment
    Replicas_YAML --> Deployment
    Image_YAML --> Deployment
    Command_YAML --> Deployment
    Args_YAML --> Deployment
    Env_YAML --> Deployment
    ServiceSelector --> Service
    ServicePort --> Service
    IngressHost --> Ingress
    IngressClass --> Ingress

    %% HPA 映射
    Replicas --> HPA_MinReplicas
    Replicas --> HPA_MaxReplicas
    CPU --> HPA_TargetCPU
    Memory --> HPA_TargetCPU
    HPA_MinReplicas --> HPA
    HPA_MaxReplicas --> HPA
    HPA_TargetCPU --> HPA

🔹 特点 #

  1. 表单字段:左侧显示前端 AppEditType 字段
  2. 生成的 YAML:中间显示 formData2Yamls 生成的对应字段路径
  3. Kubernetes 资源:右侧显示 Deployment / StatefulSet / Service / Ingress / HPA
  4. HPA 关联:replicas、CPU、memory 会映射到 HPA 的 min/max replicas 和 target utilization

如果你愿意,我可以帮你再画一个 加上每个 YAML 字段生成逻辑的“注释版”图,比如:CPU → requests: cpu = floor(cpu*0.1)m, limits: cpu = cpu m,这样就能完整看到 表单 → 数值处理 → YAML → K8s 的整个链路。

你希望我画这个注释版吗?

太好了,你把完整代码贴出来了 🙌。这个文件就是你之前问的“前端表单怎么变成后端 deployment 的 YAML”的关键逻辑。 我给你分块解释一下,让你能一眼看清楚 从前端 JSON → Kubernetes YAML 清单 的全过程。


🔑 整体思路 #

  1. 前端表单数据AppEditType)通过 API 提交到后端。
  2. 后端用这些数据生成对应的 K8s YAML/Manifest:Deployment、Service、Ingress、ConfigMap、Secret、HPA 等。
  3. YAML 最终被提交给 Kubernetes API Server,部署应用。

📌 核心函数解析 #

1. json2DeployCr #

作用:把表单数据转成 DeploymentStatefulSet 的 YAML。

流程:

  • metadata:用应用名、标签、注解描述对象。
  • spec.replicas:副本数(从表单里的 hpa.minReplicasreplicas 拿)。
  • containers:容器配置,包括镜像、命令、环境变量、CPU/内存/GPU 资源、端口。
  • volumes/volumeMounts:配置存储卷(PVC/ConfigMap)。
  • gpuMap:如果有 GPU,给 Pod 指定 nodeSelectorruntimeClassName
  • StatefulSet 特殊逻辑:生成 volumeClaimTemplates,挂载数据卷。

输出:一个 Deployment 或 StatefulSet 的 YAML 字符串


2. json2Service #

作用:根据表单里网络配置,生成 Service YAML

流程:

  • 遍历 data.networks:区分 NodePort 和 ClusterIP。
  • 给每个端口生成对应的 Service 配置。
  • 如果有 NodePort,就生成一个 type: NodePort 的 Service;否则生成默认的 ClusterIP。

输出:一个或多个 Service YAML(用 --- 分隔)。


3. json2Ingress #

作用:生成 Ingress YAML,处理域名和 TLS。

流程:

  • 遍历 data.networks,找出需要对外暴露的服务(openPublicDomain = true)。
  • 生成 Ingress 配置(rulestls、nginx annotations)。
  • 如果是自定义域名,还会额外生成 IssuerCertificate(由 cert-manager 管理)。

输出:Ingress + TLS 证书的 YAML。


4. json2ConfigMap #

作用:生成 ConfigMap YAML,把用户输入的配置文件/环境写进去。


5. json2Secret #

作用:生成 Secret YAML,主要用于存储镜像仓库认证信息(dockerconfigjson)。


6. json2HPA #

作用:生成 HPA (HorizontalPodAutoscaler) YAML

  • 根据表单里的 hpa.target(cpu/memory/gpu)生成对应的指标配置。
  • 配置 minReplicasmaxReplicas、伸缩策略。

7. yamlString2Objects #

辅助方法:把 YAML 字符串分割成多个对象,便于后续处理。


📊 整体流程图 #

flowchart TD
A[前端表单 AppEditType JSON] --> B[后端函数 json2DeployCr/json2Service/json2Ingress...]
B --> C[生成 YAML 字符串]
C --> D[提交到 K8s API Server]
D --> E[etcd 存储对象]
E --> F[Scheduler 调度 Pod]
F --> G[Kubelet 拉取镜像并运行容器]

✅ 总结一句话 #

这份代码就是一个 “表单数据 → Kubernetes YAML 工厂”

  • 把前端用户填写的参数(镜像、副本数、端口、存储、域名、环境变量、HPA …)
  • 转换成标准的 Deployment / StatefulSet / Service / Ingress / ConfigMap / Secret / HPA YAML
  • 然后再由后端去调用 Kubernetes API 部署到集群。

要不要我帮你画一个 表单字段 → YAML 字段映射表,让你快速查到「前端填的某个参数最后出现在 YAML 的哪一行」?

非常好的问题 👍 你前面贴的代码(前端表单转 YAML)是 生成 YAML 清单,而这一段代码就是 把 YAML 真正部署到 Kubernetes 集群 的逻辑。 我来帮你逐步拆解:


1. Kubernetes 客户端初始化 #

import * as k8s from '@kubernetes/client-node';

这里用的是 官方 Node.js Kubernetes Client,用来连接并操作 K8s 集群。

export function K8sApiDefault(): k8s.KubeConfig {
  const kc = new k8s.KubeConfig();
  kc.loadFromDefault(); // 默认读取 ~/.kube/config
  return kc;
}

如果是本地调试,就直接加载 ~/.kube/config 里的 kubeconfig 文件。


2. 判断是否在集群内部运行 #

export function CheckIsInCluster(): [boolean, string] {
  if (process.env.KUBERNETES_SERVICE_HOST && process.env.KUBERNETES_SERVICE_PORT) {
    return [true, `https://${process.env.KUBERNETES_SERVICE_HOST}:${process.env.KUBERNETES_SERVICE_PORT}`];
  }
  return [false, ''];
}

如果代码运行在 K8s Pod 内部,Kubernetes 会注入 KUBERNETES_SERVICE_HOSTKUBERNETES_SERVICE_PORT 环境变量。 这样可以直接连 集群内的 API Server,而不用 kubeconfig 文件。


3. 加载 kubeconfig #

export function K8sApi(config: string): k8s.KubeConfig {
  const kc = new k8s.KubeConfig();
  kc.loadFromString(config);
  ...
  return kc;
}

这里会优先用传入的 kubeconfig 字符串,并且如果代码运行在集群内部,会把 server 地址改为集群内的 API 地址。


4. 创建 YAML 到 K8s #

export async function CreateYaml(
  kc: k8s.KubeConfig,
  specs: k8s.KubernetesObject[]
)
  • specs 就是前端转出来的 YAML 解析成的 JSON 对象(Deployment, Service, Ingress 等等)。
  • 它用 KubernetesObjectApiclient.create(spec)
  • 创建成功的对象会 push 到 created 数组里。
  • 如果中途失败,会回滚:把之前成功创建的对象都删除掉。

👉 这就是 把 YAML apply 到集群


5. 更新/替换 YAML 到 K8s #

export async function replaceYaml(kc, specs)

逻辑:

  1. 先用 client.read(spec) 看资源是否存在。
  2. 如果存在,就 client.replace(spec) 更新(需要带上 resourceVersion 才能替换)。
  3. 如果不存在,就 client.create(spec) 新建。

👉 这就是 kubectl apply 的效果


6. 获取用户配额 #

export async function getUserQuota(kc, namespace)

调用 CoreV1Api.readNamespacedResourceQuota,查询命名空间的 ResourceQuota,返回用户的 CPU、内存、存储、NodePort、GPU 使用情况。 相当于 kubectl describe quota


7. 获取用户余额 #

export async function getUserBalance(kc)

这里调用的是 Sealos 自定义 CRD account.sealos.io/v1/accounts,从里面取余额。 说明这个平台在 Kubernetes 上做了 多租户和计费


8. 封装统一的 K8s 操作入口 #

export async function getK8s({ kubeconfig }: { kubeconfig: string })

这个函数会返回一个对象,里面包含:

  • applyYamlList → 负责部署 YAML(create 或 replace)
  • getDeployApp → 获取某个 app 的 Deployment/StatefulSet
  • getUserQuota → 查询用户配额
  • getUserBalance → 查询用户余额
  • 还有一些 client (k8sCore, k8sApp, k8sAutoscaling, k8sNetworkingApp 等) 用于不同的资源操作。

🔗 前后端流程串起来 #

  1. 前端表单(用户填 app 名称、镜像、端口、存储等)。
  2. 前端转 YAML(用你之前贴的 json2DeployCrjson2Service 等函数 → 生成 Deployment/Service/Ingress YAML 字符串)。
  3. 后端解析 YAML(用 yaml.loadAll 解析成 JSON 对象)。
  4. 后端调用 CreateYaml/replaceYaml → 通过 API Server 把对象部署到集群。
  5. Kubernetes 调度器负责拉镜像、创建 Pod。

总结一句话 #

👉 前端只是 表单转 YAML, 👉 后端这段代码才是 把 YAML apply 到 Kubernetes 集群,实现跟 kubectl apply -f xxx.yaml 一样的功能。


要不要我帮你画一个 前端表单 → YAML → API Server → 集群部署流程图(Mermaid),直观一点?

好的 👍 我给你画一个 前端表单 → YAML → K8s 集群部署 的流程图,用 Mermaid 表示:

flowchart TD
    A[用户在前端表单填写参数: 镜像/端口/存储/CPU等] --> B[前端将表单数据转换为 YAML]

    B --> C[后端 API 接收 YAML]
    C --> D[后端解析 YAML 为 JSON 对象]

    D --> E[调用 CreateYaml / replaceYaml 方法]
    E --> F[请求发送到 Kubernetes API Server]

    F --> G[API Server 校验并存储到 etcd]
    G --> H[调度器分配节点]
    H --> I[Kubelet 拉取镜像并启动 Pod]
    I --> J[应用成功运行在 Kubernetes 集群]

这个图走的流程其实就是:

  1. 前端:用户输入参数,转 YAML。
  2. 后端:接收 YAML → 转 JSON → 调用 K8s API。
  3. API Server:存到 etcd → 调度 → 创建 Pod。
  4. 最终应用部署成功

要不要我再给你画一个 时序图(sequence diagram),表现前端、后端、K8s 之间的调用过程?

好嘞 👍 用 Mermaid 的 sequenceDiagram 来画会更直观,表现 前端、后端、K8s API Server 的交互流程:

sequenceDiagram
    participant FE as 前端(表单页面)
    participant BE as 后端(Node.js服务)
    participant K8S as Kubernetes API Server
    participant ETCD as etcd
    participant Kubelet as 节点Kubelet

    FE->>BE: 提交表单数据 (镜像/端口/CPU/存储等)
    BE->>BE: 转换为 YAML (Deployment/Service/Ingress...)
    BE->>K8S: 调用 CreateYaml/replaceYaml<br>提交资源对象
    K8S->>ETCD: 校验并写入资源对象
    K8S->>BE: 返回成功(资源已创建)

    K8S->>Kubelet: 调度 Pod 到节点
    Kubelet->>Kubelet: 拉取镜像,启动容器
    Kubelet-->>K8S: 上报 Pod 状态 Running
    K8S-->>BE: 应用状态可查询
    BE-->>FE: 返回部署成功/状态信息

这样你能清楚看到:

  1. 前端 只管提交表单;
  2. 后端 负责转成 YAML 并调用 K8s API;
  3. API Server 校验+存储到 etcd
  4. 调度器 & Kubelet 完成实际的 Pod 创建;
  5. 最终状态再层层返回到前端。

要不要我再给你加一版 失败场景的时序图(比如 YAML 校验失败 / 资源配额不足)?

KubeSphere 的前端填写 Deployment 表单后,核心流程如下: #


1. 前端表单如何转为 Deployment 的 YAML #

KubeSphere 的前端(如控制台)填写的 Deployment 表单数据,通常会被转换为 Kubernetes Deployment 资源对象的 JSON/YAML。

  • 前端会收集表单数据,比如名称、镜像、labels、replicas、容器配置等。
  • 这些数据会被组装成标准的 K8s Deployment 资源规范(JSON),如:
{
    "apiVersion": "apps/v1",
    "kind": "Deployment",
    "metadata": {
        "name": "example-deployment",
        "namespace": "default",
        "labels": { ... }
    },
    "spec": {
        "replicas": 3,
        "selector": { ... },
        "template": { ... }
    }
}

在 KubeSphere 的代码例子参考(测试数据):

var ksApiserverDeploy = `
{
    "apiVersion": "apps/v1",
    "kind": "Deployment",
    "metadata": {
        "labels": {
            "app": "ks-apiserver",
            "tier": "backend",
        },
        "name": "ks-apiserver",
        "namespace": "kubesphere-system"
    },
    "spec": {
       ...
    }
}
`

2. 如何被部署到 K8s 集群 #

  • 前端会将 YAML/JSON 通过 API 请求发送到 KubeSphere 后端(通常是 RESTful 接口)。
  • 后端负责将这些 Deployment 对象 apply 到 Kubernetes 集群。

后端关键实现如下:

2.1 接收并 apply 资源对象 #

后端会解析前端传来的 JSON/YAML,通过 K8s Dynamic Client 进行 apply:

func (t YamlInstaller) ForApply(tasks []json.RawMessage) (err error) {
	for idx, js := range tasks {
		gvr, utd, err := GetInfoFromBytes(js, t.Mapper)
		if err != nil {
			return err
		}
		opt := metav1.PatchOptions{FieldManager: "v1.FieldManager"}
		_, err = t.DynamicCli.Resource(gvr).
			Namespace(utd.GetNamespace()).
			Patch(context.TODO(), utd.GetName(), types.ApplyPatchType, js, opt)
		if err != nil {
			return err
		}
		klog.Infof("[%d/%d] %s/%s applied", idx+1, len(tasks), gvr.Resource, utd.GetName())
	}
	return nil
}
  • 前端 JSON 数据通过 API 传到后端,后端调用 K8s API,创建或更新 Deployment 资源。

2.2 典型调用链 #

  1. 前端表单 -> 组装标准 Deployment JSON
  2. 前端通过 API 发送到后端(如 /apis/apps/v1/namespaces/{namespace}/deployments
  3. 后端用 DynamicClient、RestMapper 解析 JSON 并 apply 到集群
  4. 资源被成功创建或更新

相关代码参考 #

  • 前端表单组装 Deployment 数据(可参考测试数据):
    pkg/kapis/cluster/v1alpha1/handler_test.go:ksApiserverDeploy
  • 后端应用 YAML/JSON 到 K8s 集群:
    pkg/simple/client/application/yaml.go:YamlInstaller.ForApply

如需更详细的前端/后端源码入口或 API 路径,请补充需求!

好的,这段 Go 代码主要是 KubeSphere 里对 Kubernetes 集群配置的测试代码,虽然它本身是测试(_test.go 文件),但我们可以把它拆解来看,尤其聚焦 前端表单如何映射到 Deployment/YAML 资源 的逻辑。

我给你按层次解释:


1️⃣ 核心结构 #

  1. Cluster 对象
var cluster = &v1alpha1.Cluster{
	ObjectMeta: metav1.ObjectMeta{Name: "gondor"},
	Spec: v1alpha1.ClusterSpec{
		Connection: v1alpha1.Connection{
			Type:  v1alpha1.ConnectionTypeProxy,
			Token: "randomtoken",
		},
	},
}
  • 这里模拟了一个 KubeSphere 集群对象
  • 前端表单如果创建集群,通常会填写:
    • cluster.name
    • cluster.connection.type(直接/代理)
    • cluster.connection.token

这些字段最终映射到 Cluster CRD。


  1. ConfigMap 数据
var hostMap = map[string]string{
	"kubesphere.yaml": `
monitoring:
  endpoint: http://prometheus-operated.kubesphere-monitoring-system.svc:9090
authentication:
  jwtSecret: ...
  oauthOptions:
    accessTokenMaxAge: 0s
    accessTokenInactivityTimeout: 0s
`,
}
  • 模拟了集群的 配置数据
  • 在前端表单中,可能有 监控地址、JWT Secret、OAuth 配置
  • 后端会把这些字段组装成 ConfigMap 并写入 kubesphere-system 命名空间。

  1. Deployment 模板(JSON)
var ksApiserverDeploy = `{
    "apiVersion": "apps/v1",
    "kind": "Deployment",
    "metadata": {
        "name": "ks-apiserver",
        "namespace": "kubesphere-system",
        "labels": {...}
    },
    "spec": {
        "replicas": 1,
        "template": {
            "spec": {
                "containers": [{
                    "name": "ks-apiserver",
                    "image": "kubesphere/ks-apiserver:v3.0.0",
                    "command": ["ks-apiserver","--logtostderr=true"]
                }]
            }
        }
    }
}`
  • 这里是 KubeSphere Apiserver 的 Deployment 模板

  • 前端表单对应字段映射

    表单字段Deployment 字段
    镜像containers[0].image
    命令containers[0].command
    副本数spec.replicas
    环境变量/配置containers[0].env 或 ConfigMap 挂载
  • 后端通过 JSON/Struct 解析后再写入 Kubernetes。


2️⃣ 核心方法:addMemberClusterResource #

func addMemberClusterResource(targetCm *corev1.ConfigMap, t *testing.T) {
	// 构造 clientSet
	con, _ := clientcmd.NewClientConfigFromBytes(...)
	cli, _ := con.ClientConfig()
	c, _ := k8s.NewForConfig(cli)

	// 创建命名空间
	c.CoreV1().Namespaces().Create(...)

	// 创建 ConfigMap
	c.CoreV1().ConfigMaps(namespace).Create(targetCm)

	// 创建 Deployment
	deploy := appsv1.Deployment{}
	json.Unmarshal([]byte(ksApiserverDeploy), &deploy)
	c.AppsV1().Deployments(namespace).Create(&deploy)
}

🔹 做了什么 #

  1. 读取前端表单生成的 KubeConfig
  2. 初始化 Kubernetes client
  3. 创建 命名空间(如果不存在)
  4. 创建或更新 ConfigMap(前端表单字段:监控、JWT、OAuth)
  5. 创建 Deployment(前端表单字段:镜像、命令、replicas)

3️⃣ validateMemberClusterConfiguration #

  • 模拟了验证成员集群配置是否有效
  • 流程:
    1. 先写入 hostCm
    2. 调用 validateMemberClusterConfiguration 校验
    3. 再写入 memberCm
    4. 校验失败(确保检测到重复或错误的配置)

这里可以理解为:前端表单生成的 ConfigMap/YAML 经过后端组装 Deployment 后,最终要验证 集群是否可用、配置是否合理


4️⃣ 总结:表单字段到 Deployment 映射 #

前端表单字段Kubernetes 资源 & YAML 路径
镜像Deployment.spec.template.spec.containers[0].image
命令Deployment.spec.template.spec.containers[0].command
命令参数Deployment.spec.template.spec.containers[0].args
副本数Deployment.spec.replicas
环境变量/配置Deployment.spec.template.spec.containers[0].env / ConfigMap
JWT Secret / OAuth / MonitoringConfigMap.data[“kubesphere.yaml”]
  • 逻辑关系
    • 表单 → 内存对象(Cluster / ConfigMap / Deployment Struct) → Kubernetes API → 真实资源
  • 测试意图
    • 验证表单生成 Deployment 是否正确
    • 校验成员集群配置是否合理

如果你需要,我可以帮你画一张 “前端表单字段 → YAML → Deployment/ConfigMap 映射图”,把上表可视化成 Mermaid 图,更直观地展示前端如何组装 Deployment 数据。

你希望我画这个图吗?

好的,这段代码实现的是 KubeSphere 后端用来将 YAML/JSON 应用到 Kubernetes 集群的一个 “安装器”,用的是动态客户端(dynamic.DynamicClient),模仿 Helm 的接口。我们可以逐步解析它的逻辑,尤其是 前端提交的 YAML/JSON → K8s 资源 的过程。


1️⃣ 核心结构 #

type YamlInstaller struct {
	Mapper      meta.RESTMapper
	DynamicCli  *dynamic.DynamicClient
	GvrListInfo []InsInfo
	Namespace   string
}
type InsInfo struct {
	schema.GroupVersionResource
	Name      string
	Namespace string
}
  • Mapper:RESTMapper,用于把 GroupVersionKind(GVK)映射到 GroupVersionResource(GVR)。
  • DynamicCli:动态客户端,可以操作任意类型的 Kubernetes 资源(Deployment/Service/ConfigMap/CRD 等)。
  • GvrListInfo:记录已经安装的资源信息(用于卸载)。
  • Namespace:资源默认命名空间。

总结:这个结构体就是一个 可对任意 YAML/JSON 应用、升级、删除的动态 Helm 安装器


2️⃣ Install / Upgrade / Uninstall #

Install #

func (t YamlInstaller) Install(...) (string, error) {
	return "", nil
}
  • 这里暂未实现,实际上可以调用 Upgrade 来实现“安装”的逻辑。

Upgrade #

func (t YamlInstaller) Upgrade(ctx context.Context, release, chart string, values []byte, options ...helm.HelmOption) (string, error) {
	yamlList, err := ReadYaml(values)
	if err != nil {
		return "", err
	}
	klog.Infof("attempting to apply %d yaml files", len(yamlList))

	err = t.ForApply(yamlList)
	return "", err
}
  • values:前端传来的 YAML/JSON 内容([]byte)。
  • ReadYaml:把 YAML/JSON 转成 []json.RawMessage(每个对象一个 json.RawMessage)。
  • ForApply:核心方法,将每个对象应用到 Kubernetes。

说明:这里 Upgrade 就是“把 YAML/JSON 应用到集群”,前端表单的数据最终通过这个方法落地。


Uninstall #

func (t YamlInstaller) Uninstall(...) (string, error) {
	for _, i := range t.GvrListInfo {
		err := t.DynamicCli.Resource(i.GroupVersionResource).Namespace(i.Namespace).
			Delete(ctx, i.Name, metav1.DeleteOptions{})
		if apierrors.IsNotFound(err) {
			continue
		}
		if err != nil {
			return "", err
		}
	}
	return "", nil
}
  • 根据 GvrListInfo 卸载所有资源。
  • 使用 Dynamic Client 的 Delete 方法。

3️⃣ 核心方法:ForApply #

func (t YamlInstaller) ForApply(tasks []json.RawMessage) (err error) {
	for idx, js := range tasks {
		gvr, utd, err := GetInfoFromBytes(js, t.Mapper)
		if err != nil {
			return err
		}

		opt := metav1.PatchOptions{FieldManager: "v1.FieldManager"}
		_, err = t.DynamicCli.Resource(gvr).
			Namespace(utd.GetNamespace()).
			Patch(context.TODO(), utd.GetName(), types.ApplyPatchType, js, opt)

		if err != nil {
			return err
		}
		klog.Infof("[%d/%d] %s/%s applied", idx+1, len(tasks), gvr.Resource, utd.GetName())
	}
	return nil
}

核心步骤: #

  1. 解析 YAML/JSON → GVR + Unstructured
gvr, utd, err := GetInfoFromBytes(js, t.Mapper)
  • GetInfoFromBytes
    • 解析每个对象的 apiVersion/kind/name/namespace
    • 返回 GroupVersionResource(GVR)和 Unstructured 对象。
  • 前端表单映射
    • Deployment、Service、ConfigMap 等资源的字段都在这里被解析。
  1. Apply 到集群
t.DynamicCli.Resource(gvr).
	Namespace(utd.GetNamespace()).
	Patch(context.TODO(), utd.GetName(), types.ApplyPatchType, js, opt)
  • 使用 Server-Side ApplyApplyPatchType),类似 kubectl apply -f
    • 如果资源不存在 → 创建
    • 如果资源存在 → 更新
  • FieldManager 用于标记这个 patch 的来源。
  1. 日志记录
klog.Infof("[%d/%d] %s/%s applied", idx+1, len(tasks), gvr.Resource, utd.GetName())
  • 打印应用进度。

4️⃣ 总结逻辑(前端表单到 K8s 资源) #

  1. 前端表单 → YAML/JSON 数据
    • 例如:
      • Deployment:镜像、replicas、资源限制
      • Service:端口、类型
      • ConfigMap/Secret:配置内容
  2. 后端调用 YamlInstaller.Upgrade
  3. ReadYaml(values) → 切成多个 json.RawMessage
  4. ForApply
    • GetInfoFromBytes → 获取 GVR + Unstructured
    • DynamicClient.Patch(ApplyPatchType) → 应用到 Kubernetes
  5. 资源应用完成,GvrListInfo 保存,方便卸载

⚡ 核心点:前端表单字段并不直接写 Deployment 对象,而是被序列化成 YAML/JSON,通过 DynamicClient Apply 到集群。 也就是说:前端字段 → YAML → Unstructured → Kubernetes API。


如果你想,我可以帮你画一个 前端表单字段 → JSON/YAML → DynamicClient → Deployment/Service/ConfigMap 的流程图,把整个流程可视化,更直观。

你希望我画这个图吗?

根据 TKE 源码和文档,前端表单到后端 YAML 及部署到 K8s 的流程如下:


1. 前端如何将表单变为 Deployment 的 YAML #

  • 前端页面(如工作负载创建页面)收集用户填写的 Deployment 信息(如名称、镜像、标签、副本数等)。
  • 前端会将这些表单转换为标准的 Deployment YAML/JSON 对象。

相关配置与转换逻辑参考:

/** deployment的配置 */
export const deployment = (k8sVersion: string) => {
  return generateResourceInfo({
    k8sVersion,
    resourceName: 'deployment',
    requestType: {
      list: 'deployments'
    },
    isRelevantToNamespace: true,
    displayField,
    actionField,
    detailField
  });
};

前端页面通过配置和表单数据,生成 deployment 的 YAML 内容。

表单提交前通常会用 YAML 解析库进行格式校验:

if (appCreation.spec.values.rawValuesType === 'yaml') {
  try {
    JsYAML.safeLoad(appCreation.spec.values.rawValues);
    this.setState({
      yamlValidator: { result: 0, message: '' }
    });
  } catch (error) {
    this.setState({
      yamlValidator: { result: 2, message: t('Yaml格式错误') }
    });
    return;
  }
}

2. 如何被部署到 K8s 集群 #

  • 前端最终提交 YAML/JSON 资源,会调用 WebAPI(通过 RESTful API)将资源发送到后端。
  • 后端接收到 JSON/YAML 后,使用 K8s API 创建或更新资源。

关键 API 代码片段:

export async function updateResourceIns(resource: CreateResource[], regionId: number) {
  const { resourceIns, clusterId, resourceInfo, namespace, jsonData, isStrategic = true } = resource[0];
  const url = reduceK8sRestfulPath({ resourceInfo, namespace, specificName: resourceIns, clusterId });
  const params: RequestParams = {
    method: Method.patch,
    url,
    userDefinedHeader: {
      'Content-Type': isStrategic ? 'application/strategic-merge-patch+json' : 'application/merge-patch+json'
    },
    data: jsonData,
    apiParams: {
      module: 'tke',
      interfaceName: 'ForwardRequest',
      regionId,
      restParams: {
        Method: Method.patch,
        Path: url,
      }
    }
  };
  // 发起 HTTP 请求(patch 或 post),后端代理给 K8s API Server
}
  • 后端会将这些数据代理到 K8s API Server 创建/更新 Deployment 资源。

3. 服务端(Go)侧实际部署代码示例 #

如果涉及业务逻辑触发 K8s 部署,可以参考如下:

func (c *Controller) installDeployment(ctx context.Context, MeshManager *v1.MeshManager, kubeClient kubernetes.Interface) error {
    deploy := c.genDeployment(MeshManager)
    deploymentClient := kubeClient.AppsV1().Deployments(namespace)

    oldDeploy, err := deploymentClient.Get(ctx, deploy.Name, metav1.GetOptions{})
    if err != nil {
        if k8serrors.IsNotFound(err) {
            _, err = deploymentClient.Create(ctx, deploy, metav1.CreateOptions{})
            return err
        }
        return fmt.Errorf("get deployment failed: %v", err)
    }

    newDeploy := oldDeploy.DeepCopy()
    newDeploy.Labels = deploy.Labels
    newDeploy.Spec = deploy.Spec

    // 保留部分原有属性
    if len(oldDeploy.Spec.Template.Spec.Containers) == 1 {
        newDeploy.Spec.Template.Spec.Containers[0].Resources = oldDeploy.Spec.Template.Spec.Containers[0].Resources
    }

    _, err = deploymentClient.Update(ctx, newDeploy, metav1.UpdateOptions{})
    return err
}

4. 文档中的典型 YAML 示例 #

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
  labels:
    app: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  template:
    metadata:
      labels:
        app: nginx-deployment
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

总结流程 #

  1. 表单填写(前端收集数据并生成 Deployment YAML/JSON)。
  2. 前端校验并提交(如 YAML 校验、调用 RESTful API)。
  3. 后端处理(代理请求到 K8s API Server,创建/更新 Deployment)。
  4. K8s 集群中完成部署

如需某一部分的详细源码实现可继续追问。