Kubernetes چیست

Kubernetes چیست

Kubernetes یک سیستم متن‌باز قدرتمند است که در ابتدا توسط شرکت گوگل توسعه داده شد و اکنون توسط "بنیاد محاسبات ابری بومی" (Cloud Native Computing Foundation یا CNCF) پشتیبانی می‌شود. این سیستم برای مدیریت برنامه‌های کانتینری‌شده (containerized applications) در محیط‌های خوشه‌ای (clustered environments) طراحی شده است. هدف آن ارائه روش‌هایی بهتر برای مدیریت مؤلفه‌ها و سرویس‌های مرتبط و توزیع‌شده در زیرساخت‌های متنوع است. برای یادگیری بیشتر در مورد Kubernetes، راهنمای زیر را بررسی کنید. اگر به‌دنبال یک سرویس میزبانی Kubernetes مدیریت‌شده هستید، سرویس ساده و مدیریت‌شده‌ی ما را که برای رشد طراحی شده بررسی نمایید.

در این راهنما، درباره اینکه Kubernetes چیست و برخی از مفاهیم پایه‌ای آن صحبت خواهیم کرد. ما درباره معماری این سیستم، مسائلی که حل می‌کند، و مدلی که برای مدیریت استقرارها و مقیاس‌بندی کانتینری استفاده می‌کند، توضیح خواهیم داد.

Kubernetes چیست؟

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

به‌عنوان یک کاربر Kubernetes، شما می‌توانید تعریف کنید که برنامه‌هایتان چگونه باید اجرا شوند و به چه شکل‌هایی باید با سایر برنامه‌ها یا دنیای بیرون تعامل داشته باشند. می‌توانید سرویس‌های خود را مقیاس‌پذیر کنید (بالا یا پایین ببرید)، به‌روزرسانی‌های تدریجی (rolling updates) انجام دهید، و ترافیک را بین نسخه‌های مختلف برنامه‌تان برای آزمایش ویژگی‌ها یا بازگرداندن (rollback) استقرارهای مشکل‌دار جابجا کنید. Kubernetes رابط‌ها و اجزای قابل ترکیب پلتفرم را فراهم می‌کند که به شما اجازه می‌دهد برنامه‌هایتان را با درجه بالایی از انعطاف‌پذیری، قدرت و قابلیت اطمینان تعریف و مدیریت کنید.

معماری Kubernetes

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

در پایه‌ای‌ترین سطح، Kubernetes ماشین‌های فیزیکی یا مجازی جداگانه را با استفاده از یک شبکه مشترک گرد هم می‌آورد تا بین هر سرور (چه فیزیکی چه مجازی) ارتباط برقرار شود. این خوشه‌ی Kubernetes همان سکوی فیزیکی است که در آن تمام مؤلفه‌ها، قابلیت‌ها و بارهای کاری (workloads) Kubernetes پیکربندی می‌شوند.

ماشین‌های موجود در خوشه‌ی Kubernetes، هرکدام نقشی درون اکوسیستم Kubernetes ایفا می‌کنند. یک سرور (یا گروه کوچکی در استقرارهای با دسترس‌پذیری بالا) به‌عنوان سرور مستر (master) عمل می‌کند. این سرور به‌عنوان دروازه و مغز خوشه عمل کرده و یک API از Kubernetes را در اختیار کاربران و کلاینت‌ها قرار می‌دهد، سلامت سایر سرورها را بررسی می‌کند، درباره‌ی نحوه تقسیم و تخصیص بهترین کار (که با عنوان “زمان‌بندی” شناخته می‌شود) تصمیم‌گیری می‌کند، و ارتباط بین سایر مؤلفه‌ها را هماهنگ می‌سازد (که گاهی از آن به‌عنوان orchestration یا هماهنگی کانتینرها یاد می‌شود). سرور مستر به‌عنوان نقطه تماس اصلی با خوشه عمل کرده و مسئول بخش عمده‌ای از منطق متمرکز Kubernetes است.

سایر ماشین‌ها در خوشه به‌عنوان نودها (nodes) شناخته می‌شوند: سرورهایی که مسئول پذیرش و اجرای بارهای کاری با استفاده از منابع محلی و خارجی هستند. برای کمک به ایزوله‌سازی، مدیریت و انعطاف‌پذیری، Kubernetes برنامه‌ها و سرویس‌ها را درون کانتینرها اجرا می‌کند، بنابراین هر نود باید دارای یک محیط اجرای کانتینر (مانند Docker یا rkt) باشد. نود دستورات کاری را از سرور مستر دریافت کرده و به‌صورت مناسب کانتینرها را ایجاد یا حذف می‌کند، و قوانین شبکه‌ای را برای مسیردهی و هدایت ترافیک تنظیم می‌نماید.

