← Kursa Dön
📄 Text · 40 min

Docker Swarm — Temel Cluster Yönetimi

Bir önceki derste orchestration'ın neden gerekli olduğunu, temel kavramları ve Swarm ile Kubernetes arasındaki farkları gördük. Şimdi Docker Swarm'ı uygulamalı olarak öğrenme zamanı. Bu derste bir Swarm cluster'ı kuracağız, servisler oluşturacağız, ölçekleyeceğiz ve zero-downtime güncelleme yapacağız.

Restoran Zinciri Analojisi

Tek şubeli bir restoran düşün. Her şeyi sen yönetirsin — sipariş al, pişir, servis yap. Ama zincir büyüdüğünde merkezi bir yönetim lazım: hangi şubeye kaç aşçı atanacak, biri hastalanırsa yerine kim gelecek, yeni menü tüm şubelere nasıl dağıtılacak?

Docker Swarm, Docker'ın kendi bünyesindeki bu "restoran zinciri yönetimi" aracı. Birden fazla sunucuyu tek bir cluster gibi yönetir. Ve en güzel tarafı? Zaten bildiğin Docker komutlarının üzerine inşa edilmiş — yepyeni bir dünya öğrenmiyorsun, mevcut bilgini genişletiyorsun.

Swarm Mode Nedir?

Docker Engine'in içinde gömülü gelen orchestration özelliği. Ek kurulum gerekmez — docker swarm init dediğin anda aktif olur. Bu, Swarm'ın en büyük avantajı: sıfır kurulum karmaşıklığı.

Temel kavramları hızlıca tanıyalım:

Node: Swarm cluster'ına katılmış bir Docker host (sunucu). İki rolü var — Manager ve Worker.

Manager Node: Cluster'ı yöneten, kararları alan node. API isteklerini karşılar, scheduling yapar, cluster durumunu saklar.

Worker Node: Container'ları çalıştıran node. Manager'dan aldığı görevleri yerine getirir.

Service: Çalıştırmak istediğin uygulamanın tanımı. "3 tane Nginx çalıştır, 80 portunda" gibi.

Task: Bir service'in tek bir container instance'ı.

Cluster Kurulumu: Adım Adım

Hadi bir Swarm cluster'ı kuralım. Üç sunucumuz olduğunu varsayalım:

  • 192.168.1.10 → Manager

  • 192.168.1.11 → Worker 1

  • 192.168.1.12 → Worker 2

Adım 1: İlk Manager'ı Başlat

İlk sunucuda Swarm'ı başlat:

docker swarm init --advertise-addr 192.168.1.10

Bu komut çalıştığında çıktıda bir join komutu göreceksin — bunu kopyala, çünkü diğer sunucularda kullanacaksın:

Swarm initialized: current node (dxn1zf6l61qsb1josjja6e8pz) is now a manager.

To add a worker to this swarm, run the following command:
    docker swarm join --token SWMTKN-1-49nj1cmql0... 192.168.1.10:2377

Tebrikler — artık bir Swarm cluster'ın var! Şu anda tek node'lu ama yine de çalışan bir cluster.

Adım 2: Worker Node'ları Ekle

Diğer sunuculara git ve join komutunu çalıştır:

# Worker 1'de
docker swarm join --token SWMTKN-1-49nj1cmql0... 192.168.1.10:2377
# This node joined a swarm as a worker.

# Worker 2'de
docker swarm join --token SWMTKN-1-49nj1cmql0... 192.168.1.10:2377
# This node joined a swarm as a worker.

Bu kadar! Şimdi manager node'da cluster'ı kontrol edelim:

docker node ls

Çıktı şöyle bir şey olacak:

ID                    HOSTNAME    STATUS   AVAILABILITY   MANAGER STATUS
dxn1zf6l61qsb *      manager1    Ready    Active         Leader
j8a4f3h2k5n7...      worker1     Ready    Active
k9b5g4i3l6o8...      worker2     Ready    Active

Üç node'lu bir cluster'ın hazır. * işareti şu anda hangi node'da olduğunu gösteriyor. "Leader" ise aktif manager'ı belirtiyor.

Yüksek Erişilebilirlik: Birden Fazla Manager

Production'da tek manager tehlikeli — çökerse cluster yönetilemez hale gelir. Bu yüzden birden fazla manager kullanırsın. Manager'lar kendi aralarında Raft consensus algoritması ile çalışır — çoğunluk (quorum) sağlandığı sürece cluster çalışmaya devam eder.

