وبلاگ / راهنمای مسیر یادگیری و فناوری
راهنمای مسیر یادگیری و فناوری

عیب‌یابی Docker Compose؛ از config و ps تا logs و سلامت سرویس‌ها

اگر docker compose up اجرا می‌شود اما برنامه بالا نمی‌آید، با یک مسیر کم‌خطر پیکربندی نهایی، وضعیت سرویس‌ها، لاگ‌ها، پورت و وابستگی سلامت را جداگانه بررسی کنید و علت را پیش از بازسازی یا پاک‌کردن داده پیدا کنید.

· ۱۸ بازدید

وقتی docker compose up را اجرا می‌کنید و در پایان صفحه وب باز نمی‌شود، لازم نیست بلافاصله همه کانتینرها را پاک کنید. شکست ممکن است در یکی از چند لایه باشد: فایل Compose درست تفسیر نشده، سرویس اصلاً اجرا نشده، فرایند داخل خارج شده، برنامه هنوز آماده نیست، پورت به میزبان منتشر نشده یا درخواست به آدرس اشتباه می‌رود. این راهنما به شما کمک می‌کند این حالت‌ها را با شواهد از هم جدا کنید. تمرکز بر محیط توسعه و پروژه محلی است؛ برای سامانه Production پیش از هر اقدام، رویه پشتیبان‌گیری و مجوز عملیاتی خودتان را رعایت کنید.

اگر هنوز با مفهوم image، container و volume آشنا نیستید، ابتدا راهنمای مقدماتی Docker برای توسعه‌دهندگان را بخوانید. در این مطلب فرض می‌کنیم Docker و افزونه Compose نصب‌اند، پوشه پروژه را می‌شناسید و به فایل Compose دسترسی دارید. فرمان‌های نمونه کوتاه‌اند و باید از همان پوشه پروژه یا با تعیین فایل درست اجرا شوند. نام سرویس در خروجی شما ممکن است با مثال متفاوت باشد.

پیش از تغییر، مسئله را دقیق تعریف کنید

سه مشاهده را یادداشت کنید: فرمانی که اجرا کرده‌اید، پیام یا رفتار دیده‌شده، و چیزی که انتظار داشتید. «برنامه بالا نمی‌آید» کافی نیست؛ بنویسید آیا docker compose up خطای parsing می‌دهد، آیا سرویس در ps دیده نمی‌شود، آیا وضعیت آن exited یا restarting است، یا running است اما مرورگر خطای اتصال می‌دهد. زمان رخداد و آخرین تغییر در فایل Compose، image یا متغیرهای محیطی را هم ثبت کنید. این تفکیک، مسیر بررسی را کوتاه می‌کند.

پیش از اجرای فرمان‌های تغییر‌دهنده، نام پروژه Compose و فایل‌های فعال را مشخص کنید. یک پوشه یا نام پروژه دیگر ممکن است همان نام سرویس را داشته باشد و خروجی ظاهراً متناقض بسازد. دستور docker compose version فقط نسخه ابزار را نشان می‌دهد؛ برای واقعی باید پیکربندی و وضعیت همان پروژه را بخوانید. اگر از چند فایل با -f یا از --env-file استفاده می‌کنید، همان گزینه‌ها را در فرمان‌های تشخیصی نیز تکرار کنید تا داده‌های دو پیکربندی مختلف را با هم مقایسه نکنید.

گام اول: فایل نهایی Compose را ببینید

با docker compose config مدل نهایی‌ای را ببینید که Compose پس از ترکیب فایل‌ها، حل مسیرها و جای‌گذاری متغیرها اعمال می‌کند. مرجع رسمی Docker همین کاربرد را توضیح می‌دهد. اگر فرمان خطا داد، قبل از رسیدن به شبکه یا برنامه، مشکل را در تعریف فایل، نام سرویس، تورفتگی YAML یا متغیر لازم جست‌وجو کنید. اگر فقط می‌خواهید اعتبار پیکربندی را بدون چاپ کل مدل بسنجید، گزینه --quiet را بررسی کنید.

متغیر محیطی خالی یا اشتباه

