لود بالانسینگ (Load Balancing) چیست؟

لود بالانسینگ (Load Balancing) یعنی توزیع درخواست‌های ورودی بین چند سرور به جای اتکا به یک ماشین، تا هم ظرفیت پاسخ‌گویی بالا برود و هم از کار افتادن یک سرور، کل سرویس را از دسترس خارج نکند. دستگاه یا نرم‌افزاری که این توزیع را انجام می‌دهد «لود بالانسر» نام دارد و در عمل نقطه ورود ترافیک به مجموعه سرورهای شماست.

چرا اصلاً ترافیک را توزیع می‌کنیم؟

سه دلیل عملی پشت این کار وجود دارد. اول ظرفیت: وقتی یک سرور به سقف CPU یا حافظه می‌رسد، به جای ارتقای بی‌پایان همان ماشین (Scale Up) چند سرور هم‌ارز کنار هم می‌گذارید و بار را بینشان پخش می‌کنید (Scale Out). دوم دسترس‌پذیری: اگر یک نود خراب شود، لود بالانسر آن را از چرخه خارج می‌کند. سوم نگهداری بدون قطعی: می‌توانید یک سرور را از چرخش بیرون بیاورید، به‌روزرسانی کنید و برگردانید، بدون اینکه سرویس بخوابد.

نکته‌ای که معمولاً فراموش می‌شود این است که توزیع بار وقتی معنا دارد که نودها واقعاً از هم مستقل باشند. اگر هر دو سرور روی یک هاست فیزیکی، یک سوییچ و یک منبع تغذیه نشسته باشند، شما بار را پخش کرده‌اید نه ریسک را. برای همین نودها معمولاً روی سرور فیزیکی جدا یا رک‌های مجزا قرار می‌گیرند و برای بارهای متغیر، افزودن نودهای سبک روی ماشین مجازی راه ارزان‌تری برای بالا بردن ظرفیت است.

لایه ۴ یا لایه ۷؛ لود بالانسر روی کدام سطح کار می‌کند؟

لود بالانسر لایه ۴ (L4) در سطح TCP/UDP کار می‌کند؛ فقط آدرس مبدأ و مقصد و پورت را می‌بیند و محتوای بسته را باز نمی‌کند. سربار کمی دارد، با هر پروتکلی کار می‌کند (دیتابیس، SMTP، RDP، سرویس‌های بازی) و چون TLS را باز نمی‌کند، رمزنگاری تا خود سرور پشتیبان دست‌نخورده می‌ماند.

لود بالانسر لایه ۷ (L7) محتوای HTTP را می‌فهمد و می‌تواند بر اساس دامنه، مسیر URL، هدر یا کوکی تصمیم بگیرد؛ مثلاً /api را به یک گروه سرور و بقیه مسیرها را به گروه دیگری بفرستد، TLS را ترمینیت کند یا پاسخ را کش کند. هزینه‌اش پردازش بیشتر و رمزگشایی ترافیک روی خود لود بالانسر است.

یک نکته عملی: در حالت L4 و در TLS Passthrough، سرور پشتیبان آی‌پی واقعی کاربر را نمی‌بیند و فقط آی‌پی لود بالانسر را می‌بیند؛ راه‌حل استاندارد، فعال کردن PROXY Protocol روی هر دو سمت است. در L7 همین کار با هدرهای X-Forwarded-For و X-Real-IP انجام می‌شود، اما سرور پشتیبان هم باید طوری تنظیم شود که فقط به این هدرها از سمت لود بالانسر اعتماد کند.

