آموزش سرور

آموزش Docker Compose روی سرور مجازی؛ از فایل اول تا استقرار واقعی

تهیه: کلودیفای (نگارش با کمک هوش مصنوعی) · انتشار: · زمان مطالعه: ۷ دقیقه

DockerComposeآموزش سرورآموزش Docker Composeروی سرور مجازی؛ از فایل اولتا استقرار واقعیمجلهٔ کلودیفایclodify.net/blog
پاسخ کوتاه

Docker Compose به شما اجازه می‌دهد چند کانتینر مرتبط، مثل برنامه، دیتابیس و Redis، را در یک فایل compose.yaml تعریف کنید و همه را با docker compose up -d بالا بیاورید. در این فایل ایمیج‌ها، پورت‌ها، Volumeها، متغیرهای محیطی و ترتیب وابستگی‌ها مشخص می‌شود. برای سرور واقعی، داده‌ها را در Volume نگه دارید، رمزها را در فایل .env بگذارید، پورت‌های داخلی را منتشر نکنید و restart: unless-stopped را فراموش نکنید.

Docker Compose چیست و چرا به آن نیاز دارید؟

اجرای یک کانتینر با docker run ساده است. اما پروژه‌های واقعی معمولاً از چند بخش تشکیل می‌شوند: یک برنامهٔ وب، یک دیتابیس، شاید Redis برای کش و صف، و یک وب‌سرور جلوی همه. اگر بخواهید همهٔ این‌ها را با دستورهای جداگانهٔ docker run اجرا کنید، باید ده‌ها گزینهٔ طولانی را به خاطر بسپارید، شبکهٔ مشترک بسازید و ترتیب اجرا را دستی رعایت کنید.

Docker Compose این مشکل را حل می‌کند. کل ساختار پروژه را در یک فایل YAML می‌نویسید و با یک دستور همه‌چیز بالا می‌آید، با یک دستور متوقف می‌شود و با یک دستور به‌روز می‌شود. این فایل را می‌توانید در گیت نگه دارید تا پیکربندی سرور هم مثل کد نسخه‌بندی شود.

مسیر درخواست در یک پروژهٔ Compose
درخواستproxy_passdb:5432کاربرHTTPSNginxروی سرور، پورت ۴۴۳app127.0.0.1:8000db و redisفقط شبکهٔ داخلیدرخواستproxy_passdb:5432کاربرHTTPSNginxروی سرور، پورت ۴۴۳app127.0.0.1:8000db و redisفقط شبکهٔ داخلی
شکل ۱ فقط Nginx روی اینترنت است؛ برنامه روی localhost و دیتابیس فقط داخل شبکهٔ Compose در دسترس است.

پیش‌نیازها

  • یک سرور لینوکس (اوبونتو ۲۴.۰۴ یا دبیان ۱۲) با دست‌کم دو گیگابایت رم برای مثال این مقاله؛
  • داکر نصب‌شده از مخزن رسمی که پلاگین Compose را هم شامل می‌شود. اگر هنوز نصب نکرده‌اید، آموزش نصب داکر روی اوبونتو ۲۴.۰۴ را دنبال کنید.

بررسی کنید Compose نصب است:

docker compose version

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

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

فرض کنید یک برنامهٔ وب پایتونی دارید که به PostgreSQL و Redis نیاز دارد. ابتدا پوشهٔ پروژه را بسازید:

mkdir -p /opt/myapp && cd /opt/myapp
nano compose.yaml

محتوای فایل:

services:
  app:
    image: ghcr.io/example/myapp:1.4.0
    restart: unless-stopped
    env_file: .env
    ports:
      - "127.0.0.1:8000:8000"
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myapp"]
      interval: 10s
      retries: 5

  redis:
    image: redis:7
    restart: unless-stopped
    volumes:
      - redis_data:/data

volumes:
  db_data:
  redis_data:

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

  • services: هر سرویس یک کانتینر (یا چند نمونه از یک کانتینر) است. نام سرویس، مثل db، در شبکهٔ داخلی Compose به‌عنوان نام میزبان کار می‌کند؛ یعنی برنامه با آدرس db:5432 به دیتابیس وصل می‌شود.
  • image: ایمیج و برچسب نسخه. همیشه نسخهٔ مشخص بنویسید، نه latest، تا به‌روزرسانی‌ها کنترل‌شده باشند.
  • restart: unless-stopped: اگر کانتینر از کار افتاد یا سرور ری‌استارت شد، خودکار دوباره اجرا می‌شود.
  • ports: فقط پورت برنامه منتشر شده و آن هم روی 127.0.0.1. دیتابیس و Redis اصلاً پورتی به بیرون ندارند و فقط از داخل شبکهٔ Compose در دسترس‌اند.
  • volumes: داده‌های دیتابیس و Redis در Volumeهای نام‌دار ذخیره می‌شوند و با حذف کانتینر از بین نمی‌روند.
  • depends_on و healthcheck: برنامه فقط وقتی اجرا می‌شود که دیتابیس واقعاً آمادهٔ پذیرش اتصال باشد، نه صرفاً روشن.
