Why Containers Change the CI/CD Pipeline
Traditional CI/CD pipelines compile code and copy artifacts to servers. Container-native pipelines instead build immutable images, tag them uniquely, push them to a registry, and then update a deployment manifest that references the new tag. This shifts the unit of delivery from 'files on a server' to 'an image plus a declarative manifest', which makes rollbacks, environment parity, and auditability far easier.
Cricket analogy: Old-school scoring involved manually copying scorecards ground to ground; the modern DRS system instead locks one verified replay (the image) with a unique ball-number tag, and every stadium screen just references that certified clip.
The Core Pipeline Stages
A typical container pipeline has five stages: build the image, run unit and static analysis tests inside or against the image, scan the image for vulnerabilities, push the image to a registry with an immutable tag (commit SHA, never 'latest'), and deploy by updating the image reference in a Kubernetes manifest or Helm values file. Each stage should fail fast and block promotion to the next stage.
Cricket analogy: A team's match-day process runs in fixed stages: nets practice (build), an internal warm-up match (test), a fitness screening (scan), naming the final XI with shirt numbers locked (immutable tag), then walking onto the field (deploy) — a failed fitness check stops selection.
# .github/workflows/build-and-deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build-test-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set image tag
id: vars
run: echo "tag=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image
run: |
docker build -t ghcr.io/${{ github.repository }}:${{ steps.vars.outputs.tag }} .
- name: Run unit tests in container
run: |
docker run --rm ghcr.io/${{ github.repository }}:${{ steps.vars.outputs.tag }} npm test
- name: Scan image for vulnerabilities
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: ghcr.io/${{ github.repository }}:${{ steps.vars.outputs.tag }}
severity: CRITICAL,HIGH
exit-code: '1'
- name: Push image
run: docker push ghcr.io/${{ github.repository }}:${{ steps.vars.outputs.tag }}
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/api api=ghcr.io/${{ github.repository }}:${{ steps.vars.outputs.tag }} -n production
kubectl rollout status deployment/api -n production --timeout=120sImmutable Tags and GitOps Delivery
Never deploy using the 'latest' tag in production; it makes rollbacks impossible to reason about and can silently change what is running. Tag every image with a commit SHA or semantic version. Many mature pipelines split into CI (build, test, push image) and CD (a separate GitOps controller such as Argo CD or Flux watches a manifests repo and reconciles the cluster). In that model, the CI pipeline's last step is a pull request or commit that bumps the image tag in a Git-tracked manifest, not a direct kubectl apply.
Cricket analogy: Never select a player by the vague label 'in-form' without pinning who exactly that means; selectors (CI) recommend a specific named player via a memo, while the manager (a GitOps controller) only acts once that memo is officially filed in the squad list.
GitOps pattern: CI writes the new image tag into a Kubernetes manifest or Helm values.yaml in a separate 'config' repository. Argo CD/Flux detects the change and syncs the cluster automatically, giving you a full audit trail and a one-command rollback (git revert).
Testing and Scanning Before Promotion
A container-native pipeline should run at minimum: unit tests, a lint/static-analysis pass on the Dockerfile (e.g. hadolint), an image vulnerability scan (Trivy, Grype, or a registry's built-in scanner), and a smoke test that boots the container and hits a health endpoint. Gate the push and deploy steps on all of these passing; a red pipeline should never be able to reach production.
Cricket analogy: Before a player takes the field, they must pass a fitness test, a bat/gear inspection, an anti-doping screen, and a warm-up net session hitting the nets cleanly; failing any one of these means they don't get named in the XI, no exceptions.
Do not bake secrets (API keys, database passwords) into the image during the build stage. Anyone who pulls the image can extract layer contents. Inject secrets at runtime via Kubernetes Secrets, environment variables from a secret manager, or mounted volumes.
Multi-Environment Promotion
Build the image exactly once and promote the same immutable artifact through dev, staging, and production rather than rebuilding per environment. Rebuilding risks non-reproducible builds (different dependency versions resolved at different times). Promotion is just updating which environment's manifest points at that already-tested, already-scanned image tag.
Cricket analogy: A team doesn't re-recruit a fresh player for each round of the tournament — the same selected player who impressed in the warm-up moves through the group stage, semifinal, and final, only their squad-list entry gets updated as they advance.
- Never deploy the 'latest' tag to production — use immutable tags such as the Git commit SHA.
- Build the image once and promote the identical artifact across environments instead of rebuilding.
- Vulnerability scanning and Dockerfile linting belong in the pipeline before the push step, not after.
- GitOps tools like Argo CD and Flux separate CI (build/push) from CD (cluster reconciliation) for auditability.
- Never embed secrets in image layers; inject them at deploy/runtime instead.
- Always gate deploy steps on
kubectl rollout status(or equivalent) so a failed rollout fails the pipeline.
Practice what you learned
1. Why should production deployments avoid using the 'latest' tag?
2. In a GitOps delivery model, what does the CI pipeline typically do as its final step?
3. Where should container image vulnerability scanning occur in the pipeline?
4. What is the main risk of injecting secrets into a Dockerfile with an ENV or COPY instruction?
5. Why is 'build once, promote the same artifact' preferred over rebuilding per environment?
Was this page helpful?
You May Also Like
Container Registries
How container registries store, version, and distribute Docker images, covering tagging conventions, authentication, and push/pull workflows.
Rolling Updates and Rollbacks
Learn how Kubernetes Deployments update Pods gradually with zero downtime and how to roll back safely when a release breaks.
Docker Security Basics
Defensive practices for hardening container images and runtime, including non-root users, minimal base images, vulnerability scanning, and dropped capabilities.
Deployments and ReplicaSets
Learn how Deployments manage ReplicaSets to declaratively run, scale, and update stateless Pod replicas in Kubernetes.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics