Core Concepts
5 min readRapid overview
- Docker Core Concepts
- Container Fundamentals
- What is a Container?
- Docker Architecture
- Dockerfile Best Practices
- Basic Dockerfile
- Multi-Stage Build
- Optimized Go Build
- Layer Optimization
- Order Matters
- Minimize Layers
- Use .dockerignore
- Security Best Practices
- Non-Root User
- Read-Only Filesystem
- Scan for Vulnerabilities
- Minimal Base Images
- Docker Compose
- Development Setup
- Production Compose
- Networking
- Network Types
- Network Commands
- Storage
- Volume Types
- Volume Best Practices
- Common Commands
- Interview Questions
- 1. Explain Docker image layers
- 2. What's the difference between CMD and ENTRYPOINT?
- 3. How do you optimize Docker image size?
- 4. Explain Docker networking modes
Docker Core Concepts
Container Fundamentals
What is a Container?
A container is an isolated process with:
- Its own filesystem (image layers)
- Its own network namespace
- Resource limits (cgroups)
- Process isolation (namespaces)
Docker Architecture
┌─────────────────────────────────────────────┐
│ Docker Client │
│ (docker CLI, API calls) │
└─────────────────┬───────────────────────────┘
│ REST API
┌─────────────────▼───────────────────────────┐
│ Docker Daemon │
│ (dockerd) │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ containerd │
│ (container lifecycle manager) │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ runc │
│ (OCI runtime - spawns containers) │
└─────────────────────────────────────────────┘
Dockerfile Best Practices
Basic Dockerfile
FROM node:20-alpine
WORKDIR /app
# Copy dependency files first (better caching)
COPY package*.json ./
RUN npm ci --only=production
# Copy application code
COPY . .
# Non-root user
USER node
EXPOSE 3000
CMD ["node", "server.js"]
Multi-Stage Build
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine AS production
WORKDIR /app
ENV NODE_ENV=production
# Copy only production dependencies
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# Copy built artifacts from builder
COPY --from=builder /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
Optimized Go Build
# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Download dependencies first
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Build static binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /server ./cmd/server
# Minimal runtime
FROM scratch
COPY --from=builder /server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
Layer Optimization
Order Matters
# Bad: Copies change frequently, invalidates cache
COPY . .
RUN npm install
# Good: Dependencies cached separately
COPY package*.json ./
RUN npm install
COPY . .
Minimize Layers
# Bad: Multiple RUN commands
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
RUN apt-get clean
# Good: Combined commands
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
git && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Use .dockerignore
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
Dockerfile*
docker-compose*
*.md
tests/
coverage/
.vscode/
Security Best Practices
Non-Root User
# Create non-root user
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -D appuser
# Change ownership
COPY --chown=appuser:appgroup . /app
USER appuser
Read-Only Filesystem
docker run --read-only --tmpfs /tmp myimage
Scan for Vulnerabilities
# Using Docker Scout
docker scout cves myimage:latest
# Using Trivy
trivy image myimage:latest
# Using Snyk
snyk container test myimage:latest
Minimal Base Images
| Image | Size | Use Case |
|---|---|---|
scratch | 0 MB | Static binaries (Go, Rust) |
alpine | ~5 MB | General minimal |
distroless | ~20 MB | No shell, security-focused |
slim variants | ~50-100 MB | Reduced full OS |
Docker Compose
Development Setup
# docker-compose.yml
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./src:/app/src
- /app/node_modules # Exclude node_modules
environment:
- NODE_ENV=development
- DATABASE_URL=postgres://postgres:password@db:5432/myapp
depends_on:
db:
condition: service_healthy
networks:
- app-network
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
ports:
- "6379:6379"
networks:
- app-network
volumes:
postgres_data:
networks:
app-network:
driver: bridge
Production Compose
version: '3.8'
services:
api:
image: myregistry/api:${VERSION:-latest}
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
restart_policy:
condition: on-failure
max_attempts: 3
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Networking
Network Types
| Type | Description |
|---|---|
bridge | Default, isolated network on single host |
host | Container shares host's network stack |
none | No networking |
overlay | Multi-host networking (Swarm) |
macvlan | Container gets its own MAC address |
Network Commands
# Create network
docker network create --driver bridge my-network
# Connect container to network
docker network connect my-network container-name
# Inspect network
docker network inspect my-network
# List networks
docker network ls
Storage
Volume Types
# Named volume (managed by Docker)
docker volume create mydata
docker run -v mydata:/app/data myimage
# Bind mount (host path)
docker run -v /host/path:/container/path myimage
# tmpfs mount (memory)
docker run --tmpfs /app/cache myimage
Volume Best Practices
# Compose volumes
services:
db:
image: postgres
volumes:
# Named volume for persistent data
- db_data:/var/lib/postgresql/data
# Bind mount for init scripts (read-only)
- ./init:/docker-entrypoint-initdb.d:ro
volumes:
db_data:
driver: local
Common Commands
# Build
docker build -t myapp:v1 .
docker build -t myapp:v1 --no-cache .
docker build -t myapp:v1 --target builder .
# Run
docker run -d --name myapp -p 8080:80 myapp:v1
docker run -it --rm myapp:v1 /bin/sh
docker run --env-file .env myapp:v1
# Inspect
docker ps -a
docker logs -f container-name
docker exec -it container-name /bin/sh
docker inspect container-name
# Clean up
docker system prune -a --volumes
docker image prune -a
docker container prune
# Registry
docker tag myapp:v1 registry.example.com/myapp:v1
docker push registry.example.com/myapp:v1
docker pull registry.example.com/myapp:v1
Interview Questions
1. Explain Docker image layers
- Each Dockerfile instruction creates a layer
- Layers are cached and reused
- Changes in early layers invalidate subsequent layer cache
- Use multi-stage builds to reduce final image size
2. What's the difference between CMD and ENTRYPOINT?
- ENTRYPOINT: Defines the executable (harder to override)
- CMD: Provides default arguments (easily overridden)
- Combined:
ENTRYPOINT ["executable"]+CMD ["arg1", "arg2"]
3. How do you optimize Docker image size?
- Use minimal base images (alpine, distroless)
- Multi-stage builds
- Combine RUN commands
- Remove unnecessary files in same layer
- Use .dockerignore
- Don't install dev dependencies
4. Explain Docker networking modes
- Bridge: Default isolated network, containers communicate via container names
- Host: Container uses host's network, no isolation
- None: No network access
- Overlay: Cross-host networking for orchestration