Links#
- AWS CodeBuild User Guide
- Create a build project in AWS CodeBuild
- Buildspec reference
- Publish Docker image to an Amazon ECR image repository
- aws codebuild start-build
- aws codebuild batch-get-builds
1. Important Points#
AWS CodeBuild 是 managed build service。它适合把 build/test/package/docker build 这类动作放到 AWS 托管环境里跑,并和 S3、ECR、CloudWatch Logs、CodePipeline 集成。
CodeBuild 用来做:
compile / test / package
build Docker image
push image to ECR
run CI job from CLI / webhook / CodePipeline
produce build artifacts
CodeBuild 不负责:
long-running service runtime
deployment orchestration by itself
source code hosting
replacing buildspec discipline核心原则:
project:
defines build environment, service role, logs, timeout, source/artifact defaults
build:
one execution of a project
can override source, buildspec, env vars, image, timeout
buildspec.yml:
should live at source root
declares install/pre_build/build/post_build commands
Docker build:
requires privileged mode for Docker-in-Docker style build
service role needs ECR permissions2. Core Concepts#
| Concept | Meaning | Production Note |
|---|---|---|
| Project | reusable build definition | one per app or build pattern |
| Build | one execution | immutable build ID |
| Source | Git/S3/CodePipeline input | script can override to S3 zip |
| Buildspec | command file | keep in repo root |
| Environment | build container image and compute | Docker build needs privileged mode |
| Service role | IAM role assumed by CodeBuild | grants S3/ECR/Logs access |
| Env override | per-build variables | useful for tag, image name, deploy env |
| Logs | CloudWatch Logs / S3 | keep for debugging |
3. IAM#
deploy caller#
这个 principal 是运行 shell 脚本的人或 CI system。它负责上传 source zip、启动 CodeBuild、轮询结果、检查 ECR image。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-codebuild-source-bucket/codebuild-sources/*"
},
{
"Effect": "Allow",
"Action": [
"codebuild:StartBuild",
"codebuild:BatchGetBuilds"
],
"Resource": "arn:aws:codebuild:ap-east-1:111122223333:project/order-api-build"
},
{
"Effect": "Allow",
"Action": [
"ecr:DescribeImages"
],
"Resource": "arn:aws:ecr:ap-east-1:111122223333:repository/order-api"
}
]
}CodeBuild service role#
这个 role 是 CodeBuild project 使用的 service role。它需要读 source zip、写 CloudWatch Logs、推 ECR image。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-codebuild-source-bucket/codebuild-sources/*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
"Resource": "arn:aws:ecr:ap-east-1:111122223333:repository/order-api"
}
]
}4. Project Configuration#
CLI 创建项目示例。这里项目 source 可以先用 S3 占位;真正构建时由脚本通过 --source-type-override S3 和 --source-location-override 指定 zip。
aws codebuild create-project \
--name order-api-build \
--source type=S3,location=my-codebuild-source-bucket/codebuild-sources/placeholder.zip \
--artifacts type=NO_ARTIFACTS \
--environment type=LINUX_CONTAINER,image=aws/codebuild/standard:7.0,computeType=BUILD_GENERAL1_SMALL,privilegedMode=true \
--service-role arn:aws:iam::111122223333:role/codebuild-order-api-role \
--logs-config cloudWatchLogs='{status=ENABLED,groupName=/aws/codebuild/order-api-build}' \
--timeout-in-minutes 30 \
--region ap-east-1Notes:
privilegedMode=true:
required when buildspec runs docker build / docker push
source zip:
must contain buildspec.yml at archive root
avoid zipping the parent directory itself
logs:
CloudWatch Logs is the first place to check failed builds5. buildspec.yml For Docker Image#
Source zip 里需要包含 buildspec.yml。下面示例使用外部脚本传进来的 env vars:AWS_ACCOUNT_ID、AWS_REGION、ECR_REPOSITORY、IMAGE_TAG。
如果 Dockerfile 默认写的是 Docker Hub 镜像:
FROM node:22-alpineCodeBuild 里经常会遇到 Docker Hub anonymous pull rate limit:
toomanyrequests:
You have reached your unauthenticated pull rate limit.解决方式是先把 node:22-alpine 同步到自己的 ECR,例如:
111122223333.dkr.ecr.ap-east-1.amazonaws.com/base/node:22-alpine然后 build 时使用 ECR 里的 base image。
preferred Dockerfile#
推荐把 Dockerfile 改成支持 build arg,这样不需要在 build 时修改文件内容。
ARG NODE_BASE_IMAGE=node:22-alpine
FROM ${NODE_BASE_IMAGE}
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]对应 buildspec.yml:
version: 0.2
phases:
pre_build:
commands:
- aws ecr get-login-password --region "$AWS_REGION" | docker login --username AWS --password-stdin "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"
- export IMAGE_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG"
- export BASE_IMAGE_REPOSITORY="${BASE_IMAGE_REPOSITORY:-base/node}"
- export BASE_IMAGE_TAG="${BASE_IMAGE_TAG:-22-alpine}"
- export BASE_IMAGE_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$BASE_IMAGE_REPOSITORY:$BASE_IMAGE_TAG"
- echo "IMAGE_URI=$IMAGE_URI"
- echo "BASE_IMAGE_URI=$BASE_IMAGE_URI"
- docker pull "$BASE_IMAGE_URI"
build:
commands:
- docker build --build-arg NODE_BASE_IMAGE="$BASE_IMAGE_URI" -t "$IMAGE_URI" .
post_build:
commands:
- docker push "$IMAGE_URI"
- printf '{"imageUri":"%s"}\n' "$IMAGE_URI" > image-detail.jsonpatch existing Dockerfile during build#
如果暂时不想改应用仓库里的 Dockerfile,也可以在 CodeBuild 里临时把 FROM node:22-alpine 替换成 ECR base image。
version: 0.2
phases:
pre_build:
commands:
- aws ecr get-login-password --region "$AWS_REGION" | docker login --username AWS --password-stdin "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"
- export IMAGE_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG"
- export BASE_IMAGE_REPOSITORY="${BASE_IMAGE_REPOSITORY:-base/node}"
- export BASE_IMAGE_TAG="${BASE_IMAGE_TAG:-22-alpine}"
- export BASE_IMAGE_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$BASE_IMAGE_REPOSITORY:$BASE_IMAGE_TAG"
- echo "IMAGE_URI=$IMAGE_URI"
- echo "BASE_IMAGE_URI=$BASE_IMAGE_URI"
- docker pull "$BASE_IMAGE_URI"
build:
commands:
- cp Dockerfile Dockerfile.original
- sed -i "/^FROM /s|node:22-alpine|${BASE_IMAGE_URI}|g" Dockerfile
- grep -n '^FROM ' Dockerfile
- docker build -t "$IMAGE_URI" .
post_build:
commands:
- docker push "$IMAGE_URI"
- printf '{"imageUri":"%s"}\n' "$IMAGE_URI" > image-detail.jsonNotes:
preferred:
use Dockerfile ARG
source code stays explicit and reviewable
temporary workaround:
sed patch is acceptable for migration
grep FROM line before docker build so logs show the actual base image
BASE_IMAGE_REPOSITORY:
default: base/node
can be passed from start-build env override if your base image repo differs
BASE_IMAGE_TAG:
default: 22-alpine
should match the image tag already pushed to ECR
IAM:
CodeBuild service role must have ECR pull permission on base/node
CodeBuild service role must have ECR push permission on application repo6. Shell Script Start Build From Git Tag#
目标:脚本同步代码仓库,按 Git tag 打源码 zip 上传 S3,启动 CodeBuild,传环境变量进去,轮询等待 build 结果。成功后,再检查 ECR 中是否存在对应 tag 的镜像。
Assumptions:
local machine / CI runner:
aws cli installed and configured
git installed
zip installed
repository:
buildspec.yml exists at repo root
Dockerfile exists at repo root
CodeBuild:
project already exists
project can use S3 source override
project service role can read S3 source zip and push ECRrun-codebuild-from-tag.sh:
#!/usr/bin/env bash
# Exit immediately when any command fails.
set -euo pipefail
# Set the AWS region used by S3, CodeBuild, and ECR.
AWS_REGION="ap-east-1"
# Set the AWS account ID that owns the CodeBuild project and ECR repository.
AWS_ACCOUNT_ID="111122223333"
# Set the CodeBuild project name to start.
CODEBUILD_PROJECT="order-api-build"
# Set the Git repository URL to sync.
GIT_REPO_URL="git@github.com:example/order-api.git"
# Set the Git tag to build.
GIT_TAG="v1.2.3"
# Set the local work directory used by this script.
WORK_DIR="/tmp/codebuild-order-api"
# Set the S3 bucket used for CodeBuild source zip files.
SOURCE_BUCKET="my-codebuild-source-bucket"
# Set the S3 prefix used for source zip files.
SOURCE_PREFIX="codebuild-sources/order-api"
# Set the ECR repository where CodeBuild should push the Docker image.
ECR_REPOSITORY="order-api"
# Convert the Git tag to a Docker-safe image tag.
IMAGE_TAG="${GIT_TAG#v}"
# Build the local repository directory path.
REPO_DIR="${WORK_DIR}/repo"
# Build the local zip file path.
ZIP_FILE="${WORK_DIR}/${ECR_REPOSITORY}-${GIT_TAG}.zip"
# Build the S3 object key for this source zip.
S3_KEY="${SOURCE_PREFIX}/${ECR_REPOSITORY}-${GIT_TAG}.zip"
# Build the full S3 URI used by aws s3 cp.
S3_URI="s3://${SOURCE_BUCKET}/${S3_KEY}"
# Build the CodeBuild S3 source location format.
CODEBUILD_SOURCE_LOCATION="${SOURCE_BUCKET}/${S3_KEY}"
# Build the full expected ECR image URI.
ECR_IMAGE_URI="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}"
# Create the work directory if it does not exist.
mkdir -p "${WORK_DIR}"
# Clone the repository if the local checkout does not exist.
test -d "${REPO_DIR}/.git" || git clone "${GIT_REPO_URL}" "${REPO_DIR}"
# Enter the local repository checkout.
cd "${REPO_DIR}"
# Fetch all remote refs and tags so the requested tag is available.
git fetch --all --tags --prune
# Checkout the exact Git tag for a reproducible source package.
git checkout --force "${GIT_TAG}"
# Remove any stale local zip from previous runs.
rm -f "${ZIP_FILE}"
# Create a source zip from the checked-out tag and exclude the .git directory.
zip -r "${ZIP_FILE}" . -x ".git/*"
# Upload the source zip to S3 for CodeBuild to consume.
aws s3 cp "${ZIP_FILE}" "${S3_URI}" --region "${AWS_REGION}"
# Start CodeBuild with S3 source override and environment variable overrides.
BUILD_ID="$(aws codebuild start-build --project-name "${CODEBUILD_PROJECT}" --source-type-override S3 --source-location-override "${CODEBUILD_SOURCE_LOCATION}" --environment-variables-override name=AWS_ACCOUNT_ID,value="${AWS_ACCOUNT_ID}",type=PLAINTEXT name=AWS_REGION,value="${AWS_REGION}",type=PLAINTEXT name=ECR_REPOSITORY,value="${ECR_REPOSITORY}",type=PLAINTEXT name=IMAGE_TAG,value="${IMAGE_TAG}",type=PLAINTEXT name=GIT_TAG,value="${GIT_TAG}",type=PLAINTEXT --region "${AWS_REGION}" --query 'build.id' --output text)"
# Print the build ID for later troubleshooting.
echo "Started CodeBuild build: ${BUILD_ID}"
# Poll CodeBuild until the build reaches a terminal state.
while true; do
# Query the current build status from CodeBuild.
BUILD_STATUS="$(aws codebuild batch-get-builds --ids "${BUILD_ID}" --region "${AWS_REGION}" --query 'builds[0].buildStatus' --output text)"
# Print the current build status.
echo "CodeBuild status: ${BUILD_STATUS}"
# Stop polling when the build succeeded.
if [ "${BUILD_STATUS}" = "SUCCEEDED" ]; then
# Exit the polling loop on success.
break
fi
# Fail the script when the build reaches a failed terminal state.
if [ "${BUILD_STATUS}" = "FAILED" ] || [ "${BUILD_STATUS}" = "FAULT" ] || [ "${BUILD_STATUS}" = "STOPPED" ] || [ "${BUILD_STATUS}" = "TIMED_OUT" ]; then
# Print the final failed build status.
echo "CodeBuild failed with status: ${BUILD_STATUS}" >&2
# Exit with non-zero code so CI/CD marks this run as failed.
exit 1
fi
# Wait before polling again to avoid excessive API calls.
sleep 10
done
# Verify that the expected image tag exists in ECR after the build succeeds.
aws ecr describe-images --repository-name "${ECR_REPOSITORY}" --image-ids imageTag="${IMAGE_TAG}" --region "${AWS_REGION}" >/dev/null
# Print the pushed image URI when ECR verification succeeds.
echo "Image exists in ECR: ${ECR_IMAGE_URI}"Run:
# Make the script executable.
chmod +x run-codebuild-from-tag.sh
# Run the script to package source, start CodeBuild, wait for result, and verify ECR.
./run-codebuild-from-tag.shScript notes:
source zip:
uploaded to s3://my-codebuild-source-bucket/codebuild-sources/order-api/order-api-v1.2.3.zip
CodeBuild source override:
--source-type-override S3
--source-location-override bucket/key.zip
env override:
passes AWS_ACCOUNT_ID, AWS_REGION, ECR_REPOSITORY, IMAGE_TAG, GIT_TAG
ECR check:
describe-images imageTag=<IMAGE_TAG>
proves CodeBuild pushed the expected image tag7. Monitoring#
must watch:
build failures
queued duration
build duration
timeout count
CloudWatch Logs errors
ECR push failures
Docker Hub or package registry rate limitUseful commands:
aws codebuild batch-get-builds --ids <build-id> --region ap-east-1
aws logs tail /aws/codebuild/order-api-build --follow --region ap-east-1
aws ecr describe-images --repository-name order-api --region ap-east-18. Troubleshooting#
| Symptom | Check |
|---|---|
AccessDenied on start-build |
deploy caller lacks codebuild:StartBuild |
source object not found |
S3 bucket/key or region is wrong |
| build cannot download source | CodeBuild service role lacks s3:GetObject |
Cannot connect to Docker daemon |
project not running with privileged mode |
| ECR login fails | service role lacks ecr:GetAuthorizationToken |
| Docker push denied | service role lacks ECR upload/put image actions |
| final ECR check fails | build did not push expected IMAGE_TAG |
Failure diagnostics:
aws codebuild batch-get-builds --ids <build-id> --region ap-east-1
aws logs tail /aws/codebuild/order-api-build --since 1h --region ap-east-1
aws s3 ls s3://my-codebuild-source-bucket/codebuild-sources/order-api/
aws ecr describe-images --repository-name order-api --region ap-east-19. Checklist#
1. buildspec.yml exists at source root
2. Dockerfile exists at source root
3. source zip contains file contents at root, not an extra parent folder
4. deploy caller can upload S3, start CodeBuild, and describe ECR images
5. CodeBuild service role can read S3 source and push ECR
6. CodeBuild project has privileged mode enabled for Docker build
7. environment variables passed by start-build match buildspec.yml
8. CloudWatch Logs group is enabled
9. ECR image tag is checked after successful build