docker stack deploy, bir Compose dosyasında tanımlı uygulamanın tamamını Swarm kümesine servisler halinde yayınlar. Komut bir yönetici node'da çalıştırılır, örneğin docker stack deploy --compose-file compose.yaml stackdemo; son argüman stack'in adıdır ve oluşturulan her servis, ağ ve volume bu adla öneklenir. Swarm birden çok makineden oluştuğu için imajların bir registry'de bulunması gerekir; stack deploy build anahtarını desteklemez, imajı önceden derleyip registry'ye göndermeniz gerekir.

Bu yazıda Compose dosyasını stack olarak yayınlamayı, registry ihtiyacını, deploy anahtarıyla kopya sayısı, kademeli güncelleme, otomatik geri alma, yeniden başlatma ve yerleşim kuralları tanımlamayı, Swarm secrets ve configs kullanımını ve stack'i güncellemeyi anlatıyoruz. Swarm kümesinin kurulumu Docker Swarm kurulumu yazısında. Tüm Compose örnekleri docker compose config ile doğrulandı.

docker compose up ile docker stack deploy farkı

docker compose up ile docker stack deploy farkı: Hedef, build anahtarı, İmaj kaynağı, deploy anahtarı

İkisi aynı Compose dosya biçimini okur ama farklı hedeflere yayın yapar. docker compose up container'ları tek bir Docker Engine üzerinde çalıştırır. Belgelere göre Engine Swarm modundayken bile Compose Swarm'dan yararlanmaz, her şeyi geçerli node'a yerleştirir ve bu konuda bir uyarı yazdırır. docker stack deploy ise dosyadaki her servisi bir Swarm servisine çevirir; yönetici görevleri işçilere dağıtır, istenen kopya sayısını korur ve güncellemeleri kademeli uygular. Belgeler tüm docker stack ve docker service komutlarının bir yönetici node'dan çalıştırılması gerektiğini hatırlatır.

Konudocker compose updocker stack deploy
HedefTek Docker EngineSwarm kümesi (yönetici üzerinden)
build anahtarıİmajı yerelde derlerDesteklenmez, yok sayılır
İmaj kaynağıYerel imaj kullanılabilirTüm node'ların erişebildiği registry gerekir
deploy anahtarıYalnızca Compose'un desteklediği alanlarKopya, güncelleme, geri alma ve yerleşim kuralları
Secrets ve configsDosyadan bağlanabilirSwarm'ın şifreli Raft deposunda tutulur
AdlandırmaProje adı önekiStack adı öneki

Compose dosyasının kendisi ve tek sunucudaki kullanımı için Docker Compose rehberi yazısına bakabilirsiniz. Compose referansının bazı özellikleri stack deploy'da çalışmaz: referans extends anahtarının ve ortam değişkeninden oluşturulan environment türü secret'ların docker stack deploy ile desteklenmediğini açıkça yazar.

Registry hazırlığı

Bir swarm birden çok Docker Engine'den oluştuğu için imajları hepsine dağıtacak bir registry gerekir. Docker Hub'ı ya da kendi registry'nizi kullanabilirsiniz. Belgeler deneme için swarm üzerinde servis olarak çalışan, sonra silinebilecek geçici bir registry kurar ve durumunu docker service ls ile kontrol eder; REPLICAS sütununda 1/1 görünüyorsa registry çalışıyordur, 0/1 ise büyük olasılıkla imaj hâlâ çekiliyordur:

bash
docker service create --name registry --publish published=5000,target=5000 registry:2
docker service ls
curl http://127.0.0.1:5000/v2/

Özel bir registry kullanıyorsanız işçilerin imajı çekebilmesi için oturum bilgisinin node'lara iletilmesi gerekir. Belgeler bunu docker service create için anlatır: docker login ile giriş yaptıktan sonra --with-registry-auth bayrağı, istemcideki oturum token'ını servisin yayınlandığı node'lara şifreli WAL logları üzerinden iletir. docker stack deploy komutunun yardım metninde de aynı adlı --with-registry-auth bayrağı bulunur. Hub'dan çekilen imajlarda pull limitlerine takılıyorsanız Docker Hub pull limiti yazısına bakabilirsiniz.

Uygulamayı önce Compose ile denemek

Belgelerdeki örnek uygulama, Redis'teki bir sayacı her ziyarette artıran küçük bir Python uygulamasıdır. Compose dosyasında web servisinin imaj adı, geçici registry'nin adresi olan 127.0.0.1:5000 ile etiketlenir; bu etiket imajı swarm'a dağıtmak için önemlidir. build: . satırı yerel derleme içindir:

yaml
services:
  web:
    image: 127.0.0.1:5000/stackdemo
    build: .
    ports:
      - "8000:8000"
  redis:
    image: redis:alpine

Uygulamayı önce docker compose up -d ile tek node'da çalıştırıp curl http://localhost:8000 ile test eder, docker compose down --volumes ile kapatırsınız. Ardından derlenen imajı registry'ye gönderirsiniz; Compose bunu tek komutla yapar:

bash
docker compose up -d
docker compose down --volumes
docker compose push

Kendi CI hattınızda imajı derleyip registry'ye göndermek için GitHub Actions CI/CD rehberi yazısına bakabilirsiniz.

Stack'i yayınlamak, listelemek ve kaldırmak

İmaj registry'de olduğunda stack yayınlanabilir. Belgelerdeki çıktı, build anahtarının yok sayıldığını ve ağ ile servislerin stack adıyla öneklendiğini gösterir:

bash
docker stack deploy --compose-file compose.yaml stackdemo

Ignoring unsupported options: build

Creating network stackdemo_default
Creating service stackdemo_web
Creating service stackdemo_redis

Servislerin durumunu docker stack services stackdemo ile görürsünüz; her iki servis için REPLICAS sütununda 1/1 görünmelidir. Çok node'lu bir swarm'da imajların çekilmesi zaman alabilir. Routing mesh sayesinde uygulamaya kümedeki herhangi bir node'un 8000 portundan ulaşılır. İşiniz bitince stack'i ve geçici registry'yi kaldırırsınız:

bash
docker stack services stackdemo
docker stack rm stackdemo
docker service rm registry

docker stack komutunun alt komutları yerel CLI yardımında şöyle listelenir: config, deploy, ls, ps, rm ve services. deploy için up takma adı da vardır. Derste kullanılan --compose-file bayrağının kısa biçimi -c'dir.

Üretim için bir stack dosyası

Üretim için bir stack dosyası: mode ve replicas, update_config, rollback_config, restart_policy

Gerçek bir uygulamada deploy anahtarı stack dosyasının en önemli kısmıdır. Aşağıdaki dosyada üç kopyalı bir web servisi ve tek kopyalı bir MySQL servisi var. Web servisi kademeli güncellenir, güncelleme başarısız olursa geri alınır; veritabanı yalnızca SSD etiketli node'a yerleşir; parolalar Swarm secret'larından, web yapılandırması bir config'den gelir. Ağ, node'lar arasında çalışan bir overlay ağıdır:

yaml
services:
  web:
    image: registry.example.com/shop/web:1.4.2
    ports:
      - target: 80
        published: 8080
        protocol: tcp
        mode: ingress
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    configs:
      - source: web_conf
        target: /etc/nginx/conf.d/default.conf
    networks:
      - backend
    deploy:
      mode: replicated
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        monitor: 30s
        order: start-first
      rollback_config:
        parallelism: 1
        failure_action: pause
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      resources:
        limits:
          cpus: "0.50"
          memory: 256M
        reservations:
          cpus: "0.25"
          memory: 128M

  db:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      MYSQL_DATABASE: shop
      MYSQL_USER: shop
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_root_password
      - db_password
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - backend
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.disk==ssd

networks:
  backend:
    driver: overlay

volumes:
  db_data:

secrets:
  db_password:
    external: true
  db_root_password:
    external: true

configs:
  web_conf:
    file: ./default.conf