الگوریتم‌های توزیع بار

  • Round Robin: درخواست‌ها به ترتیب و چرخشی بین سرورها پخش می‌شوند. پیش‌فرض بیشتر ابزارهاست و وقتی سرورها هم‌توان و درخواست‌ها هم‌وزن باشند خوب کار می‌کند.
  • Weighted Round Robin: همان چرخش، اما به سرورهای قوی‌تر وزن بیشتری می‌دهید؛ مناسب وقتی سخت‌افزار نودها یکسان نیست.
  • Least Connections: درخواست به سروری می‌رود که کمترین اتصال فعال را دارد. وقتی مدت‌زمان درخواست‌ها خیلی متفاوت است (آپلود فایل کنار یک API سبک)، معمولاً نتیجه بهتری از Round Robin می‌دهد.
  • IP Hash و Hash: از آی‌پی کلاینت یا کلید دلخواهی مثل URL هش گرفته می‌شود تا یک کلاینت همیشه به یک سرور برسد. در حالت Consistent، اضافه یا کم شدن یک نود کل نگاشت را به هم نمی‌ریزد و فقط بخشی از کلیدها جابه‌جا می‌شوند.
  • Least Response Time: ترافیک به سروری می‌رود که سریع‌تر پاسخ می‌دهد. اینجا باید مراقب بود؛ سروری که سریع خطا برمی‌گرداند هم «سریع» به نظر می‌رسد و این الگوریتم بدون هلث‌چک درست، بار را روی نود خراب متمرکز می‌کند.

هلث‌چک؛ چیزی که لود بالانسینگ را واقعی می‌کند

بدون بررسی سلامت، لود بالانسر همچنان درخواست را به سرور از کارافتاده می‌فرستد و شما فقط خطا را بین کاربران پخش کرده‌اید. دو مدل هلث‌چک وجود دارد:

  • غیرفعال (Passive): لود بالانسر از روی ترافیک واقعی قضاوت می‌کند؛ اگر تعدادی درخواست پشت سر هم شکست بخورد، نود را برای مدتی کنار می‌گذارد. در نسخه متن‌باز Nginx این کار با پارامترهای max_fails و fail_timeout روی هر سرور انجام می‌شود.
  • فعال (Active): لود بالانسر مستقل از کاربران، به‌صورت دوره‌ای خودش نود را صدا می‌زند. HAProxy این را با check و option httpchk در نسخه متن‌باز دارد؛ در Nginx دستور health_check بخشی از نسخه تجاری NGINX Plus است.

عمق هلث‌چک هم مهم است. چک ساده TCP فقط ثابت می‌کند پورت باز است، نه اینکه اپلیکیشن سالم است؛ چک HTTP روی مسیری مثل /healthz که واقعاً به دیتابیس یا کش دست بزند، حالت «پروسه زنده است ولی برنامه کار نمی‌کند» را هم می‌گیرد. در عین حال این مسیر را سنگین یا بیش از حد سخت‌گیر نکنید؛ اگر همه نودها به یک دیتابیس مشترک وصل باشند و آن دیتابیس کند شود، هلث‌چک عمیق همه نودها را هم‌زمان ناسالم علامت می‌زند و سرویس کاملاً می‌خوابد.

پایداری نشست (Session Persistence)

فرض کنید کاربر روی سرور اول لاگین کرده و نشست او در حافظه همان سرور ذخیره شده است. اگر درخواست بعدی به سرور دوم برود، کاربر خودش را بیرون‌افتاده می‌بیند. دو راه‌حل وجود دارد:

  • چسبندگی نشست (Sticky Session): لود بالانسر کاربر را همیشه به همان سرور قبلی می‌فرستد؛ در L7 معمولاً با کوکی و در L4 با هش آی‌پی مبدأ. سریع است ولی توزیع بار را نامتوازن می‌کند و با مرگ آن نود، نشست کاربر به هر حال از بین می‌رود. هش آی‌پی هم وقتی گروه بزرگی از کاربران پشت یک NAT یا یک آی‌پی اپراتور هستند دقتش را از دست می‌دهد.
  • بی‌حالت کردن اپلیکیشن: نشست‌ها را در انباری مشترک مثل Redis یا دیتابیس نگه دارید یا از توکن امضاشده استفاده کنید تا هر درخواست بتواند به هر نودی برود. این راه تمیزتر است و مقیاس‌پذیری واقعی می‌دهد.

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

