You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在CodeBuild中仅构建Bitbucket单仓代码变更的Docker镜像并推送至ECR

优化CodeBuild镜像构建:只构建代码有变更的镜像

我用Bitbucket单仓库,代码推送后会触发CodeBuild构建Docker镜像并推送到ECR。现在每次推送要构建大概16个镜像,想优化成只构建代码有变更的对应镜像,避免重复推送相同镜像到ECR,以此节省CodeBuild的时间。之前试过拉取仓库里的镜像做对比,但这个拉取操作本身也挺耗时的。

当前的Buildspec.yml配置如下:

version: 0.2
phases: 
  install:  
    commands: 
      - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2&
      - timeout 15 sh -c "until docker info; do echo .; sleep 1; done"
  pre_build: 
    commands: 
    - echo Logging in to Amazon ECR.... 
    - aws --version
    # update the following line with your own region
    - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
    - COMMIT_HASH=$(echo $RESOLVED_SOURCE_VERSION | cut -c 1-7)
    - IMAGE_TAG=latest
    - REPOSITORY_URI=23685584657641.dkr.ecr.us-east-1.amazonaws.com/my-bitbucket-to-ecr-1
    - REPOSITORY_URI_2=34585584995410.dkr.ecr.us-east-1.amazonaws.com/my-bitbucket-to-ecr-2
  build: 
    commands: 
    - echo Build started on `date` 
    - echo Building the Docker image... 
    # update the following line with the name of your own ECR repository
    - ls -a
    - pwd
    - cd api
    - pwd
    - docker build -t $REPOSITORY_URI_2:latest -f Dockerfile.google .
    - cd ..
    - pwd
    - echo ${LAMBDA_TASK_ROOT}
    - docker build -t $REPOSITORY_URI:latest -f Dockerfile.lambda .
    - docker run -d --rm --name lambda_test_container $REPOSITORY_URI:latest
    - docker ps -a
    - docker exec lambda_test_container python -m flake8 /var/task/lambda_function.py /var/task/test_lambda_function.py
    - docker exec lambda_test_container python -m unittest /var/task/test_lambda_function.py
    - docker exec lambda_test_container python -m black /var/task/lambda_function.py /var/task/test_lambda_function.py
    - docker stop lambda_test_container
    # update the following line with the URI of your own ECR repository (view the Push Commands in the console)
    - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
    - docker tag $REPOSITORY_URI_2:latest $REPOSITORY_URI_2:$IMAGE_TAG 
  post_build: 
    commands: 
    - echo Build completed on `date` 
    - echo pushing to repo
    # update the following line with the URI of your own ECR repository
    - docker push $REPOSITORY_URI:latest
    - docker push $REPOSITORY_URI:$IMAGE_TAG
    - docker push $REPOSITORY_URI_2:latest
    - docker push $REPOSITORY_URI_2:$IMAGE_TAG

    - echo Writing image definitions file... 
    #- printf '[{\"ImageURI\":\"%s\"}]' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
    - printf '{\"ImageURI\":\"%s\"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
  files:
    - imageDetail.json

方法1:通过Git文件变更路径判断要构建的镜像

核心思路是在构建前用Git对比当前提交和上一次构建的提交,找出变更的文件,然后根据文件所属的目录/模块,决定要不要构建对应的镜像。

修改Buildspec.yml步骤:

  1. 在pre_build阶段添加Git差异检测,设置环境变量标记是否需要构建某个镜像:
pre_build: 
  commands: 
    # ... 原有命令保持不变 ...
    # 获取上一次提交的哈希(首次构建时用当前提交的短哈希)
    - PREV_COMMIT=$(git rev-parse HEAD^1 2>/dev/null || git rev-parse --short=7 HEAD)
    # 检测api目录下的文件是否有变更
    - CHANGED_API=$(git diff --name-only $PREV_COMMIT HEAD | grep -E "^api/" | wc -l)
    - if [ $CHANGED_API -gt 0 ]; then BUILD_API=true; else BUILD_API=false; fi
    # 检测lambda相关文件是否有变更
    - CHANGED_LAMBDA=$(git diff --name-only $PREV_COMMIT HEAD | grep -E "^Dockerfile.lambda|lambda_function.py|test_lambda_function.py" | wc -l)
    - if [ $CHANGED_LAMBDA -gt 0 ]; then BUILD_LAMBDA=true; else BUILD_LAMBDA=false; fi
  1. 在build阶段根据标记变量决定是否执行构建命令:
