Loading...

مبانی · 8 دقیقه مطالعه

502 Bad Gateway: یعنی چه و چگونه رفعش کنیم

یک 502 تقصیر بازدیدکنندهٔ شما نیست و به‌ندرت تقصیر CDN است. یعنی ماشینی که جلوی سایت شماست از سرورتان صفحه را خواسته و چیزی گرفته که نتوانسته استفاده کند. این راهنما از زاویهٔ proxy نوشته شده: edge واقعاً چه دیده، و کدام‌یک از چهار علت بوده.

به‌روزرسانی

502 Bad Gateway: یعنی چه و چگونه رفعش کنیم

واقعاً چه‌کسی می‌گوید «bad gateway»

کد وضعیت را هرچیزی که جلوی اپلیکیشن شما نشسته تولید می‌کند، نه خودِ اپلیکیشن. یک درخواست این مسیر را طی می‌کند: بازدیدکننده ← edge CDN ← سرور origin شما ← (اغلب) سرور اپ شما، و هر hopای که به hop بعدی proxy می‌کند می‌تواند دربارهٔ hop بعد از خودش یک 502 تولید کند. این مهم است چون صفحه‌ای که به آن نگاه می‌کنید را proxy نوشته: اگر اپلیکیشن شما اصلاً پاسخ داده بود، حتی با یک خطا، به‌جایش داشتید به صفحهٔ 500 خودتان نگاه می‌کردید.

پس یک 502 بیانیه‌ای دربارهٔ یک گفت‌وگوی ناموفق است، و سؤال کاربردی همیشه یکی است: کدام دو ماشین داشتند صحبت می‌کردند، و بین‌شان چه اشتباه شد؟

502 در برابر 504: تمایزی که همه از آن رد می‌شوند

این دو در پست‌های وبلاگ به‌جای هم استفاده می‌شوند، درحالی‌که نباید.

504 Gateway Timeout یعنی proxy درست وصل شده و بعد منتظر مانده. origin شما درخواست را پذیرفته و هرگز در بازهٔ timeout پاسخ‌دادن را تمام نکرده. علت‌های معمول یک کوئری کند دیتابیس، یک فراخوانی API خارجی بدون timeout خودش، یا یک process pool پر و در حال صف‌کشیدن است.

502 Bad Gateway یعنی پاسخ قابل‌استفاده‌ای برای انتظارکشیدن نبوده. کانکشن رد شده، یا پذیرفته و بعد قطع شده، یا بایت‌هایی که برگشته‌اند یک پاسخ معتبر HTTP نبوده‌اند.

اگر 504 می‌بینید، به این نگاه کنید که اپلیکیشن شما چقدر طول می‌کشد. اگر 502 می‌بینید، به این نگاه کنید که آیا اپلیکیشن شما اصلاً پاسخ می‌دهد یا نه.

علت 1 — origin کانکشن را رد کرد

proxy یک کانکشن TCP به origin شما باز کرده و بلافاصله رد شدن یا اصلاً بدون مسیر (route) گرفته. چیزی روی آن پورت listen نمی‌کرده، یا یک فایروال پکت را drop کرده.

در عمل چه چیزی این را تولید می‌کند: وب‌سرور یا container متوقف است؛ روی 127.0.0.1 به‌جای اینترفیس عمومی listen می‌کند؛ پورتی که در تنظیمات origin شماست با پورتی که سرویس رویش است مطابقت ندارد؛ یک فایروال host یا security group آدرس‌های IP CDN را مسدود می‌کند.

چگونه تأیید کنیم. از بیرون شبکهٔ خودتان، مستقیماً از origin بپرسید و هدر Hostای را بفرستید که سایت شما استفاده می‌کند — وگرنه originای که چند سایت را سرویس می‌دهد یا برای سایت اشتباه پاسخ می‌دهد یا رد می‌کند:

curl -sI -H "Host: example.com" http://ORIGIN_IP/

اگر آن curl به همان شکل شکست بخورد، CDN دارد حقیقت را گزارش می‌کند و راه‌حل روی origin است. اگر آن curl موفق شود درحالی‌که edge رد شدن می‌بیند، تفاوت تقریباً همیشه یک قانون فایروال است که شما را اجازه می‌دهد و edge را نه.

علت 2 — origin کانکشن را زودهنگام بست

کانکشن پذیرفته شد، سپس پیش از برگشتن یک پاسخ کامل از بین رفت. این رایج‌ترین 502 روی استک‌های PHP و Node زیر بار است.