Dosyadaki her anahtarın Compose Deploy Specification'daki karşılığı şöyledir:

  • mode ve replicas: replicated varsayılandır ve replicas aynı anda çalışacak container sayısını belirtir. global her fiziksel node'da tam bir görev çalıştırır.
  • update_config: parallelism aynı anda güncellenecek container sayısı, delay gruplar arası bekleme, failure_action güncelleme başarısız olursa continue, rollback veya pause (varsayılan pause), monitor her görevden sonra hatanın izlendiği süre, order ise stop-first (varsayılan) ya da yeni görevin önce başlatılıp kısa süre eskisiyle birlikte çalıştığı start-first.
  • rollback_config: Geri almanın aynı ayarlarını taşır; failure_action burada continue veya pause olabilir.
  • restart_policy: condition için none, on-failure veya varsayılan any; delay denemeler arası bekleme, max_attempts vazgeçmeden önceki başarısız deneme sayısı, window bir yeniden başlatmanın başarılı sayılması için beklenen süre.
  • placement.constraints: Node'un sağlaması gereken koşul. node.labels.disk==ssd yalnızca bu etiketi taşıyan node'ları seçer; uygun node yoksa görevler Pending durumunda bekler.
  • resources: limits üst sınır, reservations garanti edilen miktardır. Bu alanların ayrıntıları Compose deploy ve kaynak limitleri yazısında.

restart_policy için belgelerdeki bir ayrıntı sık yanlış anlaşılır: bir deneme ancak container window süresi içinde başarıyla yeniden başlamazsa max_attempts sayısına eklenir. Yani max_attempts: 3 en fazla üç kez dene anlamına gelmez; üç başarısız deneme birikene kadar denemeye devam eder. Node etiketini yönetici üzerinde docker node update --label-add disk=ssd node-3 gibi bir komutla eklersiniz.

Kademeli güncelleme ve geri alma nasıl işler?

Swarm bir güncellemede varsayılan olarak her seferinde bir görevi değiştirir. Güncellenen görev RUNNING durumuna geçince sıradakine geçilir; herhangi bir anda bir görev FAILED dönerse planlayıcı güncellemeyi duraklatır. Hangi durumda ne olacağını failure_action belirler. CLI tarafında aynı ayarlar --update-delay, --update-parallelism, --update-failure-action, --update-max-failure-ratio ve --update-monitor bayraklarıdır. CLI'de --update-monitor varsayılanı 30 saniyedir: görev başladıktan sonraki ilk 30 saniyede çökerse güncelleme hatası sayılır, sonrasında çökerse sayılmaz.

Güncellenen sürüm beklendiği gibi çalışmazsa servisi elle de geri alabilirsiniz. docker service update --rollback servisi son güncellemeden önceki yapılandırmaya döndürür; --update-delay 0s gibi seçeneklerle birleştirilebilir. Otomatik geri alma için CLI bayraklarının varsayılanları belgelerde şöyle verilir:

BayrakVarsayılanAnlamı
--rollback-delay0sBir görevi geri aldıktan sonra sıradakine geçmeden önceki bekleme
--rollback-failure-actionpauseGeri alma başarısız olursa duraklat veya devam et
--rollback-max-failure-ratio0Geri alma sırasında tolere edilen hata oranı (0 ile 1 arası)
--rollback-monitor5sHer geri almadan sonra hatanın izlendiği süre
--rollback-parallelism1Aynı anda geri alınan görev sayısı, 0 hepsi birden

Stack ile çalışırken bu ayarları komut satırı yerine dosyadaki update_config ve rollback_config bölümlerine yazarsınız. Böylece her docker stack deploy çalıştırmasında aynı politika uygulanır.

Secrets ve configs ile çalışmak

Swarm secret'ları parola, TLS anahtarı veya SSH anahtarı gibi verileri imaja ya da kaynak koda koymadan servislere vermenin yoludur. Bir secret eklendiğinde karşılıklı TLS bağlantısıyla yöneticiye gönderilir ve şifreli Raft logunda saklanır. Erişim verilen servisin container'ında şifresi çözülmüş secret bellekteki bir dosya sistemine bağlanır; Linux container'larında varsayılan yol /run/secrets/<secret_name> şeklindedir. Görev durduğunda secret bağlantısı kaldırılır ve node belleğinden silinir. Secret'lar yalnızca Swarm servislerine açıktır, tek başına çalışan container'lar kullanamaz.

