در این مطلب تلاش شده به سوالاتی در خصوص معماری میکروسرویس پاسخ داده شود. این سوالات بعضا از جنس معماری و بعضا برسی ابزار های مختلف که به پیاده سازی این معماری کمک میکند هستند.
در بخش اول ابتدا تعریفی از معماری میکروسرویس ارائه میشود. سپس به برسی تفاوت های معماری میکروسرویس و monolithic پرداخته میشود همچنین نقاط قوت و ضعف هرکدام برسی شده و اصولی برای مهاجرت از معماری monolithic به میکروسرویس ارائه میشود.
در بخش بعدی به برسی اصولی پرداخته می شود که رعایت آن در معماری میکروسرویس باعث بهبود برخی از شاخص های کیفی معماری نرم افزار میشود.
در بخش بعد به برسی نحوه ارتباط سرویس ها با هم در معماری میکروسرویس همچنین روش های به حداقل رساندن این ارتباط و ابزار هایی که برای این ارتباط ها استفاده میشود میپردازیم.
بخش آخر به برسی ابزار ها و نکات مهم در فرآیند دیپلویمنت مرتبط به این معماری پرداخته میشود و برخی از این ابزار ها با هم مقایسه میشوند.
نیاز به وجود نرم افزاری که اجزای آن به صورت کاملا مستقل قابل قابل استقرار باشند در بین سال های ۱۹۹۰ تا ۲۰۰۰ برای حس شد.ابتدا این مشکل توسط معماری سرویس محور در همان سال ها برطرف شد. به این صورت که ماژول های مختلف که وابستگی کمی از هم داشتند به صورت مستقل از هم استقرار میافتند.
در آن سال ها استاندارد ارتباط سرویس ها با هم از طریق پروتکل پیامرسانی SOAP انجام میشد. ولی بین سال های ۲۰۰۸ تا ۲۰۱۰ استاندارد REST معرفی شد که از طریق پروتکل HTTP پیام رسانی را انجام میداد و این موضوع باعث راحتی ارتباط بین سرویس های مختلف شد.
تولد معماری میکروسرویس بین سال های ۲۰۱۱ تا ۲۰۱۲ اتفاق افتاد زمانی که گروهی متشکل از James Lewis و Martin Fowler یک معماری را طراحی کردند که در آن سرویس های تا حد زیادی کوچک توسط یک سیستم پیام رسانی سبک مانند api های در بستر HTTP با هم ارتباط داشتند.
معماری ماکروسرویس به معنی تقسیم نرم افزار به واحد های کوچک که به صورت مستقل قابل طراحی ، پیاده سازی و استقرار است. به این واحد های کوچک سرویس میگوییم. این سرویس ها لازم است که به صورت کامل کپسوله شده باشند به این شکل که نحوه پیاده سازی آن از بیرون از سرویس مشخص نبوده و تنها نحوه ارتباط با آن سرویس مشخص باشد.
این ارتباط معمولا از طریق API ها یا message broker ها انجام میشود که در فصل پنجم بیشتر به این موضوع پرداخته میشود.
در طراحی این معماری لازم است دو نکته مهم مدنظر قرار بگیرد:
۱) یکپارچگی: اجزای یک سرویس (ماژول ها، کلاس ها و …) لازم است که کاملا یکپارچه باشند. به عنوان مثال اگر سرویسی شامل دو ماژول است که با هم هیچ ارتباطی ندارند این دو ماژول میتوانند به دو سرویس مجزا تقسیم شوند.
۲) دوگانگی: در صورتی که دو سرویس به هم وابسته باشند اگر یکی از این سرویس ها تغییر کرد سرویس دیگر هم لازم است که تغییر کند. پس ارتباط بین سرویس ها باید تا حد ممکن کم باشد و تنها در موارد ضروری این ارتباط ایجاد شود.
به صورت کلی باید تا جای ممکن میزان یکپارچگی در میکروسرویس ها بالا و دوگانگی کم باشد. در این صورت سرویس ها قابلیت نگهداری ، توسعه و تست بالا دارند و پرفرمنس آن ها هم تا حد زیادی مطلوب میشود.
اسپاتیفای یک نرم افزار استریم موسیقی میباشد. میلیون ها کاربر به طور همزمان از این نرم افزار برای شنیدن موسیقی استفاده میکنند. پس لازم است که زیرساخت لازم برای مدیریت حجم بالای درخواست ها را داشته باشد.
برای مدیریت بیشتر این نرم افزار از معماری میکروسرویس استفاده میکند که در اینجا به نحوه پیاده سازی این معماری در نرم افزار اسپاتیفای میپردازیم.
یک نکته مهم در مورد این نرم افزار این است که بر خلاف بیشتر نرم افزار ها که کاربران ساعات کمی را در آن صرف میکندد در این نرم افزار ممکن ساعت های زیادی گاها تا ۸ ساعت در روز را صرف کنند. در این صورت تنها مشکل تعداد بالای کاربران نیستند بلکه ساعت بالای استفاده هم یکی از مشکلات و نیازمندی های این نرم افزار است.
برای برطرف کردن این نیازمندی ها این نرم افزار اجزای مختلف را به قسمت های کوچک تر تقسیم کرده و این تقسیم حتی در سطح تیم ها نیز اعمال شده به این شکل که هر تیم مسئول بخش کوچکی از نیازمندی ها میباشد. هر تیم شامل توسعه دهنده های فرانت اند و بک اند ، نیروی تست و … میباشد. این مدل از تقسیم بندی باعث میشود که هر تیم کاملا مستقل بوده ، آزادانه کار کند و به تیم های دیگر وابسته نباشد.
در صورت نیاز به افزودن قابلیت جدید هم دقیقا مشخص است که کدام تیم باید این قابلیت را اضافه کند. در نتیجه اسپاتیفای تقسیم سرویس ها را از سطح تیم ها شروع کرده است.
خدماتی که پلتفرم اسپاتیفای ارائه میدهد به هفت دسته بندی تقسیم میشوند از جمله:
برای پیاده سازی این زیرساخت ها اسپاتیفای از تعداد زیادی سرویس های کوچک استفاده میکند. در نوامبر ۲۰۲۳ اسپاتیفای دارای ۱۶۰۰ سرویس مختلف بوده است. همچنین به طور متوسط هر ماه حدودا ۱۰۰ سرویس جدید به این تعداد افزوده میشود.
در این بخش به بررسی تعدادی از الگو های طراحی ماکروسرویس ها پرداخته میشود و برسی میکنیم که این الگو ها کدام مشکل در این معماری را برطرف میکند.
یکی از مهم ترین تصمیماتی که یک معمار در ابتدا یا اواسط توسعه یک نرم افزار باید بگیرد انتخاب میان معماری بر پایه سرویس یا معماری monolithic است. رویه کلی این انتخاب به این شکل است که نرم افزار هایی که تیم توسعه کوچک، معماری ساده یا بودجه کمی برای توسعه دارند معماری monolithic را انتخاب کرده و نرم افزار های پیچیده تر معماری بر پایه سرویس را انتخاب میکنند. البته این موضوع مطلق نبوده و موضوعی نسبی است. در این فصل به برسی رویه کامل تری از این انتخاب و trade-off بین این دو معماری، همچنین روش های مهاجرت از معماری monolithic به معماری میکروسرویس پرداخته میشود.
در ابتدا یک تعریف از معماری monolithic ارائه میشود و انواع آن برسی میشود.
بهترین تعریف که برای معماری monolthic میتوان بیان کرد نرم افزاری است که تمام اجزای آن باید در کنار هم deploy شوند. البته این تعریف شامل طیف بزرگی از نرم افزار ها میشود اما به طور کلی نرم افزار های پیاده سازی شده با این معماری را میتوان به سه دسته کلی تقسیم کرد:
با برسی نقاط قوت و ضعف این دو معماری میتوان نتیجه گرفت که تحت چه شرایطی از کدام یک از این معماری ها استفاده کرد. به طور کلی از آنجایی که در معماری Monolith روند توسعه ، تست و دیپلویمنت راحت تر است در پروژه هایی که تیم توسعه کوچک یا کم تجربه دارند انتخاب این معماری مناسب تر است. زیرا در معماری میکروسرویس لازم است که تیم ها به ازای هر سرویس از هم مستقل باشند.
فارغ از بحث ساختار تیم ها تفاوت این دو معماری را میتوان از منظر قابلیت های هرکدام همچنین ویژگی های کیفی و موارد دیگر نیز برسی کرد.
در بحث performance هیچ یک از این دو معماری برتری نسبت به یکدیگر ندارند. البته پیاده سازی نرم افزار با معماری Monolith به دلیل این که ارتباط بین ماژول ها با سرعت بالا و زمان کم انجام میشود مزیت نسبی برای ویژگی پرفرمنس ایجاد میکند ولی این مزیت مطلق نیست. در معماری میکروسرویس میتوان با استفاده از تکنیک هایی مانند صف ها ، load balancer ها ، caching و … به پرفرمنس بالاتری دست پیدا کرد البته امکان استفاده از برخی از این ابزار ها در معماری Monolith نیز وجود دارد ولی معماری ماکروسرویس استفاده از این ابزار ها را راحت تر و کارامد تر میکند. همچنین در صورتی که کاربران نرم افزار به طور گسترده ای رشد کنند امکان توسعه مقیاس نرم افزار در معماری میکروسرویس بیشتر است پس scalability معماری میکروسرویس بالاتر میباشد.
موضوع بعدی بحث maintenance میباشد در این ویژگی کیفی معماری میکروسرویس مزیت مطلق نسبت به معماری Monolith دارد. زیرا هر سرویس به طور جداگانه توسعه داده شده و نگهداری و عیب یابی در یک سرویس کوچک بسیار ، بسیار راحت تر از یک نرم افزار Monolith نسبتا بزرگ میباشد.
معیار بعدی راحتی توسعه نرم افزار میباشد در این معیار چون معماری Monolith ساختار ساده تری دارد فرآیند توسعه آن راحت تر بوده پس در این معیار معماری Monolith مزیت دارد.
در موضوع Availability معماری میکروسرویس وضعیت بهتری دارد زیرا سرویس های آن به صورت مجزا deploy شده و این مزیت را ایجاد میکند که deploy های سبک تر با تغییرات کمتر انجام شود همچنین در صورت کار نکردن یک سرویس در محیط پروداکشن تمام نرم افزار از کار نیوفتاده و بقیه سرویس ها به فعالیت خود ادامه میدهند البته این موضوع باعث برتری ویژگی Reliability در معماری میکروسرویس نیز میشود.
میتوان نتیجه گرفت که در نرم افزار هایی که توسعه سریع و راحت مورد نظر است و ویژگی های Availability, Reliability ، scalability مورد نظر نمیباشد استفاده از معماری Monolith مزیت بیشتری دارد و در نرم افزار هایی که این موارد اهمیت دارند معماری میکروسرویس انتخاب بهتری میباشد.
ممکن است یک نرم افزار در مراحل ابتدایی توسعه خود به دلیل نیاز به توسعه سریع با معماری Monolith پیاده سازی شده باشد ولی در طول زمان با بزرگتر و پیچیده تر شدن نرم افزار نیاز باشد که هر ماژول آن توسط یک سرویس جدا پیاده سازی شده و توسط تیم مستقلی نگه داری شود پس نیاز است راهکار هایی معرفی شود که این فرآیند معاجرت را ساختارمند تر و ساده تر کند. به طور کلی برای مهاجرت از معماری monolith به ماکروسرویس نیاز نیست که تمام نرم افزار از اول تولید شود بلکه با روش هایی میتوان نرم افزار موجود را به سرویس های مختلف تقسیم کرد. عملیات این مهاجرت عملیاتی زمان بر میباشد که ممکن است ماه ها یا سال ها به طول انجامد. در طول این مهاجرت به احتمال زیاد نیاز است که قابلیت های جدیدی که از سمت کسب و کار یا تیم محصول یا کاربران تعیین میشوند به نرم افزار قبلی اضافه شود و فرآیند مهاجرت نیز به صورت موازی پیش رود. برای این منظور لازم است که در یک خط زمانی به صورت تدریجی سرویس ها از نرم افزار اصلی جدا شده و به صورت یک سرویس مستقل درآیند و نرم افزار monolith نیز در طول زمان کوچک تر و کوچک تر شده تا جایی که نرم افزار به مجموعه ای از سرویس ها تبدیل شود.
برای این پیاده سازی این استراتژی از سه راه حل میتوان استفاده کرد.
با انجام این سه روش میتوان یک مهاجرت خوب و پایدار از معماری monolith به ماکروسرویس داشت.
به طور کلی پیاده سازی نرم افزار با معماری ماکروسرویس باعث بهبود برخی از ویژگی های کیفی و افت عملکرد برخی دیگر از ویژگی های کیفی میشود. در این فصل تکنیک هایی معرفی میشود که تا جای ممکن از افت ویژگی های کیفی در معماری میکروسرویس جلوگیری شود یا حداقل وضعیت کمی بهتر شود همچنین برخی از این ویژگی ها که به شکل ذاتی توسط معماری میکروسرویس وضعیت آن بهتر میشود ، وضعیت بهتر آن حفظ شده یا بهبود های جزئی داشته باشند.
در بحث کارایی همانطور که پیشتر اشاره شد معماری ماکروسرویس ضعیف تر از معماری های monolith عمل میکند. این به این دلیل است که اجزای این معماری از روش پیچیده تری برای ارتباط با هم استفاده میکنند. تکنیک بهبود پرفرمنس در این معماری حول محور روش هایی برای بهبود این ارتباط ها میباشد زیرا عدم کارایی در هر سرویس ارتباطی با معماری ماکروسرویس نداشته و باید تکنیک هایی برای بهبود آن سرویس محقق شود. به طور کلی دو دسته ارتباط در معماری ماکروسرویس وجود دارد، ارتباط همزمان و غیرهمزمان. در صورت استفاده از روش ارتباط هم زمان مشکل اصلی این است که در صورتی که یک سرویس به سرویس دیگر وابسته باشد سرویس وابسته بعد از درخواست باید منتظر جواب سرویس دیگر بماند و همین موضوع موجب کاهش پرفرمنس میشود. به عنوان مثال یک نرم افزار بانکی را در نظر بگیرید فرض کنید که در این نرم افزار دو سرویس وجود دارد که یک سرویس مربوط به انتقال وجه و سرویس دیگر لاگ های این انتقال را در یک دیتابیس سیو میکند. شیوه کار این سرویس ها به این شکل میباشد که ابتدا انتقال وجه انجام شده و سپس سرویس مربوط به ذخیره لاگ توسط سرویس انتقال وجه صدا زده میشود. در صورتی که این ارتباط از روش هم زمان استفاده کند سرویس انتقال وجه باید منتظر بازگشت درخواست سیو لاگ بماند و در صورتی که سرویس لاگ یا دیتابیسی که لاگ در آن ذخیره میشود زیر بار باشد و نتواند به سرعت نتیجه ذخیره لاگ را به سرویس انتقال بازگرداند، کارایی سرویس انتقال وجه نیز افت میکند. ولی با استفاده از روش غیر هم زمان پس از فراخوانی سرویس لاگ عملیات میتواند ادامه پیدا کند و نتیجه انتقال به کاربر نمایش داده شده و لاگ بعدا ذخیره شود.
روش های ارتباط سرویس ها در معماری ماکروسرویس به صورت مفصل در بخش آینده برسی میشود.
بحث امنیت در معماری ماکروسرویس بسیار پیچیده تر از معماری monolith میباشد. از این جهت که اولا باید به امنیت تمام سرویس ها به صورت مجزا توجه شده و در معماری کلی هم لحاظ شود. یکی از بخش هایی که ممکن است بسیار مورد توجه حمله کننده ها قرار بگیرد بخش هایی از سرویس است که پروتکل ارتباط با سرویس های دیگر را فرآهم میکند. از این جهت که این ارتباط فقط باید از طریق سرویس دیگر برقرار شود ولی شخص حمله کننده ممکن است خود را جای آن سرویس جا زده و اطلاعات را سرقت کند یا اختلالی در عملکرد سرویس های دیگر ایجاد کند. همچنین امنیت هر سرویس به صورت مستقل توسط تیم توسعه آن سرویس باید فرآهم شود. یکی از راه هایی که به بهبود این ویژگی کیفی در معماری ماکروسرویس کمک میکند این است که اهمیت به امنیت در تمام لایه های توسعه (طراحی ، توسعه و تست) مورد توجه قرار گیرد. همچنین یکی دیگر از تکنیک ها اضافه کردن احراز هویت چند مرحله ای و یک سرویس برای مدیریت دسترسی ها و احراز هویت میباشد.
از دیگر تکنیک های مهم استفاده از API gateway برای ارتباط با سرویس ها است به گونه ای که تمام ترافیک مربوط به فراخوانی API ها از این درگاه گذشته و با فرآهم کردن امنیت این درگاه میتوان تا حد زیادی ارتباط امن تری را تجربه کرد.
در معماری ماکروسرویس هر سرویس میتواند به صورت جداگانه دارای unit test برای تست کلاس ها ، متد ها و غیره و integration test برای تست ارتباط میان ماژول های درون سرویس باشد. در این حالت دو مشکل اصلی وجود دارد مشکل اول این است که ممکن است یک ماژول از یک سرویس برای کار کردن نیاز به ماژولی از سرویس دیگر داشته باشد در این صورت لازم است که یک دیتای تستی برای نمونه ای از خروجی سرویس دوم برای تست سرویس اول فرآهم شود. مشکل بعدی این است که در این حالت covarage تست بالایی در هر سرویس وجود دارد ولی covarage تست برای ارتباط بین سرویس ها صفر میباشد. برای حل این مشکل میتوان سناریو های ارتباط سرویس ها با یکدیگر را استخراج کرد سپس برای تستی را توسعه داد که این سناریو را اجرا میکند. به عنوان مثال در یک تراکنش بانکی که چندین سرویس در ارتباط هستند میتوان با داده های تستی یک تراکنش تستی ایجاد کرد به طوری که تمام سرویس های مرتبط به طور کامل پوشش داده شوند.
در معماری ماکروسرویس سرویس ها باید برای دریافت داده ها یا پیام ها با هم در ارتباط باشند. هرچند معماری بهتر به شکلی است که سرویس ها حداقل ارتباط را با هم داشته باشند ولی در برخی موارد این ارتباط اجتناب ناپذیر میباشد. به این ارتباط ، ارتباط سرویس با سرویس یا service-to-service communication یا S2S میگویند.
به طور کلی در نوع ارتباط در معماری ماکروسرویس وجود دارد. ارتباط همزمان و غیرهمزمان. ارتباط همزمان معمولا توسط پروتکل HTTP انجام شده و نتیجه مورد نظر در طول مدتی که ارتباط TCP برقرار است بازمیگردد. ولی در ارتباط غیر همزمان درخواست ها در یک صف قرار گرفته و به نوبت پردازش میشودند در این صورت هیچ درخواستی نادیده گرفته نشده و به همه درخواست ها پاسخ داده میشود.
به طور کلی در بیشتر کیس ها از ارتباط غیر همزمان استفاده میشود به جز موارد زیر.
ابزار هایی هستند که یک محیط صف را برای ارتباط غیر همزمان سرویس ها فرآهم می کنند. به طور کلی به دو دسته تقسیم میشوند.
در این روش ارسال کننده پیام خود را دقیقا برای یک گیرنده مشخص ارسال میکند. این روش برای زمانی مناسب است که احتیاج به توان عملیاتی بالا و تاخیر کم میباشد ولی مشکل این روش این است که نمیتوان پیام را هم زمان به چند سرویس متفاوت ارسال کرد.
این روش به ارسال کننده پیام اجازه میدهد پیام را هم زمان به چند سرویس متفاوت ارسال کند. به عنوان مثال هنگام ثبت نام کاربر در یک نرم افزار بر پایه ماکروسرویس ممکن است نیاز باشد که همزمان اطلاعات کاربر به یک سرویس مربوط به ذخیره اطلاعات کاربر در دیتابیس و یک سرویس مربوط به ذخیره لاگ ها ارسال شود. در این روش میتوان به صورت هم زمان این عملیات را انجام داد.
در ابزار اصلی که به عنوان message broker استفاده میشوند kafka و rabbitmq میباشد. در جدول زیر این دو ابزار برسی شده اند.
در معماری ماکروسرویس مدیریت سرویس ها از اهمیت بالایی برخوردار میباشد. به عنوان مثال در یک نرم افزار با ۲۰ سرویس متفاوت نیاز است که هر سرویس به صورت مستقل دیپلوی شود، لاگ های سیستم به ازای هر سرویس جمع آوری و تحلیل شود و همچنین منشا مشکلات سیستم مشخص شود. به این منظور از ابزار های متفاوتی میتوان استفاده کرد.
کانتینر ها ساختاری هستند که تمام موارد مورد نیاز برای اجرای یک سرویس (زیر ساخت سیستم عاملی ، محیط زبان برنامه نویسی و غیره) را برای اجرا در هر محیطی دارا میباشد. در معماری میکروسرویس برای راحتی مدیریت سرویس ها نیاز است که هر سرویس در یک کانتینر مجزا اجرا شود. همچنین دیگر نیازمندی های این معماری مانند دیتابیس ها ، صف ها و غیره نیز هر یک در یک کانتینر اجرا میشود.
یکی از مورد استفاده ترین ابزار ها برای ساخت کانتینر ابزار متن باز docker میباشد. شیوه کار این نرم افزار به این صورت است که داخل یک یا چند کانتینر فرآیند کامپایل شدن و اجرا شدن کد را به صورت خودکار انجام میدهد. خوبی استفاده از داکر در معماری ماکروسرویس این است که در صورتی که هر سرویس از تکلونوژی ها و زبان های برنامه نویسی متفاوتی استفاده کند داکر میتواند تمام این سرویس ها را بدون نیاز به پیاده سازی فرآیند نصب سرویس ها و به صورت خودکار انجام داده و در زمان و هزینه صرفه جویی کند.
داکر میتواند فرآیند اجرای سرویس ها را برای توسعه دهندگان خودکار کند. ولی در روند deployment نیاز است که یک سری از عملیات های دیگر نیز خودکار شود این عملیات ها شامل استقرار سرویس روی سرور پرداکشن ، اجرای تست های اتوماتیک ، اجرای تغییرات روی دیتابیس ها و موارد دیگر میباشد. این کار توسط ابزار هایی با طراحی پایپ لاین انجام میشود. این پایپ لاین ها شامل چندین مرحله یا stage میباشند که هر stage وظیفه خواست خود را دارد. به عنوان مثال در یک پایپ لاین در مرحله اول کد با آخرین نسخه آپدیت شده ، در مرحله بعدی تست های اتماتیک اجرا شده و در مرحله آخر کانتینر آپدیت شده سرویس به جای کانتینر قبلی روی سرور قرار میگیرد و نسخه جدید آن سرویس را ارائه میکند.
برای فرآیند ci/cd ابزار های متنوعی استفاده میشود در اینجا دو ابزار را برسی میکنیم
این ابزار متن باز مربوط به شرکت gitlab میباشد و ارتباط خوبی با ریپازیتوری هایی که بر بستر gitlab میباشند برقرار میکند. برای اجرای این نرم افزار کافی است worker مربوط به آن را روی یک سرور اجرا کرده و ارتباط آن را با سروری که gitlab روی آن قرار دارد برقرار کنیم. سپس میتوانیم به کمک یک فایل تنظیمات مربوط به مراحل آن را پیاده سازی کرده و اجرا نماییم.
یک ابزار متن باز برای انجام فرآیند ci/cd مباشد که دارای پلاگین های زیادی است و ارتباط خوبی با تمام ابزار های کنترل ورژن دارد.
مدیریت لاگ ها در معماری ماکروسرویس از اهمیت بالایی برخوردار میباشد. از این جهت که در صورت بروز مشکلی در سیستم باید به سرعت تشخیص داده شود که مشکل حاصل از کدام سرویس میباشد تا آن سرویس به سرعت دیباگ شده و مشکلاتش برسی شود به همین جهت نیاز به یک سرویس مجزا برای مدیریت لاگ میباشد. یکی از ابزار های کاربردی برای این کار نرم افزار elastic میباشد. این سرویس از سه نرم افزار مجزا تشکیل شده که هر سه نرم افزار کاملا با هم هماهنگ میباشند. این سه شامل elastic ، kibana و log stash میباشد که به طور مختصر به آن ELK میگویند.
Logstash:
این ابزار لاگ های ورودی را مرتب کرده و به یک فرمت استاندارد تبدیل میکند.
Elastic:
این ابزار لاگ ها را در یک دیتابیس ذخیره میکند.
Kibana:
این ابزار لاگ ها را نمایش داده و توسط نمودار هایی تحلیل آن را راحت تر میکند.
منابع
این مطلب، بخشی از تمرینهای درس معماری نرمافزار در دانشگاه شهیدبهشتی است.