انواع لود بالانسر بر اساس نحوه پیاده‌سازی

  • سخت‌افزاری: دستگاه فیزیکی اختصاصی که در سطح دیتاسنتر و سازمان‌های بزرگ به کار می‌رود؛ توان پردازش بالا و امکانات امنیتی یکپارچه دارد، اما هزینه تهیه و نگهداری فیزیکی هم دارد. F5 Networks، Cisco و Barracuda از تولیدکنندگان شناخته‌شده این حوزه‌اند.
  • نرم‌افزاری: روی سرورهای معمولی نصب می‌شود و انعطاف بیشتری دارد. Nginx و HAProxy رایج‌ترین گزینه‌ها هستند؛ Envoy و Traefik در معماری‌های میکروسرویس و LVS/IPVS در سطح لایه ۴ هم استفاده می‌شوند.
  • ابری و مدیریت‌شده: سرویس‌هایی مانند Cloudflare Load Balancing، AWS Elastic Load Balancing، Google Cloud Load Balancing و Azure Load Balancer که زیرساختش را خودتان نگه نمی‌دارید و معمولاً توزیع جغرافیایی و لایه‌ای از محافظت در برابر حملات DDoS را هم همراه دارند.

رابطه لود بالانسینگ با دسترس‌پذیری بالا (High Availability)

لود بالانسینگ به‌تنهایی برابر با HA نیست. اگر یک لود بالانسر جلوی چند سرور بگذارید، تک‌نقطه شکست را فقط جابه‌جا کرده‌اید: حالا خرابی خود لود بالانسر همه چیز را می‌خواباند. الگوی رایج، دو لود بالانسر با یک آی‌پی شناور است که با VRRP (مثلاً Keepalived) بینشان جابه‌جا می‌شود؛ اگر نود فعال بمیرد، نود دوم آی‌پی را برمی‌دارد.

لایه بعدی، توزیع در سطح DNS است: چند رکورد A برای یک نام یا سیاست‌های مبتنی بر موقعیت جغرافیایی. این روش برای پخش ترافیک بین دیتاسنترهای مختلف مفید است اما جایگزین لود بالانسر نمی‌شود، چون پاسخ DNS در ریزالورها کش می‌شود و تا انقضای TTL بخشی از کاربران همچنان به آدرس قدیمی می‌روند. برای هدایت کاربران هر منطقه به نزدیک‌ترین سرور، Geo DNS ابزار مناسب‌تر این لایه است.

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

پیاده‌سازی عملی Round Robin با Nginx

در این مثال سه سرور اوبونتو داریم: یکی نقش پروکسی (لود بالانسر) و دو تای دیگر نقش سرورهای پشتیبان. ابتدا روی سرور پروکسی Nginx را نصب کنید:

sudo apt update
sudo apt install nginx

سپس فایل پیکربندی را باز کنید و بخش زیر را در آن بگذارید. Round Robin در Nginx پیش‌فرض است و لازم نیست دستوری برای آن بنویسید؛ درخواست اول به سرور اول، درخواست دوم به سرور دوم و به همین ترتیب می‌رود.

