如何在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步骤:
- 在
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
- 在
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
- 在
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步骤:
- 在
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"
- 构建时使用
--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步骤:
- 在
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)
- 构建和推送时用对应的哈希标签:
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
相关产品推荐
相关产品推荐