همان‌طور که گفته شد، خودِ برنامه‌ها و سرویس‌ها درون کانتینرها در خوشه اجرا می‌شوند. مؤلفه‌های زیرین اطمینان حاصل می‌کنند که وضعیت موردنظر برنامه‌ها با وضعیت واقعی خوشه مطابقت داشته باشد. کاربران با برقراری ارتباط با سرور API اصلی Kubernetes، چه به‌صورت مستقیم چه از طریق کلاینت‌ها و کتابخانه‌ها، با خوشه تعامل دارند. برای راه‌اندازی یک برنامه یا سرویس، یک طرح اعلامی (declarative) به‌صورت JSON یا YAML ارائه می‌شود که تعریف می‌کند چه چیزی باید ایجاد شود و چگونه باید مدیریت شود. سپس سرور مستر این طرح را دریافت کرده و با بررسی نیازها و وضعیت فعلی سیستم، تصمیم می‌گیرد چگونه آن را روی زیرساخت اجرا کند. این گروه از برنامه‌های تعریف‌شده توسط کاربر که طبق یک طرح مشخص اجرا می‌شوند، لایه نهایی Kubernetes را تشکیل می‌دهند.

مؤلفه‌های سرور مستر

همان‌طور که پیش‌تر توضیح دادیم، سرور مستر به‌عنوان صفحه کنترل اصلی (Control Plane) برای خوشه‌های Kubernetes عمل می‌کند. این سرور به‌عنوان نقطه تماس اصلی برای مدیران و کاربران عمل کرده و بسیاری از سیستم‌های سراسری خوشه را برای نودهای کارگر که چندان پیچیده نیستند فراهم می‌آورد. به‌طور کلی، مؤلفه‌های موجود در سرور مستر با یکدیگر کار می‌کنند تا درخواست‌های کاربران را بپذیرند، بهترین روش‌ها برای زمان‌بندی کانتینرهای بار کاری را تعیین کنند، کلاینت‌ها و نودها را احراز هویت کنند، شبکه‌ی خوشه را در سطح کلی تنظیم نمایند، و مسئولیت‌های مقیاس‌پذیری و بررسی سلامت را مدیریت کنند.

این مؤلفه‌ها می‌توانند روی یک ماشین واحد نصب شوند یا بین چندین سرور توزیع گردند. در این بخش، به بررسی هر یک از مؤلفه‌های مربوط به سرورهای مستر در خوشه‌های Kubernetes می‌پردازیم.

etcd

یکی از مؤلفه‌های اساسی که Kubernetes برای عملکرد خود به آن نیاز دارد، یک ذخیره‌ساز پیکربندی در دسترس جهانی است. پروژه‌ی etcd که توسط تیم CoreOS (یک سیستم‌عامل لینوکسی) توسعه داده شده، یک ذخیره‌ساز سبک‌وزن، توزیع‌شده و مبتنی بر کلید-مقدار (key-value) است که می‌توان آن را طوری پیکربندی کرد که در میان چندین نود گسترش یابد.

Kubernetes از etcd برای ذخیره داده‌های پیکربندی استفاده می‌کند که می‌توانند توسط هر یک از نودهای خوشه قابل دسترسی باشند. این قابلیت می‌تواند برای کشف سرویس (service discovery) مورد استفاده قرار گیرد و به مؤلفه‌ها کمک کند تا خود را طبق اطلاعات به‌روزرسانی‌شده پیکربندی یا بازپیکربندی کنند. همچنین، etcd با ویژگی‌هایی مانند انتخاب رهبر (leader election) و قفل‌گذاری توزیع‌شده (distributed locking) به حفظ وضعیت خوشه کمک می‌کند. با ارائه یک API ساده مبتنی بر HTTP/JSON، رابط کاربری برای تنظیم یا دریافت مقادیر بسیار ساده و سرراست است.

مانند بیشتر مؤلفه‌های دیگر در صفحه کنترل، etcd را می‌توان روی یک سرور مستر منفرد پیکربندی کرد یا در سناریوهای تولیدی بین چندین ماشین توزیع نمود. تنها الزام آن است که از طریق شبکه برای هر یک از ماشین‌های Kubernetes قابل دسترسی باشد.

kube-apiserver

