본문으로 건너뛰기 Docker & Kubernetes Beginners Guide

Docker & Kubernetes Beginners Guide

Docker & Kubernetes Beginners Guide

이 글의 핵심

Docker packages your app and all its dependencies into a portable container. Kubernetes orchestrates those containers across many machines. Together they are the backbone of modern cloud deployment.

Why Containers?

The classic developer problem:

"It works on my machine!"
→ Push to staging → crashes
→ Different OS, different Python version, missing library

Docker solves this by packaging your app and all its dependencies into a container — a portable, isolated unit that runs identically everywhere.

Traditional:
App → OS dependency hell → "works on my machine"

With Docker:
App + Dependencies → Image → Container (same everywhere)

Real-World Impact

Containers have become the standard for production deployments:

  • Netflix runs thousands of containers across its microservices, enabling rapid deployment and scaling
  • Spotify migrated to Kubernetes to handle 180+ microservices, reducing deployment time from hours to minutes
  • Airbnb, Shopify, and GitHub all use Kubernetes to orchestrate containers in production

For developers, Docker means:

  • Set up a new developer in 5 minutes instead of 5 hours
  • Deploy the same container from laptop → staging → production
  • Run the full stack locally without installing dependencies

For DevOps, Kubernetes means:

  • Automatic rollbacks when deployments fail
  • Scale from 10 to 10,000 users without changing code
  • Self-healing — crashed containers restart automatically

Docker Concepts

Image     → Blueprint (read-only, like a class)
Container → Running instance of an image (like an object)
Registry  → Storage for images (Docker Hub, ECR, GCR)
Dockerfile → Instructions to build an image

VM vs Container

Virtual Machine:          Docker Container:
┌─────────────────┐       ┌─────────────────┐
│   App A │ App B │       │   App A │ App B │
├─────────┼───────┤       ├─────────┼───────┤
│  OS A   │ OS B  │       │   Docker Engine  │ ← shared kernel
├─────────────────┤       ├─────────────────┤
│   Hypervisor    │       │   Host OS        │
├─────────────────┤       ├─────────────────┤
│  Physical Server│       │  Physical Server │
└─────────────────┘       └─────────────────┘
GBs, minutes to start     MBs, milliseconds to start

Installation

Docker Desktop (macOS / Windows)

Download from docker.com/get-started. Includes Docker Engine, Docker CLI, and Docker Compose.

Linux

# Ubuntu / Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in

Verify

docker --version      # Docker version 26.x.x
docker compose version # Docker Compose version v2.x.x

Docker Basics

Your First Container

Run the following commands:

# Pull and run Nginx
docker run -d -p 8080:80 --name my-nginx nginx
# → open http://localhost:8080

# List running containers
docker ps

# View logs
docker logs my-nginx

# Stop and remove
docker stop my-nginx
docker rm my-nginx

Essential Commands

Run the following commands:

# Images
docker pull nginx:alpine          # Download image
docker images                     # List local images
docker rmi nginx:alpine           # Remove image

# Containers
docker run -d -p 3000:3000 myapp  # Run detached
docker run -it ubuntu bash        # Interactive terminal
docker ps                         # List running containers
docker ps -a                      # List all (including stopped)
docker stop <id>                  # Stop gracefully
docker rm <id>                    # Remove container
docker rm -f <id>                 # Force remove running container

# Debugging
docker logs <id>                  # View logs
docker logs -f <id>               # Follow logs (live)
docker exec -it <id> bash         # Shell into running container
docker inspect <id>               # Full container details

Writing a Dockerfile

Python / FastAPI Example

Configuration file:

# Dockerfile
FROM python:3.12-slim

# Set working directory
WORKDIR /app

# Install dependencies first (layer cache optimization)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose port
EXPOSE 8000

# Run the application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Run the following commands:

# Build the image
docker build -t my-fastapi-app .

# Run it
docker run -d -p 8000:8000 --name api my-fastapi-app

# Test
curl http://localhost:8000/

Node.js Example

Configuration file:

FROM node:20-alpine

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production

# Copy source
COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

Multi-stage Build (Smaller Images)

Configuration file:

# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production (no dev dependencies, no source)
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

The production image only contains the compiled output — much smaller than including all source files.


.dockerignore

Run the following commands:

node_modules/
.git/
.env
*.log
dist/
__pycache__/
.pytest_cache/

Exclude these to keep your build context small and fast.


Docker Compose — Multi-Container Apps

Docker Compose runs multiple containers together as a single service.

Web App + Database + Cache

Configuration file:

# docker-compose.yml
version: '3.9'

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:

Run the following commands:

# Start all services
docker compose up -d

# View logs for all services
docker compose logs -f

