مدیریت سرویس‌های چندکانتینری با Docker Compose

چرا به Docker Compose نیاز داریم؟

تا به اینجا یاد گرفتید که چطور برای هر سرویس یک دستور docker run اجرا کنید، برایش شبکه بسازید، والیوم متصل کنید و متغیرهای محیطی را پاس بدهید. اما فرض کنید یک برنامه‌ی وب معمولی شامل بخش‌های زیر باشد:

  1. یک سرویس فرانت‌اند (React)
  2. یک وب‌سرویس بک‌اند (Node.js یا FastAPI)
  3. یک پایگاه داده (PostgreSQL)
  4. یک کش موقت (Redis)

برای بالا آوردن این سیستم، باید هر بار ۴ دستور طولانی docker run با ده‌ها فلگ و پارامتر بنویسید، مراقب ترتیب اجرای آن‌ها باشید و اگر سرور ریستارت شد همه را دوباره دستی اجرا کنید!

در هفته‌ی چهارم با تفاوت رویکرد Imperative (دستوری) و Declarative (توصیفی) آشنا شدید. Docker Compose ابزاری Declarative است که به شما اجازه می‌دهد کل معماری چندکانتینری پروژه را در یک فایل متنی با فرمت YAML (به نام compose.yaml) تعریف کنید و مجموعه‌ی سرویس‌ها را با دستوراتی یکپارچه مدیریت و اجرا نمایید.


ساختار فایل compose.yaml

طبق استاندارد مدرن Compose Specification، نام پیش‌فرض فایل compose.yaml است (اگرچه نام قدیمی docker-compose.yml نیز همچنان پشتیبانی می‌شود).

یک فایل استاندارد از بخش‌های زیر تشکیل شده است:

 1services:
 2  # سرویس دیتابیس
 3  db:
 4    image: postgres:16-alpine
 5    restart: unless-stopped
 6    environment:
 7      POSTGRES_DB: myapp
 8      POSTGRES_USER: appuser
 9      POSTGRES_PASSWORD: secretpassword
10    volumes:
11      - db_data:/var/lib/postgresql/data
12    networks:
13      - backend-net
14
15  # سرویس وب اپلیکیشن
16  web:
17    build:
18      context: .
19      dockerfile: Dockerfile
20    restart: unless-stopped
21    ports:
22      - "8080:80"
23    environment:
24      DATABASE_HOST: db
25      DATABASE_PORT: 5432
26      DATABASE_NAME: myapp
27    depends_on:
28      - db
29    networks:
30      - backend-net
31
32volumes:
33  db_data:
34
35networks:
36  backend-net:

تحلیل بخش‌های کلیدی:

  1. services: کانتینرهایی که باید اجرا شوند. در مثال بالا دو سرویس db و web داریم.
  2. image در مقابل build: برای دیتابیس مستقیماً ایمیج آماده را از داکر هاب دانلود می‌کنیم (image)، اما برای وب‌سرویس، داکر آن را از روی Dockerfile محلی بیلد می‌کند (build).
  3. ارتباط شبکه و کشف خودکار: هر دو سرویس به شبکه‌ی backend-net متصل هستند. سرویس web بدون نیاز به دانستن IP دیتابیس، مستقیماً از نام سرویس یعنی db به‌عنوان آدرس میزبان استفاده می‌کند (DATABASE_HOST: db).
  4. ports: پورت 8080 هاست به پورت 80 کانتینر وب متصل شده است. دقت کنید که پورت دیتابیس را اصلاً به سیستم میزبان باز نکردیم تا از بیرون در دسترس نباشد و امنیت حفظ شود!
  5. depends_on: ترتیب روشن شدن سرویس‌ها را تعیین می‌کند؛ ابتدا db و سپس web اجرا خواهد شد.
  6. volumes و networks سراسری: تعریف والیوم ماندگار db_data و شبکه‌ی ایزوله‌ی backend-net.

مدیریت متغیرهای محیطی با فایل‌های .env و env_file

هرگز کلمات عبور و متغیرهای حساس را مستقیماً داخل فایل compose.yaml هاردکد نکنید. Docker Compose روش‌های منعطفی برای تزریق متغیرهای محیطی در اختیارتان قرار می‌دهد:

۱. جایگزینی متغیرها در فایل Compose با فایل پیش‌فرض .env

داکر کامپوز به‌صورت پیش‌فرض و خودکار فایلی به نام .env را در همان دایرکتوری جست‌وجو کرده و مقادیر آن را در متغیرهای ${VARIABLE} جایگزین (Interpolate) می‌کند:

فایل .env:

1DB_USER=appuser
2DB_PASSWORD=SuperSecretPassword123!
3DB_NAME=production_db
4APP_PORT=8080

فایل compose.yaml:

 1services:
 2  db:
 3    image: postgres:16-alpine
 4    environment:
 5      POSTGRES_DB: ${DB_NAME}
 6      POSTGRES_USER: ${DB_USER}
 7      POSTGRES_PASSWORD: ${DB_PASSWORD}
 8  web:
 9    image: myapp:1.0
10    ports:
11      - "${APP_PORT}:80"

۲. تزریق مستقیم فایل env به داخل کانتینر با env_file

گاهی نمی‌خواهید تک‌تک متغیرها را در بخش environment فایل YAML لیست کنید، بلکه می‌خواهید ده‌ها متغیر را مستقیماً از یک فایل مجزا وارد محیط کانتینر کنید. برای این کار از کلید env_file استفاده می‌شود:

1services:
2  web:
3    image: myapp:1.0
4    env_file:
5      - .env.web
6      - .env.secrets

در این حالت، تمام خطوط داخل .env.web مستقیماً به‌عنوان متغیر محیطی داخل کانتینر web در دسترس خواهند بود.


۳. استفاده از فایل‌های سفارشی با فلگ --env-file (محیط‌های مختلف: Dev، Staging، Prod)

در پروژه‌های واقعی، تنظیمات دیتابیس و متغیرها در محیط توسعه (Local) با محیط تست یا پروداکشن متفاوت است. به‌جای داشتن چندین فایل Compose، می‌توانید فایل‌های متغیر اختصاصی (Custom Env Files) بسازید:

  • .env.development
  • .env.staging
  • .env.production

برای مشخص کردن این فایل سفارشی در دستورات Compose، از فلگ --env-file استفاده کنید:

1# اجرا با متغیرهای محیط توسعه
2docker compose --env-file .env.development up -d
3
4# اجرا با متغیرهای محیط Production
5docker compose --env-file .env.production up -d

همچنین می‌توانید برای اعتبارسنجی مقادیر جایگزین‌شده قبل از اجرا، دستور زیر را بزنید:

1docker compose --env-file .env.production config

این دستور خروجی نهایی و ترکیب‌شده‌ی فایل YAML را با متغیرهای پرشده در ترمینال نمایش می‌دهد.


دستورهای پرکاربرد Docker Compose CLI

در نگارش مدرن داکر (Docker Compose V2)، دستورات با خط فاصله‌ی فاصله (docker compose) اجرا می‌شوند (به‌جای ابزار پایتونی قدیمی docker-compose).

۱. راه‌اندازی و اجرای کل پشته (Stack)

1# ساخت ایمیج‌ها (در صورت نیاز) و اجرای تمام سرویس‌ها در پس‌زمینه
2docker compose up -d
3
4# اجرای مجدد همراه با بیلد مجدد اجباری ایمیج‌ها
5docker compose up -d --build

۲. بررسی وضعیت سرویس‌ها

1# مشاهده‌ی وضعیت سرویس‌ها و پورت‌های فعال
2docker compose ps

۳. بررسی لاگ‌های یکپارچه

1# مشاهده‌ی زنده و رنگی لاگ‌های همه‌ی کانتینرها به‌صورت هم‌زمان
2docker compose logs -f
3
4# مشاهده‌ی لاگ‌های یک سرویس خاص (مثلاً web)
5docker compose logs -f web

۴. اجرای دستور داخل کانتینر سرویس

1# ورود به شل سرویس web
2docker compose exec web sh
3
4# اجرای migration در پایگاه داده
5docker compose exec web npm run db:migrate

۵. خاموش کردن و جمع‌آوری پشته

1# توقف و حذف کانتینرها و شبکه‌ها (والیوم‌ها حفظ می‌شوند)
2docker compose down
3
4# توقف کامل همراه با پاک کردن کامل والیوم‌های دیسک (داده‌ها پاک می‌شوند)
5docker compose down -v

بررسی سلامت سرویس‌ها (Healthchecks)

دستور depends_on به‌تنهایی فقط مطمئن می‌شود که پردازش دیتابیس استارت خورده است، اما پردازش دیتابیس چند ثانیه طول می‌کشد تا آماده‌ی پذیرش اتصال (Ready for connections) شود.

برای رفع این مشکل، از healthcheck استفاده می‌شود:

 1services:
 2  db:
 3    image: postgres:16-alpine
 4    environment:
 5      POSTGRES_PASSWORD: secret
 6    healthcheck:
 7      test: ["CMD-SHELL", "pg_isready -U postgres"]
 8      interval: 5s
 9      timeout: 5s
10      retries: 5
11
12  web:
13    image: myapp:1.0
14    depends_on:
15      db:
16        condition: service_healthy

با این تنظیم هوشمند، سرویس web تا زمانی که دیتابیس کاملاً آماده و سالم (healthy) نشده باشد، استارت نمی‌خورد.


منابع یادگیری

ویدیو

مستندات و مقالات