docker compose چیست؟ ابزاری است که یک برنامه چندکانتینری را در یک فایل YAML توصیف می کند: وب اپ، دیتابیس، شبکه و volume همه در همان فایل می آیند و با یک دستور روی یک سرور بالا می روند. به جای چند دستور طولانی docker run که هر کدام را باید از حفظ بدانید، یک فایل دارید که در گیت نگه می شود، بازبینی می شود و روی هر سرور همان نتیجه را می دهد.
مشکلی که Compose حل می کند
برنامه واقعی تقریبا هیچ وقت یک کانتینر نیست. یک وب اپ به دیتابیس نیاز دارد، دیتابیس به دیسک ماندگار، و گاهی یک کش یا صف هم کنارشان هست. اگر این ها را با دستور دستی بسازید، ترتیب راه اندازی، نام شبکه، مقدار متغیرها و پورت ها فقط در حافظه یک نفر یا در یک فایل متنی گم شده می ماند. Compose این اطلاعات را به یک فایل compose.yaml منتقل می کند. سرویس ها داخل یک شبکه خصوصی مشترک قرار می گیرند و می توانند با نام سرویس همدیگر را پیدا کنند، یعنی برنامه به جای IP به db:3306 وصل می شود.
پیش نیاز، نصب خود داکر است. اگر هنوز نصب نکرده اید، اول نصب Docker روی Ubuntu و AlmaLinux را انجام دهید؛ Compose در آن نصب به صورت افزونه همراه است و با docker compose (با فاصله) اجرا می شود.
یک فایل docker-compose.yml واقعی
نمونه زیر یک وب اپ، یک دیتابیس MySQL، یک volume برای داده و سیاست ری استارت و healthcheck را کنار هم می گذارد. وب اپ از پوشه ./app ساخته می شود:
services:
web:
build: ./app
image: registry.example.local/shop-web:1.4.2
restart: unless-stopped
ports:
- "127.0.0.1:8080:3000"
env_file:
- ./app.env
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
db:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_DATABASE: shop
MYSQL_USER: shop
MYSQL_PASSWORD_FILE: /run/secrets/db_pass
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root
volumes:
- db_data:/var/lib/mysql
secrets:
- db_pass
- db_root
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db_data:
secrets:
db_pass:
file: ./secrets/db_pass.txt
db_root:
file: ./secrets/db_root.txt
چند نکته در این فایل عمدی است. پورت وب اپ به آدرس loopback یعنی 127.0.0.1 بسته شده و کاربران از طریق یک reverse proxy می آیند (پیکربندی Nginx reverse proxy). برای دیتابیس هیچ پورتی منتشر نشده، چون وب اپ از داخل شبکه Compose به آن می رسد. تگ ایمیج ها نسخه مشخص دارند. بخش depends_on با شرط service_healthy صبر می کند تا MySQL واقعا آماده پذیرش اتصال باشد و فقط به شروع شدن پروسه اکتفا نمی کند. اگر می خواهید دیتابیس را دقیق تر راه بیندازید، نصب و تنظیم MySQL جزئیات بیشتری دارد.
دستورهای اصلی Compose
| دستور | کار |
|---|---|
docker compose up -d |
سرویس ها را می سازد و در پس زمینه اجرا می کند؛ فقط چیزهای تغییرکرده دوباره ساخته می شوند |
docker compose ps |
وضعیت سرویس ها و نتیجه healthcheck را نشان می دهد |
docker compose logs -f web |
لاگ زنده یک سرویس را نمایش می دهد |
docker compose exec db mysql -u shop -p |
داخل کانتینر در حال اجرا دستور اجرا می کند |
docker compose pull |
ایمیج های جدید همان تگ را می گیرد |
docker compose down |
کانتینرها و شبکه را حذف می کند، volumeها را نگه می دارد |
تفاوت down و down -v را جدی بگیرید. گزینه -v volumeها را هم پاک می کند و با آن دیتابیس هم می رود. روی سرور تولید این گزینه را فقط وقتی بزنید که مطمئنید داده بکاپ دارد. برای به روزرسانی معمولا همین دو دستور کافی است: اول pull، بعد up -d. خود Compose فقط سرویس هایی را دوباره می سازد که ایمیج یا تنظیمشان عوض شده است.
فایل env و secret؛ رمزها کجا بروند
سه جا برای تنظیمات وجود دارد و هرکدام کاربرد خودش را دارد. مقدارهای غیرحساس مثل آدرس سرویس ها یا سطح لاگ در environment یا env_file می نشینند. مقدارهای حساس مثل رمز دیتابیس بهتر است از فایل خوانده شوند؛ بخش secrets فایل را داخل کانتینر زیر /run/secrets/ می گذارد و ایمیج های رسمی مثل MySQL و PostgreSQL متغیرهایی با پسوند _FILE برای خواندنش دارند. فایل env هم چیزی است که باید از گیت بیرون بماند:
printf '.env\napp.env\nsecrets/\n' >> .gitignore
chmod 600 app.env secrets/*.txt
docker compose config
دستور docker compose config فایل نهایی را بعد از جایگزینی متغیرها چاپ می کند و خطای YAML را هم قبل از اجرا نشان می دهد. دقت کنید که خروجی آن رمزهای env را هم نمایش می دهد، پس آن را در لاگ CI یا چت نچسبانید. فایل .env کنار compose.yaml برای جایگزینی متغیرهای داخل خود فایل (${TAG}) خوانده می شود و با env_file که متغیر را به کانتینر می دهد فرق دارد؛ این دو را با هم قاطی نکنید.
Compose کی کافی است و کی باید سراغ Kubernetes رفت
Compose روی یک میزبان کار می کند. اگر برنامه روی یک سرور یا یک VM با چند سرویس جا می شود، نیازی به چیز بزرگ تری ندارید: ساده است، دیباگ آن راحت است و هر کسی که YAML بخواند آن را می فهمد. برای سایت شرکتی، ابزار داخلی، محیط staging و پروژه های کوچک تا متوسط انتخاب منطقی است.
وقتی نیاز به چند گره، جابه جایی خودکار کانتینر هنگام خرابی سرور، به روزرسانی تدریجی بدون قطع و مقیاس خودکار پیدا کردید، Compose کم می آورد. آن موقع مقایسه Docker و Kubernetes را بخوانید و بعد برای راه اندازی خوشه به نصب Kubernetes مراجعه کنید. به نظر من، مهاجرت زودهنگام به Kubernetes برای برنامه ای که یک سرور کافی است، فقط هزینه نگه داری را بالا می برد.
دسته بندی سرویس ها، شبکه و ساخت خودکار
وقتی فایل بزرگ تر می شود، چند کار کمک می کند. شبکه ها را صریح تعریف کنید و دیتابیس را در شبکه ای قرار دهید که سرویس بیرونی به آن وصل نیست. برای کش مثل Redis سرویس جدا اضافه کنید (Redis چیست) و برای هر سرویس سقف حافظه تعیین کنید تا یک نشت حافظه کل سرور را نخواباند. فایل را در گیت نگه دارید و استقرار را به pipeline بسپارید، تا pull و up -d بدون دست زدن دستی اجرا شوند (CI/CD چیست). برای تفکیک محیط ها از چند فایل override یا از profiles استفاده می شود، ولی یک فایل پایه مشترک برای همه محیط ها داشته باشید تا staging و production از هم دور نشوند.
اشتباه های رایج
- منتشر کردن پورت دیتابیس. خط
"3306:3306"یعنی روی همه اینترفیس ها باز است و داکر قوانین فایروال را جداگانه اضافه می کند، پس UFW ممکن است جلویش را نگیرد. اگر دسترسی بیرونی لازم است، آن را به loopback (127.0.0.1:3306:3306) محدود کنید. - تگ
latest. با هر pull ممکن است نسخه عوض شود و برنامه بعد از ری استارت ساده ای خراب شود. نسخه را بنویسید و عمدی ارتقا دهید. - نداشتن volume. داده ای که داخل کانتینر نوشته شود با حذف کانتینر از بین می رود. دیتابیس بدون volume را فقط برای تست بسازید.
- نداشتن بکاپ از volume. برای دیتابیس از dump خود دیتابیس استفاده کنید و پوشه داده در حال اجرا را کپی نکنید.
- رمز داخل فایل compose یا داخل ایمیج، یا ثبت فایل env در گیت.
- اتکا به
restart: alwaysبدون healthcheck. کانتینر بالا اما بی جواب، سالم حساب می شود. - mount کردن
/var/run/docker.sockداخل کانتینر بی دلیل؛ این کار دسترسی root به میزبان می دهد.
منابع و مستندات رسمی
- مستندات رسمی Docker Compose
- مستندات رسمی Compose درباره متغیرهای محیطی
- مستندات رسمی Docker درباره امنیت Engine
اگر پروژه فعلی هنوز با دستورهای دستی روی سرور اجرا می شود، یا فایل compose کنونی تان رمز و پورت باز دارد، سرویس راه اندازی داکر و کانتینرسازی ساختار فایل، رجیستری خصوصی، بکاپ volumeها و مسیر استقرار را از روی همان پروژه مرور و مرتب می کند.