ممکن است در فایل نوشته باشید ${APP_PORT} ولی متغیر در محیطی که Compose اجرا شده تعریف نشده باشد؛ پورت نهایی با انتظار شما فرق می‌کند. خروجی config را برای بخش ports، environment، نام image و مسیر volumeها بخوانید. متغیرهای حساس را بی‌محابا در اسکرین‌شات یا تیکت عمومی منتشر نکنید، زیرا خروجی نهایی می‌تواند مقدارهای حل‌شده را نشان دهد. هنگام بازبینی، مقدار حساس را پوشانده و فقط نام متغیر و این‌که تنظیم شده یا نه را ثبت کنید. فایل .env مربوط به جای‌گذاری Compose را با متغیر محیطی داخل یکی فرض نکنید؛ هرکدام در مرحله متفاوتی اثر می‌گذارند.

چند فایل یا پوشه کاری اشتباه

وقتی یک فایل پایه و یک فایل مخصوص توسعه دارید، نتیجه ترکیب آن‌ها را ببینید، نه فقط متن هر فایل را جداگانه. اگر فرمان را از پوشه دیگری اجرا کرده‌اید، Compose ممکن است فایل دیگری را پیدا کند یا اصلاً فایلی پیدا نکند. مسیر فایل، نام پروژه و سرویس‌های خروجی config را با پروژه موردنظر تطبیق دهید. تغییر شتاب‌زده در YAML قبل از دیدن مدل نهایی ممکن است خطا را در لایه نادرست درمان کند.

گام دوم: وضعیت همه سرویس‌ها را بخوانید

docker compose ps --all نقطه شروع مناسبی است، چون برخلاف نمای محدود به کانتینرهای در حال اجرا، سرویس‌های متوقف را هم نشان می‌دهد. راهنمای رسمی ps ستون‌های وضعیت و پورت را شرح می‌دهد. اگر سرویس اصلاً فهرست نشده، نام آن را در خروجی config و فعال‌بودن profile بررسی کنید. اگر exited است، فرایند اصلی پایان یافته؛ سراغ لاگ همان سرویس بروید. اگر restarting است، یک چرخه شکست در برنامه یا پیکربندی محتمل است. اگر running است، هنوز آماده‌بودن endpoint اثبات نشده است.

پورت میزبان را از ستون PORTS بخوانید. در نگاشت 8080:80، مرورگر روی میزبان باید پورت ۸۰۸۰ را ببیند و برنامه داخل روی پورت ۸۰ گوش می‌دهد. اگر فقط پورت داخلی دیده می‌شود و پورتی منتشر نشده، درخواست به localhost میزبان به آن سرویس نمی‌رسد. اگر پورت میزبان قبلاً توسط برنامه دیگری اشغال شده باشد، معمولاً هنگام ایجاد سرویس خطای bind خواهید دید؛ متن دقیق خطا را ثبت کنید و فقط پورت پروژه خودتان را تغییر دهید. سرویس سالم اما endpoint اشتباه، مشکل شبکه داخلی Docker نیست.

گام سوم: لاگ را برای علت، نه فقط آخرین خط، بخوانید

docker compose logs خروجی سرویس‌ها را نشان می‌دهد. وقتی چند سرویس دارید، نام سرویس موردنظر را به فرمان اضافه کنید تا پیام‌ها مخلوط نشوند؛ برای محدودکردن حجم می‌توان از --tail و برای دنبال‌کردن رخداد تازه از --follow استفاده کرد. مرجع رسمی logs این گزینه‌ها را ثبت کرده است. نخستین پیام خطای مرتبط و چند خط پیش از آن را بخوانید. آخرین خط «process exited» معمولاً پیامد است، نه علت.

نمونه سناریو: سرویس وب در ps --all حالت restarting دارد و لاگ آن می‌گوید اتصال به پایگاه‌داده رد شد. این شاهد به‌تنهایی نمی‌گوید رمز اشتباه است؛ شاید نام میزبان را localhost گذاشته‌اید، در حالی که وب باید نام سرویس پایگاه‌داده را در شبکه Compose به‌کار ببرد؛ شاید پایگاه‌داده هنوز آماده نشده؛ شاید مقدار محیطی حل‌شده اشتباه است. یک فرضیه را انتخاب کنید، آن را با config و وضعیت سرویس پایگاه‌داده بسنجید و فقط همان بخش را اصلاح کنید. خروجی پیش و پس از اصلاح را کنار هم نگه دارید تا تغییر قابل‌پیگیری باشد.

اگر وابستگی زودتر شروع می‌شود اما آماده نیست