یکی از مهم‌ترین سرویس‌های مستر، سرور API است. این مؤلفه نقطه‌ی مدیریت اصلی کل خوشه به‌شمار می‌آید، زیرا به کاربر اجازه می‌دهد بارهای کاری و واحدهای سازمانی Kubernetes را پیکربندی کند. همچنین مسئول اطمینان از هم‌راستا بودن بین پایگاه داده etcd و جزئیات سرویس‌های مستقر شده‌ی کانتینرها است. این سرور به‌عنوان پلی میان مؤلفه‌های مختلف عمل می‌کند تا سلامت خوشه را حفظ کرده و اطلاعات و دستورات را منتقل کند.

سرور API از یک رابط RESTful استفاده می‌کند، به‌همین دلیل بسیاری از ابزارها و کتابخانه‌های مختلف می‌توانند به‌راحتی با آن ارتباط برقرار کنند. کلاینتی به نام kubectl به‌عنوان روش پیش‌فرض تعامل با خوشه Kubernetes از یک رایانه محلی در دسترس است.

kube-controller-manager

مدیر کنترل (Controller Manager) یک سرویس عمومی است که مسئولیت‌های متعددی دارد. وظیفه‌ی اصلی آن مدیریت کنترل‌کننده‌های مختلفی است که وضعیت خوشه را تنظیم می‌کنند، چرخه عمر بارهای کاری را مدیریت کرده، و وظایف روتین را انجام می‌دهند. به‌عنوان مثال، یک کنترل‌کننده تکثیر (replication controller) اطمینان حاصل می‌کند که تعداد نسخه‌های تعریف‌شده برای یک پاد (pod) با تعداد نسخه‌هایی که در حال حاضر در خوشه مستقر هستند یکسان باشد. جزئیات این عملیات‌ها در etcd نوشته می‌شود، جایی که مدیر کنترل از طریق سرور API، تغییرات را مشاهده می‌کند.

هنگامی که تغییری دیده شود، کنترلر اطلاعات جدید را می‌خواند و رویه‌ای را که وضعیت مطلوب را برآورده می‌سازد اجرا می‌کند. این می‌تواند شامل مقیاس‌گذاری برنامه به بالا یا پایین، تنظیم نقاط پایانی (endpoints)، و غیره باشد.

kube-scheduler

فرآیندی که در واقع بارهای کاری را به نودهای خاص در خوشه اختصاص می‌دهد، زمان‌بند (scheduler) است. این سرویس نیازمندی‌های عملیاتی بارهای کاری را می‌خواند، محیط زیرساخت فعلی را تحلیل کرده، و کار را به نودهای قابل‌قبول تخصیص می‌دهد.

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

cloud-controller-manager

Kubernetes می‌تواند در محیط‌های بسیار متفاوتی مستقر شود و با ارائه‌دهندگان زیرساخت گوناگون تعامل داشته باشد تا وضعیت منابع موجود در خوشه را درک و مدیریت کند. در حالی که Kubernetes با نمایش‌های عمومی از منابعی مانند فضای ذخیره‌سازی قابل‌اتصال و توازن‌بار (load balancer) کار می‌کند، برای نگاشت آن‌ها به منابع واقعی ارائه‌شده توسط ارائه‌دهندگان ابری ناهمگن، نیاز به یک واسط دارد.

مدیران کنترل ابری (cloud controller managers) به‌عنوان پلی عمل می‌کنند که به Kubernetes اجازه می‌دهد با ارائه‌دهندگانی با قابلیت‌ها، ویژگی‌ها و APIهای متفاوت تعامل داشته باشد و در عین حال سازه‌های نسبتاً عمومی داخلی خود را حفظ کند. این قابلیت به Kubernetes اجازه می‌دهد اطلاعات وضعیت خود را بر اساس اطلاعات جمع‌آوری‌شده از ارائه‌دهنده‌ی ابری به‌روزرسانی کند، منابع ابری را طبق نیازهای سیستم تنظیم نماید، و سرویس‌های ابری اضافی برای پاسخ به نیازهای کاری ارائه‌شده به خوشه ایجاد و استفاده کند.

اجزای سرور نود

در کوبرنتیز (Kubernetes)، سرورهایی که با اجرای کانتینرها کار انجام می‌دهند، نود (Node) نام دارند. سرورهای نود چند الزام اساسی دارند که برای ارتباط با اجزای مستر، پیکربندی شبکه‌ی کانتینرها، و اجرای بارهای کاری (workloads) اختصاص‌داده‌شده به آن‌ها لازم است.

یک محیط اجرایی کانتینر

اولین مؤلفه‌ای که هر نود باید داشته باشد، یک محیط اجرایی کانتینر است. معمولاً این نیاز با نصب و اجرای Docker برآورده می‌شود، اما جایگزین‌هایی مانند rkt و runc نیز در دسترس هستند.

محیط اجرایی کانتینر مسئول شروع و مدیریت کانتینرها است؛ اپلیکیشن‌هایی که در یک محیط نسبتاً ایزوله و سبک اجرا می‌شوند. هر واحد کاری در کلاستر، در سطح پایه، به صورت یک یا چند کانتینر پیاده‌سازی می‌شود که باید اجرا شوند. محیط اجرایی کانتینر در هر نود همان مؤلفه‌ای است که نهایتاً کانتینرهایی را که در workloadها تعریف شده‌اند، اجرا می‌کند.

kubelet

نقطه‌ی تماس اصلی هر نود با گروه کلاستر، یک سرویس کوچک به نام kubelet است. این سرویس مسئول انتقال اطلاعات به‌و‌از اجزای کنترل پلن (control plane) و همچنین تعامل با مخزن etcd برای خواندن جزئیات پیکربندی یا نوشتن مقادیر جدید است.

سرویس kubelet با اجزای مستر برای احراز هویت در کلاستر و دریافت دستورات و بارهای کاری ارتباط برقرار می‌کند. بار کاری به‌صورت مانیفست (manifest) دریافت می‌شود که workload و پارامترهای اجرایی آن را تعریف می‌کند. سپس پردازش kubelet مسئول حفظ وضعیت بار کاری روی نود می‌شود. این پردازش محیط اجرایی کانتینر را برای راه‌اندازی یا حذف کانتینرها کنترل می‌کند.

kube-proxy

برای مدیریت ساب‌نت‌بندی (subnetting) میزبان و در دسترس قرار دادن سرویس‌ها برای سایر مؤلفه‌ها، سرویسی کوچک به نام kube-proxy روی هر نود اجرا می‌شود. این پردازش درخواست‌ها را به کانتینرهای درست هدایت می‌کند، قابلیت بالانس بار ساده دارد، و به‌طور کلی مسئول اطمینان از پیش‌بینی‌پذیری و دسترسی‌پذیری محیط شبکه (در عین ایزوله‌سازی مناسب) است.

اشیای کوبرنتیز و بارهای کاری

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

پادها (Pods)

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

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

پادها معمولاً از یک کانتینر اصلی که وظیفه‌ی اصلی workload را بر عهده دارد و در صورت نیاز از کانتینرهای کمکی تشکیل شده‌اند. مثلاً ممکن است یک کانتینر اپلیکیشن اصلی باشد و کانتینر دیگر فایل‌ها را از ریپازیتوری بیرونی به فایل‌سیستم مشترک بکشد.

به‌طور معمول، کاربران نباید مستقیماً پادها را مدیریت کنند، چرا که پادها ویژگی‌هایی مانند مدیریت چرخه عمر پیچیده یا مقیاس‌پذیری پیشرفته را ندارند. در عوض، توصیه می‌شود از اشیای سطح بالاتر که از پاد یا قالب پاد (pod template) استفاده می‌کنند، استفاده شود.

کنترل‌کننده‌های تکرار و مجموعه‌های تکرار (Replication Controllers and Replication Sets)

به‌جای کار با پادهای منفرد، معمولاً کاربران مجموعه‌هایی از پادهای تکراری را مدیریت می‌کنند. این پادها از روی قالب پاد ساخته می‌شوند و توسط کنترل‌کننده‌هایی مانند replication controller و replication set مقیاس‌پذیری افقی پیدا می‌کنند.

Replication controller شی‌ای است که یک قالب پاد و پارامترهای کنترلی را تعریف می‌کند تا نسخه‌های تکراری از پاد را افزایش یا کاهش دهد. کنترل‌کننده اطمینان حاصل می‌کند که تعداد پادهای در حال اجرا با پیکربندی مطابقت دارد. در صورت خرابی یک پاد یا میزبان، کنترل‌کننده پاد جدیدی را جایگزین می‌کند.

Replication set نسخه‌ی به‌روزشده‌ای از replication controller است که انعطاف‌پذیری بیشتری در انتخاب پادهایی که باید مدیریت شوند دارد. گرچه قابلیت بروزرسانی چرخشی (rolling update) را مانند replication controller ندارد.

دیپلویمنت‌ها (Deployments)

