CNB_TOKEN 在当次构建就触发了 'The CNB_TOKEN has been destroyed'#3187
构建异常,日志 url: https://cnb.cool/letmelife/backend/cas.go/-/build/runner/download/log/cnb-dsg-1jgjp1v0d-001
背景信息:
以往的构建都没有问题。 这次在 Dockerfile 的其它无关修改之后,触发了重新构建基础镜像,出现了 The CNB_TOKEN has been destroyed,CNB_TOKEN 没有值,导致 .netrc 无效,继而引起无法获取私有依赖。
对应的现象为:
#12 [builder 5/9] RUN chmod 600 /root/.netrc && cat /root/.netrc #12 0.253 machine cnb.cool #12 0.253 login cnb #12 0.253 password ****
#12 [builder 5/9] RUN chmod 600 /root/.netrc && cat /root/.netrc #12 0.239 machine cnb.cool #12 0.239 login #12 0.239 password
尽管 (项目 A 的)Dockerfile 的修改看似无关,为了排除干扰项,另外找了一个之前可以正常构建,且之后完全没有修改的项目(B),将 dockerfile-caches 制品删除,强制重新构建基础镜像,会触发同样的现象。(上述日志是 B 的失败日志)。
总结来说,我们完全理解 CNB_TOKEN 是临时令牌,只在当前构建有效,构建完成后销毁。 举例说,如果 buildA 发现基础镜像不存在,就会触发构建基础镜像,构建过程中会引用 tokenA 下载私有依赖。buildA 结束后,tokenA 会被销毁,留在在基础镜像 imageA 里面的 tokenA 后续是无法生效的。 所以我们在后续的 buildX 中,只是引用 imageA 中已下载的 go mod。如果要重新使用 .netrc,我们会基于 tokenX 重新生成。 但目前的问题是,在 buildA 中构建 imageA 时,tokenA 就已经失效。这在以往没有发生过。
下面附上 .cnb.yml 和 Dockerfile 的关键部分信息:
# .cnb.yml # 依赖配置,定义锚点以复用 .dep_conf: &dep_conf - go.mod - go.sum - Makefile # 触发分支:所有分支均可触发 "**": # 触发事件:通过页面按钮触发 web_trigger_build: # 流水线对象 web_triggered_build: # 1. 指定构建环境,本身也是构建的一部分 docker: # 不使用现成的基础镜像,而是基于 Dockerfile 构建并缓存作为基础镜像,以缓存构建依赖 build: dockerfile: Dockerfile target: builder # by 决定文件的可见性,基础镜像构建中忽略 dockerfile + by 以外的文件 by: - *dep_conf # cache-hash=sha1(dockerfile + versionBy + buildArgs) versionBy: - *dep_conf # hash 忽略 buildArgs ignoreBuildArgsInVersion: true # 构建参数,会被传递到 Dockerfile 中 buildArgs: # CNB_TOKEN 是临时令牌,流水线结束即销毁,为了避免令牌的变更导致缓存失效,所以 hash 忽略了 buildArgs NETRC_MACHINE: cnb.cool NETRC_USERNAME: ${CNB_TOKEN_USER_NAME} NETRC_TOKEN: ${CNB_TOKEN} # 构建环境额外加载的服务 services: - docker # 声明 pipeline 级环境变量 env: FINAL_IMAGE_URL: ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE} # 构建阶段,数组串行执行,对象并行执行 stages: # 省略后续 stages
Dockerfile
# 完整构建的 Dockerfile,但 CNB 为了缓存依赖,仅执行到 builder 阶段 # 定义构建参数,方便在项目间复用逻辑(只需要改动参数)) ARG BUILDER_IMAGE="golang:1.25.4-bookworm" ARG FINAL_IMAGE="debian:bookworm-slim" ARG APP_HOME="/app" ARG APP_NAME="inventory_manager" # go bin 依赖的源码太大,提前安装再复制,以减小依赖镜像大小 FROM ${BUILDER_IMAGE} as pre-builder # 仅复制 Makefile 以执行 make init COPY Makefile ./ RUN go env -w GOPROXY=https://goproxy.cn,direct RUN make init FROM ${BUILDER_IMAGE} as builder COPY --from=pre-builder /go/bin /go/bin ARG APP_HOME WORKDIR ${APP_HOME} ARG NETRC_MACHINE ARG NETRC_USERNAME ARG NETRC_TOKEN # 使用 cat + Here Document 来创建多行文件 # 为了避免被识别为 Dockerfile 指令,必须缩进,再使用 <<- 忽略公共 tab 缩进(空格不行) RUN cat <<-EOF > /root/.netrc machine ${NETRC_MACHINE} login ${NETRC_USERNAME} password ${NETRC_TOKEN} EOF RUN chmod 600 /root/.netrc && cat /root/.netrc # 复制 依赖配置 文件到工作目录 COPY go.mod go.sum ./ # 设置 Go 环境变量 # 1. 设置 GOPROXY 使用国内代理加速公共依赖下载 # 2. 设置 GOPRIVATE 指定私有模块域名,不经过 proxy 和 sumdb RUN go env -w GOPROXY=https://goproxy.cn,direct GOPRIVATE=cnb.cool && \ go env # 下载依赖项 RUN go mod download RUN apt-get update && \ apt-get install -y protobuf-compiler # 省略后续阶段,因为出错部分执行不到
已修复,辛苦重试一下,最新版做了一些改动影响到这里了
已经能构建通过了,但还是有问题: 构建得到的基础镜像无法往 dockerfile-caches 推送 问题日志:https://cnb.cool/letmelife/backend/venue.go/-/build/runner/download/log/cnb-2vo-1jgk5tlvj-001
unauthorized: unauthorized to access package: letmelife/backend/venue.go/dockerfile-caches, action: push: unauthorized to access package: letmelife/backend/venue.go/dockerfile-caches, action: push
在 dockerfile-caches 制品里,确实也没有看到新的镜像。
等于现在可以构建了,基本的需求是满足了,不至于阻塞日程开发和部署。但是基础镜像缓存优化直接失效了。
我们再看看,这里权限调整出了一些问题
已经能构建通过了,但还是有问题: 构建得到的基础镜像无法往 dockerfile-caches 推送 问题日志:https://cnb.cool/letmelife/backend/venue.go/-/build/runner/download/log/cnb-2vo-1jgk5tlvj-001 unauthorized: unauthorized to access package: letmelife/backend/venue.go/dockerfile-caches, action: push: unauthorized to access package: letmelife/backend/venue.go/dockerfile-caches, action: push 在 dockerfile-caches 制品里,确实也没有看到新的镜像。 等于现在可以构建了,基本的需求是满足了,不至于阻塞日程开发和部署。但是基础镜像缓存优化直接失效了。
@JayceChant(Jayce) 这个问题也修复了,辛苦再试试
实测基础镜像可以推送成功,缓存机制可以生效。
响应速度给力,必须点赞👍。
简单描述一下问题和重现方式
构建异常,日志 url:
https://cnb.cool/letmelife/backend/cas.go/-/build/runner/download/log/cnb-dsg-1jgjp1v0d-001
背景信息:
以往的构建都没有问题。
这次在 Dockerfile 的其它无关修改之后,触发了重新构建基础镜像,出现了 The CNB_TOKEN has been destroyed,CNB_TOKEN 没有值,导致 .netrc 无效,继而引起无法获取私有依赖。
对应的现象为:
尽管 (项目 A 的)Dockerfile 的修改看似无关,为了排除干扰项,另外找了一个之前可以正常构建,且之后完全没有修改的项目(B),将 dockerfile-caches 制品删除,强制重新构建基础镜像,会触发同样的现象。(上述日志是 B 的失败日志)。
总结来说,我们完全理解 CNB_TOKEN 是临时令牌,只在当前构建有效,构建完成后销毁。
举例说,如果 buildA 发现基础镜像不存在,就会触发构建基础镜像,构建过程中会引用 tokenA 下载私有依赖。buildA 结束后,tokenA 会被销毁,留在在基础镜像 imageA 里面的 tokenA 后续是无法生效的。
所以我们在后续的 buildX 中,只是引用 imageA 中已下载的 go mod。如果要重新使用 .netrc,我们会基于 tokenX 重新生成。
但目前的问题是,在 buildA 中构建 imageA 时,tokenA 就已经失效。这在以往没有发生过。
下面附上 .cnb.yml 和 Dockerfile 的关键部分信息:
# .cnb.yml # 依赖配置,定义锚点以复用 .dep_conf: &dep_conf - go.mod - go.sum - Makefile # 触发分支:所有分支均可触发 "**": # 触发事件:通过页面按钮触发 web_trigger_build: # 流水线对象 web_triggered_build: # 1. 指定构建环境,本身也是构建的一部分 docker: # 不使用现成的基础镜像,而是基于 Dockerfile 构建并缓存作为基础镜像,以缓存构建依赖 build: dockerfile: Dockerfile target: builder # by 决定文件的可见性,基础镜像构建中忽略 dockerfile + by 以外的文件 by: - *dep_conf # cache-hash=sha1(dockerfile + versionBy + buildArgs) versionBy: - *dep_conf # hash 忽略 buildArgs ignoreBuildArgsInVersion: true # 构建参数,会被传递到 Dockerfile 中 buildArgs: # CNB_TOKEN 是临时令牌,流水线结束即销毁,为了避免令牌的变更导致缓存失效,所以 hash 忽略了 buildArgs NETRC_MACHINE: cnb.cool NETRC_USERNAME: ${CNB_TOKEN_USER_NAME} NETRC_TOKEN: ${CNB_TOKEN} # 构建环境额外加载的服务 services: - docker # 声明 pipeline 级环境变量 env: FINAL_IMAGE_URL: ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE} # 构建阶段,数组串行执行,对象并行执行 stages: # 省略后续 stagesDockerfile