شروع پایگاه‌داده با آماده‌بودن آن برای دریافت اتصال یکی نیست. در مستند رسمی ترتیب راه‌اندازی Compose آمده است که برای منتظرماندن تا سلامت وابستگی، می‌توان healthcheck مناسب تعریف کرد و در depends_on از شرط service_healthy استفاده کرد. صرف وجود depends_on به‌معنای پاسخ‌گو بودن سرویس وابسته نیست. healthcheck باید چیزی را بسنجد که واقعاً آمادگی همان سرویس را نشان می‌دهد، نه فقط وجود یک فرایند.

اگر وضعیت healthcheck ناسالم است، خود فرمان بررسی سلامت، زمان انتظار اولیه و لاگ سرویس را جداگانه بررسی کنید. ممکن است برنامه سالم باشد ولی آزمون سلامت به مسیر یا پورت اشتباه اشاره کند؛ برعکس، ممکن است فرایند روشن باشد ولی هنوز migration یا بارگذاری اولیه را تمام نکرده باشد. شرط سلامت را برای پنهان‌کردن خطای واقعی با تأخیر طولانی جایگزین نکنید. زمان‌بندی و معیار سلامت باید با رفتار واقعی برنامه شما سازگار باشد و پس از تغییر، با یک اجرای تازه آزموده شود.

چند شاخه رایج و اقدام کم‌خطر برای هرکدام

خطای پورت یا آدرس

از config مقدار منتشرشده و از ps پورت واقعی را بخوانید. سپس آدرس مرورگر را با آن تطبیق دهید. به binding برنامه داخل هم توجه کنید: سرویسی که فقط روی loopback داخل گوش می‌دهد ممکن است از بیرون همان در دسترس نباشد. خطای اتصال، خطای HTTP و پاسخ اشتباه سه وضعیت متفاوت‌اند؛ با متن دقیق پیام تصمیم بگیرید. تغییر همه پورت‌ها و شبکه‌ها در یک نوبت، تشخیص را دشوار می‌کند.

فایل، mount یا مجوز

اگر لاگ می‌گوید فایل پیدا نشد یا دسترسی رد شد، مسیر host و container را در مدل نهایی config بخوانید. وجود فایل روی میزبان را جداگانه بررسی کنید. مسیر نسبی ممکن است نسبت به فایل Compose حل شود و با تصور شما از پوشه فعلی فرق داشته باشد. برای رفع خطا به‌طور پیش‌فرض دسترسی فراگیر یا اجرای root ندهید؛ نخست مالکیت و نوع mount را در محیط پروژه خودتان مشخص کنید. اگر mount روی مسیری قرار گرفته که image از قبل در آن فایل دارد، محتوای image ممکن است در همان مسیر زیر mount پنهان شود.

سرویس در چرخه restart

در این حالت، چند پیام قبل از پایان فرایند را بخوانید و exit را با خطای برنامه مرتبط کنید. وابستگی بیرونی، متغیر لازم، دسترسی فایل و migration ناقص از فرضیه‌های ممکن‌اند، نه تشخیص قطعی. اگر یک سرویس را به‌صورت هدفمند اصلاح کردید، همان سرویس و سرویس‌های وابسته را دوباره بررسی کنید. سبزشدن یک بدون آزمودن endpoint و داده مورد انتظار کافی نیست.

از پاک‌سازی کورکورانه داده پرهیز کنید

فرمان docker compose down کانتینرها و شبکه‌های پروژه را متوقف و حذف می‌کند، اما گزینه -v دامنه حذف را به volumeهای مربوط نیز گسترش می‌دهد. مرجع رسمی down این تفاوت را توضیح می‌دهد. برای رفع یک خطای پورت یا متغیر محیط، پاک‌کردن volume پایگاه‌داده اغلب نه لازم است و نه امن. قبل از هر فرمان حذف‌کننده، نام پروژه، volumeهای متصل، ارزش داده و وضعیت نسخه پشتیبان را بررسی کنید. حتی در محیط محلی ممکن است داده آزمون یا تنظیماتی داشته باشید که بازسازی‌اش وقت‌گیر باشد.

بازسازی image هم پاسخ همیشگی نیست. اگر config نشان می‌دهد متغیر یا پورت اشتباه است، همان علت را اصلاح کنید. اگر لاگ نشان می‌دهد برنامه داخل image فایل لازم را ندارد، آن وقت مسیر build و نسخه image را بررسی کنید. ترتیب کم‌خطر این است: مشاهده، فرضیه، یک تغییر، آزمون مجدد. با این ترتیب می‌توانید نتیجه را به هم‌تیمی خود توضیح دهید و در صورت لزوم تغییر را برگردانید.

