Lesson 5 · Build, deploy & trunk-based delivery
Building & pushing images
Turning source into the one image everything deploys — tagged by the release, built at most once.
Your win: trace how CI builds and pushes the container image — the tool, the registry, the tag scheme — and the neat "don't rebuild what already exists" trick.
One binary, one image, one tag
Recall from Course 1: the whole monorepo compiles to a single build/server
binary, and your three services (conversationmgmt, notification,
spike) all ride inside it. CI mirrors that — it builds one
backend image that every service's Helm subchart deploys. The build is a
composite action (Lesson 4).
docker/build-push-action@v6 (:102) — not
skaffold build (Skaffold is only used at deploy time, Lesson 8). Details:
- Dockerfile:
developments/release.Dockerfile,target: runner(:105,108) — the production multi-stage target (Course 1 L2). - Registry:
asia.gcr.io/student-coach-e1e95/backend(:107) — Google Container Registry. - Tag = the git release tag (
be_release_tag) — the same tag Lesson 1's chain cut from the merge. So the image version is the release version. - Cache:
cache-from/to: type=gha(:110-111) — reuse layers across builds.
The "build at most once" trick
docker manifest inspect
Before building, the action runs docker manifest inspect <image:tag>
(tbd.build-backend/action.yml:74-76). If the tag already
exists in the registry, the build is skipped. Why it matters: a release
tag is immutable, and the staging cron (Lesson 1) may pick the same tag more than once —
without this check you'd rebuild an identical image repeatedly. "Is this artifact already
built? Then don't rebuild it" is a hallmark of an efficient pipeline.
Build isn't the end — scan, then sign
security-scan-image,
:121-127) and a binary-authorization attestation
(binauthz-sign, :129-135) — a cryptographic stamp
that "this image was built by the trusted pipeline." That attestation is the supply-chain
story of Lesson 10. So the build stage's real output is: a pushed image + a scan result + a
signed attestation.
global.image.tag, Course 3 L8), there's one version identifier flowing
end-to-end: git tag → image tag → deployed pods. To know "what's running," you read one
number. To roll back, you deploy an older tag.
docker/build-push-action + Skaffold CI/CD
How the build-push action drives buildx in CI, plus Skaffold's build/deploy split.
→ github.com — docker/build-push-action
→ skaffold.dev — CI/CD
Check yourself (from memory)
Q1. The backend image is built with…
docker/build-push-action@v6 with
release.Dockerfile. Skaffold is used only at deploy time.
Q2. The image tag is…
be_release_tag — so git tag = image tag =
deployed version, one identifier end-to-end.
Q3. docker manifest inspect before building is used to…
backend image (all 3 services ride
in it) → every service's Helm subchart deploys it. BUILD:
tbd.build-backend/action.yml — DOCKER BUILDX via
docker/build-push-action@v6 (NOT skaffold build), Dockerfile
release.Dockerfile target runner, registry
asia.gcr.io/student-coach-e1e95/backend, TAG = the git RELEASE TAG
(be_release_tag) → git tag = image tag = deployed version. Cache
type=gha. IDEMPOTENT: docker manifest inspect first → SKIP build if
the tag already exists. Post-build: vulnerability SCAN → binauthz SIGN (attestation, Lesson
10). Rollback = deploy an older tag.release.Dockerfile's stages, or how the GHA layer cache speeds
rebuilds? Ask me.
1. In-repo: .github/actions/tbd.build-backend/action.yml; docker/build-push-action.