Manager sayısı her zaman tek olmalı — 1, 3, 5 veya 7. Neden? Çünkü quorum hesabı (n/2 + 1) çift sayılarda avantaj sağlamaz:

  • 3 manager → 1 çökebilir (quorum: 2)

  • 5 manager → 2 çökebilir (quorum: 3)

  • 7 manager → 3 çökebilir (quorum: 4)

Production için 3 veya 5 manager önerilir.

# Ek manager eklemek için önce token'ı al
docker swarm join-token manager

# Yeni sunucuda bu token ile join et
docker swarm join --token SWMTKN-1-...-manager-token 192.168.1.10:2377

İlk Servisimizi Oluşturalım

Swarm'da docker run yerine docker service create kullanırsın. Aralarındaki fark çok önemli: docker run tek bir container başlatır ve o kadar. docker service create ise istenen durumu tanımlar — Swarm bu durumu sürdürür.

docker service create \
    --name web \
    --replicas 3 \
    --publish 80:80 \
    nginx:alpine

Bu komut ne yaptı? Swarm'a "web adında bir servis oluştur, 3 replica olsun, dış dünyaya 80 portunda aç" dedin. Swarm bu 3 container'ı cluster'daki node'lara dağıttı.

Kontrol edelim:

docker service ls
ID        NAME   MODE        REPLICAS   IMAGE          PORTS
r3k2...   web    replicated  3/3        nginx:alpine   *:80->80/tcp

3/3 demek 3 container'dan 3'ü de çalışıyor. Hangi node'da çalıştıklarını görmek için:

docker service ps web
ID        NAME    IMAGE          NODE       DESIRED STATE   CURRENT STATE
abc...    web.1   nginx:alpine   manager1   Running         Running 2 min ago
def...    web.2   nginx:alpine   worker1    Running         Running 2 min ago
ghi...    web.3   nginx:alpine   worker2    Running         Running 2 min ago

Gördüğün gibi, Swarm her node'a bir container dağıtmış. Şimdi worker1'i kapat ve neler olduğunu gör:

# Worker1'de
sudo systemctl stop docker

# Manager'da kontrol et
docker service ps web

Swarm, worker1'deki container'ın çöktüğünü algılar ve otomatik olarak başka bir node'da yeni bir container başlatır. Bu self-healing — kendi kendini iyileştirme.

Scaling: Ölçeklendirme

Trafik arttı ve 3 replica yetmiyor? Bir komutla ölçekle:

docker service scale web=5

İki yeni container otomatik olarak uygun node'lara dağıtılır. Trafik azaldığında geri küçült:

docker service scale web=2

Birden fazla servisi aynı anda ölçekleyebilirsin:

docker service scale web=5 api=3 worker=10

Rolling Update: Sıfır Kesinti Güncelleme

Bu, Swarm'ın en güçlü özelliklerinden biri. Diyelim ki Nginx'in yeni versiyonuna geçmek istiyorsun:

docker service update \
    --image nginx:1.26-alpine \
    --update-parallelism 1 \
    --update-delay 10s \
    --update-failure-action rollback \
    --update-order start-first \
    web

Bu parametreleri açıklayalım:

  • --image nginx:1.26-alpine — yeni image

  • --update-parallelism 1 — aynı anda 1 container güncelle

  • --update-delay 10s — her güncelleme arasında 10 saniye bekle

  • --update-failure-action rollback — hata olursa otomatik geri al

  • --update-order start-first — önce yenisini başlat, sonra eskisini kaldır

start-first özellikle önemli. Bu ayarla, yeni container başlatılır ve sağlıklı olduğu kontrol edilir, ancak ondan sonra eski container durdurulur. Böylece her an en az istenen sayıda container aktif kalır — kullanıcılar hiçbir kesinti yaşamaz.

Bir sorun olursa? Manuel rollback:

docker service rollback web

Bir komutla önceki versiyona geri dönersin.

Overlay Network: Node'lar Arası İletişim

Tek sunucuda bridge network kullanıyorduk. Ama farklı sunuculardaki container'lar nasıl konuşacak? İşte burada overlay network devreye giriyor.

Overlay network, farklı node'lardaki container'ları sanki aynı ağdaymış gibi birbirine bağlar:

# Overlay network oluştur
docker network create --driver overlay my-network

# Backend için internal overlay (dış erişim yok)
docker network create --driver overlay --internal backend

Servisler bu network'ler üzerinden birbirleriyle konuşur:

docker service create \
    --name api \
    --network frontend \
    --network backend \
    myapp:v1.0

docker service create \
    --name db \
    --network backend \
    postgres:16

API servisi hem frontend hem backend network'üne bağlı — dışarıdan erişilebilir ve veritabanıyla konuşabilir. Veritabanı ise sadece backend network'ünde — dışarıdan erişilemez, sadece API ile konuşabilir.

