containerbackend

package
v0.35.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 13, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package containerbackend 任务式执行后端的**容器参考实现**(issue #109 的示例半边)。

生命周期: Experimental(v0.35.0 定级)——参考实现随契约与真实引擎形态演进, 不构成任何兼容承诺;定级理由见 api-lifecycle.yaml,分级规则见 docs/api-compat.md。

为什么它是示例而不是内核包

内核 `backend` 只给契约与判据;把某个容器引擎的接法写进内核,等于把一种 部署形态焊进内核依赖面。本目录与 examples/pgstore 的分工同构: **宿主抄它,不 import 它**。两份实现:

  • Docker(docker.go):逐字实现 `backend.Backend`,走 Docker Engine HTTP API(unix socket 或 TCP),**零第三方依赖**(stdlib net/http)—— 不引 Docker 客户端库,与内核「依赖面窄」的既有优势同一条纪律;
  • Kubernetes Job(k8sjob.go):**接入点占位**——契约已经对齐,实现 留给宿主(client-go 是宿主侧依赖;本仓不引 K8s 客户端)。

接线方式(宿主视角)

本包实现的是 `github.com/yi-nology/goagent-kernel/backend.Backend`。 宿主把它(或自己照抄的实现)登记进 backend.Registry,再按 backend.Spec 解析执行方:

var reg backend.Registry
_ = reg.Register(containerbackend.NewDocker())   // 容器隔离档
_ = reg.Register(backend.NewInline())            // 进程内兜底档
b, err := reg.Resolve(backend.Spec{
    MinIsolation: backend.IsolationContainer,
    Egress:       sandbox.EgressDenied,   // Docker 实现以 network=none 兑现
    Budget:       sandbox.Budget{MaxMemory: 256 << 20},
})
res, err := b.Exec(ctx, backend.Job{Image: "registry.example/app@sha256:…", ...})

README 的「执行后端」一节有同样的接线说明与升级边界。

dockerwire.go —— Docker 实现的**传输管道**:端点、请求骨架与日志流 demux。

拆分只为满足单文件 400 行阈值(make architecture-check):语义(自报什么、 执行步骤契约)在 docker.go,管道在这里。宿主改造接法时先读 docker.go。

k8sjob.go —— Kubernetes Job 适配的**接入点占位**(issue #109 明确本轮不做实现)。

为什么占位也要实现 backend.Backend:契约的接入点就是接口本身。占位结构体 让宿主在接线前就能编译通过(方法集齐全),同时**fail-closed**——见下面 Isolation 的自报说明:未接入前它按最弱档自报,判据不会在任何高于「无隔离」 的要求下选中它,Exec 一律报 ErrNotWired(响亮失败,不静默跑空)。

宿主接入 Kubernetes Job 的四步(写明边界,不留猜测空间)

	client-go 属**宿主侧依赖**:本仓与示例都不引 K8s 客户端(与
	「Docker 客户端不进依赖面」同一条纪律;K8s 的清单/凭据更是宿主资产)。

 1. **构造**:宿主用 client-go 的 kubernetes.NewForConfig 建客户端集,
    存进自己的结构体(本占位不持有任何客户端字段);
 2. **Isolation() 改回 Container**:Job 能落容器边界(namespace/cgroup
    由内核裁剪)——接入后按事实自报,占位期的 IsolationNone 随即作废;
 3. **实现 Exec**:Job → batchv1.Job 的映射参考 docker.go 的同一张表:
    Job.Image → Containers[0].Image(**带 digest 原样携带**)、Job.Env → Env、
    Job.Mounts → Volumes/VolumeMounts、Budget.MaxMemory → Resources.Limits、
    Budget.WallClock → activeDeadlineSeconds(到点由 kubelet 终止,
    对应 docker.go 的 kill 路径)、Job.BackoffLimit 设 1(作业失败不重试——
    「跑几次」不是本契约的字段,交给 spec 层决定);
 4. **出站能力如实声明**:集群的网络策略(NetworkPolicy / CNI)决定了
    这个后端能强制哪一档 Egress——不能强制就别实现 EgressEnforcer
    (沉默 = 判据按最弱处理,永远不会被要求禁网的 Spec 选中)。

为什么不提供一个「半成品 Exec」

任何半成品(空跑、只打日志、返回假成功)都会在宿主装配后把「没接上」 伪装成「跑过了」——那是本仓反复强调的最贵失败形态。ErrNotWired 让 未接入保持**未接入的形状**。

Index

Constants

This section is empty.

Variables

View Source
var ErrEngineCall = errors.New("containerbackend: docker engine 调用失败")

ErrEngineCall Docker Engine 调用失败(创建 / 启动 / 等待 / 读取任一步失败)。

属于 backend.Result 注释里说的「后端基础设施故障」:作业没跑起来, 修复对象是引擎侧(引擎不可达 / 镜像不存在 / 参数被拒)。

View Source
var ErrNotWired = errors.New("containerbackend: k8s job 适配未接入(client-go 归宿主侧,接入步骤见包注释)")

ErrNotWired Kubernetes Job 适配尚未接入(接入步骤见包注释)。

Functions

This section is empty.

Types

type Docker

type Docker struct {
	// contains filtered or unexported fields
}

Docker Docker Engine 参考实现。

四条自报(判据按此判,实现按此兑现,缺一即登记门禁意义的说谎):

  • Isolation() = IsolationContainer:作业跑在容器边界内(namespace / cgroup 由内核裁剪);
  • Egress() = EgressDenied:**每个**作业都以 network=none 启动——出站 被网络命名空间强制阻断,不是「设了一组环境变量」;
  • Capable:内存兜得住(容器内存上限),输出截断兜不住(如实自报 false),逐调用计数对作业形态不存在(同样 false);
  • 墙钟经 context 截止兑现(超时杀容器,见 Exec)。

func NewDocker

func NewDocker(opts ...DockerOption) *Docker

NewDocker 构造 Docker Engine 后端(缺省走宿主的缺省 socket——见 defaultSocket)。

func (*Docker) Capable

func (d *Docker) Capable(b sandbox.Budget) bool

Capable 内存兜得住(容器内存上限);输出截断与逐调用计数兜不住—— 如实自报 false,不占便宜。

func (*Docker) Egress

func (d *Docker) Egress() sandbox.EgressMode

Egress 每个作业 network=none 启动 ⇒ 能强制禁网档。

func (*Docker) Exec

func (d *Docker) Exec(ctx context.Context, j backend.Job) (backend.Result, error)

Exec 执行一次作业:create → start → wait → inspect → logs → 清理。

步骤契约:

  • 作业要求兜内存 ⇒ 容器以 Memory 上限启动(Capable 承诺的兑现点);
  • 出站一律 network=none(Egress 承诺的兑现点);
  • Budget.WallClock > 0 ⇒ wait 挂截止,到点 SIGKILL——超时不是「再等等」, 是预算的强制面;被预算终止的作业返回 Result{OK=false}(作业没跑完 是它的失败,不是引擎的故障);
  • 作业跑完(无论成败)都读退出码与日志并清理容器;清理失败不掩盖 主结果(记进 Detail)。

func (*Docker) ID

func (d *Docker) ID() string

func (*Docker) Isolation

func (d *Docker) Isolation() backend.Isolation

Isolation 容器档(见类型注释的自报四条)。

type DockerOption

type DockerOption func(*Docker)

DockerOption Docker 实现的装配项。

func WithAPIVersion

func WithAPIVersion(v string) DockerOption

WithAPIVersion 覆盖 API 版本前缀(缺省 v1.41)。

func WithEndpoint

func WithEndpoint(raw string) DockerOption

WithEndpoint 覆盖引擎端点(unix:///path/to.sock 或 http://host:port)。 测试以 httptest 端点注入假引擎。

func WithID

func WithID(id string) DockerOption

WithID 覆盖后端标识(多套引擎并存时区分登记)。

type K8sJob

type K8sJob struct {
	// contains filtered or unexported fields
}

K8sJob Kubernetes Job 的接入点占位。

func NewK8sJob

func NewK8sJob() *K8sJob

NewK8sJob 构造占位后端。

func (*K8sJob) Exec

func (k *K8sJob) Exec(_ context.Context, _ backend.Job) (backend.Result, error)

Exec 占位:一律拒绝(ErrNotWired)。

func (*K8sJob) ID

func (k *K8sJob) ID() string

ID 后端标识。

func (*K8sJob) Isolation

func (k *K8sJob) Isolation() backend.Isolation

Isolation 未接入前按**最弱档**自报。

这不是谦虚,是 fail-closed:占位实现无法兑现容器边界,若自报 Container, 判据会在「要求=container」时选中它,然后 Exec 报错——「选到了、跑不了」 比一开始就选不到更浪费一次派发。自报 IsolationNone 时,任何更高的要求 都不会走到它头上(无满足者即拒绝,降级的空间为零)。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL