انتقال Tinder به Kubernetes

توجه داشته باشید. ترجمه: کارمندان سرویس معروف Tinder اخیراً برخی از جزئیات فنی مهاجرت زیرساخت خود به Kubernetes را به اشتراک گذاشته اند. این فرآیند تقریباً دو سال طول کشید و منجر به راه‌اندازی یک پلتفرم بسیار بزرگ در K8 شد که شامل 200 سرویس میزبانی شده در 48 هزار کانتینر بود. مهندسان Tinder با چه مشکلات جالبی مواجه شدند و به چه نتایجی رسیدند؟این ترجمه را بخوانید.

انتقال Tinder به Kubernetes

چرا؟

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

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

این روند دشوار بود. در طول مهاجرت ما در اوایل سال 2019، خوشه Kubernetes به حجم بحرانی رسید و به دلیل حجم ترافیک، اندازه خوشه و DNS با مشکلات مختلفی مواجه شدیم. در طول مسیر، ما بسیاری از مشکلات جالب مربوط به مهاجرت 200 سرویس و حفظ یک خوشه Kubernetes متشکل از 1000 گره، 15000 پاد و 48000 کانتینر در حال اجرا را حل کردیم.

چگونه؟

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

ساخت تصاویر برای Kubernetes

ما بیش از 30 مخزن کد منبع برای میکروسرویس هایی داریم که روی یک خوشه Kubernetes اجرا می شوند. کد موجود در این مخازن به زبان های مختلف (مثلا Node.js، جاوا، اسکالا، گو) با محیط های زمان اجرا متعدد برای یک زبان نوشته شده است.

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

انتقال Tinder به Kubernetes
شکل 1-1. فرآیند ساخت استاندارد از طریق کانتینر بیلدر

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

اجرای کانتینر او به تکنیک های پیشرفته Docker نیاز داشت. Builder شناسه کاربر محلی و اسرار (مانند کلید SSH، اعتبارنامه AWS و غیره) مورد نیاز برای دسترسی به مخازن خصوصی Tinder را به ارث می برد. دایرکتوری های محلی حاوی منابع را برای ذخیره طبیعی مصنوعات ساخت نصب می کند. این رویکرد عملکرد را بهبود می بخشد زیرا نیاز به کپی کردن مصنوعات ساخت بین کانتینر سازنده و میزبان را از بین می برد. مصنوعات ساخت ذخیره شده را می توان بدون پیکربندی اضافی مورد استفاده مجدد قرار داد.

برای برخی از سرویس‌ها، ما مجبور شدیم ظرف دیگری ایجاد کنیم تا محیط کامپایل را به محیط زمان اجرا نگاشت کنیم (برای مثال، کتابخانه Node.js bcrypt در حین نصب، مصنوعات باینری مخصوص پلتفرم تولید می‌کند). در طول فرآیند کامپایل، نیازمندی‌ها ممکن است بین سرویس‌ها متفاوت باشد و Dockerfile نهایی به سرعت کامپایل می‌شود.

معماری و مهاجرت خوشه کوبرنتس

مدیریت اندازه خوشه

تصمیم گرفتیم استفاده کنیم kube-aws برای استقرار خودکار خوشه در نمونه های آمازون EC2. در همان ابتدا، همه چیز در یک مجموعه مشترک از گره ها کار می کرد. ما به سرعت متوجه نیاز به تفکیک بارهای کاری بر اساس اندازه و نوع نمونه برای استفاده کارآمدتر از منابع شدیم. منطق این بود که اجرای چندین غلاف بارگذاری شده چند رشته ای از نظر عملکرد قابل پیش بینی تر از همزیستی آنها با تعداد زیادی غلاف تک رشته ای بود.

در پایان به این نتیجه رسیدیم:

  • m5.4x بزرگ - برای نظارت (پرومته)؛
  • c5.4xlarge - برای حجم کاری Node.js (بار کاری تک رشته ای)؛
  • c5.2xlarge - برای جاوا و برو (بار کاری چند رشته ای)؛
  • c5.4xlarge - برای کنترل پنل (3 گره).

مهاجرت

