ProHoster > وبلاگ > اداره > تعادل بار و مقیاس بندی اتصالات طولانی مدت در Kubernetes
تعادل بار و مقیاس بندی اتصالات طولانی مدت در Kubernetes
این مقاله به شما کمک میکند تا درک کنید که تعادل بار در Kubernetes چگونه کار میکند، هنگام مقیاسبندی اتصالات طولانیمدت چه اتفاقی میافتد، و چرا در صورت استفاده از پروتکلهای HTTP/2، gRPC، RSockets، AMQP یا دیگر پروتکلهای طولانی مدت، باید تعادل سمت کلاینت را در نظر بگیرید. .
کمی در مورد نحوه توزیع مجدد ترافیک در Kubernetes
Kubernetes دو انتزاع مناسب برای استقرار برنامهها ارائه میکند: Services و Deployments.
استقرارها توضیح می دهند که چگونه و چند نسخه از برنامه شما در هر زمان معین باید اجرا شود. هر برنامه به عنوان یک Pod مستقر می شود و یک آدرس IP به آن اختصاص داده می شود.
خدمات از نظر عملکرد مشابه یک متعادل کننده بار هستند. آنها برای توزیع ترافیک در چند پاد طراحی شده اند.
بیایید ببینیم چگونه به نظر می رسد.
در نمودار زیر می توانید سه نمونه از یک برنامه و یک بار متعادل کننده را مشاهده کنید:
متعادل کننده بار یک سرویس نامیده می شود و یک آدرس IP به آن اختصاص داده می شود. هر درخواست دریافتی به یکی از پادها هدایت می شود:
سناریوی استقرار تعداد نمونه های برنامه را تعیین می کند. تقریباً هرگز مجبور نخواهید بود که مستقیماً تحت زیر گسترش دهید:
به هر پاد آدرس IP اختصاص داده شده است:
مفید است که خدمات را مجموعه ای از آدرس های IP در نظر بگیرید. هر بار که به سرویس دسترسی پیدا می کنید، یکی از آدرس های IP از لیست انتخاب شده و به عنوان آدرس مقصد استفاده می شود.
به نظر می رسد این است.
درخواست curl 10.96.45.152 به این سرویس دریافت می شود:
این سرویس یکی از سه آدرس پاد را به عنوان مقصد انتخاب می کند:
ترافیک به یک غلاف خاص هدایت می شود:
اگر برنامه شما از یک فرانت اند و یک باطن تشکیل شده باشد، برای هر کدام هم یک سرویس و هم یک استقرار خواهید داشت.
هنگامی که فرانتاند درخواستی را به باطن ارائه میکند، نیازی به دانستن دقیق چند پاد ندارد: میتواند یک، ده یا صد باشد.
همچنین، فرانت اند چیزی در مورد آدرس پادهایی که به باطن سرویس می دهند، نمی داند.
هنگامی که فرانت اند درخواستی را به باطن ارسال می کند، از آدرس IP سرویس باطن استفاده می کند که تغییر نمی کند.
در اینجا چگونه به نظر می رسد.
زیر 1 جزء پشتیبان داخلی را درخواست می کند. به جای انتخاب یک مورد خاص برای باطن، درخواستی را به سرویس ارائه می کند:
این سرویس یکی از پادهای پشتیبان را به عنوان آدرس مقصد انتخاب می کند:
ترافیک از Pod 1 به Pod 5 می رود که توسط سرویس انتخاب شده است:
زیر 1 دقیقاً نمی داند چند پاد مانند زیر 5 در پشت سرویس پنهان شده است:
اما این سرویس دقیقاً چگونه درخواست ها را توزیع می کند؟ به نظر می رسد که از بالانس دور رابین استفاده شده است؟ بیایید آن را بفهمیم.
تعادل در خدمات Kubernetes
خدمات Kubernetes وجود ندارد. هیچ فرآیندی برای سرویسی که یک آدرس IP و پورت اختصاص داده شده است وجود ندارد.
شما می توانید با ورود به هر گره در خوشه و اجرای دستور netstat -ntlp این موضوع را تأیید کنید.
شما حتی نمی توانید آدرس IP اختصاص داده شده به سرویس را پیدا کنید.
آدرس IP سرویس در لایه کنترل، در کنترلر قرار دارد و در پایگاه داده - etcd ثبت می شود. همان آدرس توسط مؤلفه دیگری - kube-proxy - استفاده می شود.
Kube-proxy لیستی از آدرسهای IP را برای همه سرویسها دریافت میکند و مجموعهای از قوانین iptables را در هر گره در خوشه ایجاد میکند.
این قوانین می گوید: "اگر آدرس IP سرویس را مشاهده کنیم، باید آدرس مقصد درخواست را تغییر دهیم و آن را به یکی از پادها ارسال کنیم."
آدرس IP سرویس فقط به عنوان یک نقطه ورودی استفاده می شود و توسط هیچ فرآیندی که به آن آدرس IP و پورت گوش می دهد ارائه نمی شود.
بیایید به این نگاه کنیم.
یک خوشه از سه گره را در نظر بگیرید. هر گره دارای غلاف است:
غلاف های گره خورده با رنگ بژ بخشی از خدمات است. از آنجایی که سرویس به عنوان یک فرآیند وجود ندارد، به رنگ خاکستری نشان داده شده است:
اولین پاد یک سرویس درخواست می کند و باید به یکی از پادهای مرتبط برود:
اما سرویس وجود ندارد، فرآیند وجود ندارد. چگونه کار می کند؟
قبل از اینکه درخواست گره را ترک کند، قوانین iptables را طی می کند:
قوانین iptables میدانند که این سرویس وجود ندارد و آدرس IP آن را با یکی از آدرسهای IP پادهای مرتبط با آن سرویس جایگزین کنید:
درخواست یک آدرس IP معتبر به عنوان آدرس مقصد دریافت می کند و به طور معمول پردازش می شود:
بسته به توپولوژی شبکه، درخواست در نهایت به pod می رسد:
آیا iptables می تواند تعادل بار را بارگذاری کند؟
خیر، iptable ها برای فیلتر کردن استفاده می شوند و برای بالانس طراحی نشده اند.
با این حال، می توان مجموعه ای از قوانین را نوشت که مانند آن کار می کند شبه متعادل کننده.
و این دقیقاً همان چیزی است که در Kubernetes پیاده سازی شده است.
اگر سه پاد دارید، kube-proxy قوانین زیر را مینویسد:
زیر اول را با احتمال 33% انتخاب کنید در غیر این صورت به قانون بعدی بروید.
دومی را با احتمال 50% انتخاب کنید در غیر این صورت به قانون بعدی بروید.
سومین زیر را انتخاب کنید.
این سیستم باعث می شود که هر غلاف با احتمال 33 درصد انتخاب شود.
و هیچ تضمینی وجود ندارد که Pod 2 بعد از Pod 1 انتخاب شود.
یادداشت: iptables از یک ماژول آماری با توزیع تصادفی استفاده می کند. بنابراین، الگوریتم تعادل بر اساس انتخاب تصادفی است.
اکنون که متوجه شدید که خدمات چگونه کار می کنند، بیایید به سناریوهای خدمات جالب تر نگاه کنیم.
اتصالات طولانی مدت در Kubernetes به طور پیش فرض مقیاس نمی شوند
هر درخواست HTTP از frontend به backend توسط یک اتصال TCP جداگانه ارائه می شود که باز و بسته می شود.
اگر فرانتاند در هر ثانیه 100 درخواست به باطن ارسال کند، 100 اتصال TCP مختلف باز و بسته میشوند.
میتوانید با باز کردن یک اتصال TCP و استفاده از آن برای تمام درخواستهای HTTP بعدی، زمان پردازش درخواست و بارگذاری را کاهش دهید.
پروتکل HTTP دارای ویژگی به نام HTTP keep-alive یا استفاده مجدد از اتصال است. در این مورد، یک اتصال TCP برای ارسال و دریافت چندین درخواست و پاسخ HTTP استفاده می شود:
این ویژگی به طور پیشفرض فعال نیست: هم سرور و هم سرویس گیرنده باید بر اساس آن پیکربندی شوند.
خود تنظیمات برای اکثر زبانها و محیطهای برنامهنویسی ساده و قابل دسترس است.
در اینجا چند پیوند به مثال ها به زبان های مختلف وجود دارد:
اگر از keep-alive در سرویس Kubernetes استفاده کنیم چه اتفاقی می افتد؟
بیایید فرض کنیم که هر دو فرانت اند و بک اند از keep-alive پشتیبانی می کنند.
ما یک کپی از frontend و سه نسخه از backend داریم. فرانتاند اولین درخواست را میدهد و یک اتصال TCP به باطن باز میکند. درخواست به سرویس می رسد، یکی از پادهای پشتیبان به عنوان آدرس مقصد انتخاب می شود. پشتیبان پاسخی را می فرستد و فرانت اند آن را دریافت می کند.
برخلاف شرایط معمول که در آن اتصال TCP پس از دریافت پاسخ بسته می شود، اکنون برای درخواست های HTTP بیشتر باز نگه داشته می شود.
اگر فرانت اند درخواست های بیشتری به باطن ارسال کند چه اتفاقی می افتد؟
برای ارسال این درخواستها، از یک اتصال TCP باز استفاده میشود، همه درخواستها به همان باطنی که درخواست اول رفت، میروند.
آیا iptables نباید ترافیک را دوباره توزیع کند؟
در این مورد نه.
هنگامی که یک اتصال TCP ایجاد می شود، از طریق قوانین iptables می گذرد، که باطن خاصی را انتخاب می کند که ترافیک در آن جا خواهد رفت.
از آنجایی که تمام درخواستهای بعدی در یک اتصال TCP از قبل باز هستند، قوانین iptables دیگر فراخوانی نمیشوند.
بیایید ببینیم چگونه به نظر می رسد.
اولین پاد درخواستی را به سرویس ارسال می کند:
شما از قبل می دانید که در ادامه چه اتفاقی خواهد افتاد. این سرویس وجود ندارد، اما قوانین iptables وجود دارد که درخواست را پردازش می کند:
یکی از غلاف های پشتیبان به عنوان آدرس مقصد انتخاب می شود:
درخواست به غلاف می رسد. در این مرحله، یک اتصال TCP پایدار بین دو پاد برقرار خواهد شد:
هر درخواست بعدی از اولین pod از طریق اتصال از قبل ایجاد شده انجام می شود:
نتیجه زمان پاسخدهی سریعتر و توان عملیاتی بالاتر است، اما شما توانایی مقیاسسازی backend را از دست میدهید.
حتی اگر دو پاد در باطن داشته باشید، با اتصال ثابت، ترافیک همیشه به یکی از آنها خواهد رفت.
آیا می توان این را برطرف کرد؟
از آنجایی که Kubernetes نمی داند چگونه اتصالات مداوم را متعادل کند، این وظیفه به عهده شماست.
سرویسها مجموعهای از آدرسهای IP و پورتهایی هستند که به آنها نقطه پایانی میگویند.
برنامه شما می تواند لیستی از نقاط پایانی را از سرویس دریافت کند و تصمیم بگیرد که چگونه درخواست ها را بین آنها توزیع کند. شما می توانید یک اتصال دائمی را به هر پاد باز کنید و با استفاده از دور روبین درخواست های بین این اتصالات را متعادل کنید.
کد سمت کلاینت که مسئول تعادل است باید از این منطق پیروی کند:
لیستی از نقاط پایانی را از سرویس دریافت کنید.
یک اتصال دائمی برای هر نقطه پایانی باز کنید.
هنگامی که نیاز به درخواست است، از یکی از اتصالات باز استفاده کنید.
به طور منظم لیست نقاط پایانی را به روز کنید، موارد جدید ایجاد کنید یا در صورت تغییر لیست، اتصالات قدیمی قدیمی را ببندید.
این چیزی است که به نظر خواهد رسید.
به جای اینکه اولین پاد درخواست را به سرویس ارسال کند، می توانید درخواست ها را در سمت مشتری متعادل کنید:
باید کدی بنویسید که بپرسد کدام پادها بخشی از سرویس هستند:
پس از دریافت لیست، آن را در سمت مشتری ذخیره کنید و از آن برای اتصال به پادها استفاده کنید:
شما مسئول الگوریتم متعادل کننده بار هستید:
حال این سوال مطرح می شود: آیا این مشکل فقط در مورد HTTP keep-alive صدق می کند؟
تعادل بار سمت مشتری
HTTP تنها پروتکلی نیست که می تواند از اتصالات TCP دائمی استفاده کند.
اگر برنامه شما از پایگاه داده استفاده می کند، هر بار که نیاز به درخواست یا بازیابی سند از پایگاه داده دارید، یک اتصال TCP باز نمی شود.
در عوض، یک اتصال TCP دائمی به پایگاه داده باز شده و استفاده می شود.
اگر پایگاه داده شما در Kubernetes مستقر شده باشد و دسترسی به عنوان یک سرویس ارائه شود، با همان مشکلاتی که در قسمت قبل توضیح داده شد مواجه خواهید شد.
یک ماکت پایگاه داده بیشتر از بقیه بارگذاری می شود. Kube-proxy و Kubernetes به تعادل اتصالات کمک نمی کنند. شما باید مراقب باشید که پرس و جوها را در پایگاه داده خود متعادل کنید.
بسته به اینکه از کدام کتابخانه برای اتصال به پایگاه داده استفاده می کنید، ممکن است گزینه های مختلفی برای حل این مشکل داشته باشید.
در زیر نمونه ای از دسترسی به کلاستر پایگاه داده MySQL از Node.js آورده شده است:
var mysql = require('mysql');
var poolCluster = mysql.createPoolCluster();
var endpoints = /* retrieve endpoints from the Service */
for (var [index, endpoint] of endpoints) {
poolCluster.add(`mysql-replica-${index}`, endpoint);
}
// Make queries to the clustered MySQL database
بسیاری از پروتکل های دیگر وجود دارند که از اتصالات TCP دائمی استفاده می کنند:
WebSockets و WebSockets ایمن
HTTP / 2
gRPC
RSockets
AMQP
شما باید از قبل با بیشتر این پروتکل ها آشنا باشید.
اما اگر این پروتکل ها بسیار محبوب هستند، چرا یک راه حل متعادل کننده استاندارد وجود ندارد؟ چرا منطق مشتری باید تغییر کند؟ آیا راه حل بومی Kubernetes وجود دارد؟
Kube-proxy و iptables برای پوشش بیشتر موارد استفاده در هنگام استقرار در Kubernetes طراحی شدهاند. این برای راحتی است.
اگر از یک سرویس وب استفاده می کنید که یک REST API را نشان می دهد، خوش شانس هستید - در این مورد، اتصالات TCP مداوم استفاده نمی شود، می توانید از هر سرویس Kubernetes استفاده کنید.
اما هنگامی که شروع به استفاده از اتصالات TCP مداوم کردید، باید نحوه توزیع یکنواخت بار را در قسمت های پشتیبان بیابید. Kubernetes حاوی راه حل های آماده برای این مورد نیست.
با این حال، مطمئناً گزینه هایی وجود دارد که می تواند کمک کند.
ایجاد تعادل در ارتباطات طولانی مدت در Kubernetes
چهار نوع سرویس در Kubernetes وجود دارد:
ClusterIP
NodePort
متعادل کننده بار
بی سر
سه سرویس اول بر اساس یک آدرس IP مجازی کار می کنند که توسط kube-proxy برای ساخت قوانین iptables استفاده می شود. اما اساس اساسی همه خدمات یک سرویس بدون سر است.
سرویس Headless هیچ آدرس IP مرتبط با خود ندارد و تنها مکانیزمی برای بازیابی لیستی از آدرس های IP و پورت های pods (نقاط پایانی) مرتبط با آن ارائه می دهد.
تمامی خدمات بر اساس سرویس هدلس است.
سرویس ClusterIP یک سرویس بدون هد با برخی موارد اضافه شده است:
لایه مدیریت به آن یک آدرس IP اختصاص می دهد.
Kube-proxy قوانین iptables لازم را تولید می کند.
به این ترتیب می توانید Kube-proxy را نادیده بگیرید و مستقیماً از لیست نقاط پایانی به دست آمده از سرویس headless برای بارگذاری برنامه خود استفاده کنید.
اما چگونه می توانیم منطق مشابهی را به همه برنامه های مستقر در خوشه اضافه کنیم؟
اگر برنامه شما قبلاً مستقر شده است، این کار ممکن است غیرممکن به نظر برسد. با این حال، یک گزینه جایگزین وجود دارد.
سرویس مش به شما کمک خواهد کرد
احتمالاً قبلاً متوجه شده اید که استراتژی متعادل کردن بار سمت مشتری کاملاً استاندارد است.
هنگامی که برنامه شروع می شود، این موارد:
لیستی از آدرس های IP را از سرویس دریافت می کند.
یک استخر اتصال را باز می کند و حفظ می کند.
به صورت دوره ای استخر را با افزودن یا حذف نقاط پایانی به روز می کند.
هنگامی که برنامه بخواهد درخواستی ارائه دهد، این موارد را انجام می دهد:
یک اتصال در دسترس را با استفاده از برخی منطق انتخاب می کند (مثلاً دور روبین).
درخواست را اجرا می کند.
این مراحل برای هر دو اتصال WebSockets، gRPC و AMQP کار می کنند.
شما می توانید این منطق را در یک کتابخانه جداگانه جدا کرده و در برنامه های خود از آن استفاده کنید.
با این حال، می توانید به جای آن از مش های سرویس مانند ایستیو یا لینکرد استفاده کنید.
Service Mesh برنامه شما را با فرآیندی تقویت می کند که:
به طور خودکار آدرس های IP سرویس را جستجو می کند.
اتصالاتی مانند WebSockets و gRPC را آزمایش می کند.
درخواست ها را با استفاده از پروتکل صحیح متعادل می کند.
Service Mesh به مدیریت ترافیک در داخل خوشه کمک می کند، اما کاملاً منابع فشرده است. گزینه های دیگر استفاده از کتابخانه های شخص ثالث مانند Netflix Ribbon یا پروکسی های قابل برنامه ریزی مانند Envoy است.
اگر مسائل مربوط به تعادل را نادیده بگیرید چه اتفاقی می افتد؟
شما می توانید انتخاب کنید که از تعادل بار استفاده نکنید و هنوز هیچ تغییری را متوجه نشوید. بیایید به چند سناریو کاری نگاه کنیم.
اگر مشتری های شما بیشتر از سرورها باشد، این مشکل بزرگی نیست.
فرض کنید پنج کلاینت وجود دارد که به دو سرور متصل می شوند. حتی اگر تعادلی وجود نداشته باشد، از هر دو سرور استفاده می شود:
اتصالات ممکن است به طور مساوی توزیع نشوند: شاید چهار کلاینت به یک سرور متصل شده باشند، اما احتمال زیادی وجود دارد که از هر دو سرور استفاده شود.
مشکل سازتر سناریوی مخالف است.
اگر مشتریان کمتر و سرورهای بیشتری دارید، ممکن است از منابع شما کم استفاده شده و یک گلوگاه بالقوه ظاهر شود.
فرض کنید دو مشتری و پنج سرور وجود دارد. در بهترین حالت، دو اتصال دائمی به دو سرور از پنج سرور وجود خواهد داشت.
سرورهای باقیمانده بیکار خواهند بود:
اگر این دو سرور نتوانند به درخواست های مشتری رسیدگی کنند، مقیاس افقی کمکی نخواهد کرد.
نتیجه
سرویسهای Kubernetes به گونهای طراحی شدهاند که در اکثر سناریوهای استاندارد برنامههای وب کار کنند.
با این حال، به محض شروع کار با پروتکلهای برنامهای که از اتصالات TCP مداوم استفاده میکنند، مانند پایگاههای داده، gRPC یا WebSockets، سرویسها دیگر مناسب نیستند. Kubernetes مکانیسم های داخلی را برای متعادل کردن اتصالات TCP پایدار ارائه نمی دهد.
این بدان معنی است که شما باید برنامه هایی را با در نظر گرفتن تعادل سمت مشتری بنویسید.