1. Explain the difference between a Docker Image and a Container.
Concept: Think of a Class vs. an Object in OOP.
- Image: A read-only template with instructions for creating a Docker container. It consists of multiple layers.
- Container: A runnable instance of an image. It adds a thin read/write layer on top of the image layers.
2. How do Docker layers work and why are they important?
Docker images are built from a series of layers. Each instruction in a Dockerfile creates a layer.
- Caching: Layers are cached. If you change one line in your Dockerfile, only that layer and subsequent layers are rebuilt.
- Efficiency: Layers are shared between images. If multiple images use the same base image (e.g., `ubuntu:20.04`), that base layer is stored only once on disk.
Optimization Tip: Order your Dockerfile instructions from least frequently changed to most frequently changed to maximize cache hits.
3. What is a Multi-Stage Build?
Multi-stage builds allow you to use multiple `FROM` statements in a single Dockerfile. Each `FROM` instruction starts a new stage.
Benefit: You can copy artifacts from one stage to another, leaving behind all the build tools and intermediate files in the final image. This drastically reduces image size.
# Build Stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp main.go
# Final Stage
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]
4. Docker Networking Modes
Question: Explain the difference between Bridge, Host, and None networks.
- Bridge (Default): Containers talk to each other via a private internal network. They need port mapping (
-p 8080:80) to be accessible from the host. - Host: The container shares the host's networking namespace directly. No port mapping needed, but port conflicts can occur. Performance is better.
- None: No networking. Useful for secure, offline batch jobs.
5. Volumes vs. Bind Mounts
Question: How do you persist data?
- Volumes: Managed by Docker (
/var/lib/docker/volumes). Best for database data and persistence independent of the container's lifecycle. - Bind Mounts: Map a specific file or directory on the host to the container. Best for development (live code reloading).
6. Docker Security Best Practices
Question: How do you secure a Docker container for production?
- Non-root User: Always create a user in the Dockerfile and switch to it (
USER appuser). - Minimal Base Images: Use
alpineordistrolessimages to reduce attack surface. - Read-only Filesystem: Run containers with
--read-onlywhere possible. - Scan Images: Use tools like Trivy or Docker Scan to find vulnerabilities.
7. What is Docker Compose?
Definition: A tool for defining and running multi-container Docker applications. You use a YAML file to configure your application's services.
Key Command: `docker-compose up -d` (starts all services in the background).
Use Case: Spinning up a full stack (Frontend + Backend + Database + Redis) with a single command for local development.
8. Explain the `ENTRYPOINT` vs `CMD` instruction.
Both define what command runs when the container starts, but they interact differently.
- CMD: Sets default arguments that can be overridden from the command line.
CMD ["echo", "hello"]-> running `docker run myimage world` will output "world". - ENTRYPOINT: Configures a container that will run as an executable. Arguments passed to `docker run` are appended to the entrypoint.
ENTRYPOINT ["echo"]-> running `docker run myimage world` will output "world" (echo + world).
Best Practice: Use `ENTRYPOINT` for the main executable and `CMD` for default flags/arguments.
9. How do you reduce the size of a Docker Image?
Strategies:
- Use Alpine Images: `FROM node:alpine` is much smaller than `FROM node`.
- Multi-Stage Builds: Compile in one stage, copy only the binary to the final stage.
- Minimize Layers: Combine commands: `RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*`.
- .dockerignore: Exclude `node_modules`, `.git`, and logs from the build context.
9. How do you handle zombie processes in Docker?
Problem: When a process terminates, it becomes a "zombie" until its parent waits for it. In Docker, if the main process (PID 1) doesn't handle this, zombies accumulate.
Solution: Use an init process like tini (built into Docker with --init flag) or dumb-init. It acts as PID 1 and properly reaps zombie processes.