اجزای یک فایل compose.yaml
servicesapp، db و redis؛ هر کدام با ایمیج و تنظیمات خود
networksشبکهٔ پیش‌فرض پروژه؛ سرویس‌ها با نام یکدیگر را پیدا می‌کنند
volumesdb_data و redis_data برای دادهٔ ماندگار
.envرمزها و تنظیمات خارج از فایل اصلی
شکل ۲ یک فایل، کل ساختار پروژه را توصیف می‌کند و می‌تواند در گیت نگهداری شود.

متغیرهای محیطی و رمزها در فایل .env

رمزها را هرگز مستقیم در compose.yaml ننویسید، به‌خصوص اگر این فایل را در گیت نگه می‌دارید. یک فایل .env کنار آن بسازید:

cat > .env <<'ENV'
DB_PASSWORD=change-this-to-a-long-random-string
DATABASE_URL=postgresql://myapp:change-this-to-a-long-random-string@db:5432/myapp
REDIS_URL=redis://redis:6379/0
ENV
chmod 600 .env
echo ".env" >> .gitignore

Compose متغیرهای این فایل را به‌طور خودکار می‌خواند و در عبارت‌هایی مثل ${DB_PASSWORD} جایگزین می‌کند. برای ساخت رمز تصادفی می‌توانید از openssl rand -base64 32 استفاده کنید.

اجرا، توقف و مشاهدهٔ وضعیت

دستورهای اصلی را باید در همان پوشه‌ای اجرا کنید که compose.yaml در آن است:

docker compose config        # بررسی صحت فایل و دیدن مقادیر نهایی
docker compose pull          # دانلود ایمیج‌ها
docker compose up -d         # اجرای همه در پس‌زمینه
docker compose ps            # وضعیت سرویس‌ها
docker compose logs -f app   # دنبال کردن لاگ یک سرویس
docker compose exec db psql -U myapp   # ورود به کانتینر دیتابیس
docker compose restart app   # ری‌استارت یک سرویس
docker compose down          # توقف و حذف کانتینرها (Volumeها می‌مانند)

هشدار: docker compose down -v علاوه بر کانتینرها، Volumeها را هم حذف می‌کند؛ یعنی کل دادهٔ دیتابیس از دست می‌رود. این گزینه را فقط وقتی به کار ببرید که واقعاً می‌خواهید همه‌چیز را از صفر شروع کنید.

اگر دانلود ایمیج روی سرور ایران با خطای ۴۰۳ یا timeout متوقف شد، علت معمولاً محدودیت دسترسی به Docker Hub است. راه‌حل‌ها را در رفع خطای ۴۰۳ داکر در ایران بخوانید.

قرار دادن Nginx و HTTPS جلوی برنامه

در مثال بالا برنامه فقط روی 127.0.0.1:8000 در دسترس است. برای انتشار عمومی، Nginx روی خود سرور نصب کنید و درخواست‌ها را به این پورت بفرستید:

