Containers have revolutionized software deployment by providing a consistent, portable way to package applications with all their dependencies. This guide covers container concepts, runtimes, and best practices beyond Docker.
Containers are lightweight, portable units that package an application and its dependencies, ensuring it runs consistently across different environments.
Key Characteristics:
- Isolation: Process and filesystem isolation
- Portability: Run anywhere (dev, test, prod)
- Efficiency: Share host OS kernel
- Consistency: Same behavior everywhere
- Scalability: Easy to scale horizontally
Virtual Machine:
┌─────────────────────────────────┐
│ Application │
│ Bins/Libs │
│ Guest OS (Full) │
│ ┌──────────────────────┐ │
│ │ Hypervisor │ │
│ └──────────────────────┘ │
│ Host OS │
│ Hardware │
└─────────────────────────────────┘
Container:
┌─────────────────────────────────┐
│ Application │
│ Bins/Libs │
│ ┌──────────────────────┐ │
│ │ Container Runtime │ │
│ │ (containerd/CRI-O) │ │
│ └──────────────────────┘ │
│ Host OS (Shared) │
│ Hardware │
└─────────────────────────────────┘
Comparison:
| Aspect | Containers | Virtual Machines |
|---|---|---|
| OS | Share host kernel | Full OS per VM |
| Size | MBs | GBs |
| Startup | Seconds | Minutes |
| Isolation | Process level | Hardware level |
| Resource Usage | Lower | Higher |
| Portability | High | Medium |
Container Image
├── Base Layer (OS files)
├── Dependency Layer (libraries)
├── Application Layer (code)
└── Configuration Layer (configs)
Industry-standard container runtime
# Install containerd
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y containerd
# Start containerd
sudo systemctl start containerd
sudo systemctl enable containerd
# Verify
sudo ctr versionFeatures:
- OCI-compliant
- Used by Docker and Kubernetes
- Supports multiple image formats
- Plugin architecture
Kubernetes-native container runtime
# Install CRI-O
# Ubuntu
OS=xUbuntu_22.04
VERSION=1.28
echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
echo "deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list
curl -L https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/Release.key | sudo apt-key add -
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install -y cri-o cri-o-runc
# Start CRI-O
sudo systemctl start crio
sudo systemctl enable crioFeatures:
- Built for Kubernetes
- Lightweight
- OCI-compliant
- No Docker dependency
Rootless container engine
# Install Podman
# Ubuntu
sudo apt-get update
sudo apt-get install -y podman
# Run container (no root needed)
podman run -d nginx:latest
# List containers
podman ps
# Docker-compatible commands
podman build -t myapp .
podman push myapp:latestFeatures:
- Rootless by default
- Docker-compatible CLI
- No daemon required
- Better security
┌─────────────────────────────┐
│ Application Code │ ← Layer 4 (writable)
├─────────────────────────────┤
│ Application Dependencies │ ← Layer 3
├─────────────────────────────┤
│ System Libraries │ ← Layer 2
├─────────────────────────────┤
│ Base OS Image │ ← Layer 1 (read-only)
└─────────────────────────────┘
Layer Benefits:
- Caching: Unchanged layers are cached
- Sharing: Multiple images share base layers
- Efficiency: Only changed layers are rebuilt
# Build stage
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/index.js"]# Good - Alpine Linux (5MB)
FROM alpine:latest
# Better - Distroless (even smaller)
FROM gcr.io/distroless/nodejs:20
# Avoid - Full OS (hundreds of MB)
FROM ubuntu:latest# Bad - Multiple layers
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
RUN apt-get clean
# Good - Single layer
RUN apt-get update && \
apt-get install -y package1 package2 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.vscode
.idea
*.md
# Create non-root user
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
USER appuser
# Or use distroless (no shell, no root)
FROM gcr.io/distroless/nodejs:20# Trivy scan
trivy image myapp:latest
# Snyk scan
snyk container test myapp:latest
# Docker Scout
docker scout cves myapp:latest# Use minimal images
FROM alpine:latest
# or
FROM distroless/nodejs:20# Bad - Secrets in image
ENV API_KEY=secret123
# Good - Use secrets management
# Pass at runtime or use secret mounts# Kubernetes
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000- PID: Process isolation
- Network: Network isolation
- Mount: Filesystem isolation
- UTS: Hostname isolation
- IPC: Inter-process communication
- User: User ID isolation
- CPU: CPU limits
- Memory: Memory limits
- I/O: Disk I/O limits
- Devices: Device access control
# Kubernetes Pod with Sidecar
apiVersion: v1
kind: Pod
metadata:
name: app-with-sidecar
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: shared-logs
mountPath: /var/log
- name: log-collector
image: fluentd:latest
volumeMounts:
- name: shared-logs
mountPath: /var/log
volumes:
- name: shared-logs
emptyDir: {}apiVersion: v1
kind: Pod
metadata:
name: app-with-init
spec:
initContainers:
- name: init-db
image: busybox
command: ['sh', '-c', 'until nslookup database; do echo waiting; sleep 2; done']
containers:
- name: app
image: myapp:latest# Proxy container
containers:
- name: app
image: myapp:latest
- name: proxy
image: envoy:latest
# Routes traffic- Scaling: Scale containers up/down
- High Availability: Restart failed containers
- Load Balancing: Distribute traffic
- Service Discovery: Find containers
- Rolling Updates: Zero-downtime deployments
- Resource Management: Optimize resource usage
- Kubernetes: Industry standard
- Docker Swarm: Docker-native
- Nomad: HashiCorp's orchestrator
- ECS: AWS container service
- Azure Container Instances: Azure service
# Development Dockerfile
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]# Production Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
RUN npm ci --only=production
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js
CMD ["node", "dist/index.js"]# Semantic versioning
docker build -t myapp:1.0.0 .
docker build -t myapp:1.0 .
docker build -t myapp:1 .
docker build -t myapp:latest .
# Git commit SHA
docker build -t myapp:$(git rev-parse --short HEAD) .
# Build number
docker build -t myapp:${BUILD_NUMBER} .- Docker Hub: docker.io
- GitHub Container Registry: ghcr.io
- Google Container Registry: gcr.io
- Amazon ECR: Public Gallery
# Docker Hub
docker login
docker push username/myapp:latest
# GitHub Container Registry
docker login ghcr.io -u USERNAME -p TOKEN
docker push ghcr.io/username/myapp:latest
# AWS ECR
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest- Understand container concepts
- Use multiple container runtimes
- Optimize container images
- Implement security best practices
- Use multi-stage builds
- Understand container patterns
- Manage container registries
- Monitor container performance
- Troubleshoot container issues
- Scale containers effectively
Next Steps:
- Learn Docker in detail
- Master Kubernetes for orchestration
- Explore Container Security
Remember: Containers are the foundation of modern DevOps. Master image optimization, security practices, and orchestration. Start simple and gradually add complexity.