وبسایت شخصی حسن هاشمی

برنامه نویس. ایران. قم :))

نکاتی حول service discovery و load balancing

همونطور که می دونید موقعی که از تئوری مایکروسرویس و این چیزای قشنگی که در موردش میگن میاید بیرون و وارد عمل میشید تازه روی بد قضیه رو می بینید، این وسط 2 مشکل خیلی خودش رو نشون میده:

1- Service discovery

2- Scale out


مسئله اول Service discovery و deployment

به این معناست که وقتی برنامه شما، از سرویس های مختلف تشکیل شده بالاخره این سرویس های باید بتونن همدیگرو پیاده کنن. اینجا جائیه که باید 30 ثانیه مکث کنید و فکر کنید.

فرض کنید سرویس A یه سری خدمات میده به سرویس B، حالا موقعی که میخوایم برنامه رو توی دیتاسنتر منتشر کنیم باید یه مکانیزمی برای سرویس B تدارک ببینیم که بتونه محل فیزیکی سرویس A رو شناسایی کنه.

اون اوایل که شروع کردم اولین چیزی که به ذهنم زد، یه lookup مرکزی بود، اما چیزی نگذشت که دیدم مدیریت اون lookup هم خودش یه پروژه ای هست.

هرمکانیزمی که طراحی می کنید باید به این موارد توجه داشته باشید:

  • location transparency: نمیخواید که همه جای برنامه های دیگه پر باشه از ip های سرویس دیگه!
  • Fault tolerance: خطاهای داخل سرویس ها یا down شدن سرو��های فیزیکی باید reflect بشن.

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

Scaleout و Elasticity
هر موقع خواستین به cluster سرور فیزیکی اضافه کنید و روش سرویس های تون رو منتشر کنید و انتظار داشته باشید که سیستم کش بیاد. جدای ازین باید یه مکانیزمی باشه که به شما اجازه بده سرویس هایی که لود روشون زیاده رو replicate کنیم (یکی از دلایل اصلی که اصلا رفتیم سراغ مایکروسرویس همین بود از اول)
این مواردی که گفتم به همین آسونیا نیست، به عنوان مثال وضعیتی رو تصور کنید که فرضا 50 تا سرویس دارید، و یکی از این سرویس ها رو میخواید روی 5 تا سرور فیزیکی منتشر کنید و اگه در صورت نیاز لود بیشتری اومد 5می رو هم بیارید توی بازی. اما این آوردن توی بازی مستلزم این هست که اولا بتونید به سرعت در موقع نیاز سرویس رو سرور جدید منتشر کنید دوما بقیه سرویس ها بدون تغییر بتونن، نمونه جدید از سرویس رو بشناسن و بهش متصل بشن، حالا اینجا اگه شما مورد location transparency رو رعایت نکرده باشین به مشکل بزرگی میخورین که عملا ادامه کارو غیر ممکن می کنه.

با توجه به این دو موردی که توضیح داده شد، میخوایم ببینیم Docker چه کمکی در این میان می کنه، من اینجا نمی خوام توضیح بدم که Docker چیه و ... . در نتیجه اگه با داکر آشنایی ندارین فکر می کنم زمانش باشه که برید سراغش.

داکر برای حل دوتا مشکلی که بالا گفتم یه سولوشن built-in ارائه کرده که بنام Docker swarm، البته روش های دیگه هم هستن (به عنوان مثال ما رفتیم سراغ ( Kubernetes) ).

Docker swarm
یه ابزار container orchestration هست که به شما اجازه میده 2 مورد بالا رو خیلی راحت انجام بدین. مزیت خیلی خوبش هم سادگی فوق العاده ش هست (شما با چند تا command می تونید یه cluster بسازین و بهش node اضافه کنید).

توی این پست بیشتر میخوایم روی این که swarm چطور این کارو انجام میده تمرکز کنیم. خب بیاید از یه کانفیگ ساده شروع کنیم

services:      
  api-app:
    replicas: 3
    ports:
      - "8080:80"
  ui-app:
    ports:
      - "8085:80"
    environment:
      - "apiAppAddress:Url=http://api-app:8080"     
networks:
  myapp-net:


کافیه من این فایل کانفیگ رو بدم به Swarm تا دوتا image از 2 تا سرویس من که یکیش ui هست و اون یکی api رو توی دو تا container منتشر کنه. به نحوه ارجاع به سرویس اول از سرویس دوم دقت کنید که فقط اسم سرویس هست، و به این معنا که docker خودش به صورت داخلی یه DNS داره برای containre های یه network .
قسمت networks هم  که مسئله auto scaleout رو برای ما هندل می کنه.

حالا میمونه مورد load balancing:
خب اولین چیزی که به ذهن میرسه اینه که اصلا در مورد Load balancer های معمولی صحبت نمی کنیم، مثل HA Proxy و امثالش چون کاملا بی ربط هستن به بحث و تنها دلیلی که الان اسمش رو آوردم به خاطر اینه که از اشتباهات بعدی جلوگیزی کنم.
در واقع داخل دیتاسنتر اولین بار ریکوئست میاد به یکی از لود بالانسرهای بالایی مثل HA Proxy و یا لودبالانسینگ که خود IIS میده و ...، بعد ازینکه ریکوئست به لود بالانسر اومد، باید به سروری که هندل کننده درخواست هست برسه و فرض کنید که یه سرور هست که رابط کاربری رو به کاربر نشون میده مثل یه برنامه aspnet mvc, php و ... موضوعی که مهمه اینه که این سرورها باید خودشون به سرورهای عقب تری درخواست رو بزنن که در واقع سرویس های اصلی ما هستن و باز نکته اصلی اینه که ما اینجا لود بالانسری نداریم! ممکنه 10 تا سرور ui همگی به یه سرویس دهنده ریکوئست بزنن که منجر به ترکیدنش بشن.

توضیح کوچیکی که بالا نوشتم نشون میده که باید یه مکانیزم لودبالانسینگ بین خود سرویس ها باشه.

Docker swarm به صورت built-in مکانیزم خیلی خوبی داره به نام ingress mode ( یه حالت دیگه هم به نام Host Mode هست ولی کارایی کمتری داره برای پروژه های بزرگ، این مورد رو اینجا پوشش نمیدم)

توی این حالت، موقعی که ریکوئست به یه node توی کلاستر میاد، 2 حالت هست: یا این که container ی که باید درخواست رو هندل کنه توی همون node هست که درخواست مستقیما بهش route میشه و تمام. در غیر این صورت docker ریکوئست رو فروارد می کنه به اون node ی که container رو داخل خودش نگه داری می کنه.



اگه به عکس بالا نگاه کنید، نکته ش این هست که فرض کنید ریکوئست از یه لود بالانسر خارجی میاد به یکی ازین 3 تا هاست (سرور) ولی فقط یکیشون سرویسی که باید هندل کنه رو داره و در نتیجه اون دوتای دیگه دارن ریکوئست رو فروارد می کنن به Host 1 . ممکن بود ما چندین replica از container 1 داشتیم که در اون صورت ریکوئست ها به صورت لوکال هندل می شدن.
Loading