دیپلویمنت‌ها یکی از متداول‌ترین workloadهایی هستند که کاربران ایجاد و مدیریت می‌کنند. آن‌ها از replication set استفاده کرده و قابلیت‌های مدیریت چرخه عمر منعطفی اضافه می‌کنند.

دیپلویمنت‌ها مشکلات موجود در بروزرسانی‌های replication controller را حل می‌کنند، مانند پیگیری تاریخچه، بازیابی از خطاهای شبکه‌ای و بازگشت به نسخه‌های قبلی. کاربران تنها با تغییر پیکربندی می‌توانند نسخه‌های جدیدی از اپلیکیشن را منتشر کنند.

Stateful Sets

Stateful Setها کنترل‌کننده‌های پاد تخصصی هستند که تضمین‌هایی در مورد ترتیب و یکتایی ارائه می‌دهند. معمولاً برای اپلیکیشن‌هایی با نیاز به داده پایدار، ترتیب استقرار یا شبکه‌سازی پایدار، مانند دیتابیس‌ها، استفاده می‌شوند.

Stateful Set به هر پاد یک نام منحصربه‌فرد و ثابت می‌دهد و امکان انتقال حجم ذخیره‌سازی با پاد را هنگام جابجایی فراهم می‌کند.

Daemon Sets

Daemon Set ها کنترل‌کننده‌هایی هستند که یک نسخه از یک پاد را روی هر نود در کلاستر (یا زیرمجموعه‌ای خاص) اجرا می‌کنند. برای کارهایی مثل جمع‌آوری لاگ، مانیتورینگ، یا افزایش قابلیت‌های نود استفاده می‌شوند.

Daemon Setها محدودیت‌های زمان‌بندی معمول را دور می‌زنند و می‌توانند حتی روی سرور مستر هم پاد اجرا کنند.

Jobs و Cron Jobs

Jobها workloadهایی با چرخه عمر محدود هستند که پس از اتمام کار به‌درستی خاتمه می‌یابند. برای کارهای یک‌باره یا پردازش‌های دسته‌ای مناسب‌اند.

Cron Jobها نسخه‌ی زمان‌بندی‌شده‌ی jobها هستند و مشابه cronهای لینوکس، برای اجرای منظم اسکریپت‌ها استفاده می‌شوند.

اجزای دیگر کوبرنتیز

علاوه بر workloadها، کوبرنتیز انتزاعات دیگری نیز ارائه می‌دهد تا به مدیریت اپلیکیشن‌ها، شبکه و پایگاه‌های داده کمک کند.

سرویس‌ها (Kubernetes Services)

در کوبرنتیز، یک Service مؤلفه‌ای است که به‌عنوان یک لود بالانسر داخلی و واسط برای پادها عمل می‌کند. سرویس مجموعه‌ای از پادهای مشابه را در قالب یک موجودیت واحد ارائه می‌دهد.

سرویس‌ها آدرس IP پایداری دارند و باعث می‌شوند مصرف‌کنندگان تنها با آدرس سرویس کار کنند، بدون آنکه از تغییرات پادهای زیرمجموعه مطلع باشند.

برای در دسترس قرار دادن پادها برای اپلیکیشن‌های دیگر یا کاربران خارجی، باید سرویس تعریف کرد. روش‌های مختلفی برای در دسترس قرار دادن سرویس از بیرون وجود دارد: NodePort، LoadBalancer و غیره.

ولوم‌ها و ولوم‌های پایدار (Volumes and Persistent Volumes)

برای نگهداری داده‌ها پس از ریستارت کانتینر، کوبرنتیز انتزاعی به نام Volume دارد که در سطح پاد به اشتراک گذاشته می‌شود و پس از حذف پاد از بین می‌رود.

**Persistent Volume (PV)**ها ذخیره‌سازهای پایدارتری هستند که مستقل از چرخه عمر پاد بوده و توسط ادمین‌ها تعریف می‌شوند. کاربران می‌توانند آن‌ها را از طریق PersistentVolumeClaim درخواست کنند.

برچسب‌ها و توضیحات (Labels and Annotations)

Label برچسبی معنایی است که به اشیای کوبرنتیز اضافه می‌شود و برای دسته‌بندی و انتخاب آن‌ها استفاده می‌شود. به‌صورت key-value تعریف می‌شوند.

Annotationها نیز به‌صورت key-value هستند ولی برای متادیتاهای بدون ساختار و صرفاً جهت ذخیره اطلاعات اضافی به‌کار می‌روند.

نتیجه‌گیری

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

 

منبع DigitalOcean

۱۵۱
۱۴۰۴/۵/۶