Yukarıdaki stack dosyasında secret'lar external: true olarak tanımlı, yani stack'ten önce oluşturulmuş olmalıdır. Belgelerdeki yöntem değeri standart girdiden okumaktır:

bash
printf "my-secret" | docker secret create db_password -
openssl rand -base64 20 | docker secret create db_root_password -

Docker secret'ları ortam değişkeni oluşturmaz; belgeler bunun bilinçli bir karar olduğunu, çünkü ortam değişkenlerinin container'lar arasında istemeden sızabildiğini anlatır. Bu yüzden resmî imajların birçoğu MYSQL_PASSWORD_FILE veya WORDPRESS_DB_PASSWORD_FILE gibi, değeri dosyadan okuyan değişken biçimlerini destekler. Örnekteki _FILE değişkenleri bu yüzden /run/secrets altındaki dosyaları gösterir. Kendi imajınızda da her gizli parametrenin bir dosyadan okunabilmesini sağlamanız belgelerin önerisidir.

Configs, gizli olmayan yapılandırma dosyaları içindir. Secret'lara benzer çalışırlar, ancak diskte şifrelenmez ve RAM diski kullanmadan doğrudan container'ın dosya sistemine bağlanırlar. Secret ve config değerleri en fazla 500 KB olabilir. Config'ler değiştirilemez (immutable): mevcut bir servisin config dosyasını değiştiremezsiniz, yeni adla yeni bir config oluşturursunuz. Belgeler bu yüzden secret ve config adlarına sürüm numarası veya tarih eklemeyi önerir; örneğin web_conf_v2. Çalışan bir servisin kullandığı secret veya config silinemez. docker stack rm ise o stack adıyla docker stack deploy tarafından oluşturulmuş tüm config'leri kaldırır. Compose tarafında secrets ve configs tanımlamanın ayrıntıları Compose secrets ve configs yazısında.

Stack'i güncellemek

Bir stack'i güncellemek için Compose dosyasını değiştirip docker stack deploy komutunu aynı stack adıyla yeniden çalıştırırsınız; configs belgesi bu yöntemi docker stack deploy -c <new-compose-file> <stack-name> biçiminde gösterir ve dosyada yeni bir config kullanırsanız servislerin onu kullanmaya başlayacağını yazar. Değişen servisler update_config politikasına göre kademeli güncellenir. Yeni bir imaj sürümü için dosyadaki etiketi değiştirmek yeterlidir. Belgeler servisin imajının, ölçekleme veya ağ ekleme gibi diğer güncellemelerle değişmediğini, yalnızca açık bir imaj güncellemesiyle değiştiğini vurgular.

Bir secret'ı döndürmek (rotate) için belgelerdeki sıra şöyledir: yeni değerle mysql_password_v2 gibi yeni bir secret oluşturun, servise hem eskisine hem yenisine erişim verin, uygulama tarafında parolayı değiştirin, servisi yeni secret'a geçirin ve son olarak eski secret'ı kaldırın. Belgeler bir secret'ı güncelleyemeyeceğinizi veya yeniden adlandıramayacağınızı, ancak erişimi kaldırıp yeni bir hedef dosya adıyla verebileceğinizi belirtir. Veritabanı parolası gibi değerlerde Docker dışında ek adımlar gerekebilir.

bash
docker service update \
     --secret-rm mysql_password \
     --secret-add source=mysql_password_v2,target=wp_db_password \
     wordpress

Küresel servisler, host modu portlar ve tek seferlik işler

Her node'da bir kopya çalışması gereken izleme ajanları gibi servisler için mode: global kullanılır; yerleşim kısıtıyla birleştirilirse yalnızca koşulu sağlayan node'larda çalışır. Belgelere göre yerleşim tercihleri (placement preferences) global servislerde yok sayılır. Routing mesh'i atlamak için port tanımında mode: host kullanılır; bu durumda port yalnızca görevin çalıştığı node'da açılır ve her node'da tek görev olması gerekir, bu yüzden global servisle birlikte kullanılması mantıklıdır. Deploy specification ayrıca tamamlanıp 0 koduyla çıkan işler için replicated-job ve global-job modlarını tanımlar; tamamlanan görevler siz kaldırana kadar kalır:

yaml
services:
  node-agent:
    image: example/agent:2.1
    deploy:
      mode: global
      placement:
        constraints:
          - node.labels.monitoring==true

  dns-cache:
    image: example/dns-cache:1.0
    ports:
      - target: 53
        published: 53
        protocol: udp
        mode: host
    deploy:
      mode: global

  migrate:
    image: registry.example.com/shop/migrate:1.4.2
    deploy:
      mode: replicated-job
      replicas: 1

Deploy specification, job modlarında eşzamanlılığı sınırlayan max-concurrent seçeneğinin yalnızca CLI ile kullanılabildiğini ve Compose'da bulunmadığını da not eder.

Sık yapılan hatalar

Sık yapılan hatalar: build ile stack yayınlamak, Komutu işçi node'da çalıştırmak, external secret'ı oluşturmadan yayınlamak, Config içeriğini yerinde değiştirmeye çalışmak
  • build ile stack yayınlamak: build yok sayılır; imaj registry'de yoksa görevler başlamaz. Önce derleyip docker compose push ile gönderin.
  • Komutu işçi node'da çalıştırmak: docker stack ve docker service komutları yönetici gerektirir.
  • external secret'ı oluşturmadan yayınlamak: external: true tanımlı secret'lar stack'ten önce docker secret create ile oluşturulmalıdır.
  • Config içeriğini yerinde değiştirmeye çalışmak: Config'ler değiştirilemez; yeni adla yeni config oluşturup dosyayı ona yönlendirin.
  • latest etiketiyle yayın: Sık değişen etiketler kopyaların farklı sürümler çalıştırmasına yol açabilir; sabit sürüm etiketi kullanın.
  • Karşılanamayan yerleşim kuralı: Hiçbir node koşulu sağlamıyorsa görevler Pending durumunda bekler; node etiketlerini kontrol edin.

Sık Sorulan Sorular

Compose dosyamda version anahtarı olmalı mı?

Hayır. Güncel Compose dosya biçimi Compose Specification'dır ve 2.x ile 3.x sürümlerini birleştirir; eski sürümlü dosya başvuruları artık etkin olarak bakımı yapılmayan arşive taşınmıştır. Bu yazıdaki dosyalar version anahtarı olmadan yazıldı ve doğrulandı.

Stack'teki servislere nasıl isimle erişirim?

Swarm'ın iç DNS bileşeni her servise bir kayıt atar ve aynı overlay ağındaki servisler birbirine servis adıyla ulaşır. Belgelerdeki örnekte Python uygulaması Redis'e redis adıyla bağlanır. Overlay ve diğer ağ sürücüleri için Docker network türleri yazısına bakabilirsiniz.

Stack'i kaldırınca volume'lar da silinir mi?

Belgelerdeki docker stack rm çıktısı servislerin ve ağın kaldırıldığını gösterir. Config'ler için belgeler, stack adıyla oluşturulan tüm config'lerin kaldırıldığını açıkça yazar. Volume'lar için kaldırma çıktısında bir satır yoktur; veritabanı verisi gibi kalıcı veriyi koruma ve yedekleme konusu için Docker volume yedekleme yazısına bakabilirsiniz.

Image imzası doğrulamasını stack ile kullanabilir miyim?

Swarm belgeleri Docker Content Trust açıkken istemcinin etiketi digest'e kendisinin çözdüğünü anlatır. Ancak Engine 29.0.0 sürüm notlarına göre Docker Content Trust Docker CLI'den kaldırıldı ve ayrı bir eklenti olarak derlenebiliyor. 29 sürümünü kullanıyorsanız bu davranışa güvenmeden önce sürüm notlarını okuyun.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Docker öğrenme rehberi · Docker Swarm kurulumu · Compose deploy ve kaynak limitleri · Compose secrets ve configs · Docker Compose rehberi · GitHub Actions ile imaj derleme