Documentation
¶
Overview ¶
Package build 是构建管线(task-breakdown T2.8 构建管线产品化 + T2.9 镜像身份 v0.1 免 registry):compose 服务的 build/image 两模式到本机 镜像 digest 的生产链——Builder 端口 + Railpack/Dockerfile 双实现 + 构建队列(并发 ≤2、资源限额)+ plan/日志归档 + 本机镜像登记与 preflight(供发布引擎消费)。
── buildkit 接入选型(架构 §2.2 构建行「Railpack + BuildKit」,D6)──
v0.1 单机形态 = 平台自管的 buildkitd 容器(moby/buildkit 钉版镜像, privileged + cgroup 内存/CPU 硬限额 + 缓存持久化卷),经 moby/buildkit/client 以 `docker-container://<name>` connhelper 连接 (connhelper 经 docker CLI `exec -i <ctr> buildctl dial-stdio` 建流, docker CLI 在 PATH 是 fleetly 目标用户「会 Docker」的既定前提)。 理由(2026-09-17 实证):
- dockerd 内嵌 buildkit 的直连形态在本机被探针否定:Windows named pipe 上 buildkit gRPC 的 h2c preface 被 HTTP/1.1 应答拒绝 (dockerd 未在主 socket 暴露 Control 服务);
- Railpack 产物是 LLB,只能经 buildkit Solve 执行——moby /build 路由无 LLB 通道,且 secrets session 管线只有 buildkit client 成形;
- Spike A 的全部证据(本地层缓存 85s→4s、secrets-hash 失效、 1GiB/1.5CPU 限额下构建可完成)都在该形态取得。
端点可配(config build.buildkit_host):指向外部 buildkitd(隔离升级, Spike A 独立 buildkitd 容器形态)或未来 dockerd 原生端点都是纯配置 变更,不动代码。
── Spike A 硬约束落点 ──
- provenance/sbom 必须关闭(#9:attestation manifest list 与 swarm 本地 digest 引用不兼容,任务崩溃循环):本包导出走 docker 导出器 + 客户端侧管道(docker-format tar 结构上不可能携带 manifest list), 且 frontend attrs 显式不请求任何 attest:*(见 buildopt_test.go 断言);
- 缓存是默认能力不是优化(双持久化:buildkitd 内部层缓存命名卷 + 宿主侧 local cache 目录,路径可配;缓存键含 secrets-hash,凭证变化 正确失效——E2a 形态。客户端侧机制勘误见 config.go 头注释);
- 构建在 cgroup 限额内(默认 1GiB/1.5CPU,Spike A E4 实证值);
- 镜像不自动清理(T2.9 策略:release-semantics §2.4「不自动清理镜像」, 见 imagestore.go)。
核心无第三方概念纪律(架构 §2.8):moby/buildkit、railpack 类型只存在 于本包适配层(solve.go / railpack.go),出口是本包核心类型。
Index ¶
- Constants
- Variables
- func ArtifactsDir(base, app, buildID string) string
- func CacheKeyPrefix(app string) string
- func DriverFor(svc compose.Service) (state.Driver, bool, error)
- func ImageRef(app, buildID string) (string, error)
- func ImageTag(app, buildID string) (string, error)
- func SecretsHash(secrets map[string]string) string
- type Builder
- type Config
- type DaemonManager
- type DaemonSpec
- type Executor
- type ImageInfo
- type ImageSource
- type PreflightResult
- type Queue
- type Request
- type Result
Constants ¶
const ( // DefaultBuildkitHost 是 buildkitd 连接端点缺省(docker-container:// // connhelper 经 docker CLI exec buildctl dial-stdio)。 DefaultBuildkitHost = "docker-container://" + DefaultDaemonContainerName // DefaultDaemonContainerName 是平台自管 buildkitd 容器名。 DefaultDaemonContainerName = "fleetly-buildkit" // BuildkitImage 是 buildkitd 钉版镜像(与 Spike A 一致;升级 = 独立 // 变更票,不跟随 railpack 传递依赖漂移)。 BuildkitImage = "moby/buildkit:v0.32.2" // DefaultCacheVolume 是 buildkitd 内部工作缓存的持久化命名卷(挂载点 // InternalCacheMountPath;容器重建不丢层缓存)。 DefaultCacheVolume = "fleetly-buildkit-cache" // InternalCacheMountPath 是 buildkitd 容器内内部工作缓存目录(镜像 // VOLUME 声明路径;命名卷持久化的挂载点)。 InternalCacheMountPath = "/var/lib/buildkit" // DefaultCacheDir 是 local cache 导入/导出的宿主目录缺省(客户端侧 // 数据根;相对 fleetlyd 工作目录,与 db_path/artifacts_dir 同约定)。 DefaultCacheDir = "./build-cache" // DefaultConcurrency 是构建队列并发上限缺省(架构 §4.2:并发构建 2)。 DefaultConcurrency = 2 // MaxConcurrency 是并发上限配置天花板(防误配打穿宿主;超限取上限)。 MaxConcurrency = 8 // DefaultMemoryBytes / DefaultNanoCPUs 是 buildkitd 容器资源限额缺省 // (Spike A E4 实证:1GiB/1.5CPU 下 node 冷构建可完成、峰值 59% 内存 // 天花板)。 DefaultMemoryBytes = int64(1) << 30 DefaultNanoCPUs = int64(1_500_000_000) // DefaultPollInterval 是队列扫描 builds queued 行的周期(CLI 入队为 // 跨进程通道;wake 信号提供同进程加速路径)。 DefaultPollInterval = 2 * time.Second // DefaultArtifactsDir 是产物归档缺省根目录(相对 daemon 工作目录)。 DefaultArtifactsDir = "./build-artifacts" // DefaultTimeout 是单条构建执行的超时预算缺省(30min)。预算覆盖执行 // 全程(buildkitd 就绪收敛(自身 ≤6min)+ solve + inspect);到点未完 // 成即终态 failed——挂起的执行(网络挂起/buildkitd 半死)不得永久占用 // 并发槽(并发 2 时两个挂起即堵死全队列)。 DefaultTimeout = 30 * time.Minute )
buildkitd 资源与命名的平台缺省(Spike A 钉版与实证值)。
const ImageRepoPrefix = "fleetly-local"
ImageRepoPrefix 是本机构建镜像的仓前缀(fleetly-local/<app>;v0.1 免 registry——镜像只落本机 daemon,部署以 `名称@sha256:` 引用,D9)。
const RailpackVersion = "v0.39.0"
RailpackVersion 是平台钉死的 railpack 版本(唯一真源:本常量与 go.mod require 行,单测互钉;平台注入 GenerateBuildPlanOptions.RailpackVersion, 不信任环境)。
const WarningRollbackImageRisk = "W_ROLLBACK_IMAGE_RISK"
WarningRollbackImageRisk 是镜像缺失时随 E_IMAGE_UNAVAILABLE 携带的警告码 字面量(注册表 W_ROLLBACK_IMAGE_RISK:提示保留镜像或重建)。
Variables ¶
var ErrImageNotFound = errors.New("image not found in local daemon")
ErrImageNotFound 表示镜像在本机 daemon 不存在(preflight 的 E_IMAGE_UNAVAILABLE 归因来源)。
Functions ¶
func ArtifactsDir ¶
ArtifactsDir 返回一次构建的产物目录(<artifacts>/<app>/<buildid>)。
func CacheKeyPrefix ¶
CacheKeyPrefix 返回 railpack 缓存挂载键前缀(按应用稳定:同应用跨构建 复用 cache mount,跨应用隔离)。计算失败仅在 app 名非法时发生(调用方 已受控子集校验),失败回落固定前缀并携带错误说明。
func DriverFor ¶
DriverFor 按 compose 服务声明裁决构建驱动(架构 §2.4:无 dockerfile → Railpack 自动;有 build.dockerfile → Dockerfile;仅 image → 镜像模式 直通,无构建)。第二返回值 false = 无构建(image 直通)。
func SecretsHash ¶
SecretsHash 计算构建凭证的稳定失效键(Spike A E2a 形态:缓存键含 secrets-hash,任一凭证值变化即相关层失效;内容不变则哈希稳定命中)。
实现说明:railpack 自带 getSecretsHash 按 map 迭代序拼接值——多凭证时 顺序随机、哈希漂移(上游缺陷)。本实现按 key 排序拼接 `k=v\n`,多凭证 下确定;键集与键名变化同样失效。凭证值只进哈希、不落盘不进日志。
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder 依赖:配置 + 状态库 + 本机镜像端口 + buildkitd 编排端口 + solve 执行器 + 就绪探测。
func NewBuilder ¶
func NewBuilder(cfg Config, store *state.Store, images ImageSource, daemon DaemonManager, log *slog.Logger) *Builder
NewBuilder 构建构建执行器。daemon 可为 nil(装配测试干跑;自管容器形态 下执行前的就绪收敛由 ensureDaemonReady 承载)。
func (*Builder) CacheLocalPath ¶
CacheLocalPath 返回 buildkit local cache 的宿主数据根(客户端侧路径)。
func (*Builder) DaemonSpec ¶
func (b *Builder) DaemonSpec() (DaemonSpec, bool)
DaemonSpec 返回平台自管 buildkitd 容器的目标形态(ManageDaemon=false 时 返回 ok=false——外部端点形态不自管容器)。
func (*Builder) EnsureDaemonReady ¶
EnsureDaemonReady 是 ensureDaemonReady 的公开形态(服务壳预热路径用; 幂等,可在构建执行前任意次调用)。
func (*Builder) Execute ¶
func (b *Builder) Execute(ctx context.Context, rec state.BuildRecord) (state.BuildRecord, error)
Execute 实现执行器契约。
type Config ¶
type Config struct {
// BuildkitHost 是 buildkit 端点(docker-container://<name> 或
// tcp://…/unix://… 直连外部 buildkitd——隔离升级路径)。
BuildkitHost string
// ManageDaemon 报告是否由平台自管 buildkitd 容器(EnsureRunning 幂等
// 拉起钉版容器 + 限额 + 内部缓存卷)。指向外部 buildkitd 时应置 false。
ManageDaemon bool
// DaemonContainerName 是自管 buildkitd 容器名。
DaemonContainerName string
// CacheVolume 是 buildkitd 内部工作缓存的持久化命名卷(缺省)。
CacheVolume string
// CacheDir 是 local cache 导入/导出的宿主目录(客户端侧数据根)。
CacheDir string
// ArtifactsDir 是 plan JSON / 构建日志归档根目录。
ArtifactsDir string
// Concurrency 是构建队列并发上限(≤0 取缺省 2,天花板 8)。
Concurrency int
// MemoryBytes / NanoCPUs 是 buildkitd 容器 cgroup 限额。
MemoryBytes int64
NanoCPUs int64
// PollInterval 是队列 DB 扫描周期。
PollInterval time.Duration
// Timeout 是单条构建执行的超时预算(≤0 取缺省 30min;超时 → 终态
// failed(E_BUILD_FAILED),由队列在认领执行处包裹生效)。
Timeout time.Duration
// ContextRoots 是构建上下文的受管根集合(H14 宿主目录信任边界):
// 执行侧只接受位于受管根内的 context_dir——builds.request 是跨进程
// 通道,直写库的越界请求不得把宿主任意目录整目录打进镜像。Normalize
// 保证系统 temp 根恒在集合内(API 层 compose 暂存/回落基准的落点,
// MkdirTemp("", …) 所在);daemon 装配再并入 git 根(gitserver 裸
// 仓库根,v0.2 worktree 物化路径的前缀形态)与显式配置根。越界构建
// 终态失败(不静默放宽)。v0.2 挂账:CLI 同宿主构建目录(显式
// base_dir)改为上传/物化到受管根后,该集合即对全部入队来源完备闭合。
ContextRoots []string
}
Config 是构建管线配置。零值经 Normalize 回落缺省。
type DaemonManager ¶
type DaemonManager interface {
// EnsureImagePresent 确保镜像在本机 daemon 存在(缺失时拉取并排空
// 响应流)。
EnsureImagePresent(ctx context.Context, image string) error
// EnsureVolumePresent 确保命名卷存在(幂等)。
EnsureVolumePresent(ctx context.Context, name string) error
// EnsureContainerRunning 把容器收敛到「存在且运行」。
EnsureContainerRunning(ctx context.Context, spec DaemonSpec) error
}
DaemonManager 是 buildkitd 容器编排端口(容器形态的平台自管;实现必须 幂等:镜像缺失拉取、容器缺失创建、未运行启动、已在运行 no-op)。
type DaemonSpec ¶
type DaemonSpec struct {
// Name 是容器名(缺省 fleetly-buildkit)。
Name string
// Image 是钉版镜像(moby/buildkit:v0.32.2)。
Image string
// MemoryBytes / NanoCPUs 是 cgroup 硬限额(Spike A E4 形态)。
MemoryBytes int64
NanoCPUs int64
// CacheVolume 是挂到容器内部工作缓存目录(InternalCacheMountPath)的
// 命名卷——层缓存随容器重建不丢。空 = 无内部缓存挂载。
CacheVolume string
}
DaemonSpec 是平台自管 buildkitd 容器的目标形态(幂等收敛的期望态)。
type Executor ¶
type Executor interface {
// Execute 执行一条已 claim(building)的构建记录,终态落库后返回最新
// 记录;返回的错误已归一为应用错误信封。
Execute(ctx context.Context, rec state.BuildRecord) (state.BuildRecord, error)
}
Executor 是构建队列消费的执行契约(单测注入假执行器测并发上限)。
type ImageInfo ¶
type ImageInfo struct {
// ID 是不可变镜像 ID(`sha256:<hex>` 配置摘要,D9 部署引用形态)。
ID string
}
ImageInfo 是一次本机镜像可见性查询的结果。
type ImageSource ¶
type ImageSource interface {
// InspectImage 按引用检查本机镜像;缺失返回 ErrImageNotFound(适配器
// 必须把底座 not-found 归一为该哨兵)。
InspectImage(ctx context.Context, ref string) (ImageInfo, error)
// LoadImage 把 docker-format tar 流装入本机 daemon(构建产物落本机;
// 流由 buildkit docker 导出器客户端侧管道给出)。
LoadImage(ctx context.Context, dockerTar io.Reader) error
}
ImageSource 是本机镜像可见性端口(T2.9 镜像身份:构建后取 digest、 preflight 检查、digest→ref 登记的消费源)。
type PreflightResult ¶
type PreflightResult struct {
// Ref 是被检查的镜像引用(tag 或 `名称@sha256:` 形态)。
Ref string
// Digest 是本机镜像 ID(`sha256:<hex>`;不可用时为空)。
Digest string
// Available 报告镜像在本机 daemon 可得。
Available bool
// Warning 是缺失时的警告码(W_ROLLBACK_IMAGE_RISK);可得时为空。
Warning string
}
PreflightResult 是镜像 preflight 的结果(供发布引擎消费的接口形态)。
func PreflightImage ¶
func PreflightImage(ctx context.Context, images ImageSource, ref string) (PreflightResult, error)
PreflightImage 检查镜像在本机的可得性(release-semantics §2.4 preflight 第一项:镜像可得性,在动底座之前失败)。
- 可得:返回 Available=true + digest;
- 缺失:返回 ErrImageNotFound 哨兵包裹的 E_IMAGE_UNAVAILABLE 信封 (context.warning = W_ROLLBACK_IMAGE_RISK),同时返回带警告标注的 结果供调用方渲染;
- 底座不可达等其他错误:原样返回(引擎侧按 E_RUNTIME_UNAVAILABLE 面 处理,本包不挪用镜像码语义)。
type Queue ¶
type Queue struct {
// contains filtered or unexported fields
}
Queue 是构建队列调度器(dispatcher 单 goroutine + 信号量限并发执行)。
func NewQueue ¶
func NewQueue(store *state.Store, exec Executor, concurrency int, pollInterval, timeout time.Duration, log *slog.Logger) *Queue
NewQueue 构建队列。concurrency ≤0 回落缺省 2、pollInterval/timeout ≤0 回落各自缺省(Normalize 语义)。
func (*Queue) Enqueue ¶
func (q *Queue) Enqueue(ctx context.Context, rec state.BuildRecord, audit ...state.AuditEntry) (state.BuildRecord, error)
Enqueue 入队一条构建(builds queued 行 + 唤醒信号加速同进程拾取)。 audit(可选,至多一枚)透传给 state 层 build.create 审计的调用方归因 (M4-8:TriggerBuild 带 API 调用方 token;缺省 = system 归因,与内部 入队路径一致)。variadic 形态保持既有调用点签名兼容。
type Request ¶
type Request struct {
// BuildID 与 builds.id 一致(产物归档目录与镜像 tag 的可追溯成分)。
BuildID string `json:"build_id"`
// AppID / AppName 是平台应用标识与 compose 项目名(镜像 tag 成分)。
AppID string `json:"app_id"`
AppName string `json:"app_name"`
// Service 是 compose 服务名。
Service string `json:"service"`
// Driver 是构建驱动(railpack | dockerfile;由 compose 模式裁决)。
Driver state.Driver `json:"driver"`
// ContextDir 是构建上下文绝对路径(compose build.context 相对 compose
// 文件目录解析后;受控子集内校验过的目录)。
ContextDir string `json:"context_dir"`
// Dockerfile 是相对 ContextDir 的 dockerfile 路径(仅 dockerfile 驱动)。
Dockerfile string `json:"dockerfile,omitempty"`
// Target 是可选构建目标 stage(v0.1 不开,字段预留 compose 受控子集)。
Target string `json:"target,omitempty"`
// SpecHash 是归一化 compose 的 spec_hash(缓存键与可追溯成分)。
SpecHash string `json:"spec_hash,omitempty"`
// Platform 是构建目标平台(空 = 宿主默认 linux/amd64)。
Platform string `json:"platform,omitempty"`
// Secrets 是构建期凭证(key=值;经 buildkit session secrets 挂载,永不
// 进镜像层——Spike A E3)。v0.1 构建无凭证输入(compose secrets 是运行
// 期 Swarm secret),字段存在即契约:加入缓存键 secrets-hash,未来接入
// 时凭证变化自动正确失效。
Secrets map[string]string `json:"secrets,omitempty"`
}
Request 是一次构建的执行输入(跨进程契约,JSON 落 builds.request)。
func DecodeRequest ¶
DecodeRequest 从 builds.request 列还原请求。
type Result ¶
type Result struct {
// ImageRef 是本机镜像引用 fleetly-local/<app>:<tag>。
ImageRef string
// ImageDigest 是不可变镜像 ID(`sha256:<hex>` 配置摘要;部署引用取
// digest,D9)。
ImageDigest string
// PlanPath 是 railpack plan JSON 归档路径(dockerfile 驱动为空)。
PlanPath string
// LogPath 是构建日志路径。
LogPath string
// Duration 是构建净耗时(二次构建加速对照的取证口径)。
DurationSeconds float64
}
Result 是一次构建的产出(成功路径;失败经 E_BUILD_FAILED 错误信封表达)。