چه چیزی این را تولید می‌کند: یک PHP-FPM pool که همهٔ workerهایش مشغول‌اند، پس کانکشن‌های جدید توسط kernel backlog پذیرفته و بعد drop می‌شوند؛ workerای که به سقف حافظه‌اش رسیده و وسط پاسخ‌دادن kill شده؛ یک پروسهٔ اپلیکیشن که روی آن درخواست خاص crash کرده؛ یک ناهماهنگی keepalive که در آن origin کانکشن‌های idle را زودتر از چیزی که proxy برای استفادهٔ دوباره انتظار دارد می‌بندد.

چگونه تأیید کنیم. به لاگ خطای خودِ origin در لحظهٔ 502 نگاه کنید — این تنها موردی است که origin داستان روشنی برای گفتن دارد. یک Premature end of script headers، یک خط segfault، یا یک هشدار PHP-FPM با متن server reached pm.max_children علت را رک‌وراست نام می‌برد. اگر 502ها در اوج ترافیک خوشه‌بندی می‌شوند نه اینکه تصادفی ظاهر شوند، دارید به اتمام ظرفیت pool نگاه می‌کنید، نه یک باگ.

علت 3 — handshake TLS به origin شکست خورد

اگر proxy با origin شما روی HTTPS صحبت می‌کند، handshake می‌تواند به دلایلی شکست بخورد که هیچ ربطی به گواهی‌ای که بازدیدکنندگان شما می‌بینند ندارد. edge گواهی عمومی را نگه می‌دارد؛ کانکشن پشت آن یک گفت‌وگوی جدا با گواهی خودش است.

چه چیزی این را تولید می‌کند: گواهی origin منقضی شده و کسی متوجه نشده چون گواهی عمومی خودکار تمدید می‌شود؛ origin چند نام را سرویس می‌دهد و برای انتخاب درست یکی به SNI نیاز دارد؛ origin فقط نسخه‌های TLS یا سایفرهایی را می‌پذیرد که proxy ارائه نمی‌دهد؛ گواهی www.example.com را پوشش می‌دهد اما proxy با درخواست example.com وصل می‌شود.

چگونه تأیید کنیم.

openssl s_client -connect ORIGIN_IP:443 -servername example.com </dev/null | head -20

زنجیره و تاریخ‌ها را بخوانید. در cdn.com.tr، edge به origin شما SNI می‌فرستد، پس گواهی‌ای که برای هاست‌نیمی که پیکربندی کرده‌اید معتبر است مذاکره می‌کند؛ گواهی‌ای که فقط برای vhost پیش‌فرض معتبر است، نه.

علت 4 — پاسخ یک HTTP معتبر نبود

کمیاب‌تر، و پیداکردنش رضایت‌بخش است. origin پاسخ داده، اما چیزی که برگشته قابل parse‌شدن نبوده: یک خط هدر بلندتر از بافر proxy، یک خط خروجی سرگردان که پیش از هدرها چاپ شده (یک هشدار PHP، یک byte-order mark در یک فایل include)، پاسخی که یک Content-Length ادعا می‌کند که با بدنه مطابقت ندارد، یا یک crash dump متن‌ساده که روی پورت 443 سرویس داده شده.

چگونه تأیید کنیم. مستقیم از origin بگیرید و به بایت‌های خام نگاه کنید، نه یک صفحهٔ رندرشده:

curl -sv -H "Host: example.com" http://ORIGIN_IP/ -o /dev/null

اگر اولین خط برگشتی HTTP/1.1 ... نباشد، پیدایش کرده‌اید. راه‌حل در اپلیکیشن شما یا بافرینگ خروجی آن است، و proxy حق داشته آن را رد کند.

وقتی یک CDN جلوی سایت است چه کنیم

یک CDN تشخیص را به دو شکل مفید تغییر می‌دهد.

صفحات cache‌شده به سرویس‌دهی ادامه می‌دهند. بازدیدکننده‌ای که چیزی را می‌خواهد که از قبل در cache edge هست، حتی وقتی origin پایین است آن را می‌گیرد، پس یک 502 نسبی — برخی صفحات خراب، بقیه سالم — معمولاً یعنی origin بالاست اما روی مسیرهای cache‌نشده، که معمولاً همان مسیرهای پویا هستند، دارد شکست می‌خورد.

