如何在Google Cloud Builder中缓存Docker多阶段构建及构建步骤
我来帮你梳理下在GCP Cloud Build里实现构建步骤缓存(比如Go的/vendor依赖)+ 最终镜像缓存的最简单方案,结合你现有的Docker多阶段构建和Cloud Build配置来调整:
核心思路
要同时缓存最终镜像和构建阶段的依赖,关键是利用Docker分层缓存特性和Cloud Build的镜像缓存机制,让依赖下载层被重复利用,而不是每次构建都重新拉取。
第一步:优化你的Dockerfile(针对依赖缓存)
你的原Dockerfile里,先执行了go get再复制代码,这会导致每次代码变动时,依赖层也会重新构建。我们调整顺序,先复制依赖配置文件,再下载依赖,最后复制代码:
# 构建阶段(命名为builder,方便后续引用) FROM golang:1.7.3 AS builder WORKDIR /go/src/github.com/alexellis/href-counter/ # 1. 先复制依赖管理文件(如果用dep,就是Gopkg.toml和Gopkg.lock) COPY Gopkg.toml Gopkg.lock ./ # 下载依赖并安装dep(如果还没装的话) RUN go get -u github.com/golang/dep/cmd/dep && dep ensure -v # 2. 再复制应用代码 COPY app.go . # 执行构建 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . # 最终镜像阶段 FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /go/src/github.com/alexellis/href-counter/app . CMD ["./app"]
这样调整后,只要Gopkg.toml和Gopkg.lock不变,依赖下载的这一层就会被Docker缓存,不会每次都重新拉取/vendor依赖。
第二步:修改Cloud Build配置,缓存构建阶段镜像
你的原cloudbuild.yml已经在缓存最终镜像,但还需要缓存构建阶段的builder镜像,这样下次构建时可以直接复用builder里的依赖层。修改后的配置如下:
steps: # 先拉取所有可能的缓存镜像(包括builder和最终镜像) - name: 'gcr.io/cloud-builders/docker' args: ['pull', 'gcr.io/$PROJECT_ID/app-builder:$BRANCH_NAME'] - name: 'gcr.io/cloud-builders/docker' args: ['pull', 'gcr.io/$PROJECT_ID/app-builder:latest'] - name: 'gcr.io/cloud-builders/docker' args: ['pull', 'gcr.io/$PROJECT_ID/app:$BRANCH_NAME'] - name: 'gcr.io/cloud-builders/docker' args: ['pull', 'gcr.io/$PROJECT_ID/app:latest'] # 构建时指定多个缓存来源,包括builder镜像 - name: 'gcr.io/cloud-builders/docker' args: [ 'build', '--cache-from', 'gcr.io/$PROJECT_ID/app-builder:latest', '--cache-from', 'gcr.io/$PROJECT_ID/app-builder:$BRANCH_NAME', '--cache-from', 'gcr.io/$PROJECT_ID/app:latest', '--cache-from', 'gcr.io/$PROJECT_ID/app:$BRANCH_NAME', '--build-arg', 'COMMIT_HASH=$COMMIT_SHA', '-t', 'gcr.io/$PROJECT_ID/app:$COMMIT_SHA', '-t', 'gcr.io/$PROJECT_ID/app-builder:$COMMIT_SHA', # 给builder镜像打标签 '-f', 'config/dockerfiles/app.dockerfile', '.' ] # 给镜像打分支和latest标签 - name: 'gcr.io/cloud-builders/docker' args: ["tag", "gcr.io/$PROJECT_ID/app:$COMMIT_SHA", "gcr.io/$PROJECT_ID/app:$BRANCH_NAME"] - name: 'gcr.io/cloud-builders/docker' args: ["tag", "gcr.io/$PROJECT_ID/app:$COMMIT_SHA", "gcr.io/$PROJECT_ID/app:latest"] - name: 'gcr.io/cloud-builders/docker' args: ["tag", "gcr.io/$PROJECT_ID/app-builder:$COMMIT_SHA", "gcr.io/$PROJECT_ID/app-builder:$BRANCH_NAME"] - name: 'gcr.io/cloud-builders/docker' args: ["tag", "gcr.io/$PROJECT_ID/app-builder:$COMMIT_SHA", "gcr.io/$PROJECT_ID/app-builder:latest"] # 推送所有镜像(包括builder缓存镜像)到GCR images: - 'gcr.io/$PROJECT_ID/app:$COMMIT_SHA' - 'gcr.io/$PROJECT_ID/app:$BRANCH_NAME' - 'gcr.io/$PROJECT_ID/app:latest' - 'gcr.io/$PROJECT_ID/app-builder:$COMMIT_SHA' - 'gcr.io/$PROJECT_ID/app-builder:$BRANCH_NAME' - 'gcr.io/$PROJECT_ID/app-builder:latest'
可选:用Cloud Build缓存卷进一步加速
如果想让依赖缓存更持久(比如跨不同分支构建复用),可以在cloudbuild.yml里添加缓存卷,挂载到Go的依赖缓存目录:
# 在cloudbuild.yml末尾添加 options: caching_options: cache_size: '10GB' # 按需调整大小 volumes: - name: go-mod-cache path: /go/pkg/mod # Go Modules的缓存目录,如果是dep的话是/vendor或者GOPATH下的对应目录
这个卷会在Cloud Build的构建之间保留,即使Docker镜像缓存失效,也能直接复用本地的依赖缓存。
为什么这是最简单的方式?
- 不需要额外的服务或工具,完全基于Docker和Cloud Build的原生特性
- 只需要调整Dockerfile的步骤顺序和Cloud Build的镜像标签/缓存参数,改动很小
- 同时覆盖了依赖层缓存和最终镜像缓存,构建速度提升明显
内容的提问来源于stack exchange,提问作者nmiculinic
相关产品推荐
相关产品推荐

