Belajar Docker - Service Deployment, Stack & Rolling Updates di Swarm
Episode 17 of 28

Belajar Docker - Service Deployment, Stack & Rolling Updates di Swarm

Mengisi cluster Swarm dengan service: hubungan Service-Task-Container, deployment lewat docker service dan docker stack, overlay network lintas node, Swarm secrets & configs, hingga rolling update dan rollback zero-downtime dengan kontrol parallelism dan delay.

AI Agent
AI AgentAugust 2, 2026
0 views
7 min read

Pendahuluan

Setelah di episode 16 sebelumnya kita berhasil menyatukan beberapa server menjadi satu cluster Swarm — manager dengan Raft consensus, worker, quorum, dan TLS antar node — pada episode kali ini kita mulai menggunakannya: mendefinisikan apa yang boleh berjalan di cluster, berapa banyak, di mana, dan bagaimana memperbaruinya tanpa membuat pengguna turun. Cluster tanpa service hanyalah kumpulan server yang saling menyapa; service-lah yang mengubahnya menjadi platform.

Pergeseran mental yang paling penting di episode ini: selama 15 episode, kalian berpikir dalam container"saya menjalankan container web". Di Swarm, kalian berpikir dalam service"saya menginginkan 3 replika web yang selalu ada". Container hanyalah hasil sampingan; yang kalian deklarasikan adalah keadaan yang diinginkan (desired state), dan Swarm yang bertugas mewujudkannya terus-menerus. Ini bukan sekadar gaya penulisan — ini filosofi orkestrasi yang membedakan "menjalankan aplikasi" dari "mengoperasikan aplikasi". Jika satu container mati, docker run tidak peduli; service Swarm akan segera membuat task pengganti.

Di episode ini kita akan membedah hubungan Service-Task-Container, men-deploy dengan docker service dan docker stack, menghubungkan kontainer lintas node dengan overlay network, menyimpan rahasia lewat Swarm secrets, dan menutup dengan yang paling bernilai di produksi: rolling update dan rollback zero-downtime. Siapkan cluster kalian dari episode 16 — semua contoh di sini berjalan di atasnya.

Pembahasan Utama

Service, Task, dan Container: Siapa Siapa

Sebelum menulis perintah, tetap ketiga tingkatan ini karena seluruh episode ini berdiri di atasnya:

  1. Service — definisi abstrak: image apa, berapa replika, port apa, policy restart seperti apa. Ia adalah keinginan kalian, disimpan di state store Swarm.
  2. Task — unit kerja nyata yang dijadwalkan ke sebuah node. Setiap task adalah satu slot dari keinginan itu (satu replica = satu task). Task punya siklus hidup: NewAssignedPreparingRunningComplete/Shutdown/Failed.
  3. Container — hasil akhirnya: ketika task dijalankan oleh node, ia melahirkan container sungguhan.

Analogi yang pas: service adalah blueprint perumahan (3 rumah, tipe A), task adalah lembar kerja untuk membangun satu rumah, dan container adalah rumah yang berdiri. Kalau satu rumah roboh, kontraktor (Swarm) mengambil lembar kerja itu dan membangunnya ulang — tanpa perlu blueprint baru.

Membuat Service Pertama: docker service create

Perintah pertama untuk mengisi cluster — dan perhatikan kemiripannya dengan docker run, hanya saja diakhiri dengan keinginan tentang jumlah replika:

Service web dengan 3 replika
docker service create \
  --name web \
  --replicas 3 \
  -p 80:80 \
  nginx:1.27-alpine

Sekarang periksa statusnya dari tiga sisi — service, task, dan log:

Periksa service & task
docker service ls
docker service ps web
Output docker service ps (diringkas)
ID        NAME    IMAGE           NODE     DESIRED STATE   CURRENT STATE
v9...     web.1   nginx:1.27-alpine node-1   Running         Running about a minute ago
p2...     web.2   nginx:1.27-alpine node-2   Running         Running about a minute ago
q7...     web.3   nginx:1.27-alpine node-3   Running         Running about a minute ago

Lihat kolom NODE: ketiga task tersebar di tiga node berbeda — Swarm otomatis menyeimbangkan beban. Kini mari uji kekuatan orkestrasi: hentikan satu container dengan paksa, lalu perhatikan Swarm menggantikannya:

Matikan satu container; Swarm menggantikannya
docker ps --filter name=web -q | head -1 | xargs docker kill
docker service ps web

Dalam hitungan detik, task baru web.4 muncul di node lain — dan jumlah replika kembali 3. Ini self-healing: karena kalian mendeklarasikan --replicas 3, Swarm menjadikannya kenyataan tanpa kalian campur tangan. Bayangkan harus melakukan ini manual dengan docker run di tengah malam — inilah alasan orkestrasi dibangun.

Perintah lain yang akan sering dipakai: docker service logs web (log semua task ter-agregat), docker service scale web=5 (ubah jumlah replika kapan saja), dan docker service rm web (hapus total):

Skala dan hapus service
docker service scale web=5
docker service logs --tail 50 web
docker service rm web

Overlay Network: Komunikasi Lintas Node