Swarm ayrıca routing mesh (ingress network) sağlar. Bu, herhangi bir node'un IP'sine gelen trafiği doğru container'a yönlendirir:

Client → herhangi bir node:80 → Routing Mesh → doğru container

Yani 3 node'un var ve web servisi sadece worker1'de çalışıyor olsa bile, worker2'nin IP'sine 80 portuna istek yapsan, routing mesh bu isteği worker1'deki container'a yönlendirir. Kullanıcının hangi node'a bağlandığı önemli değil.

Swarm Secrets: Güvenli Konfigürasyon

Bir önceki bölümde secret management'ı öğrenmiştik. Swarm'ın kendi dahili secret mekanizması var ve oldukça güçlü — secret'ları şifreli olarak Raft store'da saklar:

# Secret oluştur
echo "supersecretpassword" | docker secret create db_password -

# Servis oluştururken secret'ı bağla
docker service create \
    --name api \
    --secret db_password \
    myapp:v1.0

Container içinde secret /run/secrets/db_password dosyası olarak görünür. docker inspect ile görünmez — güvenli.

Stack Deploy: Compose Benzeri Tanımlama

Tek tek servis oluşturmak zahmetli. Stack, Docker Compose benzeri YAML dosyasıyla tüm uygulamanı tek komutla deploy etmeni sağlar:

# docker-stack.yml
version: "3.8"

services:
  web:
    image: nginx:alpine
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
    ports:
      - "80:80"
    networks:
      - frontend

  api:
    image: myapp:v1.0
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 512M
    secrets:
      - db_password
    networks:
      - frontend
      - backend

  db:
    image: postgres:16-alpine
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    volumes:
      - pgdata:/var/lib/postgresql/data
    secrets:
      - db_password
    networks:
      - backend

secrets:
  db_password:
    external: true

volumes:
  pgdata:

networks:
  frontend:
  backend:
    driver: overlay
    internal: true

Deploy etmek tek komut:

docker stack deploy -c docker-stack.yml myapp

Stack durumunu kontrol etmek:

docker stack ls
docker stack services myapp
docker stack ps myapp

Güncellemek? YAML dosyasını değiştir ve aynı komutu tekrar çalıştır — Swarm sadece değişen kısımları günceller.

Node Yönetimi

Bakım Modu (Drain)

Bir node'u bakıma almak istiyorsun — donanım güncellemesi, işletim sistemi yaması... Önce üzerindeki tüm container'ları başka node'lara taşıman gerekir:

# Node'u drain moduna al
docker node update --availability drain worker1

Bu komut, worker1 üzerindeki tüm task'ları diğer node'lara taşır. Bakımı bitirdiğinde:

docker node update --availability active worker1

Node Etiketleri (Labels)

Bazı servislerin belirli node'larda çalışmasını isteyebilirsin — mesela veritabanı sadece SSD disk olan sunucuda çalışsın:

# Node'a etiket ekle
docker node update --label-add disk=ssd worker1
docker node update --label-add disk=hdd worker2

# Servisi sadece SSD node'lara yerleştir
docker service create \
    --name db \
    --constraint 'node.labels.disk == ssd' \
    postgres:16

Yaygın Sorunlar ve Çözümleri

"No suitable node" hatası: Service'in bir constraint'i karşılanamıyor veya node'larda yeterli kaynak yok. docker service inspect ile constraint'leri, docker node ls ile node durumlarını kontrol et.

Container sürekli restart ediyor: docker service logs web --tail 100 ile logları incele. Genellikle uygulama hatası, eksik environment variable veya bağımlı servis henüz hazır değil.

Stack update çalışmıyor: Image tag değişmediyse Swarm güncelleme yapmaz. Tag'ı değiştir veya --resolve-image always kullan.

Docker Swarm vs Docker Compose: Ne Zaman Hangisi?

Kafan karışmasın: Compose geliştirme için, Swarm production için. Ama bazı durumlar:

  • Geliştirme ortamı → Docker Compose

  • Tek sunucu production → Docker Compose (basit uygulamalar için yeterli)

  • 2-10 sunucu, basit mimari → Docker Swarm

  • 10+ sunucu, karmaşık mimari → Kubernetes

Monitoring ve Health Check

Swarm'da servislerin sağlığını izlemek çok önemli. Health check tanımlayarak, Swarm'ın sağlıksız container'ları otomatik yeniden başlatmasını sağlarsın:

services:
  api:
    image: myapp:v1.0
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s       # Her 30 saniyede kontrol
      timeout: 5s         # 5 saniye cevap vermezse unhealthy
      retries: 3          # 3 kez başarısız olursa unhealthy
      start_period: 40s   # İlk 40 saniye kontrol etme

