Files
ayatori/docs/api/jobclass-v1alpha1.md
panxiao81 4846ff2aba
Verify / lint (pull_request) Failing after 5m37s
Verify / test (pull_request) Successful in 5m38s
feat: scaffold job execution API
2026-09-17 18:58:11 +00:00

412 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# JobClass API v1alpha1 草案
- 状态:Draft
- 日期:2026-09-17
- API group:`execution.ayatori.ddupan.top`
- Kind:`JobClass`
- Scope:Cluster
## 目标
`JobClass` 是平台管理员提供给 Job 调用者的执行服务等级。名称表达稳定的用户语义,
例如 `default`、`rootless`、`microvm` 或 `trusted-infra`;调用者不需要知道它当前由 Kubernetes
还是 OpenSandbox 实现。
JobClass 负责:
- 选择拥有该 class 的 adapter/controller;
- 引用 adapter 自己的强类型参数对象;
- 限制允许使用该 class 的 namespace;
- 提供跨后端一致的资源默认值与范围;
- 向 Job controller 报告配置是否被接受、后端是否可用。
它不负责保存队列状态、并发配额、Job history 或任意后端 raw config。
## 设计依据
- Kubernetes `RuntimeClass` 使用 cluster-scoped class 将调用者与具体 runtime handler、调度约束
和 overhead 隔离。
- `StorageClass` 允许管理员用稳定名称提供不同服务等级,并由调用者显式或默认选择。
- Gateway API `GatewayClass` 使用 `controllerName + parametersRef` 将稳定 class API 与实现专用
参数分离,并通过 `Accepted` Condition 报告配置有效性。
- Kueue `ResourceFlavor` 将资源规格与具体节点标签、taint 等 placement 细节分开。
Ayatori 采用 GatewayClass 风格的参数引用,不在 JobClass 中建立随 adapter 数量膨胀的
union,也不使用 `map[string]any`。
## 示例
### Kubernetes execution
```yaml
apiVersion: execution.ayatori.ddupan.top/v1alpha1
kind: JobClass
metadata:
name: rootless
annotations:
execution.ayatori.ddupan.top/is-default-job-class: "true"
spec:
controllerName: execution.ayatori.ddupan.top/kubernetes
parametersRef:
group: execution.ayatori.ddupan.top
kind: KubernetesExecutionParameters
name: rootless
allowedNamespaces:
matchLabels:
ayatori.ddupan.top/execution: enabled
resources:
defaults:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "2"
memory: 2Gi
maximum:
limits:
cpu: "8"
memory: 16Gi
---
apiVersion: execution.ayatori.ddupan.top/v1alpha1
kind: KubernetesExecutionParameters
metadata:
name: rootless
spec:
serviceAccountName: ayatori-job
runtimeClassName: runc
scheduling:
nodeSelector:
ayatori.ddupan.top/node-role: execution
tolerations:
- key: ayatori.ddupan.top/execution
operator: Equal
value: "true"
effect: NoSchedule
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
```
### OpenSandbox execution
```yaml
apiVersion: execution.ayatori.ddupan.top/v1alpha1
kind: JobClass
metadata:
name: microvm
spec:
controllerName: execution.ayatori.ddupan.top/opensandbox
parametersRef:
group: execution.ayatori.ddupan.top
kind: OpenSandboxExecutionParameters
name: microvm
allowedNamespaces:
matchLabels:
ayatori.ddupan.top/microvm-access: "true"
resources:
defaults:
requests:
cpu: "1"
memory: 1Gi
limits:
cpu: "2"
memory: 2Gi
maximum:
limits:
cpu: "8"
memory: 16Gi
---
apiVersion: execution.ayatori.ddupan.top/v1alpha1
kind: OpenSandboxExecutionParameters
metadata:
name: microvm
spec:
endpoint: https://opensandbox-api.example.internal
apiKeySecretRef:
namespace: ayatori-system
name: opensandbox-api
key: api-key
poolRef: microvm
requestMapping: AdmissionOnly
allowSecretEnv: true
allowImageAuth: true
allowInsecureHTTP: false
```
`endpoint` 默认必须使用 HTTPS。隔离的本地开发环境可以显式设置
`allowInsecureHTTP: true` 使用 HTTP;生产配置不得启用该开关。
## JobClass spec
```yaml
spec:
controllerName: string
parametersRef: ParametersReference
allowedNamespaces: LabelSelector
resources: ExecutionResourcePolicy
```
### `controllerName`
必填、创建后不可变。使用 domain-prefixed path 标识负责处理该 class 和 Job 的 controller,例如:
```text
execution.ayatori.ddupan.top/kubernetes
execution.ayatori.ddupan.top/opensandbox
```
它是 controller 所有权标识,不是任意可执行插件名称。一个 controller 只能处理自己明确支持
的名称。未来 adapter 拆成独立 Deployment 时,class 和 Job API 无需改变。
### `parametersRef`
必填、创建后不可变:
```yaml
parametersRef:
group: execution.ayatori.ddupan.top
kind: KubernetesExecutionParameters
name: rootless
```
v1alpha1 只允许引用 cluster-scoped 参数对象,并限制 `group`、`kind`、`name` 的长度与格式。
每个 controller 明确列出支持的 kind;引用 ConfigMap、Secret 或未知 CRD 不被接受。
参数引用只有一层,参数 CRD 可以进一步引用 Secret 等运行配置。禁止嵌套通用参数链和 raw
JSON,避免 class 成为无法校验的配置转发器。
### `allowedNamespaces`
可选 Kubernetes `LabelSelector`。Job 所在 namespace 必须匹配才可使用该 class。省略表示允许
所有 namespace;这是显式的管理员选择,而不是用户能力。
该检查由 Job controller 执行,并应尽可能增加 CEL/admission policy 作为快速反馈。用户即使
知道 privileged class 名称,也不能仅靠设置 `jobClassName` 绕过授权。
Namespace label 在 Job 被接受后发生变化,不中断已经运行的 Job,但影响新的 Job。紧急终止
使用独立管理员操作,不通过修改 selector 隐式杀死任务。
### `resources`
可选,定义后端无关的 CPU、内存策略:
```yaml
resources:
defaults:
requests: {cpu, memory}
limits: {cpu, memory}
minimum:
requests: {cpu, memory}
limits: {cpu, memory}
maximum:
requests: {cpu, memory}
limits: {cpu, memory}
```
规则为:
1. Job 未设置的值由 defaults 补齐;
2. 解析结果必须满足 minimum/maximum;
3. CPU 与内存 request 不得大于对应 limit;
4. 解析后的 effective resources 写入 Job status;
5. 后续修改 class 不改变已经接受的 Job;
6. adapter 不能静默降低 effective resources。
只支持 CPU 和内存。GPU、临时磁盘等资源在出现真实后端需求后增加,不先复制完整 Kubernetes
ResourceList。
Runtime/VM overhead 是 adapter 参数或后端调度实现,不计入用户请求的 task resources。
Kubernetes adapter 应优先利用 RuntimeClass Pod overhead;OpenSandbox adapter 在其 capacity
profile 中计算 microVM overhead。
## 默认 class 选择
Job 显式设置 `spec.jobClassName` 时始终优先使用该值。省略时按以下顺序解析:
1. Job namespace annotation
`execution.ayatori.ddupan.top/default-job-class`;
2. 唯一带有
`execution.ayatori.ddupan.top/is-default-job-class: "true"` annotation 的 JobClass。
若不存在默认 class,Job 保持 `Accepted=False/NoDefaultJobClass`。若存在多个全局默认值,
Job 保持 `Accepted=False/AmbiguousDefaultJobClass`,同时产生平台告警;不得模仿
StorageClass 选择最新创建对象,因为执行隔离与权限不应随创建时间变化。
解析后 Job status 保存:
```yaml
resolvedJobClass:
name: rootless
uid: 8aa4...
controllerName: execution.ayatori.ddupan.top/kubernetes
parametersRef:
group: execution.ayatori.ddupan.top
kind: KubernetesExecutionParameters
name: rootless
uid: c413...
```
Job 后续 reconcile 使用已解析引用,不能因 namespace 默认值或全局默认 class 改变而切换
adapter。若同名 class 被删除并重建,UID 不匹配,现存 Job 不得自动采用新对象。
## Status
```yaml
status:
observedGeneration: 1
conditions:
- type: Accepted
status: "True"
reason: Accepted
observedGeneration: 1
lastTransitionTime: ...
- type: Ready
status: "True"
reason: BackendReachable
observedGeneration: 1
lastTransitionTime: ...
```
### `Accepted`
表示 controller 已识别 controllerName,parametersRef 指向受支持且 schema 有效的对象,通用
resource policy 自洽。无效 class 使用 `Accepted=False` 和稳定 reason,例如:
- `UnsupportedController`;
- `InvalidParametersReference`;
- `ParametersNotFound`;
- `InvalidResourcePolicy`。
### `Ready`
表示该 class 当前具备接受新执行的基本条件。Kubernetes adapter 检查 RuntimeClass 等集群级
依赖;具体 namespace 中的 ServiceAccount 在 Job 调度时检查。OpenSandbox adapter 检查参数
引用、认证材料和后端健康端点。
`Ready=False` 阻止创建新的后端执行,但不改变已经开始 Job 的终态。Controller 仍必须尝试
Observe、Cancel 和 Delete 已存在执行,不能因 class 不 Ready 而停止清理。
Ready 是观测值,不是容量预留。容量不足、排队和并发配额属于调度系统,不通过 Ready 频繁
抖动。
## 生命周期与修改
- `controllerName` 和 `parametersRef` 不可变;切换后端必须创建新 class 名称。
- `allowedNamespaces` 与 resource policy 可以修改,只影响尚未接受的新 Job。
- adapter 参数对象允许更新 endpoint、Secret 引用和其他运维配置,以支持凭据轮换与故障切换。
- 参数更新不得使 adapter 为现存 Job 创建新的逻辑执行;Job 中已持久化的 execution reference
始终优先。
JobClass controller 添加保护 finalizer。删除 class 前必须确认不存在引用其 UID 的非终态
Job。终态 Job 已完成后端清理,不阻塞 class 删除;其 TTL 回收不再需要 class 后端配置。
参数对象删除保护由各 adapter controller 负责。在仍有 class 引用时,参数对象不得被无提示
删除。强制移除 finalizer 是管理员恢复操作,必须有 runbook 和审计记录。
## KubernetesExecutionParameters
这是 Kubernetes adapter 自己拥有的 cluster-scoped 管理员 API,不是 Job 用户 API。首版字段:
```yaml
spec:
serviceAccountName: string
runtimeClassName: string
scheduling:
nodeSelector: map[string]string
tolerations: []Toleration
podSecurityContext: PodSecurityContext
imagePullPolicy: Always | IfNotPresent | Never
```
首版原生 `batch/v1 Job` 与 Ayatori Job 位于同一 namespace,因此 Secret/ConfigMap、ResourceQuota、
NetworkPolicy、日志和 owner reference 都保持原生语义。普通调用者只拥有 Ayatori Job 权限,
不应拥有修改生成的 batch Job/Pod 的权限。
`serviceAccountName` 是每个允许 namespace 中预先提供的同名 ServiceAccount。缺失时 Job 保持
未调度并报告原因,不回退到 `default` ServiceAccount。
参数允许使用 Kubernetes 强类型的 Toleration 和 PodSecurityContext,因为这是明确属于
Kubernetes adapter 的管理员 API;这不构成向 Job API 透传 PodSpec。
## OpenSandboxExecutionParameters
这是 OpenSandbox adapter 自己拥有的 cluster-scoped 管理员 API。首版字段:
```yaml
spec:
endpoint: string
apiKeySecretRef:
namespace: string
name: string
key: string
poolRef: string
requestMapping: AdmissionOnly | Native
allowSecretEnv: bool
allowImageAuth: bool
```
- endpoint 必须为 HTTPS,Dev 显式允许的本地配置除外;不得包含认证信息。
- API key 只通过 namespaced Secret 引用,status/Event 不显示内容。
- poolRef 映射为 OpenSandbox 支持的 pool/profile 选择,不允许 Job 覆盖。
- `requestMapping=Native` 要求后端忠实接受 requests 与 limits;`AdmissionOnly` 表示 requests
只参与 Ayatori 准入,limits 映射为 OpenSandbox resourceLimits。
- Secret env 与 per-request image auth 都会使 adapter 读取 Kubernetes Secret 并把解析值发送
到 OpenSandbox API,必须由管理员分别显式启用。
OpenSandbox 参数不暴露任意 `extensions` map。未来确需使用某项 extension 时,将其提升为该
参数 CRD 中经过校验的命名字段。
## Condition 与 Job 状态机交互
Job 只有在以下条件同时满足时进入 `Accepted=True`:
- class 已按默认或显式名称解析;
- class UID 与已解析引用一致;
- class `Accepted=True`;
- namespace 符合 allowedNamespaces;
- Job resources 成功解析并处于允许范围;
- Job 使用的 Secret/ConfigMap 存在且可以按 optional 语义解析。
`JobClass Ready=False` 时,Job 保持 `Accepted=True`、`Scheduled=False`,等待后端恢复。
这样 class 配置合法性与当前可用性不会混成同一状态。
若 Job 已经 Scheduled,后续 class Ready 或 namespace label 变化不撤销执行。若 class 或参数
对象意外消失,controller 仍以 Job status 中的 controllerName、参数 UID 和 execution
references 尝试恢复;无法安全观察时进入非终态 `ResultUnknown`,不能切换 class 重跑。
## 测试矩阵
实现至少覆盖:
- 显式 class、namespace 默认和全局默认的优先级;
- 零个与多个全局默认 class;
- allowedNamespaces 允许、拒绝及接受后 label 变化;
- unsupported controllerName 和错误 parameters kind;
- parameters 不存在、稍后出现、UID 删除重建;
- resource defaults、min/max、request 大于 limit 和 quantity 边界;
- class policy 更新不改变已接受 Job 的 effective resources;
- class Ready=False 阻止新 Ensure,但不阻止现存执行 Observe/Cancel/Delete;
- Kubernetes ServiceAccount/runtime 配置缺失且不回退;
- OpenSandbox API key Secret 缺失、轮换及后端健康恢复;
- allowSecretEnv/allowImageAuth 拒绝不允许的 Job;
- class 删除被非终态 Job 阻止,终态清理后允许删除;
- 同名 class 或参数对象以新 UID 重建时不劫持现存 Job。
## 延后事项
- class 级并发和速率限制;
- Kueue LocalQueue/ClusterQueue 映射;
- capability-based 自动 class 选择;
- 多集群 Kubernetes executor;
- GPU、临时磁盘和其他扩展资源;
- workload identity 与 OpenSandbox Credential Vault;
- class 成本、优先级与抢占策略。
## 成熟实现参考
- [Kubernetes RuntimeClass](https://kubernetes.io/docs/concepts/containers/runtime-class/)
- [Kubernetes StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/)
- [Gateway API GatewayClass](https://gateway-api.sigs.k8s.io/reference/api-types/gatewayclass/)
- [Kueue ResourceFlavor](https://kueue.sigs.k8s.io/docs/concepts/resource_flavor/)