12 KiB
جدا کردن MariaDB و Redis از docker-compose به Resourceهای مستقل Coolify
پروژه
clinicpro (backend / deploy)
زمینه
الان clinicpro/docker-compose.yml یک استک کامل است: app + worker-async + worker-scheduler + mariadb + redis. هر سه سرویس اپ از یک image مشترک (clinicpro-app:latest) استفاده میکنند و mariadb/redis داخل همین compose بهصورت سرویس تعریف شدهاند با volumeهای محلی (mariadb_data, redis_data).
کاربر میخواهد در Coolify، MariaDB و Redis را بهصورت Database Resource مستقل بسازد و مدیریت کند (بکاپ، monitoring، آپدیت جدا، ریاستارت مستقل از اپ). یعنی این دو باید از docker-compose.yml اپ حذف شوند و اپ به آنها از طریق شبکهی Coolify وصل شود.
هدف
docker-compose.yml فقط شامل سرویسهای اپلیکیشن باشد (app + دو worker). MariaDB و Redis از compose حذف شوند و اپ به Resourceهای مستقل Coolify وصل شود.
قوانین Coolify (از داکیومنتها — رعایت اجباری)
- شبکه: Coolify برای هر Resource یک شبکهی bridge ایزوله (بر اساس UUID) میسازد. سرویسهای داخل یک compose با نام سرویس همدیگر را میبینند. اما برای دسترسی به یک Resource دیگر (دیتابیس مستقل)، باید گزینهی "Connect to Predefined Network" روی استک اپ فعال شود و دیتابیس با شناسهی کامل
<service>-<uuid>بهعنوان hostname رفرنس داده شود (مثلpostgresql-abc123...؛ برای ماmariadb-<uuid>/redis-<uuid>). - هرگز
networks:سفارشی در compose تعریف نکن — Coolify خودش مدیریت میکند؛ شبکهی سفارشی routing Traefik را میشکند و باعث قطعی متناوب میشود. - مقادیر اتصال (DATABASE_URL / REDIS_URL / MESSENGER_TRANSPORT_DSN) دیگر به نام سرویس داخلی اشاره نمیکنند؛ از تب Environment Variables در Coolify ست میشوند و به hostname دیتابیس مستقل (
mariadb-<uuid>/redis-<uuid>) اشاره میکنند.
فایلهای مرتبط
| فایل | نقش |
|---|---|
clinicpro/docker-compose.yml |
استک دیپلوی Coolify — باید mariadb/redis از آن حذف شود |
clinicpro/.env.coolify.example |
نمونه env vars برای Coolify — باید برای اتصال به دیتابیس مستقل بهروز شود |
clinicpro/docs/DEPLOY.md |
راهنمای دیپلوی — باید مراحل ساخت Resource مستقل اضافه شود |
clinicpro/docker/entrypoint.sh |
فقط روی app (RUN_INIT=1) migration میزند — منطق بدون تغییر، فقط باید به DB مستقل وصل شود |
وضعیت فعلی
استک فعلی mariadb/redis را داخل خودش دارد و depends_on به healthcheck آنها گره خورده:
# x-app-env
DATABASE_URL: "mysql://clinic:${DB_PASSWORD}@mariadb:3306/clinic_pro?serverVersion=mariadb-11.8.0&charset=utf8mb4"
MESSENGER_TRANSPORT_DSN: redis://redis:6379/messages
REDIS_URL: redis://redis:6379
# x-app-depends
mariadb:
condition: service_healthy
redis:
condition: service_started
services:
app: ...
depends_on: *app-depends
worker-async: ...
depends_on: *app-depends
worker-scheduler: ...
depends_on: *app-depends
mariadb:
image: mariadb:11.8
...
volumes:
- mariadb_data:/var/lib/mysql
healthcheck: ...
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
volumes:
jwt_keys:
uploads_public:
uploads_var:
mariadb_data: # ← متعلق به دیتابیس داخلی
redis_data: # ← متعلق به redis داخلی
وظایف
۱. حذف سرویسهای mariadb و redis از docker-compose.yml
- بلوک سرویس
mariadbوredisکامل حذف شوند. - volumeهای
mariadb_dataوredis_dataاز بخشvolumes:حذف شوند (دیگر داخل این استک نیستند؛ دادهی DB مستقل توسط خود Resource نگه داشته میشود).jwt_keys,uploads_public,uploads_varبمانند.
۲. حذف depends_on به mariadb/redis
x-app-depends (و ارجاع depends_on: *app-depends در هر سه سرویس) دیگر بیمعناست — این سرویسها در استک نیستند. دو گزینه:
- پیشنهادی: anchor
x-app-dependsو همهیdepends_on: *app-dependsحذف شوند. بهجای آن، تحمل قطعی موقت در استارتآپ تضمین شود (وظیفه ۴). - اگر میخواهی ترتیب استارت
appقبل از workerها حفظ شود، فقط workerها میتوانندdepends_on: [app]ساده داشته باشند (بدون condition healthcheck).
نمونهی نهایی موردانتظار (شِمای کلی):
x-app-env: &app-env
APP_ENV: prod
APP_DEBUG: "0"
APP_SECRET: ${APP_SECRET}
# به دیتابیس/Redis مستقل Coolify اشاره میکند — مقدار واقعی از تب Environment Variables
DATABASE_URL: ${DATABASE_URL}
MESSENGER_TRANSPORT_DSN: ${MESSENGER_TRANSPORT_DSN}
REDIS_URL: ${REDIS_URL}
JWT_SECRET_KEY: "%kernel.project_dir%/config/jwt/private.pem"
JWT_PUBLIC_KEY: "%kernel.project_dir%/config/jwt/public.pem"
JWT_PASSPHRASE: ${JWT_PASSPHRASE}
CORS_ALLOW_ORIGIN: ${CORS_ALLOW_ORIGIN}
DEFAULT_URI: ${APP_BASE_URL}
APP_BASE_URL: ${APP_BASE_URL}
ALLOWED_FRONTEND_HOSTS: ${ALLOWED_FRONTEND_HOSTS}
TRUSTED_PROXIES: ${TRUSTED_PROXIES}
API_IR_BASE_URL: ${API_IR_BASE_URL}
API_IR_TOKEN: ${API_IR_TOKEN}
REFRESH_TOKEN_TTL: "2592000"
OTP_TTL: "1200"
MAX_FILE_SIZE_BYTES: "5242880"
UPLOAD_DIR: var/uploads
x-app-volumes: &app-volumes
- jwt_keys:/app/config/jwt
- uploads_public:/app/public/uploads
- uploads_var:/app/var/uploads
services:
app:
image: clinicpro-app:latest
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
environment:
<<: *app-env
RUN_INIT: "1"
volumes: *app-volumes
healthcheck:
test: ["CMD", "php", "-r", "exit(@fsockopen('127.0.0.1', 80) ? 0 : 1);"]
interval: 15s
timeout: 5s
retries: 5
start_period: 60s
worker-async:
image: clinicpro-app:latest
restart: unless-stopped
command: php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v
environment:
<<: *app-env
RUN_INIT: "0"
volumes: *app-volumes
depends_on: [app]
worker-scheduler:
image: clinicpro-app:latest
restart: unless-stopped
command: php bin/console messenger:consume scheduler_default --time-limit=3600 -v
environment:
<<: *app-env
RUN_INIT: "0"
volumes: *app-volumes
depends_on: [app]
volumes:
jwt_keys:
uploads_public:
uploads_var:
توجه:
DATABASE_URL/REDIS_URL/MESSENGER_TRANSPORT_DSNاز hardcode داخل compose به${...}تبدیل شدند تا از Coolify (با hostname دیتابیس مستقل) تزریق شوند.DB_PASSWORD/DB_ROOT_PASSWORDدیگر در این compose استفاده نمیشوند (متعلق به Resource دیتابیساند).
۳. بهروزرسانی .env.coolify.example
- متغیرهای
DB_PASSWORD/DB_ROOT_PASSWORDکه مخصوص mariadb داخلی بودند را حذف یا به بخش «اینها را موقع ساخت Database Resource ست کن» منتقل کن. - سه متغیر اتصال اضافه/مستندسازی شوند که به Resource مستقل اشاره میکنند. hostname باید
<service>-<uuid>باشد (UUID از صفحهی Resource در Coolify گرفته میشود):
# اتصال به MariaDB مستقل Coolify (hostname = mariadb-<uuid> از صفحهی Resource)
DATABASE_URL="mysql://clinic:PASSWORD@mariadb-XXXXXXXX:3306/clinic_pro?serverVersion=mariadb-11.8.0&charset=utf8mb4"
# اتصال به Redis مستقل Coolify (hostname = redis-<uuid>)
REDIS_URL="redis://redis-XXXXXXXX:6379"
MESSENGER_TRANSPORT_DSN="redis://redis-XXXXXXXX:6379/messages"
- توضیح بده که اگر Redis مستقل Coolify رمز دارد، فرمت
redis://:PASSWORD@redis-<uuid>:6379است.
۴. تحمل قطعی DB در استارتآپ (چون دیگر depends_on: service_healthy نیست)
چون اپ دیگر منتظر healthcheck دیتابیس نمیماند، ممکن است هنگام بوت، DB هنوز آماده نباشد و doctrine:migrations:migrate در entrypoint.sh شکست بخورد. در docker/entrypoint.sh، قبل از migration یک انتظار کوتاه برای آمادهشدن DB اضافه کن (مثلاً حلقهای که با یک کوئری سادهی Doctrine اتصال را چک میکند، چند بار با تأخیر retry):
# منتظر آمادهشدن دیتابیس مستقل بمان (حداکثر ~60s) قبل از migration
if [ "${RUN_INIT:-1}" = "1" ]; then
i=0
until php bin/console dbal:run-sql "SELECT 1" >/dev/null 2>&1; do
i=$((i+1))
[ "$i" -ge 30 ] && echo "DB not reachable after 60s" && exit 1
echo "waiting for database... ($i)"
sleep 2
done
# سپس: JWT keygen + cache + migrations (منطق فعلی)
fi
منطق فعلی JWT keygen / cache:clear / cache:warmup / migrate دستنخورده بماند؛ فقط حلقهی انتظار قبل از آن اضافه شود. (در صورت نبودن دستور dbal:run-sql، از doctrine:query:sql "SELECT 1" استفاده کن — اول با ddev exec php bin/console list doctrine | grep -i sql چک کن کدام موجود است.)
۵. بهروزرسانی docs/DEPLOY.md
بخش «استک» و «متغیرهای محیطی» را اصلاح کن:
- جدول سرویسها:
mariadbوredisاز استک اپ حذف شدهاند و حالا Database Resource مستقل Coolify هستند. - مراحل جدید: (الف) ساخت Resource مستقل MariaDB در Coolify، (ب) ساخت Resource مستقل Redis، (ج) فعالکردن "Connect to Predefined Network" روی استک اپ، (د) ستکردن
DATABASE_URL/REDIS_URL/MESSENGER_TRANSPORT_DSNبا hostname<service>-<uuid>. - توضیح بده که volumeهای
mariadb_data/redis_dataدیگر در استک اپ نیستند؛ ماندگاری داده برعهدهی خود Resourceهاست.
نکات مهم
- هیچ
networks:سفارشی اضافه نشود؛ اتصال cross-resource فقط با "Connect to Predefined Network" + hostname<service>-<uuid>. - مقادیر
DATABASE_URL/REDIS_URL/MESSENGER_TRANSPORT_DSNنباید داخل compose hardcode شوند؛ از${...}و تب Coolify بیایند (چون UUID برای هر نصب فرق دارد). serverVersion=mariadb-11.8.0درDATABASE_URLحفظ شود (Doctrine برای تولید SQL درست لازم دارد) — مطمئن شو نسخهی Resource مستقل MariaDB هم 11.8 انتخاب شود تا با migrationها همخوان باشد.- فقط سرویس
app(RUN_INIT=1) migration میزند؛ workerها (RUN_INIT=0) نباید — این رفتار حفظ شود تا race در migration پیش نیاید. image: clinicpro-app:latestمشترک حفظ شود (app یکبار build، workerها reuse) — این فیکس قبلی برای جلوگیری از سهبار build است؛ خرابش نکن.- بعد از تغییر، با
docker compose configاعتبار YAML را چک کن. - این تغییر فقط deploy/infra است؛ هیچ controller/route/Entity تغییر نمیکند، پس نیازی به آپدیت
docs/api/*نیست (فقطdocs/DEPLOY.md).