Dockerfile → Image → running Container. Images follow OCI format: portable across Docker, Podman, containerd, Kubernetes.
Dockerfile: key instructions
FROM node:18-alpine # base image (alpine = small)
WORKDIR /app # sets working dir for subsequent cmds
COPY package*.json ./ # copy dependency files first (cache layer)
RUN npm ci --only=production # install deps
COPY . . # copy source code
EXPOSE 3000 # document port (does NOT publish it)
ENV NODE_ENV=production # set env var at build + runtime
ARG BUILD_VERSION=1.0 # build-time variable only (not in final image)
ENTRYPOINT ["node"] # fixed executable: cannot be overridden by args
CMD ["server.js"] # default args: overridable at runtime
Multi-stage build
# Stage 1: build
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
# Stage 2: minimal runtime image
FROM gcr.io/distroless/base
COPY /app/myapp /myapp
ENTRYPOINT ["/myapp"]
# Final image has NO shell, no build tools: very small & secure
.dockerignore
.git
node_modules
*.md
Dockerfile
Build and tag
docker build -t myapp:1.0 .
docker build -f docker/Dockerfile.prod -t myapp:1.0 .
docker build --build-arg VERSION=2.0 -t myapp:2.0 .
docker build -t myapp:latest -t myapp:v1.0 -t registry.local/myapp:v1.0 .
Retag existing images
docker tag does not copy image data: new name pointing at same image ID.
docker tag myapp:1.0 registry.io/user/myapp:1.0
docker tag nginx:1.25 myregistry.local/nginx:prod
docker images # same IMAGE ID, different REPOSITORY:TAG
Registry: pull and push
docker pull nginx:1.25
docker push registry.io/user/myapp:1.0
docker tag myapp:1.0 myregistry.local:5000/myapp:1.0
docker push myregistry.local:5000/myapp:1.0
Save and load (OCI image tar)
docker save exports image layers + metadata. docker load restores it.
docker save nginx:1.25 -o nginx.tar
docker save -o bundle.tar nginx:1.25 busybox:1.36
docker load -i nginx.tar
docker images | grep nginx
Typical exam workflow:
docker pullordocker builddocker save -o image.tar <name>:<tag>docker load -i image.tar- Deploy with
imagePullPolicy: Never
spec:
containers:
- name: app
image: myapp:1.0
imagePullPolicy: Never
save/load vs export/import
| Command | Source | Output | Restores |
|---|---|---|---|
docker save | Image (layers + metadata) | OCI image tar | docker load → full image with history |
docker load | Image tar from save | Local image | : |
docker export | Running container filesystem | Flat filesystem tar | No layer history |
docker import | Container export tar | New image (no Dockerfile history) | : |
Inspect and debug images
docker images
docker history myapp:1.0 # layers and sizes
docker inspect myapp:1.0 # full metadata (ENTRYPOINT, ENV, etc.)
docker rmi myapp:1.0
docker image prune -f # remove dangling images
Run containers (local testing)
docker run -d -p 8080:80 nginx:1.25
docker run --rm -it alpine ping -c 3 8.8.8.8
docker ps / docker ps -a
docker logs <id> / docker logs -f <id>
docker exec -it <id> /bin/sh
docker stop <id> && docker rm <id>
Podman / nerdctl equivalents
| Task | docker | podman | nerdctl |
|---|---|---|---|
| Build | docker build -t app:1.0 . | podman build -t app:1.0 . | nerdctl build -t app:1.0 . |
| Save | docker save -o app.tar app:1.0 | podman save -o app.tar app:1.0 | nerdctl save -o app.tar app:1.0 |
| Load | docker load -i app.tar | podman load -i app.tar | nerdctl load -i app.tar |
| Tag | docker tag src dst | podman tag src dst | nerdctl tag src dst |
| Push | docker push dst | podman push dst | nerdctl push dst |
Run which docker podman nerdctl at the start of any image task.
CMD vs ENTRYPOINT
| Override | Runtime args | |
|---|---|---|
CMD | Any docker run arg replaces CMD | Replace entirely |
ENTRYPOINT | --entrypoint only | Appended to ENTRYPOINT |
| Both | --entrypoint + args | Args override CMD default |
Kubernetes mapping
| Docker | K8s Pod field |
|---|---|
ENTRYPOINT | command |
CMD | args |
Override in Pod spec
spec:
containers:
- name: app
image: myapp:1.0
command: ["python"] # overrides ENTRYPOINT
args: ["-u", "server.py"] # overrides CMD
kubectl run mypod --image=busybox:1.36.1 -o yaml --dry-run=client \
-- /bin/sh -c "while true; do date; sleep 10; done"
# everything after -- → args
imagePullPolicy
| Policy | Behaviour |
|---|---|
IfNotPresent | Pull only if not already cached |
Always | Pull on every Pod start |
Never | Use only local image; fail if not present |
Defaults: :latest (or omitted) → Always; any other tag → IfNotPresent
spec:
containers:
- name: app
image: myapp:1.0
imagePullPolicy: IfNotPresent
ImagePullBackOff→ wrong tag, missing registry auth, orNeverwith no local image
Exam gotchas
command= ENTRYPOINT,args= CMD:latest→imagePullPolicy: Alwaysby default; pin a version forIfNotPresent- "Save image to tar" =
docker save, notdocker export - After
docker load, setimagePullPolicy: Never(orIfNotPresent) docker tagonly adds a name: to change an image, edit Dockerfile and rebuild- Check which CLI is installed:
which docker podman nerdctl - Layer caching: fix Dockerfile errors and rebuild; cached layers before the failure are reused