چک‌لیست ده‌دقیقه‌ای یک گزارش قابل‌پیگیری

  1. پوشه پروژه، فایل‌های Compose فعال و نام پروژه را ثبت کنید.
  2. رفتار مورد انتظار و خطای دقیق docker compose up یا مرورگر را بنویسید.
  3. با docker compose config فایل نهایی، متغیرها، پورت‌ها و مسیرها را بررسی کنید؛ مقدار حساس را منتشر نکنید.
  4. با docker compose ps --all سرویس غایب، متوقف، در حال restart یا running را مشخص کنید.
  5. در صورت نیاز، لاگ همان سرویس را با docker compose logs و تعداد خط محدود بخوانید.
  6. اولین پیام خطای واقعی و زمان آن را کنار تغییر اخیر پروژه قرار دهید.
  7. اگر مسئله اتصال میان سرویس‌هاست، نام سرویس، وضعیت وابستگی و healthcheck را بررسی کنید.
  8. اگر مسئله دسترسی از مرورگر است، پورت host، آدرس و binding برنامه را تطبیق دهید.
  9. فقط یک فرضیه و یک تغییر کوچک را در هر نوبت اجرا کنید و نتیجه را دوباره با config، ps و logs بسنجید.
  10. هیچ volume یا داده‌ای را برای «امتحان» حذف نکنید؛ ابتدا اثر فرمان و نسخه پشتیبان را روشن کنید.

برای گزارش تیمی، سه شاهد کافی و مفیدتر از یک اسکرین‌شات شلوغ‌اند: بخش غیرحساس پیکربندی نهایی، وضعیت سرویس موردنظر و چند خط مرتبط از لاگ با زمان رخداد. هر ادعا را از شاهد جدا کنید؛ مثلاً «وب به پایگاه‌داده وصل نشد» مشاهده است، اما «رمز اشتباه است» فقط یک فرضیه تا وقتی آن را سنجیده‌اید.

پرسش‌های متداول

چرا running است ولی صفحه باز نمی‌شود؟

running فقط وضعیت فرایند است. پورت منتشرشده در ps، آدرس درخواست، binding برنامه و آمادگی endpoint را جداگانه بررسی کنید. ممکن است برنامه هنوز در حال راه‌اندازی باشد یا روی پورت دیگری گوش دهد.

config بهتر است یا logs؟

برای خطاهای تعریف و مقدار نهایی متغیرها از config شروع کنید؛ برای خطای فرایند داخل سرویس به logs بروید. این دو مکمل هم‌اند و هیچ‌کدام به‌تنهایی همه لایه‌ها را نشان نمی‌دهند.

آیا down -v راه‌حل سریع خطاهای Compose است؟

خیر. این گزینه می‌تواند داده volume را حذف کند. فقط وقتی اثر آن را دقیق می‌دانید، داده قابل‌حذف است و نسخه پشتیبان لازم را دارید به آن فکر کنید؛ برای اولیه لازم نیست.

depends_on چرا مشکل پایگاه‌داده را حل نکرد؟

ترتیب شروع با آمادگی یکی نیست. healthcheck معتبر و شرط service_healthy می‌تواند منتظر آماده‌شدن وابستگی بماند، اما باید خود معیار سلامت را هم درست طراحی و با لاگ بررسی کنید.

جمع‌بندی و گام بعدی

Compose را از لایه‌ای شروع کنید که کمترین تغییر را می‌خواهد: config برای مدل نهایی، ps --all برای وضعیت، logs برای رفتار برنامه و سپس پورت یا healthcheck برای اتصال و آمادگی. تا وقتی شواهد ندارید، حذف volume، تغییر دسترسی فراگیر یا بازسازی همه سرویس‌ها فقط مسئله را مبهم‌تر می‌کند. یک گزارش کوتاه از شواهد و فرضیه بسازید و پس از هر اصلاح، همان مسیر را دوباره طی کنید تا بدانید کدام تغییر واقعاً اثر داشته است.

در مشکل بعدی Compose، پیش از پاک‌کردن یا بازسازی، خروجی config، وضعیت ps --all و چند خط مرتبط از logs را کنار هم ثبت کنید و فقط یک فرضیه را در هر نوبت بیازمایید.