یکی از مراحل آماده‌سازی برای مهاجرت از زیرساخت قدیمی به Kubernetes، تغییر مسیر ارتباط مستقیم موجود بین سرویس‌ها به متعادل‌کننده‌های بار جدید (Elastic Load Balancer (ELB) بود. آنها بر روی یک زیرشبکه خاص از یک ابر خصوصی مجازی (VPC) ایجاد شدند. این زیر شبکه به یک Kubernetes VPC متصل شد. این به ما اجازه داد تا ماژول‌ها را به تدریج و بدون در نظر گرفتن ترتیب خاصی از وابستگی‌های سرویس منتقل کنیم.

این نقاط پایانی با استفاده از مجموعه‌های وزنی از رکوردهای DNS ایجاد شدند که دارای CNAME بودند که به هر ELB جدید اشاره می‌کردند. برای جابجایی، یک ورودی جدید اضافه کردیم که به ELB جدید سرویس Kubernetes با وزن 0 اشاره می کند. سپس Time To Live (TTL) مجموعه ورودی را روی 0 تنظیم کردیم. پس از این، وزن های قدیمی و جدید تعیین شدند. به آرامی تنظیم شد و در نهایت 100% بار به سرور جدید ارسال شد. پس از تکمیل سوئیچینگ، مقدار TTL به سطح مناسب تری بازگشت.

ماژول‌های جاوا که ما داشتیم می‌توانستند با DNS کم TTL کنار بیایند، اما برنامه‌های Node نتوانستند. یکی از مهندسان بخشی از کد استخر اتصال را بازنویسی کرد و آن را در مدیری پیچید که هر 60 ثانیه استخرها را به روز می کرد. رویکرد انتخاب شده بسیار خوب و بدون کاهش عملکرد قابل توجه کار کرد.

درسها

محدودیت های فابریک شبکه

در اوایل صبح 8 ژانویه 2019، سکوی Tinder به طور غیرمنتظره ای سقوط کرد. در پاسخ به افزایش نامرتبط در تاخیر پلت فرم در اوایل صبح، تعداد غلاف ها و گره ها در خوشه افزایش یافت. این باعث شد که کش ARP در تمام گره‌های ما تمام شود.

سه گزینه لینوکس مربوط به کش ARP وجود دارد:

انتقال Tinder به Kubernetes
(منبع)

gc_thresh3 - این یک محدودیت سخت است. ظهور ورودی‌های «سرریز جدول همسایه» در گزارش به این معنی است که حتی پس از جمع‌آوری همزمان زباله (GC)، فضای کافی در حافظه پنهان ARP برای ذخیره ورودی همسایه وجود ندارد. در این مورد، هسته به سادگی بسته را به طور کامل دور انداخت.

ما استفاده می کنیم فلنج به عنوان یک بافت شبکه در Kubernetes. بسته ها از طریق VXLAN منتقل می شوند. VXLAN یک تونل L2 است که در بالای شبکه L3 ایجاد شده است. این فناوری از کپسوله سازی MAC-in-UDP (MAC Address-in-User Datagram Protocol) استفاده می کند و امکان گسترش بخش های شبکه لایه 2 را فراهم می کند. پروتکل انتقال در شبکه مرکز داده فیزیکی IP به اضافه UDP است.

انتقال Tinder به Kubernetes
شکل 2-1. نمودار فلانل (منبع)

انتقال Tinder به Kubernetes
شکل 2-2. بسته VXLAN (منبع)

هر گره کارگر Kubernetes یک فضای آدرس مجازی با ماسک 24/ از بلوک 9/ بزرگتر اختصاص می دهد. برای هر گره این است به معنی یک ورودی در جدول مسیریابی، یک ورودی در جدول ARP (در رابط flannel.1)، و یک ورودی در جدول سوئیچینگ (FDB). آنها اولین باری که یک گره کارگر شروع می شود یا هر بار که یک گره جدید کشف می شود اضافه می شوند.

علاوه بر این، ارتباط node-pod (یا pod-pod) در نهایت از طریق رابط انجام می شود eth0 (همانطور که در نمودار فلانل بالا نشان داده شده است). این منجر به یک ورودی اضافی در جدول ARP برای هر میزبان منبع و مقصد مربوطه می شود.

در محیط ما، این نوع ارتباط بسیار رایج است. برای اشیاء سرویس در Kubernetes، یک ELB ایجاد می شود و Kubernetes هر گره را با ELB ثبت می کند. ELB چیزی در مورد pod ها نمی داند و گره انتخاب شده ممکن است مقصد نهایی بسته نباشد. نکته این است که وقتی یک گره بسته ای را از ELB دریافت می کند، آن را با در نظر گرفتن قوانین در نظر می گیرد. از iptables برای یک سرویس خاص و به طور تصادفی یک pod را در گره دیگری انتخاب می کند.

در زمان شکست، 605 گره در خوشه وجود داشت. به دلایل ذکر شده در بالا، این برای غلبه بر اهمیت کافی بود gc_thresh3، که پیش فرض است. هنگامی که این اتفاق می افتد، نه تنها بسته ها شروع به حذف می کنند، بلکه کل فضای آدرس مجازی Flannel با ماسک 24/ از جدول ARP ناپدید می شود. ارتباط Node-pod و پرس و جوهای DNS قطع می شود (DNS در یک خوشه میزبانی می شود؛ برای جزئیات بیشتر در این مقاله بخوانید).

برای حل این مشکل، باید مقادیر را افزایش دهید gc_thresh1, gc_thresh2 и gc_thresh3 و Flannel را مجدداً راه اندازی کنید تا شبکه های از دست رفته را مجدداً ثبت کنید.

مقیاس غیرمنتظره DNS

در طول فرآیند مهاجرت، ما به طور فعال از DNS برای مدیریت ترافیک و انتقال تدریجی خدمات از زیرساخت قدیمی به Kubernetes استفاده کردیم. ما مقادیر نسبتاً پایین TTL را برای RecordSets مرتبط در Route53 تنظیم کردیم. هنگامی که زیرساخت قدیمی روی نمونه های EC2 اجرا می شد، پیکربندی حل کننده ما به DNS آمازون اشاره کرد. ما این را بدیهی تلقی کردیم و تأثیر TTL پایین بر خدمات ما و خدمات آمازون (مانند DynamoDB) تا حد زیادی مورد توجه قرار نگرفت.

همانطور که ما خدمات را به Kubernetes منتقل کردیم، متوجه شدیم که DNS در هر ثانیه 250 هزار درخواست را پردازش می کند. در نتیجه، برنامه‌ها شروع به تجربه وقفه‌های دائمی و جدی برای درخواست‌های DNS کردند. این اتفاق علی‌رغم تلاش‌های باورنکردنی برای بهینه‌سازی و تغییر ارائه‌دهنده DNS به CoreDNS (که در اوج بار به 1000 پاد روی 120 هسته می‌رسید) رخ داد.

در حین بررسی علل و راه حل های احتمالی دیگر، کشف کردیم یک مقاله، شرایط مسابقه را توصیف می کند که بر چارچوب فیلتر بسته ها تأثیر می گذارد فیلتر فیلتر در لینوکس تایم اوت هایی که مشاهده کردیم، همراه با شمارنده فزاینده insert_failed در رابط Flannel با یافته های مقاله سازگار بود.

مشکل در مرحله ترجمه آدرس شبکه منبع و مقصد (SNAT و DNAT) و ورود بعدی به جدول رخ می دهد. کنتراک. یکی از راه‌حل‌هایی که در داخل مورد بحث قرار گرفت و توسط جامعه پیشنهاد شد، انتقال DNS به خود گره کارگر بود. در این مورد:

  • SNAT مورد نیاز نیست زیرا ترافیک در داخل گره باقی می ماند. نیازی نیست که از طریق رابط مسیریابی شود eth0.
  • DNAT مورد نیاز نیست زیرا IP مقصد محلی برای گره است و طبق قوانین یک pod انتخابی تصادفی نیست. از iptables.

ما تصمیم گرفتیم این رویکرد را حفظ کنیم. CoreDNS به عنوان یک DaemonSet در Kubernetes مستقر شد و ما یک سرور DNS گره محلی را در resolutionv.conf هر غلاف با تنظیم یک پرچم --cluster-dns دستورات کوبلت . این راه حل برای وقفه های زمانی DNS موثر بود.

با این حال، ما همچنان شاهد از دست دادن بسته و افزایش شمارنده بودیم insert_failed در رابط فلانل این پس از اجرای راه حل ادامه یافت زیرا ما توانستیم SNAT و/یا DNAT را فقط برای ترافیک DNS حذف کنیم. شرایط مسابقه برای انواع دیگر ترافیک حفظ شد. خوشبختانه اکثر بسته های ما TCP هستند و اگر مشکلی پیش بیاید به سادگی دوباره ارسال می شوند. ما همچنان در تلاشیم تا راه حلی مناسب برای انواع ترافیک پیدا کنیم.

استفاده از Envoy برای تعادل بار بهتر

با انتقال خدمات باطن به Kubernetes، از بار نامتعادل بین پادها رنج می بردیم. ما متوجه شدیم که HTTP Keepalive باعث می‌شود که اتصالات ELB در اولین پادهای آماده هر استقرار نصب شود. بنابراین، بخش عمده ای از ترافیک از طریق درصد کمی از غلاف های موجود انجام می شود. اولین راه حلی که ما آزمایش کردیم تنظیم MaxSurge روی 100% در استقرارهای جدید برای بدترین سناریوها بود. این اثر از نظر استقرار بزرگتر ناچیز و امیدبخش بود.

راه حل دیگری که ما استفاده کردیم افزایش مصنوعی درخواست منابع برای خدمات حیاتی بود. در این مورد، غلاف هایی که در نزدیکی قرار می گیرند، در مقایسه با سایر غلاف های سنگین، فضای بیشتری برای مانور دارند. در درازمدت نیز کارساز نخواهد بود زیرا باعث اتلاف منابع خواهد شد. علاوه بر این، برنامه های Node ما تک رشته ای بودند و بر این اساس، فقط می توانستند از یک هسته استفاده کنند. تنها راه حل واقعی استفاده از تعادل بار بهتر بود.

ما مدتهاست که می خواهیم به طور کامل قدردانی کنیم فرستاده. شرایط فعلی به ما این امکان را داد که آن را به صورت بسیار محدود مستقر کنیم و نتایج فوری بگیریم. Envoy یک پروکسی لایه XNUMX منبع باز و با کارایی بالا است که برای برنامه های بزرگ SOA طراحی شده است. می‌تواند تکنیک‌های متعادل‌سازی بار پیشرفته، از جمله تلاش‌های مجدد خودکار، قطع‌کننده‌های مدار، و محدود کردن نرخ جهانی را پیاده‌سازی کند. (توجه داشته باشید. ترجمه: می توانید در این مورد بیشتر بخوانید این مقاله در مورد ایستیو، که بر اساس فرستاده است.)

ما به پیکربندی زیر رسیدیم: یک Envoy sidecar برای هر pod و یک مسیر واحد داشته باشید و خوشه را به صورت محلی از طریق پورت به کانتینر متصل کنید. برای به حداقل رساندن آبشار احتمالی و حفظ شعاع ضربه کوچک، ما از ناوگانی از غلاف‌های جلویی پروکسی Envoy استفاده کردیم، یکی در منطقه دسترسی (AZ) برای هر سرویس. آنها به یک موتور کشف سرویس ساده که توسط یکی از مهندسان ما نوشته شده بود، تکیه کردند که به سادگی فهرستی از غلاف ها را در هر AZ برای یک سرویس معین برمی گرداند.

سرویس front-Envoys سپس از این مکانیسم کشف سرویس با یک خوشه و مسیر بالادست استفاده کردند. ما وقفه های زمانی کافی را تنظیم کردیم، تمام تنظیمات قطع کننده مدار را افزایش دادیم و حداقل پیکربندی تلاش مجدد را برای کمک به شکست های منفرد و اطمینان از استقرار روان اضافه کردیم. ما یک TCP ELB را در مقابل هر یک از این سرویس‌های front-Envoy قرار دادیم. حتی اگر keepalive از لایه اصلی پروکسی ما روی برخی از غلاف‌های Envoy گیر کرده بود، آنها همچنان می‌توانستند بار را بسیار بهتر مدیریت کنند و به گونه‌ای پیکربندی شده بودند که از طریق minimum_request در backend تعادل برقرار کنند.

برای استقرار، ما از قلاب preStop در هر دو غلاف برنامه و غلاف سایدکار استفاده کردیم. قلاب خطایی را در بررسی وضعیت نقطه پایانی سرپرست واقع در ظرف کناری ایجاد کرد و برای مدتی به خواب رفت تا اتصالات فعال پایان یابد.

یکی از دلایلی که توانستیم خیلی سریع حرکت کنیم، به دلیل معیارهای دقیق است که توانستیم به راحتی در یک نصب معمولی Prometheus ادغام کنیم. این به ما این امکان را داد که دقیقاً ببینیم چه اتفاقی می‌افتد در حالی که پارامترهای پیکربندی را تنظیم می‌کردیم و ترافیک را دوباره توزیع می‌کردیم.

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

انتقال Tinder به Kubernetes
شکل 3-1. همگرایی CPU یک سرویس در طول انتقال به Envoy

انتقال Tinder به Kubernetes

انتقال Tinder به Kubernetes

نتیجه نهایی

از طریق این تجربه و تحقیقات اضافی، ما یک تیم زیرساخت قوی با مهارت‌های قوی در طراحی، استقرار و راه‌اندازی خوشه‌های بزرگ Kubernetes ایجاد کرده‌ایم. همه مهندسان Tinder اکنون دانش و تجربه بسته بندی کانتینرها و استقرار برنامه های کاربردی را در Kubernetes دارند.

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

مهاجرت تقریبا دو سال طول کشید، اما ما آن را در مارس 2019 به پایان رساندیم. در حال حاضر، پلتفرم Tinder به طور انحصاری بر روی یک خوشه Kubernetes متشکل از 200 سرویس، 1000 گره، 15 پاد و 000 کانتینر در حال اجرا اجرا می شود. زیرساخت دیگر تنها حوزه تیم های عملیاتی نیست. همه مهندسان ما در این مسئولیت سهیم هستند و فرآیند ساخت و استقرار برنامه های خود را فقط با استفاده از کد کنترل می کنند.

PS از مترجم

همچنین مجموعه ای از مقالات را در وبلاگ ما بخوانید:

منبع: www.habr.com

اضافه کردن نظر