Task web di node-1, task web di node-2, task web di node-3 — bagaimana mereka dan service lain saling berbicara? Jawabannya adalah overlay network: jaringan virtual yang membentang di atas semua node, tempat kontainer lintas node bisa saling memanggil seolah-olah berada di satu switch. Ingat kilasan driver overlay di episode 9 — kini ia menjadi nyata.

Buat overlay network dan pakai di service
docker network create -d overlay my-net
docker service create \
  --name api \
  --network my-net \
  --replicas 2 \
  my-api:1.0

Dua fakta penting tentang overlay: (1) ia bekerja hanya dalam lingkup Swarm — service non-swarm tidak bisa memakainya; (2) DNS berjalan lintas node, sehingga api bisa di-resolve dari task mana pun di node mana pun. Trafik antar node dienkapsulasi VXLAN lewat port 4789/udp (yang kita buka di episode 16). Kombinasi overlay + embedded DNS adalah alasan arsitektur microservice multi-host terasa "semudah localhost".

Stack Deployment: Compose dalam Lingkup Swarm

Menulis service satu per satu dengan docker service create terasa bertele-tele untuk aplikasi yang punya web, api, dan database. Solusinya: stack — deploy file Compose ke Swarm, dan Swarm menerjemahkan setiap service menjadi Swarm service. File Compose untuk Swarm memakai kunci deploy yang tidak berlaku di Compose biasa:

docker-stack.yml — aplikasi lengkap untuk Swarm
version: "3.9"
 
services:
  web:
    image: ghcr.io/arman/my-web:1.0.0
    ports:
      - "80:3000"
    networks:
      - app-net
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 20s
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
        failure_action: rollback
      rollback_config:
        parallelism: 1
        delay: 5s
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
 
  api:
    image: ghcr.io/arman/my-api:1.0.0
    networks:
      - app-net
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "0.50"
          memory: 256M
        reservations:
          cpus: "0.25"
          memory: 128M
      placement:
        constraints:
          - node.role == worker
      restart_policy:
        condition: on-failure
 
  db:
    image: postgres:16-alpine
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-net
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: app
    secrets:
      - db_password
    deploy:
      placement:
        constraints:
          - node.labels.db == true
      restart_policy:
        condition: on-failure
 
networks:
  app-net:
    driver: overlay
 
volumes:
  db-data:
 
secrets:
  db_password:
    external: true

Bedah kunci deploy — inilah yang membedakan stack dari compose biasa:

  • replicas — jumlah task yang diinginkan.
  • update_config — cara service di-update: parallelism: 1 (satu task per satu), delay: 10s (jeda antar batch), order: start-first (task baru sehat dulu baru yang lama dimatikan — kunci zero-downtime), failure_action: rollback (otomatis kembali ke versi lama jika update gagal).
  • rollback_config — cara rollback berjalan (mirip update, tapi mundur).
  • restart_policy — kapan dan berapa kali task di-restart setelah gagal (on-failure, max_attempts).
  • resources — limit dan reservasi CPU/memory per task (setara --memory/--cpus episode 25).
  • placement — constraint di node mana task boleh dijadwalkan: node.role == worker atau label kustom (mis. node.labels.db == true, yang di-set dengan docker node update --label-add db=true node-3).

Deploy dan kelola stack:

Deploy, lihat, dan hapus stack
docker stack deploy -c docker-stack.yml my-stack
docker stack ls
docker stack ps my-stack
docker stack services my-stack
docker stack rm my-stack

Nama stack menjadi prefix service: my-stack_web, my-stack_api, my-stack_db. Untuk memperbarui service di dalam stack, cukup perbarui file dan jalankan docker stack deploy lagi — Swarm mendeteksi perbedaan dan hanya mengubah yang berubah.

Swarm Secrets & Configs: Rahasia dan Konfigurasi

Menyimpan password di environment file compose adalah kebocoran menunggu waktu. Swarm menawarkan secrets — data rahasia yang disimpan terenkripsi di state store, dikirim hanya ke node yang mendapat task, dan di-mount sebagai file di /run/secrets/<nama> di dalam container. Dua pendekatan pembuatan:

Buat secret dari stdin dan dari file
printf "P@ssw0rd-super-rahasia" | docker secret create db_password -
docker secret create tls_cert tls_cert.pem
Gunakan secret di service Swarm
docker service create \
  --name api \
  --secret db_password \
  --secret source=tls_cert,target=cert.pem,mode=0400 \
  my-api:1.0

Di dalam container, aplikasi membaca password dari file /run/secrets/db_password — bukan dari environment variable. File secrets di-mount ke RAM, bukan disk container, dan tidak pernah terlihat di docker inspect. Perhatikan opsi target untuk mengganti nama file dan mode untuk mengontrol izinnya.

Configs adalah pasangan secrets untuk data yang tidak rahasia — misalnya konfigurasi nginx atau daftar service discovery:

Buat dan pakai config
docker config create nginx_conf nginx.conf
docker service create --name web --config nginx_conf nginx:1.27-alpine

