Docker
The container artifact that connects CI builds to Kubernetes runtime.
How This Fits Our End-to-End Pipeline
Docker is documented here as part of one connected build, not as an isolated tutorial. The goal is to explain how code moved from a developer laptop into a monitored Kubernetes service.
Developer laptop
| git add / commit / push
v
GitHub repository
| pull request + branch rules
v
GitHub Actions workflow
| test -> docker build -> tag -> push
v
Docker Hub image registry
| immutable image tag
v
GitOps manifests repository/path
| ArgoCD watches desired state
v
Kubernetes cluster
| app pods + services + ingress
v
Prometheus scrapes metrics -> Grafana dashboards
Theory
Docker packages the application and its runtime into an image. The image is built once, tagged with a meaningful version, pushed to Docker Hub, and then pulled by Kubernetes. This removes the classic problem where an app works on one machine but fails somewhere else.
Our Docker journey followed the same path most real teams follow: write a Dockerfile, build locally, run the container, fix missing files or ports, tag the image, log in to Docker Hub, push it, and then reference that tag from Kubernetes.
Architecture Diagram
Dockerfile + app source
|
| docker build
v
Local image: devops-demo:local
|
| docker run / test
v
Tagged image: dockerhub-user/devops-demo:v1
|
| docker push
v
Docker Hub registry
|
| Kubernetes image pull
v
Running pods
Hands-on Project
- Place the application source and dependency manifest in the repository.
- Create a Dockerfile with a small base image and a deterministic install step.
- Build the image locally and run it with a mapped port.
- Tag the image using the Docker Hub namespace and an immutable version.
- Push to Docker Hub and use that pushed tag inside Kubernetes manifests.
Dockerfile
A good Dockerfile is boring: it copies only what is needed, installs dependencies predictably, exposes the runtime port, and starts the application with one clear command.
# Example for a Node.js service. Adapt package names for your app.
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Build, Run, Tag, Push
Build locally
docker build -t devops-demo:local .
Builds an image from the Dockerfile in the current directory.
Run locally
docker run --rm -p 3000:3000 devops-demo:local
Runs the container and maps host port 3000 to container port 3000.
Check running containers
docker ps
docker logs <container-id>
Confirms whether the app started and shows runtime logs.
Tag for Docker Hub
docker tag devops-demo:local <dockerhub-user>/devops-demo:v1
Adds the registry namespace and version tag expected by Docker Hub.
Log in and push
docker login
docker push <dockerhub-user>/devops-demo:v1
Publishes the image so Kubernetes and CI systems can pull it.
Docker Hub
- Use a repository such as
<dockerhub-user>/devops-demo. - Use immutable tags like the Git commit SHA for CI builds, for example
sha-9f3a21c. - Avoid relying on
latestin Kubernetes because it hides which version is actually running. - Use Docker Hub access tokens in GitHub Actions instead of saving your account password.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Container exits immediately | The start command failed | Run docker logs and test the command locally. |
| Port does not respond | Wrong port mapping or app binds to localhost | Use -p host:container and make the app listen on 0.0.0.0. |
| Build is slow | Docker cache is invalidated too early | Copy dependency manifests before copying all source files. |
| Push denied | Wrong Docker Hub namespace or not logged in | Run docker login and tag as user/repo:tag. |
| Kubernetes ImagePullBackOff | Private image, typo, or missing tag | Check image name, pull secret, and registry visibility. |
Troubleshooting Commands
docker inspect <container-id>
docker exec -it <container-id> sh
docker system df
docker builder prune
Interview Notes
- An image is the packaged artifact; a container is a running instance of that image.
- A Dockerfile is an image build recipe, not a deployment file.
- Layer caching is one reason Docker builds can be fast or painfully slow.
- Multi-stage builds keep final images smaller by leaving build tools behind.
- Kubernetes does not build images by default; it pulls images from registries.
FAQs
Why not run the app directly on the server?
Containers make the runtime repeatable and give Kubernetes a standard artifact to schedule.
Should we use latest?
Use it for quick local experiments only. In CI/CD, prefer commit SHA or semantic version tags.
What belongs in .dockerignore?
Dependency folders, Git metadata, local env files, test output, and anything not required at runtime.
Memorize vs Look-up
Memorize
docker build,docker run,docker ps,docker logs,docker tag,docker push.- Image versus container.
- Registry naming format:
registry/user/repository:tag.
Look Up
- All Dockerfile instructions and BuildKit flags.
- Exact syntax for advanced networking and volume modes.
- Registry-specific retention and security settings.
Project Implementation Journal
Baseline
We first made sure the application or configuration worked before introducing Docker. A broken baseline makes every later automation failure harder to understand.
Local proof
We validated the Docker workflow locally where possible, because local feedback is faster than waiting for CI or a cluster reconciliation loop.
Repository proof
We committed the Docker change as a reviewable unit so the reason for the pipeline change was visible in Git history.
Automation handoff
We connected Docker to the next tool in the chain instead of treating it as a standalone exercise.
Failure check
We intentionally inspected the common failure signals for Docker: logs, status output, permissions, names, tags, and configuration paths.
Rollback thinking
We asked how to return to the previous working state if the Docker change caused a bad deployment.
Interview compression
We reduced Docker into a few sentences that explain purpose, implementation, and failure modes clearly.
Operations note
We documented what someone should check the day after the deployment, not only what to run during setup.
Decision Records
- Keep Docker configuration in Git where it can be reviewed.
- Prefer explicit names over clever names: repository, image, namespace, workflow, and application names should be searchable.
- Use immutable versions for anything that can be deployed or rolled back.
- Put secrets in the platform secret store, not in code, documentation screenshots, or shell history.
- Automate only after the manual path is understood.
- Make the happy path visible, then document the first five things to check when it fails.
- Separate staging and production concerns before the project becomes too large.
- Choose boring defaults unless there is a real operational reason to customize.
- Write commands so they can be pasted into a terminal after replacing obvious placeholders.
- Treat dashboards, manifests, and workflow files as production code once people rely on them.
Failure Modes We Learned To Recognize
Wrong name
The most ordinary failures came from mismatched names: image repository, namespace, service selector, branch, workflow file, or dashboard variable.
Wrong permission
Automation failed when a token could read but not write, push but not pull, or access staging but not production.
Wrong version
A deployment looked successful while the cluster still ran an old image tag or an image tag that had been overwritten.
Wrong assumption
A command that worked locally failed in CI because the runner had a different shell, path, network, or credential context.
Missing feedback
Without logs, status commands, metrics, or dashboards, the system gave no quick answer about what changed.
Manual drift
Manual cluster edits solved a momentary problem but made the GitOps source of truth inaccurate.
Interview Drill Questions
- What problem does Docker solve in this pipeline?
- What artifact or state does Docker produce?
- Which tool consumes the output of Docker next?
- What is the most likely beginner mistake with Docker?
- How would you prove Docker worked without guessing?
- How would you roll back a bad change involving Docker?
- What should be memorized versus looked up for Docker?
- Which security boundary matters most for Docker?
- How would you explain Docker to someone who only knows basic Linux?
- What metric, log, status, or command would you check first during an incident?
Glossary For This Stage
Artifact
A build output or configuration object that can be handed to another stage.
Desired state
The state declared in Git or YAML that controllers try to make real.
Reconciliation
The loop where a tool compares desired state with actual state and fixes differences.
Immutable version
A version reference that should never change meaning after publication.
Rollback
A controlled return to a previously known working state.
Drift
A difference between what Git says should exist and what is actually running.
Health
A status signal that says whether the service is ready and operating correctly.
Traceability
The ability to connect a running system back to a commit, workflow run, image, and manifest change.
Practical Runbook
Confirm source
Identify the exact repository, branch, commit, file path, or dashboard connected to Docker.
Confirm identity
Check the account, token, kube context, registry namespace, or runner label before assuming the tool is broken.
Confirm version
Write down the version or tag you expected and compare it with the version the platform reports.
Confirm status
Use the native status command or UI first; it usually tells you whether the failure is configuration, permission, or runtime.
Confirm logs
Logs explain what happened after the tool accepted the configuration but the process still failed.
Confirm network
Many CI/CD failures are actually DNS, registry, cluster, firewall, or service discovery failures.
Confirm ownership
Know whether the application team, platform team, security team, or repository owner controls the failing setting.
Confirm rollback
Before changing more things, decide whether the quickest safe move is to revert, resync, rebuild, or redeploy.
Confirm documentation
After fixing the issue, add the command, symptom, and fix to the project notes so the next run is faster.
Confirm automation
If the Docker step is repeated manually more than twice, turn it into a workflow, manifest, script, or checklist.
Verification Checklist
- Can I point to the exact Git commit connected to this Docker change?
- Can I explain what changed in one sentence?
- Can I prove the change worked with a command or status screen?
- Can I identify the next tool that consumes this output?
- Can I identify the secret, token, or permission that would break this step?
- Can I roll back without manually editing production state?
- Can I tell whether the failure is build-time, deploy-time, or runtime?
- Can I show the relevant logs or metrics?
- Can I repeat the setup on a fresh machine or cluster?
- Can I teach this stage without reading every command from the page?