Deploying Apps dengan Docker & Kubernetes
Pernahkah Anda mendengar kalimat "Tapi ini jalan di laptop saya!" saat aplikasi Anda error di server produksi?
Masalah ini diselesaikan oleh Containerization. Docker membungkus aplikasi Anda beserta semua dependensinya (library Python, OS settings, environment variables) ke dalam satu wadah ("container") yang bisa berjalan di mana saja β laptop, server, atau cloud.
π³ Apa itu Docker?
Docker adalah platform untuk membangun, menjalankan, dan mendistribusikan container. Container mirip dengan virtual machine (VM), tapi jauh lebih ringan karena berbagi kernel OS host.
| Aspek | Virtual Machine | Docker Container |
|---|---|---|
| Ukuran | GB (termasuk OS) | MB (hanya app + deps) |
| Startup | Menit | Detik |
| Resource | Berat | Sangat ringan |
| Isolasi | Penuh (OS sendiri) | Proses level |
| Use Case | Legacy apps, different OS | Modern apps, microservices |
π¦ Container Image
Image adalah blueprint/template untuk membuat container. Mirip seperti class di OOP β Image adalah class, Container adalah instance/object-nya.
Dockerfile
Dockerfile adalah resep untuk membangun image. Setiap instruksi menciptakan layer baru.
# Gunakan base image Python resmi (alpine = versi ringan)
FROM python:3.11-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
# Set direktori kerja
WORKDIR /app
# Install dependencies sistem (jika diperlukan)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Copy dan install Python dependencies terlebih dahulu (untuk caching)
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install -r requirements.txt
# Copy seluruh kode aplikasi
COPY . .
# Buat user non-root untuk keamanan
RUN adduser --disabled-password --gecos '' appuser && \
chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Command untuk menjalankan aplikasi
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Multi-Stage Build (Optimasi)
Untuk image yang lebih kecil, gunakan multi-stage build:
# Stage 1: Build
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# Stage 2: Production
FROM python:3.11-slim
WORKDIR /app
# Copy installed packages dari builder stage
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
π¨ Build & Run Container
Build Image
# Build image dengan nama dan tag
docker build -t myapp:1.0.0 .
# Lihat daftar images
docker images
Run Container
# Run container dari image
docker run -d \
--name myapp-container \
-p 8000:8000 \
-e DATABASE_URL="postgresql://user:pass@host/db" \
-v $(pwd)/logs:/app/logs \
myapp:1.0.0
# Cek container yang berjalan
docker ps
# Lihat logs
docker logs -f myapp-container
# Masuk ke dalam container
docker exec -it myapp-container bash
# Stop dan hapus container
docker stop myapp-container
docker rm myapp-container
πΌ Docker Compose
Untuk aplikasi dengan multiple services (database, Redis, dll), gunakan Docker Compose:
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
- SECRET_KEY=${SECRET_KEY}
volumes:
- ./app:/app/app # Hot reload untuk development
- static_volume:/app/static
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
db:
image: postgres:15-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
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- static_volume:/app/static
- ./certs:/etc/nginx/certs
depends_on:
- web
celery:
build: .
command: celery -A app.celery worker --loglevel=info
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
volumes:
postgres_data:
redis_data:
static_volume:
Menjalankan Docker Compose
# Start semua services (background)
docker-compose up -d
# Lihat status
docker-compose ps
# Lihat logs semua services
docker-compose logs -f
# Lihat logs service tertentu
docker-compose logs -f web
# Stop semua services
docker-compose down
# Stop dan hapus volumes (HATI-HATI: data hilang!)
docker-compose down -v
# Rebuild images
docker-compose build --no-cache
π Container Registry
Untuk deployment ke production, Anda perlu menyimpan image di Container Registry:
| Registry | Keterangan |
|---|---|
| Docker Hub | Default, gratis untuk public images |
| GitHub Container Registry (ghcr.io) | Terintegrasi dengan GitHub Actions |
| Google Container Registry (gcr.io) | Untuk Google Cloud |
| Amazon ECR | Untuk AWS |
| Azure Container Registry | Untuk Azure |
Push ke Docker Hub
# Login ke Docker Hub
docker login
# Tag image
docker tag myapp:1.0.0 username/myapp:1.0.0
# Push ke registry
docker push username/myapp:1.0.0
Push ke GitHub Container Registry
# Login dengan Personal Access Token
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Tag image
docker tag myapp:1.0.0 ghcr.io/username/myapp:1.0.0
# Push
docker push ghcr.io/username/myapp:1.0.0
βΈοΈ Kubernetes (K8s)
Jika Docker adalah kapal pengangkut kontainer, maka Kubernetes adalah nahkoda pelabuhan yang mengatur ribuan kapal tersebut.
Kubernetes (K8s) adalah sistem orchestration untuk mengelola container dalam skala besar. Jika Anda punya 100 container aplikasi yang harus berjalan di 10 server berbeda, K8s yang akan mengaturnya.
Fitur Utama K8s
| Fitur | Deskripsi |
|---|---|
| Load Balancing | Membagi trafik ke beberapa container |
| Self-healing | Restart otomatis jika container crash |
| Horizontal Scaling | Tambah/kurangi container sesuai load |
| Rolling Updates | Deploy versi baru tanpa downtime |
| Secret Management | Kelola credentials dengan aman |
| Service Discovery | Container bisa saling menemukan |
Komponen Utama K8s
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Kubernetes Cluster β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Control Plane β β
β β ββββββββββββ ββββββββββββ ββββββββββββββββββββ β β
β β βAPI Serverβ βScheduler β βController Managerβ β β
β β ββββββββββββ ββββββββββββ ββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Worker Nodes β β
β β ββββββββββββββββββ ββββββββββββββββββ β β
β β β Node 1 β β Node 2 β β β
β β β ββββββββββββ β β ββββββββββββ β β β
β β β β Pod β β β β Pod β β β β
β β β β[Container]β β β β[Container]β β β β
β β β ββββββββββββ β β ββββββββββββ β β β
β β β ββββββββββββ β β ββββββββββββ β β β
β β β β Pod β β β β Pod β β β β
β β β β[Container]β β β β[Container]β β β β
β β β ββββββββββββ β β ββββββββββββ β β β
β β ββββββββββββββββββ ββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π Kubernetes Manifests
Kubernetes menggunakan file YAML untuk mendefinisikan resources.
1. Deployment
Deployment mengontrol bagaimana Pod di-deploy dan di-scale:
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
labels:
app: myapp
spec:
replicas: 3 # Jalankan 3 replicas
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: ghcr.io/username/myapp:1.0.0
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: database-url
- name: ENV
value: "production"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
imagePullSecrets:
- name: ghcr-secret
2. Service
Service mengekspos Deployment dan melakukan load balancing:
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: ClusterIP # Internal only
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 8000
---
# Load Balancer (untuk cloud providers)
apiVersion: v1
kind: Service
metadata:
name: myapp-loadbalancer
spec:
type: LoadBalancer
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 8000
3. Ingress
Ingress mengatur routing HTTP/HTTPS dari luar cluster:
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- api.myapp.com
secretName: myapp-tls
rules:
- host: api.myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
4. ConfigMap & Secrets
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
APP_NAME: "My Awesome App"
LOG_LEVEL: "INFO"
ALLOWED_HOSTS: "api.myapp.com,www.myapp.com"
---
# k8s/secrets.yaml (values harus base64 encoded)
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
data:
database-url: cG9zdGdyZXNxbDovL3VzZXI6cGFzc0Bob3N0L2Ri # base64 encoded
secret-key: c3VwZXItc2VjcmV0LWtleQ==
5. Horizontal Pod Autoscaler
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp-deployment
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
π Deploy ke Kubernetes
Local Development dengan Minikube
# Install Minikube (untuk local development)
# Windows: choco install minikube
# Mac: brew install minikube
# Start cluster
minikube start
# Apply manifests
kubectl apply -f k8s/
# Cek status
kubectl get pods
kubectl get services
kubectl get deployments
# Lihat logs
kubectl logs -f deployment/myapp-deployment
# Port forward untuk testing
kubectl port-forward service/myapp-service 8080:80
# Masuk ke pod
kubectl exec -it <pod-name> -- bash
Managed Kubernetes (Production)
| Provider | Service Name | Kelebihan |
|---|---|---|
| Google Cloud | GKE | Integrasi terbaik, autopilot mode |
| AWS | EKS | Ekosistem AWS lengkap |
| Azure | AKS | Integrasi dengan Azure DevOps |
| DigitalOcean | DOKS | Murah, simple |
| Linode | LKE | Affordable, developer-friendly |
π CI/CD Pipeline
Otomatisasi build dan deploy dengan GitHub Actions:
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up kubectl
uses: azure/setup-kubectl@v3
- name: Configure kubectl
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
- name: Update deployment
run: |
kubectl set image deployment/myapp-deployment \
myapp=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
- name: Wait for rollout
run: |
kubectl rollout status deployment/myapp-deployment --timeout=300s
π‘οΈ Best Practices Production
Docker Best Practices
| Practice | Alasan |
|---|---|
Gunakan image spesifik (bukan latest) |
Reproducibility |
| Multi-stage build | Image lebih kecil |
| Run as non-root user | Keamanan |
Use .dockerignore |
Build lebih cepat |
| Scan vulnerabilities | docker scan myapp:latest |
| Pin dependency versions | Avoid breaking changes |
Kubernetes Best Practices
| Practice | Alasan |
|---|---|
| Set resource limits | Hindari noisy neighbors |
| Use liveness/readiness probes | Self-healing |
| Gunakan namespaces | Isolasi environment |
| Implement network policies | Zero trust security |
| Use secrets management | Jangan hardcode credentials |
| Enable RBAC | Principle of least privilege |
π§° Tools Tambahan yang Berguna
| Tool | Fungsi |
|---|---|
| Helm | Package manager untuk K8s (charts) |
| ArgoCD | GitOps continuous delivery |
| Lens | GUI untuk manage K8s clusters |
| k9s | TUI dashboard untuk K8s |
| Prometheus + Grafana | Monitoring & visualization |
| Jaeger | Distributed tracing |
π Kesimpulan
Kapan Pakai Apa?
| Skala | Rekomendasi |
|---|---|
| Development/Testing | Docker + Docker Compose |
| Small Production | Docker Compose + VPS (Railway, Render) |
| Medium Production | Managed K8s (GKE, EKS, DOKS) |
| Enterprise | Self-managed K8s + Advanced DevOps |
Learning Path
- Mulai dengan Docker β build image, run container
- Pelajari Docker Compose β multi-container apps
- Pahami konsep K8s β pods, deployments, services
- Praktik dengan Minikube β local K8s cluster
- Deploy ke Managed K8s β GKE, EKS, atau DOKS
- Implementasi CI/CD β GitHub Actions, GitLab CI
- Advanced Topics β Helm, Istio, GitOps
Edit tutorial ini
Gabung Komunitas Developer & Kreator Digital
Dapatkan teman coding, sharing project, networking dengan expert, dan update teknologi terbaru.
Selamat! Anda telah sukses mendaftar di newsletter.