Skip to content

CNB lifecycle

When kpack
Kubernetes-native build service that runs CNB builds on-cluster and pushes images to a registry.
builds Pulse
Sample app in this lab — HTTP service monitor with dashboard, API, and Kubernetes health probes.
, Cloud Native Buildpacks
Cloud Native Buildpacks (CNB) — turn application source into OCI images without a Dockerfile per app.
buildpacks.io
run in ordered phases inside a build pod on EKS
Amazon Elastic Kubernetes Service — this lab uses cluster name `cluster-1`.
— trigger, lifecycle, then the image in ECR.

CodeCommit pulse-app and kpack Image CR trigger a build pod that runs Analyze Detect Restore Build Export then pushes pulse:main to ECR
Familiar (CodeBuild) This lab
buildspec.yml phases CNB lifecycle phases (diagram above)
Build project pulls git, runs Docker kpack
Kubernetes-native build service that runs CNB builds on-cluster and pushes images to a registry.
Image schedules a build pod
docker build / push in post_build Lifecycle ExportECR
Amazon Elastic Container Registry — stores OCI images built by kpack; EKS pulls from here.
via IRSA
IAM Roles for Service Accounts — pods use a Kubernetes service account annotated with an IAM role ARN.
App repo owns a Dockerfile Source + optional project.toml only

kpack polls CodeCommit
AWS managed Git hosting — GitHub alternative used for app source and deploy manifests in this lab.
pulse-app (or reacts after you push). Watch the Image:

Terminal window
export AWS_PROFILE=sandbox
export AWS_PAGER=""
export AWS_REGION=ap-southeast-2
kubectl get image pulse -w

A Ready Image with latestSuccessfulImage pointing at …/pulse:main means a build already finished. To re-watch phases, push a commit to pulse-app or note the next build pod name below.

Terminal window
# No Dockerfile in the app tree
find demo/app -iname 'Dockerfile*' -o -iname '*.dockerfile' | head
cat demo/app/project.toml

project.toml tells Paketo
Paketo Buildpacks — mature open-source builders for Go, Node, Java, Python, and more.
which Go package to build (BP_GO_TARGETS):

[[build.env]]
name = "BP_GO_TARGETS"
value = "./cmd/pulse"

Builder on the cluster:

Terminal window
kubectl get clusterbuilder default

Find the latest build pod and follow its logs:

Terminal window
kubectl get pods -n default -l image.kpack.io/image=pulse \
--sort-by=.metadata.creationTimestamp
BUILD_POD=$(kubectl get pods -n default -l image.kpack.io/image=pulse \
--sort-by=.metadata.creationTimestamp \
-o jsonpath='{.items[-1:].metadata.name}')
echo "BUILD_POD=$BUILD_POD"
kubectl -n default logs -f "$BUILD_POD" --all-containers

If no build pods remain, trigger a rebuild (push to pulse-app) or inspect a completed Build:

Terminal window
kubectl get builds -n default
kubectl describe build pulse-build-1 -n default

Phases match the diagram. In kpack’s creator logs you often see Detect / Restore / Build / Export as these signatures (not always the classic === DETECTING === banners):

Validates registry access and reads metadata from the previous app image so later phases can reuse layers. Look for lines about restoring metadata / previous image.

Chooses which Paketo buildpacks apply. For Pulse expect something like:

5 of 9 buildpacks participating
paketo-buildpacks/go-dist
paketo-buildpacks/go-mod-vendor
paketo-buildpacks/go-build

Copies cached layers from earlier builds (Restoring metadata for "…" from app image).

Buildpacks transform source into runnable artifacts. With BP_GO_TARGETS=./cmd/pulse, Paketo runs Go build — for example:

Paketo Buildpack for Go Build …
Running 'go build … ./cmd/pulse'

Assembles the OCI image and pushes pulse:main to ECR (kpack service account + IRSA — no docker push from your laptop):

Saving …/pulse:main...
Build successful

Confirm the Image and registry:

Terminal window
kubectl get image pulse
aws ecr describe-images --repository-name pulse --image-ids imageTag=main \
--query 'imageDetails[0].{pushed:imagePushedAt,digest:imageDigest}' \
--output table
  1. Edit demo/app/, commit, push to CodeCommit pulse-app
  2. kubectl get image pulse -w until Ready again
  3. Watch the new build pod logs through Detect → Build → Export
  4. Restart the workload so it picks up the :main tag (eval uses a mutable tag):
Terminal window
kubectl rollout restart deployment/pulse -n default

No Dockerfile and no manual docker push.

Optional local check (same engine as the cluster): see kpack vs pack and pack
CLI from the CNB project — builds images locally or in CI using builders and buildpacks.
inspect-image once you have pulled pulse:main.

  • No Dockerfile under demo/app / pulse-app
  • Build pod logs show Detect → Build → Export (CNB lifecycle)
  • pulse:main in ECR after the build
  • Rebuild on CodeCommit push without a laptop docker push

Verify the service — LoadBalancer, dashboard, /healthz.