# View logs for one service
docker compose logs -f api

# Stop all services
docker compose down

# Stop and remove volumes (⚠️ deletes data)
docker compose down -v

# Rebuild after code changes
docker compose up -d --build

Kubernetes Basics

Kubernetes (K8s) orchestrates containers across a cluster of machines.

Run the following commands:

You describe desired state → K8s makes it happen and keeps it that way

"I want 3 replicas of my API, always"
→ K8s runs 3 pods
→ If one crashes → K8s starts a new one automatically
→ If traffic spikes → K8s scales up

Core Objects

ObjectWhat it does
PodSmallest deployable unit — one or more containers
DeploymentManages Pods — rolling updates, scaling, self-healing
ServiceStable network endpoint to reach Pods (load balancing)
IngressRoutes external HTTP traffic to Services
ConfigMapStore non-secret config (env vars, config files)
SecretStore sensitive data (passwords, API keys)

Local Setup (for learning)

Run the following commands:

# Install kubectl
brew install kubectl  # macOS
# Windows: winget install Kubernetes.kubectl

# minikube (local single-node cluster)
brew install minikube
minikube start

Pod & Deployment

Pod (basic unit)

Configuration file:

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-api-pod
  labels:
    app: my-api
spec:
  containers:
    - name: api
      image: my-fastapi-app:latest
      ports:
        - containerPort: 8000
      env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: url

Pods are ephemeral — don’t use them directly. Use Deployments.

Configuration file:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-api
spec:
  replicas: 3                          # Run 3 copies
  selector:
    matchLabels:
      app: my-api
  template:
    metadata:
      labels:
        app: my-api
    spec:
      containers:
        - name: api
          image: myregistry/my-api:1.0.0
          ports:
            - containerPort: 8000
          resources:
            requests:
              memory: "128Mi"
              cpu: "250m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 5
            periodSeconds: 10
kubectl apply -f deployment.yaml

# Check status
kubectl get deployments
kubectl get pods
kubectl describe deployment my-api

# Scale up/down
kubectl scale deployment my-api --replicas=5

# Rolling update (zero downtime)
kubectl set image deployment/my-api api=myregistry/my-api:1.1.0

# Rollback
kubectl rollout undo deployment/my-api

Service & Ingress

Service (internal load balancer)

Configuration file:

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-api-service
spec:
  selector:
    app: my-api          # Routes to Pods with this label
  ports:
    - port: 80
      targetPort: 8000
  type: ClusterIP        # Internal only (default)
  # type: LoadBalancer   # External IP (cloud providers)
  # type: NodePort       # Port on each node

Ingress (external HTTP routing)

Configuration file:

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-api-service
                port:
                  number: 80

Secrets & ConfigMaps

Configuration file:

# secret.yaml (values must be base64 encoded)
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
stringData:                        # kubectl auto-encodes
  DATABASE_URL: "postgresql://user:pass@db:5432/mydb"
  API_KEY: "sk-..."

---
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
kubectl apply -f secret.yaml
kubectl apply -f configmap.yaml

# Use in a Deployment
# env:
#   - name: DATABASE_URL
#     valueFrom:
#       secretKeyRef:
#         name: db-secret
#         key: DATABASE_URL

Essential kubectl Commands

Run the following commands:

# Context (cluster) management
kubectl config get-contexts
kubectl config use-context my-cluster

# Resources
kubectl get pods
kubectl get pods -n my-namespace       # Specific namespace
kubectl get all                        # All resource types
kubectl describe pod <name>            # Detailed info + events

# Debugging
kubectl logs <pod-name>
kubectl logs -f <pod-name>             # Follow
kubectl exec -it <pod-name> -- bash    # Shell into pod

# Apply / delete
kubectl apply -f manifest.yaml
kubectl delete -f manifest.yaml
kubectl delete pod <name> --force

# Port forwarding (local testing)
kubectl port-forward pod/<name> 8080:8000
kubectl port-forward service/<name> 8080:80

When to Use What

ScenarioTool
Local developmentDocker + docker compose
Single-server deploymentDocker + docker compose
Multi-server, auto-scalingKubernetes
Managed cloud (AWS/GCP/Azure)EKS / GKE / AKS
Simple side projectDocker alone

Conclusion

  1. Start with Docker — containerize your app, run it locally with Docker Compose
  2. Graduate to Kubernetes when you need horizontal scaling, rolling updates, or self-healing across multiple nodes
  3. Use managed Kubernetes (GKE, EKS, AKS) in production — avoid managing the control plane yourself

Related posts:


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Learn Docker and Kubernetes from scratch.

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers Docker, Kubernetes, Container, DevOps, k8s, kubectl, Pod, Deployment.