server {
    server_name app.example.com;
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

سپس با Certbot گواهی رایگان بگیرید؛ مراحل کامل در آموزش Nginx و Let's Encrypt آمده است. این ساختار امن‌تر از انتشار مستقیم پورت کانتینر روی اینترنت است، چون پورت‌های منتشرشدهٔ داکر قوانین UFW را دور می‌زنند؛ جزئیات این رفتار را در آموزش فایروال UFW ببینید.

root@server: /opt/myapp
$ docker compose up -d[+] Running 4/4 - Network myapp_default  Created - Container myapp-db-1     Healthy - Container myapp-redis-1  Started - Container myapp-app-1    Started$ docker compose psNAME           SERVICE  STATUSmyapp-app-1    app      Up ...$ 
شکل ۳ Compose ابتدا شبکه را می‌سازد، منتظر سالم شدن دیتابیس می‌ماند و سپس برنامه را اجرا می‌کند.

به‌روزرسانی بدون دردسر

برای ارتقای برنامه، برچسب ایمیج را در compose.yaml تغییر دهید (مثلاً از 1.4.0 به 1.5.0) و اجرا کنید:

docker compose pull app
docker compose up -d app
docker image prune -f

Compose فقط سرویسی را که تغییر کرده دوباره می‌سازد و بقیه دست‌نخورده می‌مانند. اگر نسخهٔ جدید مشکل داشت، برچسب قبلی را برگردانید و همین دستورها را تکرار کنید. پیش از ارتقای دیتابیس به نسخهٔ اصلی بالاتر (مثلاً PostgreSQL ۱۶ به ۱۷) حتماً بکاپ بگیرید، چون فرمت داده بین نسخه‌های اصلی سازگار نیست.

بکاپ گرفتن از داده‌ها

بکاپ دیتابیس را با ابزار خود دیتابیس بگیرید، نه با کپی کردن پوشهٔ Volume در حالی که دیتابیس در حال اجراست:

docker compose exec -T db pg_dump -U myapp myapp | gzip > /root/backup/myapp-$(date +%F).sql.gz

این دستور را می‌توانید در cron قرار دهید و فایل‌ها را به سرور یا فضای دیگری منتقل کنید. بکاپی که روی همان سرور بماند، در صورت خرابی یا حذف سرور کمکی نمی‌کند.

خطاهای رایج و رفع آن‌ها

port is already allocated: پورتی که در بخش ports نوشته‌اید را برنامهٔ دیگری روی سرور اشغال کرده است. با sudo ss -tlnp | grep 8000 ببینید کدام فرایند از آن استفاده می‌کند و یا آن را متوقف کنید یا پورت دیگری انتخاب کنید.

برنامه به دیتابیس وصل نمی‌شود: رایج‌ترین اشتباه استفاده از localhost در رشتهٔ اتصال است. داخل کانتینر، localhost یعنی خود همان کانتینر؛ باید نام سرویس (db) را بنویسید. اگر نام درست است، با docker compose logs db مطمئن شوید دیتابیس بدون خطا بالا آمده است.

کانتینر مدام ری‌استارت می‌شود: وضعیت Restarting در خروجی ps یعنی برنامه بلافاصله پس از اجرا خطا می‌دهد. لاگ همان سرویس را بخوانید؛ معمولاً متغیر محیطی جاافتاده یا مجوز نادرست پوشه علت است.

تغییرات فایل اعمال نشد: پس از ویرایش compose.yaml یا .env، باید دوباره docker compose up -d را اجرا کنید؛ ری‌استارت ساده تنظیمات جدید را نمی‌خواند.

چند نکتهٔ حرفه‌ای

  • برای محدود کردن منابع یک سرویس، از کلید deploy.resources.limits (مثلاً memory: 512M) استفاده کنید تا یک کانتینر پرمصرف کل سرور را از کار نیندازد.
  • حجم لاگ کانتینرها را محدود کنید؛ در غیر این صورت ممکن است به‌مرور دیسک را پر کنند. گزینهٔ logging با درایور json-file و max-size این کار را انجام می‌دهد.
  • برای محیط‌های مختلف (توسعه و تولید) می‌توانید فایل‌های جداگانه مثل compose.override.yaml داشته باشید.
  • مرجع کامل گزینه‌ها در مستندات رسمی Compose موجود است.

اگر سروری برای اجرای پروژه‌های Compose می‌خواهید، سرور مجازی ایران و سرور خارج کلودیفای با صورتحساب ساعتی در دسترس‌اند؛ برای تست پیکربندی می‌توانید یک سرور موقت بسازید و پس از اطمینان، روی سرور اصلی مستقر کنید.

سؤالات متداول

تفاوت docker-compose و docker compose چیست؟

docker-compose با خط تیره نسخهٔ اول و قدیمی Compose است که جداگانه نصب می‌شد. docker compose با فاصله نسخهٔ دوم است که به‌صورت پلاگین داخل خود داکر قرار دارد و نسخهٔ توصیه‌شده است. ساختار فایل‌ها تقریباً یکسان است.

آیا با docker compose down داده‌های دیتابیس پاک می‌شود؟

خیر، اگر داده‌ها در Volume نام‌دار باشند و از گزینهٔ -v استفاده نکنید، باقی می‌مانند. فقط کانتینرها و شبکه حذف می‌شوند و با up دوباره به همان داده وصل می‌شوید.

چطور بعد از ری‌استارت سرور، کانتینرها خودکار اجرا شوند؟

سرویس داکر را با systemctl enable docker فعال کنید و برای هر سرویس در compose.yaml مقدار restart: unless-stopped یا always بگذارید. با این کار پس از بوت، کانتینرها خودکار بالا می‌آیند.

چند پروژهٔ Compose را می‌توان روی یک سرور اجرا کرد؟

هر تعداد که منابع سرور اجازه دهد. هر پروژه در پوشهٔ جداگانه با فایل خودش قرار می‌گیرد و شبکهٔ مجزایی دارد. فقط مراقب باشید دو پروژه یک پورت میزبان را منتشر نکنند.

آماده‌اید؟ سرورتان را بسازید.

از ۱٬۲۰۰ تومان در ساعت · پرداخت ریالی

سرورتان را همین حالا بسازید.

ساعتی بپردازید. هر وقت خواستید حذف کنید.