build: 
  commands: 
    - echo Build started on `date` 
    # ... 原有ls、pwd等命令保持不变 ...
    # 仅api目录有变更时构建REPOSITORY_URI_2镜像
    - if [ "$BUILD_API" = true ]; then cd api && docker build -t $REPOSITORY_URI_2:latest -f Dockerfile.google . && cd ..; fi
    # 仅lambda相关文件有变更时构建REPOSITORY_URI镜像并执行测试
    - if [ "$BUILD_LAMBDA" = true ]; then 
        docker build -t $REPOSITORY_URI:latest -f Dockerfile.lambda .;
        docker run -d --rm --name lambda_test_container $REPOSITORY_URI:latest;
        docker ps -a;
        docker exec lambda_test_container python -m flake8 /var/task/lambda_function.py /var/task/test_lambda_function.py;
        docker exec lambda_test_container python -m unittest /var/task/test_lambda_function.py;
        docker exec lambda_test_container python -m black /var/task/lambda_function.py /var/task/test_lambda_function.py;
        docker stop lambda_test_container;
        docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG;
      fi
    # 仅api目录有变更时打标签
    - if [ "$BUILD_API" = true ]; then docker tag $REPOSITORY_URI_2:latest $REPOSITORY_URI_2:$IMAGE_TAG; fi
  1. 在post_build阶段根据标记变量决定是否推送镜像:
post_build: 
  commands: 
    - echo Build completed on `date` 
    - echo pushing to repo
    - if [ "$BUILD_LAMBDA" = true ]; then 
        docker push $REPOSITORY_URI:latest;
        docker push $REPOSITORY_URI:$IMAGE_TAG;
      fi
    - if [ "$BUILD_API" = true ]; then 
        docker push $REPOSITORY_URI_2:latest;
        docker push $REPOSITORY_URI_2:$IMAGE_TAG;
      fi
    # ... 原有imageDetail.json生成命令按需调整 ...

方法2:用Docker BuildKit优化缓存,减少重复构建时间

如果部分镜像必须构建,开启BuildKit可以利用镜像层缓存,大幅缩短构建时间——若镜像内容无变更,会直接复用缓存,无需重新构建。

修改Buildspec.yml步骤:

  1. 在install阶段开启BuildKit:
install:  
  commands: 
    - export DOCKER_BUILDKIT=1
    - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2&
    - timeout 15 sh -c "until docker info; do echo .; sleep 1; done"
  1. 构建时使用--cache-from参数复用ECR上的已有镜像缓存:
build: 
  commands: 
    # ... 原有命令 ...
    - cd api
    # 尝试拉取已有镜像作为缓存源,拉取不到不影响构建
    - docker pull $REPOSITORY_URI_2:latest || true
    - docker build --cache-from $REPOSITORY_URI_2:latest -t $REPOSITORY_URI_2:latest -f Dockerfile.google .
    - cd ..
    # lambda镜像同理
    - docker pull $REPOSITORY_URI:latest || true
    - docker build --cache-from $REPOSITORY_URI:latest -t $REPOSITORY_URI:latest -f Dockerfile.lambda .
    # ... 后续测试命令不变 ...

方法3:给镜像添加内容哈希标签,避免重复推送

把IMAGE_TAG改成基于镜像内容的哈希值,而非固定的latest。若镜像内容无变化,哈希值会和ECR上的现有标签一致,推送时Docker会自动跳过上传操作。

修改Buildspec.yml步骤:

  1. 在pre_build阶段生成内容哈希标签:
pre_build: 
  commands: 
    # ... 原有命令 ...
    # 针对api模块的构建上下文生成哈希
    - API_CONTEXT_HASH=$(find api/ -type f -exec sha256sum {} \; | sha256sum | cut -c 1-7)
    # 针对lambda模块的关键文件生成哈希
    - LAMBDA_CONTEXT_HASH=$(find . -maxdepth 1 -type f \( -name "Dockerfile.lambda" -o -name "lambda_function.py" -o -name "test_lambda_function.py" \) -exec sha256sum {} \; | sha256sum | cut -c 1-7)
  1. 构建和推送时用对应的哈希标签:
build: 
  commands: 
    # ... 原有命令 ...
    - docker build -t $REPOSITORY_URI_2:$API_CONTEXT_HASH -f Dockerfile.google api/
    - docker tag $REPOSITORY_URI_2:$API_CONTEXT_HASH $REPOSITORY_URI_2:latest
    # lambda镜像同理
    - docker build -t $REPOSITORY_URI:$LAMBDA_CONTEXT_HASH -f Dockerfile.lambda .
    - docker tag $REPOSITORY_URI:$LAMBDA_CONTEXT_HASH $REPOSITORY_URI:latest
    # ... 测试命令不变 ...

内容的提问来源于stack exchange,提问作者Aldo Matus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 00:00:07