شبی که docker-compose پروداکشن از کار افتاد

ساعت ۲ بامداد پیام رفت: سرویس down. لاگها پر از خطای connection refused بود. علت؟ یک تغییر کوچک در docker-compose که volume را به مسیر اشتباه mount کرده بود — دیتابیس به جای /var/lib/postgresql/data به یک پوشه خالی وصل شده بود.
تا آن لحظه health check نداشتیم و restart policy روی always بود — یعنی کانتینر مدام بالا میآمد و میافتاد بدون اینکه کسی بفهمد ریشه مشکل چیست. از بیرون فقط میدیدیم سرویس «بالاست» ولی درخواستها timeout میخوردند.
اولین کاری که کردیم rollback بود، ولی backup آخرین snapshot سه روز قبل بود. سه روز داده از دست رفت — نه به خاطر حذف عمدی، بلکه چون اپلیکیشن روی volume خالی بالا آمده بود و دیتابیس جدید initialize شده بود.
بعد از incident، چند قانون ساده گذاشتیم: هیچ تغییری در docker-compose بدون review دوم، هیچ deploy بدون backup خودکار قبل از merge، و staging باید از نظر volume path دقیقاً مثل پروداکشن باشد — نه «تقریباً شبیه».
healthcheck را به همه سرویسهای حیاتی اضافه کردیم. برای Postgres از pg_isready، برای API از endpoint /health که واقعاً به DB وصل میشود — نه فقط return 200 خالی.
یک نکته فنی که کمتر کسی میگوید: depends_on فقط ترتیب start را تضمین میکند، نه آماده بودن سرویس. اگر healthcheck نداری، race condition در startup عملاً حتمی است.
الان هر deploy شامل smoke test بعد از بالا آمدن stack است: یک query ساده به DB، یک request به API، و چک کردن mount point داخل کانتینر. شاید ۳۰ ثانیه اضافه کند، ولی از یک شب بیداری جلوگیری میکند.
گرانترین درسها معمولاً رایگان نیستند — فقط وقت و اعصاب میگیرند. ولی اگر از همان incident یک checklist درست بیرون بیاید، حداقل دوباره همان اشتباه را تکرار نمیکنی.