Lesson 9 of 28
Module 3 · Concepts — What a CI/CD pipeline actually does
The five jobs of a pipeline
Strip away the vendor features and a deployment pipeline does five things, in order:
- Build — turn source code into an artifact (a container image, a binary, a tarball). Same source in → same artifact out.
- Test — run the artifact or its source through checks (unit tests, linters, security scans). If any fail, the pipeline stops.
- Publish — push the artifact somewhere immutable and addressable. For containers that's a registry (Docker Hub, GHCR, ECR).
- Deploy — tell a target environment to start running the new artifact. For Kubernetes, this is almost always "update the image tag in a manifest and apply."
- Verify — confirm the deployment is actually healthy after the switch. The simplest version is a health-check curl. More mature pipelines run smoke tests or compare metrics.
Every pipeline you encounter — GitHub Actions, GitLab CI, Jenkins, CircleCI, Buildkite — is a syntax wrapper around these five jobs.
Why GitHub Actions for this module
- It's free for public repos and generous for private ones.
- You don't need to run your own runner infrastructure.
- The syntax is widely understood; what you learn here transfers.
You'll encounter Jenkins in regulated enterprises, GitLab CI in GitLab-first shops, and team-built pipelines in larger tech companies. The concepts are the same.
The key idea: immutable, SHA-tagged images
This is the most important convention to internalise:
Never use
:latestin production. Tag every image with the Git commit SHA it was built from.
Why: :latest is a moving pointer. Two deployments an hour apart can resolve it to different images — you can't reproduce a production incident if you don't know what :latest actually was. A SHA-tagged image is immutable; ghcr.io/me/app:abc1234 is always the same thing.
A good pipeline outputs ghcr.io/me/app:$GITHUB_SHA on every build, and the deploy step references that SHA. If you want a "latest" convenience tag, add it alongside the SHA, never as the only tag.
GitHub Actions vocabulary
The syntax you need to know to write and read pipelines:
- Workflow — a file in
.github/workflows/*.yml. Triggered on events. - Job — a collection of steps that run on one runner. Jobs can depend on each other via
needs:. - Step — a single action or shell command.
- Action — a reusable unit.
actions/checkout@v4checks out your code;docker/build-push-action@v5builds and pushes an image. You'll use both today. - Runner — the machine the job executes on.
ubuntu-latestis the default, hosted by GitHub. - Secrets — values injected as env vars, available only in the workflow run.
${{ secrets.GITHUB_TOKEN }}is auto-provided; you add anything else under repo Settings → Secrets.
What you'll build in the task
A single workflow file that, on every push to main, does all five jobs end-to-end:
- Builds a tiny Go HTTP service into a container image
- Runs
go testandgo vet - Pushes the image to GitHub Container Registry (GHCR) tagged with the commit SHA
- Starts a
kindcluster inside the runner, installs the Helm chart you wrote in module 2 with--set image.repository=... image.tag=$SHA, and waits for the pods to be Ready - Hits
/healthzas a smoke test
All in one run. When it passes, you own a minimal, reproducible, end-to-end pipeline.
Relevant links
- GitHub Actions syntax reference — bookmarkable.
docker/build-push-action— the canonical image-build step.helm/kind-action— spins up a kind cluster inside a runner; lets you test the deploy step against a real cluster without leaving CI.