构建完成以后,把结果交给谁
上一课比较了服务器构建和构建后分发。Dockerfile 没有消除原生编译时的交付问题:构建结果需要从一台机器交给另一台机器,还要知道交付的是哪个版本。
基础篇已经通过 scp 传递过文件。仓库将这种传递扩展为有版本、有权限、可追溯的存储与分发服务。本课先手动完成交接,下一阶段再把这些操作放进。
普通制品与镜像放在一起理解
| 已有产物 | 常见使用方式 | 仓库承担什么 |
|---|---|---|
dist.tar.gz、JAR、原生二进制 | 构建后上传,部署时下载 | 保存版本包、摘要与关联记录 |
| Maven、npm、PyPI 包 | 按包名或坐标、版本发布和下载 | 向其他项目提供依赖,可能还代理上游包 |
| OCI 镜像 | 构建后 push,运行节点 pull | 按镜像协议存取 manifest 与层数据 |
镜像本身也是制品。这里区分“普通制品”和“镜像”,是为了比较格式和消费方式,不是把它们当作互不相关的两类东西。
镜像仓库不能代替所有依赖仓库:构建 Java 项目时可能仍需从 Maven 仓库下载依赖,构建完成后才把运行环境和应用封装为镜像。一个镜像也不会自动解决 CPU 架构、外部数据库和配置兼容性。
从普通发布包到镜像
在练习项目目录用现有 site 生成普通发布包:
mkdir -p artifacts
tar -czf artifacts/site-v1.tar.gz -C site .
# Linux 使用 sha256sum;macOS 可用 shasum -a 256。
shasum -a 256 artifacts/site-v1.tar.gz > artifacts/site-v1.tar.gz.sha256
shasum -a 256 -c artifacts/site-v1.tar.gz.sha256
mkdir -p /tmp/delivery-unpack
tar -xzf artifacts/site-v1.tar.gz -C /tmp/delivery-unpack
cmp site/index.html /tmp/delivery-unpack/index.html解包后还需目标服务器提供 Web 运行环境。换成上一课的镜像时,页面和 Nginx 被一起交付。普通包可存放在团队现有制品服务;本课不要求为了一个压缩包自建 Nexus 或 Artifactory。
摘要帮助检查内容一致性,但期望摘要必须来自可信记录;签名与构建证明留到高级篇。不要把“包与摘要一起上传”当作来源认证已经完成。
选择镜像仓库
| 方案 | 适合的起点 | 需要核对 |
|---|---|---|
| Docker Hub | 使用 Docker 生态的公开或私有镜像 | 命名空间、读取限制、网络与费用 |
| GHCR | 源码已在 GitHub,后续使用 Actions | package 权限、可见性、目标服务器连通性 |
| Harbor | 团队已有内网基础设施和维护能力 | TLS、账号、存储、备份、升级与保留策略 |
| 云厂商镜像服务,如 ACR | 与目标云网络和身份体系集成 | 实例能力、认证方式、网络与费用 |
这些都是候选承载方式,不需要按顺序全部部署。课程主线选 GHCR;网络条件不满足时,可整体换成团队可用仓库。服务价格、额度与产品能力应以实际使用时的官方说明为准。
手动 push,再按 digest 拉取
在 Bash 中执行,将用户名和仓库名替换为自己的小写路径。向 GHCR 推送可使用有 write:packages 的令牌;组织启用 SSO 时还需授权。令牌不写进 Git 或命令历史。
read -r -p 'GitHub 用户名: ' GHCR_USER
read -r -s -p '镜像写入令牌: ' GHCR_TOKEN
printf '\n'
printf '%s' "$GHCR_TOKEN" | docker login ghcr.io -u "$GHCR_USER" --password-stdin
unset GHCR_TOKEN
export IMAGE_NAME="ghcr.io/你的账号/delivery-demo"
docker tag delivery-demo:local "$IMAGE_NAME:lesson13-v1"
docker push "$IMAGE_NAME:lesson13-v1"
docker buildx imagetools inspect "$IMAGE_NAME:lesson13-v1"上面的中文占位符必须替换。读取输出中的顶层 Digest,完整引用形如 ghcr.io/账号/delivery-demo@sha256:…。用下列命令生成交接文件:
DIGEST=$(docker buildx imagetools inspect "$IMAGE_NAME:lesson13-v1" --format '{{.Manifest.Digest}}')
printf '%s@%s\n' "$IMAGE_NAME" "$DIGEST" > image.txt
docker pull "$(cat image.txt)"在目标服务器用只读账号重复 pull,才算验证了交接路径。开发机能拉取不代表服务器或 K3s 已有权限。多架构镜像顶层 digest 与单个平台子镜像 digest 可能不同,发布记录需统一采用哪一层。
版本、权限与保留
标签便于查找,digest 固定内容。正式流程可用完整源码 commit 作为标签,并记录构建编号;同一源码重新构建可能产生不同内容,不能只看源码版本判断镜像相同。
构建身份只写本项目仓库,部署身份只读所需镜像,删除权限单独管理。至少保留当前运行版本和约定恢复窗口内的稳定版本,并验证它们仍可拉取。固定 digest 不会阻止管理员删除底层制品。
| 现象 | 排查方向 |
|---|---|
unauthorized / denied | 账号权限、package 可见性、组织授权 |
manifest unknown | 镜像路径、标签、旧版本是否被清理 |
| 超时 | DNS、证书、出口网络 |
exec format error | 镜像与服务器 CPU 架构 |
本课验收:普通包能校验并解包,镜像能推送并按 digest 拉取,能解释两者的部署差异。下一课明确“运行正常”的判断标准,然后再从仓库交接推导 CI/CD 分离。