یک نقطهٔ دید دوم به‌دست می‌آورید. edge origin شما را از بیرون، به‌طور پیوسته می‌بیند. اگر edge گزارش 502 می‌دهد و مرورگر خودتان به origin می‌رسد، تفاوت بین آن دو درخواست همان باگ است: فایروالی که IP شما را اجازه می‌دهد، رکورد DNSای که برای شما جای دیگری اشاره می‌کند، یا گواهی‌ای که فقط با نامی که شما اتفاقاً استفاده می‌کنید مطابقت دارد.

در cdn.com.tr، قانون‌های تحویل به شما اجازه می‌دهند روی مسیرهایی که می‌توانند تحملش کنند یک TTL طولانی‌تر cache نگه دارید، و همین است که یک حادثهٔ origin را به‌جای یک سایت مرده، به یک سایت باکیفیت‌پایین‌تر تبدیل می‌کند. این چیزی است که ارزش پیکربندی‌کردن پیش از نیازداشتنش را دارد، نه در حینش.

502ای که اصلاً دربارهٔ origin شما نیست

یک مورد ارزش نام‌بردن دارد چون ساعت‌ها وقت تلف می‌کند. اگر DNS شما به یک edge CDN اشاره می‌کند اما هاست‌نیم هنوز روی آن edge پیکربندی نشده، شما 502 نمی‌گیرید — شما یک 503 با صفحه‌ای می‌گیرید که می‌گوید هیچ سرویسی برای این دامنه پیکربندی نشده. این یعنی edge دارد به شما می‌گوید هیچ ایده‌ای ندارد از کدام origin بپرسد.

افراد بلافاصله بعد از تغییر DNS یک صفحهٔ خطا می‌بینند، فرض می‌کنند origin خراب است، و شروع می‌کنند به ری‌استارت‌کردن سرویس‌هایی که هرگز درگیر نبوده‌اند. اول کد وضعیت را بررسی کنید: 502 یعنی edge سراغ origin شما رفته و شکست خورده؛ آن نوع 503 یعنی edge هرگز originای برای امتحان‌کردن نداشته.

پرسش‌های پرتکرار

آیا 502 تقصیر من است یا CDN؟

معمولاً هیچ‌کدام — تقصیر origin است. CDN شکستی را که تجربه کرده گزارش می‌دهد؛ آن را اختراع نمی‌کند. استثنایی که ارزش بررسی دارد اتصال‌پذیری است: اگر فایروال origin آدرس‌های IP edge را مسدود کند، origin برای شما سالم است و برای CDN غیرقابل‌دسترس، و راه‌حل یک قانون allow است، نه چیزی روی اپلیکیشن.

چرا فقط گاهی 502 می‌گیرم؟

502های متناوب تقریباً همیشه یعنی ظرفیت، نه پیکربندی. یک process pool که در اوج‌ها پر می‌شود، workerای که recycle می‌شود، یا originای که کانکشن‌های keepalive را زودتر از چیزی که proxy انتظار دارد می‌بندد، همگی خطاهایی تولید می‌کنند که می‌آیند و می‌روند درحالی‌که یک تست ساده از لپ‌تاپ شما هر بار موفق می‌شود.

آیا رفرش‌کردن کمک می‌کند؟

چیزی به شما می‌گوید. اگر یک رفرش کار کند، شکست متناوب است و دارید به ظرفیت یا یک worker خاص نگاه می‌کنید. اگر هر رفرش دقیقاً به همان شکل شکست بخورد، پیکربندی است — یک کانکشن ردشده، یک پورت اشتباه، یک گواهی origin منقضی‌شده — و رفرش‌کردن آن را تغییر نمی‌دهد.

بدون کد وضعیت، چگونه 502 را از 504 تشخیص دهم؟

با اینکه چقدر منتظر مانده‌اید. یک 504 شما را وادار می‌کند تا پایان timeout، معمولاً چند ده ثانیه، منتظر بمانید، چون proxy واقعاً منتظر یک پاسخ است. یک 502 معمولاً سریع می‌رسد، چون یک کانکشن ردشده یا خراب سریع شکست می‌خورد.

آیا می‌توانم چیزی بهتر از صفحهٔ خطای پیش‌فرض به بازدیدکنندگانم نشان دهم؟

بله، و باید هم این کار را بکنید. یک CDN به‌جای پیش‌فرض proxy، محتوای cache‌شدهٔ قدیمی یا یک صفحهٔ خطای اختصاصی سرویس می‌دهد، که یک قطعی را به‌جای یک تجربهٔ خراب، به یک تجربهٔ بدترازحدمعمول تبدیل می‌کند. آن را پیش از یک حادثه تنظیم کنید، چون در حینش توجه اضافه‌ای برایتان نمی‌ماند.