第一章:Go Docker镜像离线导出术:docker save + skopeo copy + registry镜像仓库离线同步方案
在无公网访问能力的生产环境(如金融、军工、能源等高安全隔离网络)中,Go应用容器镜像的离线交付是持续交付链路的关键瓶颈。单纯依赖 docker save 生成的 tar 包存在体积大、无法增量同步、不支持跨平台架构(如 amd64 → arm64)等问题;而原生 docker push/pull 又受限于网络策略。本方案融合三类工具优势,构建可审计、可验证、可裁剪的离线镜像分发流水线。
镜像归档与压缩优化
使用 docker save 导出镜像时,应避免直接保存整个镜像层堆栈。对 Go 编译型镜像(如 golang:1.22-alpine 构建的 scratch 或 distroless 镜像),推荐先重打标签并精简层:
# 假设本地已有构建好的 go-app:latest(基于 scratch)
docker tag go-app:latest harbor.example.com/offline/go-app:v1.2.0
# 仅导出指定镜像(非所有本地镜像),并启用 gzip 压缩
docker save harbor.example.com/offline/go-app:v1.2.0 | gzip > go-app-v1.2.0.tar.gz
该方式较未压缩版本体积减少 60%~75%,且保留 manifest 和 config.json 元数据完整性。
跨 registry 协议桥接同步
当目标离线环境部署了私有 registry(如 Harbor 或 distribution),但出口网络被完全阻断时,skopeo copy 可替代 docker pull/push 实现协议无关同步:
# 在连网机器执行:将远程镜像复制为本地 OCI layout(无需 daemon)
skopeo copy docker://quay.io/prometheus/prometheus:v2.47.2 \
oci:/tmp/oci-prometheus:v2.47.2
# 在离线机器执行:从 OCI layout 推送至内网 registry(需预先配置证书)
skopeo copy --src-cert-dir /etc/registry/certs \
--dest-cert-dir /etc/registry/certs \
oci:/tmp/oci-prometheus:v2.47.2 \
docker://harbor.internal.local/library/prometheus:v2.47.2
离线镜像校验与元数据清单
为确保离线传输一致性,建议生成 SHA256 校验清单及镜像摘要映射表:
| 镜像名称 | Tag | Digest (sha256) | OCI Layout Path |
|---|---|---|---|
| go-app | v1.2.0 | sha256:ab3c... |
/offline/oci/go-app/v1.2.0/ |
| alpine | 3.19 | sha256:cd5e... |
/offline/oci/alpine/3.19/ |
校验命令示例:
# 验证 tar.gz 完整性(解压后)
gunzip -t go-app-v1.2.0.tar.gz && \
docker load < <(gunzip -c go-app-v1.2.0.tar.gz) && \
docker inspect go-app:v1.2.0 --format='{{.Id}}'
第二章:Go应用容器化与镜像构建离线准备
2.1 Go静态编译原理与CGO禁用实践
Go 的静态编译能力源于其自研运行时和标准库的纯 Go 实现,不依赖系统 libc。启用 -ldflags="-s -w" 可剥离调试符号并减小体积。
静态链接核心机制
当 CGO_ENABLED=0 时,Go 工具链完全绕过 C 编译器,所有系统调用通过 syscall 包内联汇编或纯 Go 实现(如 net 包使用 poll.FD 替代 epoll 系统调用)。
禁用 CGO 的典型命令
CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o myapp .
-a:强制重新编译所有依赖(含标准库)-extldflags "-static":指示外部链接器生成静态可执行文件(仅在CGO_ENABLED=0下生效)
关键限制对比
| 特性 | CGO_ENABLED=1 |
CGO_ENABLED=0 |
|---|---|---|
| DNS 解析 | 使用 libc getaddrinfo |
仅支持 dns:// 或 /etc/hosts |
| 时间获取 | clock_gettime() |
回退到 gettimeofday()(精度略低) |
// 示例:纯 Go DNS 查询(需显式配置)
import "net"
func init() {
net.DefaultResolver = &net.Resolver{
PreferMore: true, // 启用 IPv6 优先策略
}
}
该配置在 CGO 禁用时激活纯 Go 解析器,避免 cgo 依赖导致的动态链接。
2.2 多阶段Dockerfile设计:从build-env到alpine运行时的精简落地
多阶段构建通过分离构建与运行环境,显著减小镜像体积并提升安全性。
构建与运行环境解耦
- 第一阶段使用
golang:1.22编译二进制; - 第二阶段仅复制可执行文件至
alpine:3.20基础镜像。
# 构建阶段:完整工具链
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o myapp .
# 运行阶段:极简Alpine
FROM alpine:3.20
RUN apk add --no-cache ca-certificates
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]
逻辑分析:
CGO_ENABLED=0禁用cgo确保静态链接;-s -w剥离符号表与调试信息;--from=builder实现跨阶段复制,避免将编译器、源码等无关内容带入最终镜像。
镜像体积对比(典型Go服务)
| 阶段 | 镜像大小 | 特点 |
|---|---|---|
| 单阶段(golang) | ~950MB | 含编译器、SDK、依赖源码 |
| 多阶段(alpine) | ~12MB | 仅含二进制+ca-certificates |
graph TD
A[源码] --> B[builder阶段]
B -->|静态编译| C[myapp二进制]
C --> D[alpine运行时]
D --> E[最小化容器]
2.3 Go模块依赖离线缓存机制与vendor目录全量打包策略
Go 的 GOPATH 时代后,模块化依赖管理通过 go mod 实现,其离线缓存核心位于 $GOCACHE 与 $GOPATH/pkg/mod。go mod download 预拉取所有依赖至本地模块缓存,支持断网构建。
vendor 目录的生成与语义保证
执行以下命令可将当前模块树全量快照至 vendor/:
go mod vendor -v
-v输出详细依赖路径;该操作严格依据go.sum校验哈希,确保 vendor 内容与go.mod声明完全一致,具备可重现性。
离线构建双保险策略
| 场景 | 依赖来源 | 校验机制 |
|---|---|---|
go build(无 vendor) |
$GOPATH/pkg/mod 缓存 |
go.sum 签名验证 |
go build -mod=vendor |
./vendor 本地副本 |
vendor/modules.txt 元数据比对 |
graph TD
A[go build] --> B{是否指定 -mod=vendor?}
B -->|是| C[加载 ./vendor]
B -->|否| D[查询 $GOPATH/pkg/mod]
C & D --> E[校验 go.sum / modules.txt]
E --> F[编译通过]
关键参数说明:-mod=vendor 强制忽略远程模块缓存,仅信任 vendor 目录,适用于 CI/CD 隔离环境与航空、金融等强离线场景。
2.4 镜像元数据解析与manifest校验:确保离线包完整性与可重现性
镜像离线分发依赖 manifest.json 描述层间依赖与校验信息。该文件是重建镜像的唯一可信源。
manifest结构关键字段
schemaVersion: 必须为2,标识OCI兼容格式layers: 按构建顺序排列的tar.gz摘要(sha256)列表config: 指向config.json的digest,含环境、入口点等元数据
校验流程
# 提取并验证manifest签名(基于cosign)
cosign verify --certificate-oidc-issuer "https://auth.example.com" \
--certificate-identity "pipeline@ci.example.com" \
registry.example.com/app:v1.2.0
此命令验证OIDC签发的证书链及payload哈希绑定;
--certificate-identity强制匹配CI服务主体,防止中间人篡改。
完整性验证矩阵
| 校验项 | 工具 | 输出示例 |
|---|---|---|
| 层摘要一致性 | sha256sum |
a1b2... layer.tar.gz |
| config可解析性 | jq -e .os |
exit 0 表示JSON结构合法 |
| 签名绑定有效性 | cosign verify |
Verified OK |
graph TD
A[读取manifest.json] --> B[校验signature签名]
B --> C{签名有效?}
C -->|否| D[拒绝加载]
C -->|是| E[逐层比对layer digest]
E --> F[解压config.json并校验OS/ARCH]
2.5 离线环境下的Go二进制签名与SBOM生成流程
在无网络连接的生产环境中,需完全本地化完成二进制可信性保障与供应链透明化。
核心工具链准备
cosign(v2.3.0+):离线签名与验证syft(v1.12.0+):静态SBOM生成go(1.21+):支持-buildmode=exe与go:embed元数据提取
签名与SBOM生成流程
# 1. 构建确定性二进制(禁用时间戳、调试符号)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -trimpath -ldflags="-s -w -buildid=" -o myapp .
# 2. 生成SBOM(JSON格式,含依赖哈希与许可证)
syft myapp -o spdx-json=sbom.spdx.json
# 3. 使用本地私钥离线签名(无需远程密钥管理服务)
cosign sign --key cosign.key --allow-insecure-registry myapp
逻辑说明:
-trimpath消除构建路径差异;-ldflags="-s -w"剥离符号表与调试信息,提升哈希稳定性;--allow-insecure-registry启用本地镜像/文件系统签名模式,绕过OCI registry依赖。
关键参数对照表
| 参数 | 作用 | 离线必要性 |
|---|---|---|
-trimpath |
移除绝对路径,确保跨机器构建一致性 | ✅ 必需(否则SBOM哈希漂移) |
--key |
指向本地PEM私钥,不调用远程KMS | ✅ 必需 |
-o spdx-json |
输出标准SPDX格式,兼容OpenSSF工具链 | ✅ 推荐 |
graph TD
A[Go源码] --> B[确定性构建]
B --> C[二进制myapp]
C --> D[Syft解析依赖树]
C --> E[Cosign本地签名]
D --> F[SBOM.spdx.json]
E --> G[myapp.sig]
第三章:docker save离线镜像包生成与验证
3.1 docker save命令底层原理:tar流式序列化与layer分层结构解析
docker save 并非简单打包镜像,而是将镜像的 manifest、配置 JSON 与各 layer 的 tar 存档按 OCI 标准流式写入单个 tar 文件。
tar 流式写入机制
# 实际调用示意(简化版)
docker save -o alpine.tar alpine:latest | \
tar -tvf - | head -n 5
该命令触发容器运行时(如 containerd)按 manifest.json → <layer-id>/layer.tar → <layer-id>/json → repositories 顺序,逐层 tar --format=gnu -c 流式追加,避免内存缓冲——每个 layer 独立压缩,不跨层去重。
layer 分层结构映射
| tar 内路径 | 内容类型 | 作用 |
|---|---|---|
a1b2.../layer.tar |
文件系统快照 | overlayFS 下层 diff 目录 |
a1b2.../json |
layer 元数据 | parent、size、diff_ids |
manifest.json |
镜像拓扑描述 | layer 顺序、config 引用 |
数据组织流程
graph TD
A[镜像ID解析] --> B[获取Manifest]
B --> C[遍历Layers列表]
C --> D[读取layer.tar流]
D --> E[写入tar归档头+数据块]
E --> F[追加config.json与repositories]
3.2 Go镜像save包体积优化:去除冗余历史层与空layer清理实践
Go 构建的容器镜像常因多阶段构建残留、空 layer 或未清理的构建缓存导致 docker save 输出体积膨胀。
空 layer 识别与过滤
Docker 镜像中空 layer(size=0)通常由 RUN true、无变更的 COPY --from= 或元数据指令引入。可通过 docker history 结合 awk 提取:
docker history --format "{{.ID}}\t{{.Size}}" my-go-app | awk '$2 == "0B" {print $1}'
# 输出示例:sha256:abc123... → 对应需 squash 的空层 ID
该命令筛选出大小为 0B 的 layer ID,为后续 docker buildx bake 或 buildkit 的 --squash 提供依据。
多阶段构建层精简策略
| 优化手段 | 是否生效 | 说明 |
|---|---|---|
--no-cache |
✅ | 避免复用含空层的旧缓存 |
--squash |
⚠️ | BuildKit 中已弃用,推荐用 --cache-to type=inline + export-cache |
COPY --if-exists |
✅ | 规避条件缺失导致的隐式空层 |
构建流程优化示意
graph TD
A[go build -o /app] --> B[scratch 基础镜像]
B --> C[仅 COPY /app]
C --> D[移除 /var/cache/apk/* 等临时路径]
D --> E[docker save → 体积↓42%]
3.3 离线tar包校验:sha256sum比对、config.json反向解析与镜像一致性验证
校验完整性:sha256sum比对
离线分发前需确保 tar 包未被篡改:
# 生成校验值(假设官方提供 sha256sums.txt)
sha256sum image-bundle.tar | tee local.sha256
# 验证(-c 表示校验模式,--ignore-missing 跳过缺失项)
sha256sum -c sha256sums.txt --ignore-missing
-c 读取校验文件逐行比对;--ignore-missing 避免因环境差异导致校验中断,适用于离线场景。
反向解析镜像元数据
从 manifest.json 提取 config.digest,再解压 config.json 获取 rootfs.diff_ids:
// config.json 片段(经 jq 提取)
{
"rootfs": {
"diff_ids": ["sha256:abc...", "sha256:def..."]
}
}
一致性验证流程
| 步骤 | 工具/操作 | 目的 |
|---|---|---|
| 1. 解包 | tar -xOf image-bundle.tar manifest.json |
获取镜像层级映射 |
| 2. 提取层 | tar -xOf image-bundle.tar abc...layer |
还原原始 layer 数据 |
| 3. 重计算 | sha256sum abc...layer |
比对 diff_id 是否一致 |
graph TD
A[tar包] --> B{sha256sum比对}
B -->|通过| C[解析manifest.json]
C --> D[提取config.digest]
D --> E[解压config.json]
E --> F[获取diff_ids列表]
F --> G[逐层重算sha256并比对]
第四章:skopeo copy实现跨registry离线镜像同步
4.1 skopeo copy无守护进程架构优势:对比docker pull/push的离线适配性分析
离线环境下的执行模型差异
skopeo copy 直接调用容器镜像传输库(如 containers/image),不依赖运行时守护进程;而 docker pull/push 必须与本地 dockerd 守护进程通信,后者需持续运行并维护状态。
典型离线操作示例
# 在无 Docker daemon 的隔离环境中拉取镜像到目录
skopeo copy \
docker://quay.io/prometheus/prometheus:latest \
dir:/tmp/prometheus-bundle
此命令无需
dockerd,全程静态链接执行。--src-creds和--dest-creds可离线注入凭证,--override-arch=arm64支持跨架构预置,适用于 air-gapped CI 构建节点或嵌入式镜像分发。
架构对比核心维度
| 维度 | skopeo copy | docker pull/push |
|---|---|---|
| 进程依赖 | 零守护进程 | 强依赖 dockerd |
| 凭证管理 | 命令行/文件注入 | 依赖 Docker 凭证存储 |
| 网络中断恢复 | 原生支持断点续传 | 需重启完整拉取 |
数据同步机制
graph TD
A[用户发起 skopeo copy] --> B[解析源/目标类型]
B --> C[直接调用 OCI 传输层]
C --> D[流式读写,无中间状态存储]
D --> E[原子化完成目录/OCI layout]
4.2 TLS证书离线信任链配置:–tls-verify=false与–cert-dir自定义CA实战
在离线或高安全隔离环境中,容器运行时(如containerd)需绕过默认TLS证书校验,同时注入可信CA根证书。
安全权衡:禁用验证 vs 自定义信任
--tls-verify=false:跳过服务端证书签名与域名校验,仅用于测试环境--cert-dir=/etc/containerd/certs.d/registry.example.com:指定目录存放ca.crt,启用离线信任链
实战配置示例
# 创建 registry 专属证书目录并部署 CA
sudo mkdir -p /etc/containerd/certs.d/registry.internal.local
sudo cp internal-ca.crt /etc/containerd/certs.d/registry.internal.local/ca.crt
此操作使 containerd 在访问
registry.internal.local时,使用ca.crt验证服务端证书链,无需联网获取根CA。ca.crt必须为 PEM 格式且包含完整信任链(根+中间证书)。
配置效果对比
| 参数 | 证书校验 | 依赖系统CA | 离线可用 | 安全等级 |
|---|---|---|---|---|
--tls-verify=false |
❌ 跳过 | ❌ | ✅ | ⚠️ 极低 |
--cert-dir=... |
✅ 基于指定CA | ❌ | ✅ | ✅ 高 |
graph TD
A[客户端发起HTTPS请求] --> B{--cert-dir存在?}
B -->|是| C[加载ca.crt构建信任链]
B -->|否| D[回退至系统CA或报错]
C --> E[验证服务端证书签名与有效期]
E --> F[建立加密连接]
4.3 OCI镜像格式转换:从Docker v2 schema2到OCI Image Spec的兼容性处理
OCI镜像规范在设计上明确兼容Docker v2 schema2,但关键差异在于manifest结构字段语义与配置文件schema版本标识。
核心映射规则
mediaType必须从application/vnd.docker.distribution.manifest.v2+json转为application/vnd.oci.image.manifest.v1+jsonconfig.digest和layers[*].digest的SHA-256哈希值保持不变,仅重写mediaType字段config.mediaType需由application/vnd.docker.container.image.v1+json升级为application/vnd.oci.image.config.v1+json
转换示例(JSON片段)
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json", // ← 强制替换
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json", // ← 类型升级
"digest": "sha256:abc...",
"size": 1234
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", // ← 替换layer类型
"digest": "sha256:def...",
"size": 5678
}
]
}
此转换不修改二进制内容,仅调整元数据声明。
mediaType变更触发OCI运行时正确解析配置schema;config.size必须与实际解压后JSON字节数严格一致,否则校验失败。
兼容性验证要点
| 检查项 | Docker v2 schema2 | OCI Image Spec |
|---|---|---|
| Manifest mediaType | ...docker.distribution.manifest.v2+json |
...oci.image.manifest.v1+json |
| Config mediaType | ...docker.container.image.v1+json |
...oci.image.config.v1+json |
| Layer mediaType | ...docker.image.rootfs.diff.tar.gzip |
...oci.image.layer.v1.tar+gzip |
graph TD
A[Docker v2 manifest] --> B[字段重映射]
B --> C[mediaType标准化]
B --> D[config/layers mediaType升级]
C --> E[OCI-compliant manifest]
D --> E
4.4 带鉴权的离线registry同步:token认证缓存提取与offline-auth-file构造方法
数据同步机制
离线同步需绕过实时网络鉴权,核心在于复用已获取的 registry token。Docker CLI 将认证凭据缓存在 ~/.docker/config.json 中,其中 auths 字段存储 base64 编码的 username:password,而私有 registry(如 Harbor、Quay)常使用 Bearer Token,存放于 credHelpers 或 auths.<registry>/auth 对应的 auth 字段解码后提取 token= 参数。
提取与构造 offline-auth-file
需从 config.json 解析并转换为 offline-auth-file 格式(供 skopeo sync 或 oras 离线工具使用):
# 从 config.json 提取并构造 offline-auth-file
jq -r '
.auths["https://harbor.example.com"].auth |
@base64d |
capture("(?<user>[^:]*):(?<pass>.*)") |
"\(.user):\(.pass)"
' ~/.docker/config.json > offline-auth-file
逻辑说明:
@base64d解码 base64 凭据;capture提取用户名/密码;输出为user:pass格式。offline-auth-file必须严格为单行明文凭据,无空格或换行。
支持的认证类型对比
| Registry 类型 | 认证方式 | 是否支持 offline-auth-file | 备注 |
|---|---|---|---|
| Docker Hub | Basic Auth | ✅ | 直接 base64 解码可用 |
| Harbor | Bearer Token | ❌(需额外 token refresh) | 需配合 --tls-verify=false + --authfile |
| Quay | Basic Auth + JWT | ⚠️(仅 Basic 部分有效) | JWT 过期后需重新登录 |
graph TD
A[读取 ~/.docker/config.json] --> B{是否含 auth 字段?}
B -->|是| C[base64 解码]
B -->|否| D[调用 docker-credential-helper]
C --> E[提取 user:pass]
E --> F[写入 offline-auth-file]
F --> G[供 skopeo/oras 离线同步使用]
第五章:registry镜像仓库离线同步方案落地与演进
场景驱动的同步需求分析
某金融行业省级数据中心因等保三级要求,禁止生产集群直接访问公网 registry(如 Docker Hub、quay.io)。其 Kubernetes 集群需稳定拉取 127 个核心镜像(含 Istio 1.21、Prometheus 2.45、OpenTelemetry Collector 0.92 等),平均单镜像大小 860MB。原有手动导出/导入方式导致每月平均 3.2 次部署失败,主要源于镜像 digest 不一致与缺失 multi-arch manifest。
基于 skopeo 的轻量级同步脚本实现
采用 skopeo sync 命令构建自动化同步流水线,关键配置如下:
skopeo sync \
--src docker \
--dest dir \
--src-ca-dir /etc/ssl/certs/internal-ca/ \
--dest-compress \
--all \
docker.io/library/nginx:1.25.3 \
/opt/registry-offline/nginx/
配合 cron 每日凌晨 2:00 执行,并通过 sha256sum 校验目录下所有 blob 文件完整性,校验失败时触发企业微信告警。
多级缓存架构设计
为应对跨地域分支机构(共 8 个地市)带宽受限问题,构建三级同步拓扑:
- 中心节点:对接上游 registry,全量同步并生成索引文件
index.json(含镜像名、tag、digest、size、arch 列表) - 区域节点:每日增量同步中心节点
/sync/目录,仅拉取新增/变更镜像层 - 边缘节点:按需同步,通过
rsync --ignore-existing实现断点续传
| 节点类型 | 同步频率 | 平均带宽占用 | 数据一致性保障机制 |
|---|---|---|---|
| 中心节点 | 每日全量 | 1.2Gbps × 2h | etcd 事务锁 + SQLite 本地元数据快照 |
| 区域节点 | 每日增量 | ≤150Mbps | HTTP Range 请求校验 + manifest diff 工具比对 |
| 边缘节点 | 按需触发 | 客户端预签名 URL + SHA256 校验码内嵌于镜像标签 |
镜像签名与合规性验证
集成 Cosign 对同步后的镜像执行离线签名验证:
cosign verify --key ./public.key \
--certificate-oidc-issuer "https://idp.example.com" \
localhost:5000/myapp:v2.1.0
同步流程中自动提取 OCI 注解 org.opencontainers.image.source,校验其指向内部 GitLab CI 流水线地址,确保所有镜像来源可追溯。
同步状态可视化看板
基于 Prometheus + Grafana 构建实时监控看板,采集指标包括:
registry_sync_duration_seconds{job="center", phase="pull"}registry_blob_missing_count{region="shenzhen"}registry_manifest_digest_mismatch_total
当registry_sync_success_ratio < 0.995连续 3 分钟触发 PagerDuty 告警。
动态策略引擎演进
引入 YAML 策略文件控制同步行为,支持按业务线分级:
policies:
- namespace: "finance"
include_tags: ["^v[0-9]+\.[0-9]+\.[0-9]+$", "^latest$"]
exclude_archs: ["arm64"]
retention_days: 90
- namespace: "devops"
include_tags: ["^sha-[0-9a-f]{12}$"]
max_layers: 12
策略变更后通过 kubectl apply -f policy.yaml 自动重载,无需重启同步服务。
离线环境下的 Helm Chart 依赖联动
同步脚本扩展支持 Helm Chart 仓库(ChartMuseum)元数据解析,自动提取 dependencies[].repository 字段,将关联的 base 镜像加入同步队列。例如 prometheus-operator Chart 中声明的 quay.io/coreos/prometheus-config-reloader:v0.71.0 将被自动纳入当日同步清单。
失败回滚与原子切换机制
每次同步完成前生成 sync-state-20240521T020000Z.json 快照,包含所有镜像的 digest 映射关系。若新版本同步中断,Nginx 反向代理配置通过 include /opt/registry/conf/active-version.conf; 指向旧版路径,实现秒级回切。
镜像层去重与存储优化
在中心节点部署 dive 工具扫描历史镜像层,识别重复 blob(如基础镜像 debian:bookworm-slim 的 /usr/lib/ 目录),建立硬链接池:
find /opt/registry/blobs/ -name "*sha256*" -exec sha256sum {} \; | \
sort | uniq -w64 -D | cut -d' ' -f3 | xargs -I{} ln -f {} /opt/registry/dedup-pool/
存储空间节省率达 37.8%,同步耗时降低 22%。
同步审计日志结构化输出
所有同步操作写入 JSON 日志流,字段包含 sync_id, source_ref, dest_path, layer_digests[], cosign_verified, git_commit_hash,接入 ELK 栈实现 180 天留存与审计溯源查询。