start_period özellikle önemli — uygulamanın başlaması zaman alabilir (veritabanı bağlantısı, cache ısınması vs). Bu süre dolmadan health check başarısız olsa bile container yeniden başlatılmaz.

Health check'i CLI'dan da izleyebilirsin:

docker service ps web --format "table {{.Name}}\t{{.CurrentState}}\t{{.Error}}"

Gerçek Zamanlı Event İzleme

Swarm'da neler olduğunu canlı olarak izlemek istersen:

# Service event'leri
docker events --filter type=service

# Container event'leri (ölüm, restart vs)
docker events --filter type=container --filter event=die

# Son 1 saatin event'leri
docker events --since 1h --filter type=service

Bu komutlar özellikle sorun giderme sırasında çok işe yarıyor. Bir container neden çöktü, ne zaman yeniden başlatıldı, hangi node'a taşındı — hepsini görebilirsin.

Private Registry ile Çalışmak

Production'da Docker Hub'dan değil, kendi private registry'nden image çekersin. Swarm'da bunun bir inceliği var — tüm node'ların registry'ye erişimi olmalı:

# Tüm node'larda registry'ye login
docker login registry.example.com

# Service oluştururken --with-registry-auth flag'i
docker service create \
    --name api \
    --with-registry-auth \
    registry.example.com/myapp:v1.0

--with-registry-auth çok önemli — bu flag olmadan, worker node'lar private registry'den image çekemez. Bu flag, manager'ın registry credential'larını worker'lara güvenli bir şekilde iletmesini sağlar.

Stack deploy'da da aynı şekilde:

docker stack deploy --with-registry-auth -c docker-stack.yml myapp

Production-Ready Stack: Tam Örnek

Şimdi öğrendiğimiz her şeyi bir araya getiren production-ready bir stack tanımı yapalım:

# docker-stack.yml — Production Ready
version: "3.8"

services:
  traefik:
    image: traefik:v3.0
    command:
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.exposedByDefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - traefik-public
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
      restart_policy:
        condition: on-failure

  api:
    image: registry.example.com/myapp:v1.0
    environment:
      NODE_ENV: production
      DB_HOST: db
      REDIS_URL: redis://redis:6379
    secrets:
      - db_password
      - jwt_secret
    networks:
      - traefik-public
      - backend
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 15s
        failure_action: rollback
        order: start-first
      rollback_config:
        parallelism: 0
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 128M
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.api.rule=Host(`api.example.com`)"
        - "traefik.http.services.api.loadbalancer.server.port=3000"
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 40s

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - backend
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.db == true
      resources:
        limits:
          cpus: "2.0"
          memory: 1G

  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    networks:
      - backend
    deploy:
      replicas: 1
      resources:
        limits:
          memory: 384M

secrets:
  db_password:
    external: true
  jwt_secret:
    external: true

volumes:
  pgdata:
  redis-data:

networks:
  traefik-public:
    driver: overlay
  backend:
    driver: overlay
    internal: true

Bu stack'te neler var? Traefik reverse proxy, 3 replica API servisi, PostgreSQL veritabanı, Redis cache. Secret'lar güvenli, network'ler izole, resource limit'ler tanımlı, health check'ler aktif, rolling update konfigüre edilmiş. Production'a hazır!

Deploy etmek:

# Önce secret'ları oluştur
echo "db-password-here" | docker secret create db_password -
echo "jwt-secret-here" | docker secret create jwt_secret -

# DB node'unu etiketle
docker node update --label-add db=true worker1

# Stack'i deploy et
docker stack deploy --with-registry-auth -c docker-stack.yml myapp

Güncellemek istediğinde YAML'ı değiştir ve aynı komutu tekrar çalıştır. Swarm sadece değişen kısımları günceller.

Bu Derste Ne Öğrendik?

  • Docker Swarm, Docker Engine'e dahil gelen, kurulumu kolay orchestration aracı

  • Manager node'lar karar alır, worker node'lar container çalıştırır

  • Service istenen durumu tanımlar, Swarm bu durumu sürdürür (self-healing)

  • Rolling update ile sıfır kesinti güncelleme, rollback ile geri alma

  • Overlay network ile farklı sunuculardaki container'lar iletişim kurar

  • Routing mesh ile herhangi bir node'a gelen trafik doğru container'a yönlenir

  • Stack deploy ile tüm uygulamayı tek YAML dosyasıyla yönetirsin

  • Production'da 3-5 manager, resource limits, healthcheck ve private registry kullan

Sonraki derste Kubernetes'e giriş yapacağız — Pod, Service, Deployment kavramlarını öğreneceğiz.