GitLab CI任务间复用Docker构建缓存的问题排查
GitLab CI/CD 镜像构建问题排查与修复
问题背景
仓库现有.gitlab-ci.yml配置意图通过镜像缓存加速构建,同时在打标签时触发发布构建,但遇到两个问题:
- 缓存未生效:即便
build阶段无任何变更,构建仍会从头执行 docker-build-tag任务未触发:创建新标签时该任务始终不运行
当前.gitlab-ci.yml配置
# This file is a template, and might need editing before it works on your project. docker-build-main: # Official docker image, develop tag image: docker:latest stage: build services: - docker:dind before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - docker pull "$CI_REGISTRY_IMAGE:develop-depsonly" || true - docker build --cache-from "$CI_REGISTRY_IMAGE:develop-depsonly" --target build -t "$CI_REGISTRY_IMAGE:develop-depsonly" . - docker push "$CI_REGISTRY_IMAGE:develop-depsonly" - docker build -t "$CI_REGISTRY_IMAGE:develop" . - docker push "$CI_REGISTRY_IMAGE:develop" rules: - if: $CI_COMMIT_BRANCH != "main" when: never - if: $CI_COMMIT_TAG == null when: on_success docker-build-tag: # Official docker image, tagged release (also updates latest) image: docker:latest stage: build services: - docker:dind before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - docker pull "$CI_REGISTRY_IMAGE:develop-depsonly" || true - docker build --cache-from "$CI_REGISTRY_IMAGE:develop-depsonly" -t "$CI_REGISTRY_IMAGE:develop-depsonly" - docker push "$CI_REGISTRY_IMAGE:develop-depsonly" - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" -t "$CI_REGISTRY_IMAGE" . - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" - docker push "$CI_REGISTRY_IMAGE:latest" rules: - if: $CI_COMMIT_BRANCH != "main" when: never - if: $CI_COMMIT_TAG when: on_success
当前Dockerfile配置
FROM rust:1 as build RUN cargo install cargo-build-deps RUN cargo new --bin simply-shorten WORKDIR /pkg COPY ./actix/Cargo.toml . COPY ./actix/Cargo.lock . RUN cargo build-deps --release COPY ./actix/src ./src COPY ./actix/resources ./resources RUN cargo build --release FROM frolvlad/alpine-glibc:latest EXPOSE 2000 RUN apk add sqlite-libs WORKDIR /opt COPY --from=build /pkg/target/release/pkg /opt/pkg COPY --from=build /pkg/resources /opt/resources CMD ["./pkg"]
问题1:缓存未生效的修复
原因
docker-build-tag任务中构建develop-depsonly镜像时,缺少--target build参数,导致没有正确复用缓存阶段- Dockerfile中
RUN cargo new --bin simply-shorten会生成新文件,破坏后续缓存层的哈希匹配
修复方案
- 在
docker-build-tag的构建命令中添加--target build参数,确保复用缓存阶段 - 移除Dockerfile中不必要的
RUN cargo new --bin simply-shorten命令,优化分层顺序,先复制依赖文件再复制源码
修改后的docker-build-tag脚本片段
- docker build --cache-from "$CI_REGISTRY_IMAGE:develop-depsonly" --target build -t "$CI_REGISTRY_IMAGE:develop-depsonly" .
优化后的Dockerfile
FROM rust:1 as build RUN cargo install cargo-build-deps WORKDIR /pkg # 先复制依赖文件,优先复用缓存 COPY ./actix/Cargo.toml ./actix/Cargo.lock ./ RUN cargo build-deps --release # 再复制源码和资源文件 COPY ./actix/src ./src COPY ./actix/resources ./resources RUN cargo build --release FROM frolvlad/alpine-glibc:latest EXPOSE 2000 RUN apk add sqlite-libs WORKDIR /opt COPY --from=build /pkg/target/release/pkg /opt/pkg COPY --from=build /pkg/resources /opt/resources CMD ["./pkg"]
问题2:docker-build-tag任务未触发的修复
原因
创建标签时,CI_COMMIT_BRANCH变量不会被赋值,导致第一条规则if: $CI_COMMIT_BRANCH != "main"直接跳过任务(标签不属于任何分支)
修复方案
修改docker-build-tag的rules,移除分支判断逻辑,确保标签触发时任务正常执行:
修改后的docker-build-tag规则片段
rules: - if: $CI_COMMIT_TAG when: on_success
如果需要确保标签来自main分支的提交,可以改用CI_COMMIT_REF_NAME变量做判断:
rules: - if: $CI_COMMIT_TAG && $CI_COMMIT_REF_NAME == "main" when: on_success
最终完整的.gitlab-ci.yml配置
# This file is a template, and might need editing before it works on your project. docker-build-main: # Official docker image, develop tag image: docker:latest stage: build services: - docker:dind before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - docker pull "$CI_REGISTRY_IMAGE:develop-depsonly" || true - docker build --cache-from "$CI_REGISTRY_IMAGE:develop-depsonly" --target build -t "$CI_REGISTRY_IMAGE:develop-depsonly" . - docker push "$CI_REGISTRY_IMAGE:develop-depsonly" - docker build -t "$CI_REGISTRY_IMAGE:develop" . - docker push "$CI_REGISTRY_IMAGE:develop" rules: - if: $CI_COMMIT_BRANCH != "main" when: never - if: $CI_COMMIT_TAG == null when: on_success docker-build-tag: # Official docker image, tagged release (also updates latest) image: docker:latest stage: build services: - docker:dind before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - docker pull "$CI_REGISTRY_IMAGE:develop-depsonly" || true - docker build --cache-from "$CI_REGISTRY_IMAGE:develop-depsonly" --target build -t "$CI_REGISTRY_IMAGE:develop-depsonly" . - docker push "$CI_REGISTRY_IMAGE:develop-depsonly" - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" -t "$CI_REGISTRY_IMAGE" . - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" - docker push "$CI_REGISTRY_IMAGE:latest" rules: - if: $CI_COMMIT_TAG when: on_success
内容的提问来源于stack exchange,提问作者Sntn
相关产品推荐
相关产品推荐