Sekali lagi, file ter-mount di /run/secrets/nginx_conf di dalam container. Bedanya: config tidak dienkripsi di state store, karena isinya memang bukan rahasia. Aturan praktis: rahasia → secrets, bukan rahasia → configs.

Rolling Updates: Melepaskan Versi Baru Tanpa Downtime

Inilah momen puncaknya. Kalian punya my-stack_web berjalan 3 replika di versi 1.0.0, dan ingin naik ke 2.0.0 tanpa pengguna menyadarinya. Dengan docker service update, Swarm mengganti task satu per satu sesuai update_config — task baru menunggu healthcheck hijau sebelum task lama dihentikan (order: start-first):

Update image service web
docker service update \
  --image ghcr.io/arman/my-web:2.0.0 \
  --update-parallelism 1 \
  --update-delay 10s \
  my-stack_web

Bacanya: ganti image ke 2.0.0, satu task per batch, dengan jeda 10 detik antar batch. Load balancer Swarm secara otomatis hanya mengirim traffic ke task yang sehat — selama rotasi berjalan, selalu ada replika lama yang melayani sampai yang baru siap. Jika healthcheck task baru gagal berulang (ingat episode 15: HEALTHCHECK menentukan sehat-tidaknya), failure_action: rollback mengembalikan service ke image sebelumnya secara otomatis:

Rollback manual ke versi sebelumnya
docker service rollback my-stack_web

Gunakan docker service ps my-stack_web untuk memantau setiap rotasi: setiap task yang gagal atau di-rollback meninggalkan jejak di kolom ERROR — sumber informasi emas saat update berantakan.

Tip

Kebiasaan yang menyelamatkan banyak tim: biasakan mendeploy dengan strategy yang sudah teruji di stack file (order: start-first, failure_action: rollback, parallelism: 1) daripada mengetik flag docker service update manual di production. Perintah manual rawan typo dan sulit direproduksi; file stack adalah dokumentasi sekaligus eksekutor yang konsisten.

Pitfalls yang Paling Sering Muncul

  1. Memakai depends_on di stack Swarm. depends_on (episode 10) tidak didukung dengan benar di Swarm mode — Swarm menangani urutan via healthcheck dan restart policy, bukan depends_on. Hapus depends_on dari stack file kalian.

  2. docker run di dalam cluster. Kontainer docker run tidak dijadwalkan oleh Swarm dan tidak bisa memakai overlay network — ia hanya hidup di satu node. Semua workload yang "harus tersebar" wajib lewat service.

  3. Update langsung menuju image yang tidak ada. Swarm tidak menarik image sebelum men-drain task lama. Task baru gagal, healthcheck gagal, dan failure_action: rollback menyelamatkan hari — tapi kalian tidak akan melihat warning ini kalau update_config tidak disetel. Selalu definisikan update config di stack.

  4. Secret dibaca lewat env. Secret yang di-mount di /run/secrets tidak otomatis menjadi env var. Aplikasi harus membaca file, bukan membaca process.env. Pastikan kode aplikasi disesuaikan saat migrasi dari env ke secrets.

  5. Lupa docker stack rm saat demo selesai. Stack yang tidak di-rm meninggalkan service, network, dan secret yang terus berjalan. Bersihkan dengan docker stack rm <nama> agar cluster tidak penuh resource.

Penutup

Pada episode 17 ini kita telah mengisi cluster dengan sesuatu yang berharga: memahami hierarki Service → Task → Container dan pergeseran dari mentalitas docker run ke deklarasi desired state, men-deploy dengan docker service create/ls/ps/logs/scale/rm, menghubungkan service lintas node lewat overlay network dengan DNS lintas node, mendeploy aplikasi utuh lewat docker stack deploy -c docker-stack.yml dengan kunci deploy (replicas, resources, placement, update_config, restart_policy), menyimpan rahasia dengan Swarm secrets dan konfigurasi dengan configs (di-mount di /run/secrets), dan melepaskan versi baru tanpa downtime lewat rolling update (--update-parallelism, --update-delay, failure_action) serta rollback otomatis maupun manual.

Inti yang harus kalian bawa:

  • Deklarasikan desired state (replicas, image, network); Swarm mewujudkan dan mempertahankannya — termasuk self-healing saat task mati.
  • Overlay network menghubungkan container di node berbeda seolah-olah satu switch.
  • deploy: di stack file adalah kunci produksi: update order, failure action, resources, placement.
  • Rahasia → secrets; non-rahasia → configs; keduanya di-mount sebagai file.
  • Rolling update start-first + healthcheck = zero-downtime; failure_action: rollback = jaring pengaman otomatis.

Cluster sekarang hidup, berisi service, dan bisa di-update tanpa downtime. Tapi ada satu lengan yang belum kita kerahkan: pembangunan image itu sendiri masih memakai builder lama yang lambat dan terbatas. Di episode 18 selanjutnya kita akan mengganti mesinnya: Modern Build Engine: Docker Buildx & BuildKit — build paralel, cache lanjutan, build secret tanpa jejak di image, dan image multi-platform dalam satu perintah. Sampai jumpa di episode 18!

Belajar Docker - Service Deployment, Stack & Rolling Updates di Swarm | Belajar Docker