sudo nano /etc/nginx/nginx.conf
http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    '"$upstream_addr"';   # backend that answered the request

    access_log /var/log/nginx/access.log main;

    upstream backend {
        server 192.168.1.2 max_fails=3 fail_timeout=15s;   # backend node 1
        server 192.168.1.3 max_fails=3 fail_timeout=15s;   # backend node 2
    }

    server {
        listen 80;
        server_name proxy.example.com;   # load balancer domain or IP

        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

توضیح خط‌به‌خط

  • upstream backend: گروه سرورهای پشتیبان؛ به جای آی‌پی‌های نمونه، آی‌پی داخلی سرورهای خودتان را بگذارید. max_fails تعیین می‌کند بعد از چند خطای پیاپی نود کنار گذاشته شود و fail_timeout مدت این کنارگذاری را مشخص می‌کند؛ همین دو پارامتر، هلث‌چک غیرفعال Nginx متن‌باز هستند.
  • log_format و access_log: با افزودن $upstream_addr به فرمت لاگ، در هر خط می‌بینید آن درخواست به کدام سرور پشتیبان رفته است؛ ساده‌ترین راه برای تأیید اینکه توزیع بار واقعاً کار می‌کند.
  • proxy_set_header: بدون این هدرها، سرور پشتیبان همه درخواست‌ها را از آی‌پی لود بالانسر می‌بیند و لاگ‌ها و محدودیت‌های مبتنی بر آی‌پی بی‌معنا می‌شوند.
  • برای تغییر الگوریتم، داخل بلوک upstream دستور least_conn; یا ip_hash; را اضافه کنید، یا با weight= روی هر خط server وزن‌دهی کنید.

پیش از ری‌استارت، پیکربندی را اعتبارسنجی کنید و بعد سرویس را بارگذاری مجدد کنید:

sudo nginx -t
sudo systemctl reload nginx

روی هر دو سرور پشتیبان هم سرویس موردنظر (مثلاً Nginx یا اپلیکیشن خودتان) را نصب کنید و مطمئن شوید پورت‌های لازم فقط از سمت لود بالانسر قابل دسترسی‌اند؛ باز گذاشتن این پورت‌ها روی کل اینترنت یعنی کاربران می‌توانند لود بالانسر را دور بزنند.

sudo ufw allow from 192.168.1.10 to any port 80

برای تست، از خود سرور پروکسی به هر نود درخواست بزنید و سپس لاگ زنده را ببینید تا مطمئن شوید درخواست‌ها بین دو سرور می‌چرخند:

curl http://192.168.1.2
tail -f /var/log/nginx/access.log

در نهایت می‌توانید یک دامنه یا زیردامنه را با رکورد A به آی‌پی لود بالانسر اشاره دهید و رفتار واقعی را زیر ترافیک بررسی کنید.

قبل از راه‌اندازی این موارد را بررسی کنید

  • کران‌جاب‌ها: زمان‌بندی‌هایی که روی همه نودها کپی شده‌اند، یک کار را چند بار اجرا می‌کنند؛ یا آن‌ها را به یک نود مشخص بسپارید یا از قفل توزیع‌شده استفاده کنید.
  • محل ترمینیت TLS: گواهی روی لود بالانسر بنشیند یا روی نودها؟ این انتخاب روی تمدید گواهی، لاگ‌ها و الزامات امنیتی اثر مستقیم دارد.
  • خروج تدریجی نود: هنگام به‌روزرسانی، اول نود را از چرخش خارج کنید، بگذارید اتصالات باز تمام شوند و بعد سرویس را ری‌استارت کنید.
  • مانیتورینگ به تفکیک نود: نرخ خطا و زمان پاسخ را جدا برای هر سرور پشتیبان ببینید؛ میانگین کل، نود بیمار را پنهان می‌کند.

لود بالانسینگ بیش از آنکه یک محصول باشد، یک تصمیم معماری است: باید بدانید چه چیزی را توزیع می‌کنید، چه چیزی مشترک می‌ماند و بعد از خرابی هر جزء، سرویس چه شکلی پیدا می‌کند. اگر در حال طراحی چنین ساختاری هستید و می‌خواهید پیش از خرید سخت‌افزار درباره تعداد نودها، لایه توزیع و مسیر رشد سرویس تصمیم بگیرید، استفاده از مشاوره فنی پیش از پیاده‌سازی معمولاً هزینه بازطراحی بعدی را حذف می‌کند.

ارسال نظر