Docker + Kubernetes Workflow
Önceki derslerde Docker Swarm ve Kubernetes'i ayrı ayrı öğrendik. Şimdi büyük resmi görelim: Docker ve Kubernetes birlikte nasıl çalışır? Bir feature'ı geliştirmeden production'a kadar götüren tam workflow nedir?
Yemek Tarifi Kitabı Analojisi
Bir yemek tarifi kitabı yazıyorsun. Mutfağında (lokal ortam) tarifi deniyorsun — Docker Compose ile. Tarif oturduğunda profesyonel çekim yapıyorsun (staging) — Kubernetes'e deploy ediyorsun. Her şey tamam olduğunda kitap basılıyor (production) — managed Kubernetes cluster'ında. Ama aradaki geçiş pürüzsüz olmalı: mutfaktaki tarif ile kitaptaki tarif aynı olmalı.
Docker container'ları oluşturur, Kubernetes container'ları yönetir. İkisi birlikte çalışır ve bu ders, tam bu birlikteliği anlatıyor.
Büyük Resim: 4 Adımlı Workflow
Geliştirmeden production'a kadar olan yolculuk dört adımdan oluşuyor:
1. DEVELOP 2. BUILD 3. SHIP 4. RUN
Dockerfile docker build docker push kubectl apply
Compose image oluştur registry'ye Kubernetes'e
Kod yaz gönder deploy etHer adımda farklı araçlar kullanırsın ama hepsinin çıktısı bir sonrakinin girdisi. Ve en önemlisi: aynı Dockerfile hem geliştirmede hem production'da kullanılır. Bu tutarlılık, "bende çalışıyordu" problemini ortadan kaldırır.
Adım 1: Lokal Geliştirme — Docker Compose
Geliştirme ortamında Docker Compose kullanırsın. Hızlı iteration, hot reload, kolay debug — bunlar geliştirme sırasında kritik.
Multi-stage Dockerfile ile hem development hem production image'ını aynı dosyadan çıkarırsın:
# === Development Stage ===
FROM node:20-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm install # devDependencies dahil
COPY . .
EXPOSE 3000 9229
CMD ["npm", "run", "dev"]
# === Build Stage ===
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm prune --production
# === Production Stage ===
FROM node:20-alpine AS production
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]Geliştirme ortamı Compose dosyası:
# docker-compose.yml (geliştirme)
services:
api:
build:
context: .
target: development
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
- "9229:9229"
environment:
NODE_ENV: development
DATABASE_URL: postgres://dev:dev@db:5432/myapp
depends_on:
db:
condition: service_healthy
command: npm run dev
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
POSTGRES_DB: myapp
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dev"]
interval: 5s
retries: 5Dikkat et: target: development ile Dockerfile'ın development stage'ini kullanıyoruz. Bind mount ile kaynak kodu container'a mount ediyoruz — dosya değiştirdiğinde hot reload çalışır, image yeniden build etmene gerek yok.
# Geliştirmeye başla
docker compose up -d
# Kod yaz, hot reload ile anında göreceksin
# http://localhost:3000Adım 2: Image Build ve Registry Push
Kod hazır olduğunda production image'ını build edip registry'ye push edersin. Bu genellikle CI/CD pipeline'ında otomatik olur:
# Production image build
docker build --target production -t myapp:v1.0 .
# Tag ve push
docker tag myapp:v1.0 ghcr.io/myorg/myapp:v1.0
docker push ghcr.io/myorg/myapp:v1.0Multi-platform build yaparak hem AMD64 hem ARM64 destekleyebilirsin:
docker buildx build \
--platform linux/amd64,linux/arm64 \
--target production \
-t ghcr.io/myorg/myapp:v1.0 \
--push .CI/CD'de (GitHub Actions) bu otomatikleştirilir:
- uses: docker/build-push-action@v5
with:
push: true
target: production
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
cache-from: type=gha
cache-to: type=gha,mode=maxAdım 3: Kubernetes Manifest'leri
Docker Compose'dan Kubernetes'e geçerken, aynı uygulamayı K8s manifest'leri ile tanımlarsın. Eşleştirme şöyle:
services:→ Deployment + Serviceports:→ Service (NodePort/LoadBalancer)volumes:→ PersistentVolumeClaimenvironment:→ ConfigMap / Secretbuild:→ CI/CD pipeline (K8s image build etmez)
Helm ile Ortam Yönetimi
Farklı ortamlar (staging, production) için ayrı YAML dosyası yazmak zahmetli. Helm bunu çözer — template + values yaklaşımıyla:
# values.yaml (varsayılan)
replicaCount: 2
image:
repository: ghcr.io/myorg/myapp
tag: "latest"
resources:
limits:
memory: "256Mi"
cpu: "500m"
# values-production.yaml
replicaCount: 3
image:
tag: "v1.0.0"
resources:
limits:
memory: "1Gi"
cpu: "1"
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources:
{{- toYaml .Values.resources | nindent 12 }}Tek template, farklı values dosyalarıyla farklı ortamlara deploy:
# Staging
helm upgrade --install myapp ./chart -f values-staging.yaml -n staging
# Production
helm upgrade --install myapp ./chart -f values-production.yaml -n productionKustomize Alternatifi
Helm'e alternatif olarak Kustomize, template'siz overlay tabanlı bir yaklaşım sunar. kubectl'a dahildir:
k8s/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ └── service.yaml
└── overlays/
├── staging/
│ ├── kustomization.yaml
│ └── patch-replicas.yaml
└── production/
├── kustomization.yaml
└── patch-replicas.yamlkubectl apply -k k8s/overlays/production/Adım 4: Tam Workflow — Feature'dan Production'a
Hadi bir feature'ı baştan sona götürelim:
1. Geliştirme:
git checkout -b feature/user-profiles
docker compose up -d
# Kod yaz, test et
docker compose exec api npm test
git commit -m "feat: add user profiles"
git push origin feature/user-profiles2. CI Pipeline (otomatik): PR açıldığında: lint → test → security scan PR merge edildiğinde: build → push → tag
3. Staging deploy:
helm upgrade --install myapp ./chart \
-f values-staging.yaml \
--set image.tag=sha-abc1234 \
--namespace staging4. Production release:
git tag v1.2.0
git push origin v1.2.0
# CI/CD otomatik build + push yapar
helm upgrade --install myapp ./chart \
-f values-production.yaml \
--set image.tag=v1.2.0 \
--namespace productionIngress: Dış Trafik Yönetimi
Production'da kullanıcıların api.example.com gibi bir domain'den erişmesini istersin. Kubernetes Ingress bunu sağlar:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: myapp-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80Bu Ingress tanımı: api.example.com'a gelen HTTPS trafiğini api servisine yönlendirir. cert-manager ile Let's Encrypt SSL sertifikası otomatik alınır ve yenilenir.
Skaffold: Geliştirme Döngüsünü Hızlandır
Lokal geliştirmede Docker Compose kullanıyorsun, production'da Kubernetes. Ama ya Kubernetes'e deploy ederek geliştirmek istersen? Her kod değişikliğinde image build edip push edip kubectl apply yapmak çok yavaş olur.
Skaffold bu sorunu çözer. Kod değiştikçe otomatik olarak image build eder ve Kubernetes'e deploy eder:
# skaffold.yaml
apiVersion: skaffold/v4beta6
kind: Config
metadata:
name: myapp
build:
artifacts:
- image: myapp
docker:
dockerfile: Dockerfile
target: development
local:
push: false
manifests:
rawYaml:
- k8s/base/*.yaml
deploy:
kubectl: {}
profiles:
- name: production
build:
artifacts:
- image: myapp
docker:
target: production
deploy:
helm:
releases:
- name: myapp
chartPath: ./chart
valuesFiles:
- values-production.yamlKullanımı çok basit:
# Geliştirme modu — kod değiştikçe otomatik deploy
skaffold dev
# Tek seferlik deploy
skaffold run
# Production profile ile
skaffold run -p productionskaffold dev çalıştırdığında, dosyalarını izler. Bir dosya değiştirdiğinde otomatik olarak image'ı yeniden build eder ve Kubernetes'e deploy eder. Hot reload kadar hızlı olmasa da, Kubernetes ortamında geliştirmeyi çok kolaylaştırır.
Compose → K8s Eşleştirmesi Detay
Docker Compose'dan Kubernetes'e geçerken en çok kafaları karıştıran konu, aynı kavramın farklı isimlerle temsil edilmesi. Hadi bunu somutlaştıralım.
Compose'da depends_on ile servis bağımlılıklarını tanımlıyordun. Kubernetes'te doğrudan bir karşılığı yok — bunun yerine initContainers veya readinessProbe kullanırsın. Mesela API'n veritabanının hazır olmasını bekliyorsa:
spec:
initContainers:
- name: wait-for-db
image: busybox
command: ['sh', '-c', 'until nc -z db 5432; do sleep 2; done']
containers:
- name: api
image: myapp:v1.0initContainer, ana container başlamadan önce çalışır ve tamamlanır. Veritabanı hazır olana kadar bekler, sonra API container'ı başlar.
Compose'da volumes: ile bind mount yapıyordun. Kubernetes'te geliştirme için hostPath veya emptyDir, production için PersistentVolumeClaim (PVC) kullanırsın.
Compose'da build: ile image build ediyordun. Kubernetes image build etmez — hazır image kullanır. Build işi CI/CD pipeline'ının sorumluluğunda.
Bu farkları anlamak, geçiş sürecini çok kolaylaştırır.
Kustomize ile Detaylı Örnek
Helm'e alternatif olarak Kustomize'ı daha detaylı inceleyelim. Kustomize'ın güzel yanı template kullanmaması — base manifest'lerin üzerine patch'ler eklersin:
# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml# k8s/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: production
namePrefix: prod-
patches:
- path: patch-replicas.yaml
- path: patch-resources.yaml
images:
- name: ghcr.io/myorg/myapp
newTag: v1.0.0# k8s/overlays/production/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 5Gördüğün gibi, base'deki deployment'ı değiştirmeden sadece patch ile replica sayısını artırdık. images bloğu ile image tag'ini belirledik. namePrefix ile tüm kaynaklara "prod-" öneki eklendi.
Deploy etmek:
# Staging
kubectl apply -k k8s/overlays/staging/
# Production
kubectl apply -k k8s/overlays/production/
# Preview (deploy etmeden gör)
kubectl kustomize k8s/overlays/production/Kustomize'ın avantajı: base manifest'ler her zaman okunabilir ve geçerli YAML. Template değişkenleri yok, koşullu ifadeler yok. Dezavantajı: çok karmaşık konfigürasyon farkları için Helm daha esnek.
CI/CD ile Tam Otomatik Akış
Gerçek dünyada bu workflow genellikle CI/CD pipeline'ı ile otomatikleştirilir. Tipik bir akış:
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
target: production
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to K8s
run: |
helm upgrade --install myapp ./chart \
-f values-production.yaml \
--set image.tag=${{ github.ref_name }} \
--namespace productionv1.2.0 tag'ı push'ladığında: otomatik build → registry'ye push → Kubernetes'e deploy. Tüm süreç 5-10 dakika içinde tamamlanır. Manuel müdahale yok.
Ortam Yönetimi: Dev, Staging, Production
Gerçek projelerde en az üç ortam olur ve her birinin farklı gereksinimleri vardır. Docker + Kubernetes workflow'unda bu ortamları nasıl yöneteceğini bilmek çok önemli.
Development (Lokal): Docker Compose kullanırsın. Hot reload aktif, debug port açık, veritabanı lokalde, fake/test verilerle çalışırsın. Image'lar lokal build edilir, registry'ye push edilmez.
Staging: Kubernetes'te ayrı bir namespace. Production'ın birebir kopyası ama daha az kaynak ile. Gerçek verinin bir kısmı (anonimleştirilmiş), gerçek entegrasyonlar (ödeme sistemi sandbox'u gibi). Amacı: production'a deploy etmeden önce son test.
Production: Kubernetes'te ayrı namespace veya ayrı cluster. Maximum kaynak, otomatik ölçekleme, SSL/TLS, monitoring, alerting hepsi aktif.
Bu ortamları yönetmek için environment-specific dosyalar kullanırsın:
chart/
├── values.yaml # Varsayılan (development)
├── values-staging.yaml # Staging override
└── values-production.yaml # Production overrideVeya Kustomize ile:
k8s/
├── base/ # Ortak tanımlar
└── overlays/
├── staging/ # Staging patch'leri
└── production/ # Production patch'leriHer ortam için ayrı secret'lar kullanmayı unutma! Development'ta kullandığın veritabanı şifresi production'daki ile aynı olmamalı. Bir ortamdaki sızıntı, diğer ortamları etkilemesin.
Staging'de test ettiğin image tag'i ile production'a deploy ettiğin tag aynı olmalı. "Staging'de sha-abc1234 test ettim, production'a sha-abc1234 deploy ediyorum." Araya yeni bir build girmemeli — çünkü o build farklı bir şey üretmiş olabilir.
Image Tag Stratejisi
Doğru tag stratejisi workflow'un güvenilirliği için kritik:
# Her commit'te
myapp:sha-abc1234f # Git commit hash — benzersiz ve immutable
# Release'lerde
myapp:v1.2.3 # Semantic version — anlamlı ve takip edilebilir
# Branch'lerde
myapp:main-abc1234f # Branch + commit — hangi branch'ten geldiği bellilatest tag'ini production'da asla kullanma. Çünkü latest hangi versiyonun çalıştığını söylemez ve her pull'da farklı bir image gelebilir. Production'da her zaman belirli ve immutable bir tag kullan.
Yaygın Hatalar
"Lokalde çalışıyor, K8s'te çalışmıyor": En yaygın sebepler — eksik environment variable, yanlış port, volume mount eksikliği, farklı DNS isimleri. Debug için: kubectl exec -it pod-name -- env | sort
Image pull hatası: Private registry kullanıyorsan imagePullSecrets gerekli. kubectl create secret docker-registry regcred --docker-server=ghcr.io --docker-username=user --docker-password=token
Helm values override çalışmıyor: helm template myapp ./chart -f values-production.yaml ile render edip kontrol et.
Bu Derste Ne Öğrendik?
Docker + Kubernetes workflow: Dockerfile ile build → Registry'ye push → Kubernetes'e deploy
Multi-stage Dockerfile hem development hem production için — tek kaynak, tutarlılık
Lokal geliştirme Docker Compose ile, deploy Kubernetes ile
Helm veya Kustomize ile farklı ortamları (staging/production) yönet
Ingress ile dış trafiği yönet, SSL otomatikleştir
CI/CD pipeline ile build → test → push → deploy akışını otomatikleştir
Workflow'un anahtarı tutarlılık: lokal, staging ve production aynı image'ı çalıştırmalı
Bu dersle birlikte orchestration bölümünü tamamladık! Sonraki bölümde CI/CD ve Docker entegrasyonunu derinlemesine inceleyeceğiz.
AI Asistan
Sorularını yanıtlamaya hazır