> ## Content Index
> Fetch the complete content index at: https://itsamir.ir/llms.txt
> Use this file to discover other available public pages before exploring further.

# آموزش Docker Swarm روی سه سرور Ubuntu؛ از ساخت کلاستر تا تست HA
- URL: https://itsamir.ir/docker-swarm-ubuntu-homelab-ha/
- Published: 2026-09-18T13:23:28.000Z
- Updated: 2026-09-18T13:30:02.000Z
- Description: تجربهٔ ساخت یک کلاستر سه‌نودی Docker Swarm روی Ubuntu و VMware؛ از شبکه و اجرای اولین سرویس تا Failover، Rolling Update و مدیریت سرویس‌ها.
- Author: ItsAmir
- Tags: DevOps, Docker, Ubuntu, HomeLab

مدتی بود می‌خواستم Docker Swarm را نه فقط در حد چند دستور پراکنده، بلکه در یک سناریوی نزدیک به محیط واقعی یاد بگیرم. برای همین روی VMware سه سرور Ubuntu ساختم و تصمیم گرفتم تمام مراحل راه‌اندازی Docker Swarm، اجرای سرویس، Scale، Failover، Rolling Update و مدیریت سرویس‌ها را روی همین Homelab آزمایش کنم.

قبلاً در مطلب [راه‌اندازی ایمیل‌سرور با Stalwart و Docker](https://itsamir.ir/stalwart-mail-server-docker-vps/) از Docker برای اجرای یک سرویس مستقل استفاده کرده بودم. این بار هدفم این بود که یک مرحله جلوتر بروم و چند Docker Engine را به یک کلاستر تبدیل کنم.

سناریوی من شامل این سه سرور است:

| Hostname       | IP             | نقش اولیه |
| -------------- | -------------- | --------- |
| swarm-manager  | 192.168.79.132 | Manager   |
| swarm-worker-1 | 192.168.79.133 | Worker    |
| swarm-worker-2 | 192.168.79.134 | Worker    |

در این راهنما می‌خواهم موارد زیر را آزمایش کنم:

- ساخت کلاستر Docker Swarm
- اضافه کردن Workerها
- ساخت Overlay Network
- اجرای یک سرویس Replicated
- تست Routing Mesh
- افزایش و کاهش Replicaها
- Stop، Start، Restart و Remove کردن سرویس‌ها
- خارج کردن یک Node از مدار
- تست Self-healing و Failover
- Rolling Update و Rollback
- Deploy کردن Stack
- بررسی محدودیت‌های HA و سرویس‌های Stateful

## Docker Swarm چیست و چرا سراغش رفتم؟

Docker Swarm ابزار Orchestration داخلی Docker است. با Swarm می‌توانم چند سرور Docker را به یک کلاستر تبدیل کنم و سرویس‌ها را به‌جای اجرای مستقیم روی یک سرور، در سطح کلاستر مدیریت کنم.

برای این Homelab انتخاب مناسبی بود چون:

- راه‌اندازی ساده‌تری نسبت به Kubernetes دارد.
- از همان Docker CLI استفاده می‌کند.
- برای یادگیری مفاهیمی مثل Service، Replica، Rolling Update و Overlay Network مناسب است.
- برای پروژه‌های کوچک و متوسط پیچیدگی زیادی ایجاد نمی‌کند.
- Routing Mesh و Service Discovery داخلی دارد.

البته Swarm قرار نیست در تمام سناریوها جای Kubernetes را بگیرد. هدف من این بود که ابتدا مفاهیم Orchestration و High Availability را در یک محیط کوچک و قابل‌کنترل یاد بگیرم.

## آماده‌سازی شبکه و Hostnameها

IP سرورها باید ثابت باشند؛ مخصوصاً IP سرورهای Manager. اگر IP یک Manager بعد از Restart تغییر کند، ارتباط اعضای کلاستر می‌تواند دچار مشکل شود.

روی هر سرور Hostname مناسب تنظیم کردم:

روی سرور `192.168.79.132`:

```bash
sudo hostnamectl set-hostname swarm-manager

```

روی سرور `192.168.79.133`:

```bash
sudo hostnamectl set-hostname swarm-worker-1

```

روی سرور `192.168.79.134`:

```bash
sudo hostnamectl set-hostname swarm-worker-2

```

سپس روی هر سه سرور این رکوردها را به `/etc/hosts` اضافه کردم:

```text
192.168.79.132 swarm-manager
192.168.79.133 swarm-worker-1
192.168.79.134 swarm-worker-2

```

قبل از ساخت Swarm باید ارتباط شبکه را بررسی کرد:

```bash
ping -c 3 192.168.79.132
ping -c 3 192.168.79.133
ping -c 3 192.168.79.134

```

و وضعیت Docker را روی هر سه سرور دید:

```bash
sudo systemctl is-active docker
sudo docker version

```

## پورت‌های موردنیاز Docker Swarm

برای اینکه Nodeهای Swarm بتوانند با یکدیگر ارتباط برقرار کنند، این پورت‌ها باید بین سرورها باز باشند:

| پورت | پروتکل  | کاربرد                        |
| ---- | ------- | ----------------------------- |
| 2377 | TCP     | ارتباط مدیریتی Swarm          |
| 7946 | TCP/UDP | ارتباط و Discovery بین Nodeها |
| 4789 | UDP     | ترافیک Overlay Network        |
| 8080 | TCP     | سرویس آزمایشی این مقاله       |

اگر UFW فعال است، روی Manager پورت مدیریت را باز می‌کنم:

```bash
sudo ufw allow from 192.168.79.0/24 to any port 2377 proto tcp

```

روی هر سه Node:

```bash
sudo ufw allow from 192.168.79.0/24 to any port 7946 proto tcp
sudo ufw allow from 192.168.79.0/24 to any port 7946 proto udp
sudo ufw allow from 192.168.79.0/24 to any port 4789 proto udp
sudo ufw allow 8080/tcp

```

پورت `4789/udp` نباید بدون دلیل روی اینترنت عمومی باز شود.

## ساخت Manager و راه‌اندازی Docker Swarm

روی سرور `192.168.79.132` کلاستر را ایجاد می‌کنم:

```bash
sudo docker swarm init \
  --advertise-addr 192.168.79.132 \
  --default-addr-pool 10.20.0.0/16 \
  --default-addr-pool-mask-length 24

```

استفاده از Address Pool اختصاصی اختیاری است، اما کمک می‌کند شبکه‌های Overlay با LAN، VMware، VPN یا شبکه‌های دیگر Docker تداخل نداشته باشند.

خروجی دستور یک Join Token برای Workerها نشان می‌دهد. اگر بعداً به آن نیاز داشته باشم، می‌توانم دوباره آن را بگیرم:

```bash
sudo docker swarm join-token worker

```

Join Token یک Secret محسوب می‌شود و نباید داخل Git، مقاله یا لاگ عمومی قرار بگیرد.

## اضافه کردن Workerها به کلاستر

دستور نمایش‌داده‌شده توسط Manager را روی هر دو Worker اجرا می‌کنم:

```bash
sudo docker swarm join \
  --token SWMTKN-1-REPLACE_WITH_REAL_TOKEN \
  192.168.79.132:2377

```

سپس روی Manager وضعیت Nodeها را بررسی می‌کنم:

```bash
sudo docker node ls

```

باید هر سه Node را در وضعیت `Ready` ببینم:

```text
swarm-manager    Ready    Active    Leader
swarm-worker-1   Ready    Active
swarm-worker-2   Ready    Active

```

دستورات مدیریتی مثل `docker node ls` و `docker service create` باید روی Manager اجرا شوند.

## ساخت Overlay Network

برای ارتباط داخلی سرویس‌ها یک Overlay Network می‌سازم:

```bash
sudo docker network create \
  --driver overlay \
  --attachable \
  app-network

```

و آن را بررسی می‌کنم:

```bash
sudo docker network inspect app-network

```

Overlay Network اجازه می‌دهد Containerهایی که روی Nodeهای مختلف اجرا شده‌اند، از طریق شبکه مجازی مشترک با یکدیگر ارتباط داشته باشند.

## اجرای اولین سرویس روی Docker Swarm

برای آزمایش یک سرویس ساده Whoami با سه Replica ایجاد می‌کنم:

```bash
sudo docker service create \
  --name whoami \
  --network app-network \
  --replicas 3 \
  --publish published=8080,target=80,mode=ingress \
  --update-parallelism 1 \
  --update-delay 5s \
  --restart-condition any \
  traefik/whoami:v1.10.3

```

وضعیت سرویس:

```bash
sudo docker service ls

```

محل اجرای Taskها:

```bash
sudo docker service ps whoami

```

جزئیات کامل سرویس:

```bash
sudo docker service inspect --pretty whoami

```

Swarm تلاش می‌کند Taskها را بین Nodeها توزیع کند، اما وجود سه Replica الزاماً به معنی قرار گرفتن دقیقاً یک Replica روی هر Node نیست. تصمیم نهایی بر اساس وضعیت Nodeها، منابع و Placement Constraintها گرفته می‌شود.

## تست Routing Mesh

به‌دلیل استفاده از `mode=ingress`، پورت `8080` روی Routing Mesh منتشر شده است. بنابراین می‌توانم از طریق IP هرکدام از Nodeها به سرویس دسترسی داشته باشم؛ حتی اگر روی همان Node هیچ Taskای از سرویس اجرا نشده باشد.

```bash
curl http://192.168.79.132:8080
curl http://192.168.79.133:8080
curl http://192.168.79.134:8080

```

از ویندوز نیز می‌توانم اجرا کنم:

```powershell
curl.exe http://192.168.79.132:8080
curl.exe http://192.168.79.133:8080
curl.exe http://192.168.79.134:8080

```

برای مشاهده Load Balancing چند درخواست پشت سر هم می‌فرستم:

```powershell
1..10 | ForEach-Object {
    curl.exe -s http://192.168.79.132:8080 |
        Select-String "Hostname"
    Start-Sleep -Seconds 1
}

```

با تغییر مقدار `Hostname` می‌توان دید که درخواست‌ها به Taskهای مختلف هدایت می‌شوند.

## عیب‌یابی عدم دسترسی از ویندوز

اگر مرورگر یا `curl` نتوانست به پورت `8080` متصل شود، ابتدا از PowerShell بررسی می‌کنم:

```powershell
Test-NetConnection 192.168.79.132 -Port 8080
Test-NetConnection 192.168.79.133 -Port 8080
Test-NetConnection 192.168.79.134 -Port 8080

```

روی Manager نیز موارد زیر را بررسی می‌کنم:

```bash
sudo docker node ls
sudo docker service ls
sudo docker service ps whoami --no-trunc
sudo docker service inspect whoami --pretty
sudo ufw status verbose

```

در VMware باید Network Adapter مربوط به VMها روی VMnet صحیح باشد و گزینه‌های `Connected` و `Connect at power on` فعال باشند.

اگر VPN روشن باشد، ممکن است Route شبکه `192.168.79.0/24` را تغییر دهد. در این حالت به‌جای حذف کامل Network Adapter، ابتدا VPN را Disconnect می‌کنم و Routeها را می‌بینم:

```powershell
Get-NetRoute -DestinationPrefix 192.168.79.0/24

```

برای SSH نیز اول خود پورت را تست می‌کنم:

```powershell
Test-NetConnection 192.168.79.132 -Port 22

```

و سپس:

```powershell
ssh amir@192.168.79.132

```

## افزایش و کاهش تعداد Replicaها

برای افزایش Replicaها:

```bash
sudo docker service scale whoami=6

```

برای کاهش آن‌ها:

```bash
sudo docker service scale whoami=2

```

سپس نتیجه را می‌بینم:

```bash
sudo docker service ps whoami

```

این همان Declarative State است: من وضعیت مطلوب را مشخص می‌کنم و Swarm تلاش می‌کند وضعیت واقعی را با آن هماهنگ کند.

## Stop و Start کردن سرویس

Docker Swarm دستور مستقیمی با نام `docker service stop` ندارد. برای متوقف کردن سرویس بدون حذف Definition آن، تعداد Replicaها را صفر می‌کنم:

```bash
sudo docker service scale whoami=0

```

برای اجرای مجدد:

```bash
sudo docker service scale whoami=3

```

این روش برای Maintenance موقت مناسب است، چون تنظیمات سرویس حذف نمی‌شوند.

## Restart کردن سرویس

برای Restart کردن تمام Taskهای یک سرویس:

```bash
sudo docker service update --force whoami

```

برای اینکه Restart به‌صورت تدریجی انجام شود:

```bash
sudo docker service update \
  --force \
  --update-parallelism 1 \
  --update-delay 5s \
  whoami

```

این دستور Taskها را مرحله‌به‌مرحله جایگزین می‌کند و از Restart هم‌زمان تمام Replicaها جلوگیری می‌کند.

## حذف کامل سرویس

برای حذف Definition سرویس و تمام Taskهای آن:

```bash
sudo docker service rm whoami

```

تفاوت این دستور با `scale=0` مهم است:

- `scale=0` سرویس را نگه می‌دارد و فقط Taskهای درحال اجرا را متوقف می‌کند.
- `service rm` سرویس را به‌طور کامل از Swarm حذف می‌کند.

## تست Self-healing

ابتدا Taskها را پیدا می‌کنم:

```bash
sudo docker service ps whoami

```

روی Node میزبان یکی از Containerها:

```bash
sudo docker ps
sudo docker kill CONTAINER_ID

```

بعد از چند ثانیه:

```bash
sudo docker service ps whoami

```

Swarm متوجه می‌شود یکی از Taskها از بین رفته و Task جدیدی ایجاد می‌کند. این رفتار یکی از مهم‌ترین تفاوت‌های Service با Container مستقل است.

## تست Failover یک Worker

برای شبیه‌سازی خرابی Worker، روی یکی از Workerها Docker را متوقف می‌کنم:

```bash
sudo systemctl stop docker

```

روی Manager وضعیت را بررسی می‌کنم:

```bash
sudo docker node ls
sudo docker service ps whoami

```

اگر ظرفیت کافی وجود داشته باشد، Swarm Taskهای از دست‌رفته را روی Nodeهای سالم دوباره ایجاد می‌کند.

برای برگرداندن Worker:

```bash
sudo systemctl start docker

```

باید توجه داشت که Failover سرویس با Failover داده متفاوت است. Swarm می‌تواند Container را دوباره اجرا کند، اما Docker Volume محلی را به‌صورت خودکار روی Node دیگر Replicate نمی‌کند.

## خارج کردن امن Node از مدار

قبل از Maintenance بهتر است Node را Drain کنم:

```bash
sudo docker node update \
  --availability drain \
  swarm-worker-1

```

Swarm Taskهای آن Node را به Nodeهای دیگر منتقل می‌کند.

بعد از پایان Maintenance:

```bash
sudo docker node update \
  --availability active \
  swarm-worker-1

```

این روش از خاموش کردن ناگهانی Node تمیزتر است.

## Rolling Update و Rollback

برای انتشار نسخه جدید یک Image:

```bash
sudo docker service update \
  --image registry.example.com/myapp:v2 \
  --update-order start-first \
  --update-parallelism 1 \
  --update-delay 10s \
  --update-failure-action rollback \
  whoami

```

وضعیت Update:

```bash
sudo docker service ps whoami
sudo docker service inspect --pretty whoami

```

اگر نسخه جدید مشکل داشته باشد:

```bash
sudo docker service rollback whoami

```

در محیط واقعی بهتر است Imageها با Tag ثابت یا Digest منتشر شوند و از `latest` استفاده نشود.

## Deploy کردن سرویس با Docker Stack

مدیریت سرویس‌ها با فایل YAML برای من قابل‌تکرارتر از مجموعه‌ای از دستورات طولانی است.

فایل `stack.yml`:

```yaml
services:
  whoami:
    image: traefik/whoami:v1.10.3
    networks:
      - frontend
    ports:
      - target: 80
        published: 8080
        protocol: tcp
        mode: ingress
    deploy:
      replicas: 3
      restart_policy:
        condition: any
        delay: 5s
      update_config:
        parallelism: 1
        delay: 5s
        order: start-first
        failure_action: rollback
      rollback_config:
        parallelism: 1
        delay: 5s
        order: stop-first

networks:
  frontend:
    driver: overlay

```

Deploy کردن Stack:

```bash
sudo docker stack deploy \
  --compose-file stack.yml \
  lab

```

بررسی Stack:

```bash
sudo docker stack ls
sudo docker stack services lab
sudo docker stack ps lab

```

نام واقعی سرویس به‌شکل `lab_whoami` خواهد بود.

### Stop کردن سرویس Stack

```bash
sudo docker service scale lab_whoami=0

```

### Start کردن مجدد

```bash
sudo docker service scale lab_whoami=3

```

### Restart کردن

```bash
sudo docker service update --force lab_whoami

```

### حذف Stack

```bash
sudo docker stack rm lab

```

حذف Stack الزاماً داده‌های Volumeهای محلی یا خارجی را حذف نمی‌کند؛ مدیریت و Backup داده باید جداگانه انجام شود.

## استفاده از Secret به‌جای Password داخل YAML

قرار دادن Password داخل Compose File یا Environment Variable عمومی روش مناسبی نیست. Swarm برای این کار Secret دارد:

```bash
printf '%s' 'change-this-password' |
  sudo docker secret create db_password -

```

بررسی Secretها:

```bash
sudo docker secret ls

```

سپس می‌توان Secret را در Stack تعریف و داخل Container از مسیر `/run/secrets/db_password` خواند.

## آیا یک Manager واقعاً HA است؟

خیر. معماری اولیه من شامل یک Manager و دو Worker است. در این حالت Replicaهای درحال اجرا ممکن است بعد از خرابی Manager همچنان کار کنند، اما Control Plane در دسترس نخواهد بود و Swarm نمی‌تواند Task جدید ایجاد، جابه‌جا یا Update کند.

برای HA واقعی Managerها به حداقل سه Manager نیاز دارم. سه Manager می‌توانند خرابی یک Manager را تحمل کنند.

در همین Homelab می‌توانم دو Worker را Promote کنم:

```bash
sudo docker node promote \
  swarm-worker-1 \
  swarm-worker-2

```

بعد از آن:

```bash
sudo docker node ls

```

هر سه Node به Manager تبدیل می‌شوند. Managerهای Swarm به‌صورت پیش‌فرض می‌توانند Workload هم اجرا کنند، مگر اینکه Availability آن‌ها را تغییر بدهم.

قاعده کلی این است:

| تعداد Manager | Quorum | تحمل خرابی |
| ------------- | ------ | ---------- |
| 1             | 1      | 0          |
| 3             | 2      | 1          |
| 5             | 3      | 2          |

تعداد فرد Managerها انتخاب مناسب‌تری است. دو Manager مزیت تحمل خرابی ایجاد نمی‌کنند، چون برای Quorum همچنان به هر دو نیاز است.

## قرار دادن Caddy جلوی Swarm

Routing Mesh باعث می‌شود یک Published Port از طریق تمام Nodeها قابل‌دسترسی باشد. بنابراین Caddy می‌تواند درخواست‌ها را به هر سه Node بفرستد:

```caddyfile
swarm.example.local {
    reverse_proxy \
        192.168.79.132:8080 \
        192.168.79.133:8080 \
        192.168.79.134:8080 {
        lb_policy round_robin
        health_uri /
        health_interval 10s
    }
}

```

اگر Caddy فقط روی یک سرور اجرا شود، خود Caddy به Single Point of Failure تبدیل می‌شود. برای HA کامل ورودی باید حداقل دو Reverse Proxy، یک Load Balancer یا یک Virtual IP در نظر گرفت.

## محدودیت سرویس‌های Stateful

Swarm برای سرویس‌های Stateless مثل API، Frontend و Workerهای پردازشی ساده‌تر است. اما سرویس‌هایی مثل Elasticsearch، Redis، PostgreSQL، Prometheus و Nexus فقط با زیاد کردن Replicaها HA نمی‌شوند.

برای سرویس Stateful باید موارد زیر جداگانه طراحی شوند:

- Replication در سطح خود نرم‌افزار
- Storage مشترک یا Distributed Storage
- Backup و Restore
- Placement Constraint
- Health Check
- Quorum
- سیاست Disaster Recovery

Swarm Container را مدیریت می‌کند، اما به‌صورت خودکار داده یک Local Volume را بین Nodeها کپی نمی‌کند.

## پاک‌سازی کامل محیط آزمایش

ابتدا سرویس یا Stack را حذف می‌کنم:

```bash
sudo docker service rm whoami

```

یا:

```bash
sudo docker stack rm lab

```

سپس روی Workerها:

```bash
sudo docker swarm leave

```

در پایان، فقط اگر قصد نابود کردن کامل کلاستر را دارم، روی آخرین Manager اجرا می‌کنم:

```bash
sudo docker swarm leave --force

```

استفاده از `--force` روی Manager یک عملیات مخرب است و نباید برای عیب‌یابی معمولی استفاده شود.

## جمع‌بندی

هدف من از ساخت این Homelab فقط بالا آوردن سه VM نبود. می‌خواستم ببینم وقتی یک Container از بین می‌رود، یک Worker خاموش می‌شود یا نسخه جدید Application مشکل دارد، Orchestrator دقیقاً چه واکنشی نشان می‌دهد.

در این سناریو توانستم موارد زیر را عملاً آزمایش کنم:

- ساخت یک Docker Swarm سه‌نودی
- اجرای سرویس روی Overlay Network
- دسترسی از طریق Routing Mesh
- Scale کردن Replicaها
- Stop، Start، Restart و Remove کردن سرویس‌ها
- Self-healing و Failover
- Drain کردن Node
- Rolling Update و Rollback
- مدیریت سرویس‌ها با Docker Stack
- تفاوت Service Availability با Manager HA
- محدودیت سرویس‌های Stateful

برای یک Homelab یا پروژه کوچک، Docker Swarm نقطه خوبی بین Docker Compose و Kubernetes است: آن‌قدر ساده است که بتوانم تمام اجزایش را بفهمم و آن‌قدر قابلیت دارد که مفاهیم واقعی Orchestration را تجربه کنم.

## سوالات متداول درباره Docker Swarm

### Docker Swarm چیست؟

Docker Swarm ابزار Orchestration داخلی Docker برای مدیریت چند Docker Engine به‌عنوان یک کلاستر است. سرویس‌ها می‌توانند چند Replica داشته باشند و Swarm وضعیت مطلوب آن‌ها را حفظ می‌کند.

### آیا یک Manager برای High Availability کافی است؟

خیر. یک Manager برای یادگیری و محیط آزمایش مناسب است، اما خرابی آن Control Plane را از دسترس خارج می‌کند. برای تحمل خرابی یک Manager باید سه Manager داشته باشیم.

### چطور یک Docker Swarm Service را Stop کنیم؟

با صفر کردن تعداد Replicaها:

```bash
sudo docker service scale SERVICE_NAME=0

```

### چطور سرویس را دوباره Start کنیم؟

تعداد Replicaها را به مقدار موردنظر برمی‌گردانیم:

```bash
sudo docker service scale SERVICE_NAME=3

```

### چطور یک سرویس را Restart کنیم؟

```bash
sudo docker service update --force SERVICE_NAME

```

### تفاوت Scale صفر و حذف سرویس چیست؟

`scale=0` تنظیمات سرویس را نگه می‌دارد، اما `docker service rm` سرویس و Taskهای آن را به‌طور کامل از Swarm حذف می‌کند.

### آیا Docker Swarm داده Volumeها را Replicate می‌کند؟

خیر. Local Volume فقط روی همان Node قرار دارد. برای HA داده باید از Storage مشترک، Distributed Storage یا Replication داخلی نرم‌افزار استفاده شود.

### آیا Docker Swarm هنوز برای پروژه‌های کوچک مناسب است؟

اگر تیم به Orchestration ساده، Rolling Update، Service Discovery و مدیریت چند Node نیاز داشته باشد و پیچیدگی Kubernetes توجیه نداشته باشد، Swarm همچنان می‌تواند انتخاب قابل‌قبولی باشد.