logo
51
166
WeChat Login

CNB_TOKEN 在当次构建就触发了 'The CNB_TOKEN has been destroyed'#3187

Resolved
created 2 months ago
Edit

简单描述一下问题和重现方式

构建异常,日志 url:
https://cnb.cool/letmelife/backend/cas.go/-/build/runner/download/log/cnb-dsg-1jgjp1v0d-001

背景信息:

  • go 项目
  • 基于 docker.build.dockerfile 字段启用了基础镜像缓存
  • 基础镜像里,基于 .netrc 获取私有依赖,通过 buildArgs 传参写入了 CNB_TOKEN,但只在当次构建引用;然后预先执行 go mod download 将依赖包含在基础镜像中,后续只要 go.mod 不修改,可以跳过获取依赖,加快构建速度。

以往的构建都没有问题。
这次在 Dockerfile 的其它无关修改之后,触发了重新构建基础镜像,出现了 The CNB_TOKEN has been destroyed,CNB_TOKEN 没有值,导致 .netrc 无效,继而引起无法获取私有依赖。

对应的现象为:

  • 正常构建时,.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 ****
  • 问题发生时,.netrc 的输出
#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

# 省略后续阶段,因为出错部分执行不到
withdrew a comment.
added priority
P0

已修复,辛苦重试一下,最新版做了一些改动影响到这里了

added labels
已解决:已生效
使用问题:已解决
removed labels
使用问题:已解决
Creator

已经能构建通过了,但还是有问题:
构建得到的基础镜像无法往 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 制品里,确实也没有看到新的镜像。

等于现在可以构建了,基本的需求是满足了,不至于阻塞日程开发和部署。但是基础镜像缓存优化直接失效了。

我们再看看,这里权限调整出了一些问题

assigned self

已经能构建通过了,但还是有问题:
构建得到的基础镜像无法往 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) 这个问题也修复了,辛苦再试试

Creator

实测基础镜像可以推送成功,缓存机制可以生效。

Resolved ISSUE
Creator

响应速度给力,必须点赞👍。

Assignee
(卢嘉辉)
(folger)
Label
一定是bug:待鉴定
已解决:已生效
Priority
P0
Time period
-
Property
Add custom properties to record and label key information
Participant