محدودیت های CPU و throttling تهاجمی در Kubernetes

توجه داشته باشید. ترجمه: این تاریخچه چشمگیر Omio - یک گردآورنده سفر اروپایی - خوانندگان را از نظریه پایه به پیچیدگی های عملی جذاب پیکربندی Kubernetes می برد. آشنایی با چنین مواردی نه تنها به گسترش افق دید شما کمک می کند، بلکه از مشکلات غیر پیش پا افتاده نیز جلوگیری می کند.

محدودیت های CPU و throttling تهاجمی در Kubernetes

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

TL؛ DR:
اگر از نسخه‌ای از هسته لینوکس با باگ سهمیه CFS استفاده می‌کنید، اکیداً توصیه می‌کنیم محدودیت‌های CPU را در Kubernetes غیرفعال کنید (یا سهمیه‌های CFS را در Kubelet غیرفعال کنید). در هسته در دسترس است جدی و مشهور - معروف اشکالی که منجر به دریچه گاز و تاخیر بیش از حد می شود
.

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

خلاصه مقاله:

  • چند کلمه در مورد ظروف و Kubernetes.
  • نحوه اجرای درخواست ها و محدودیت های CPU؛
  • چگونه محدودیت CPU در محیط های چند هسته ای کار می کند.
  • نحوه ردیابی throttling CPU.
  • راه حل مشکل و تفاوت های ظریف.

چند کلمه در مورد کانتینرها و Kubernetes

Kubernetes اساسا استاندارد مدرن در دنیای زیرساخت است. وظیفه اصلی آن ارکستراسیون کانتینر است.

ظروف

در گذشته، ما مجبور بودیم مصنوعاتی مانند Java JARs/WARs، ​​Python Egg یا فایل های اجرایی برای اجرا روی سرورها ایجاد کنیم. با این حال، برای عملکرد آنها، کار اضافی باید انجام می شد: نصب محیط زمان اجرا (جاوا/پایتون)، قرار دادن فایل های لازم در مکان های مناسب، اطمینان از سازگاری با نسخه خاصی از سیستم عامل و غیره. به عبارت دیگر، توجه دقیق باید به مدیریت پیکربندی (که اغلب منبع اختلاف بین توسعه دهندگان و مدیران سیستم بود) می شد.

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

علاوه بر این، کانتینرها در محیط sandbox خودشان کار می کنند. آنها آداپتور شبکه مجازی خود را دارند، سیستم فایل خود را با دسترسی محدود، سلسله مراتب فرآیندهای خود، محدودیت های خود را در CPU و حافظه، و غیره.

کوبرنیتس

همانطور که قبلا گفته شد، Kubernetes یک ارکستراتور کانتینر است. این کار به این صورت است: شما مجموعه‌ای از ماشین‌ها را به آن می‌دهید، و سپس می‌گویید: "هی، Kubernetes، بیایید ده نمونه از کانتینر من را با 2 پردازنده و هر کدام 3 گیگابایت حافظه راه‌اندازی کنیم و آنها را در حال اجرا نگه داریم!" Kubernetes بقیه موارد را بر عهده خواهد گرفت. ظرفیت آزاد را پیدا می کند، کانتینرها را راه اندازی می کند و در صورت لزوم آنها را راه اندازی مجدد می کند، هنگام تغییر نسخه ها، به روز رسانی ارائه می دهد و غیره. اساساً، Kubernetes به شما اجازه می دهد تا اجزای سخت افزاری را انتزاعی کنید و سیستم های متنوعی را برای استقرار و اجرای برنامه ها مناسب می کند.

محدودیت های CPU و throttling تهاجمی در Kubernetes
Kubernetes از دیدگاه مردم عادی

درخواست ها و محدودیت ها در Kubernetes چیست؟

بسیار خوب، ما کانتینرها و Kubernetes را پوشش داده ایم. همچنین می دانیم که چندین کانتینر می توانند روی یک دستگاه قرار گیرند.

می توان با یک آپارتمان مشترک قیاس کرد. یک محل بزرگ (ماشین آلات / واحدها) گرفته شده و به چندین مستأجر (کانتینر) اجاره داده می شود. Kubernetes به عنوان یک مسکن عمل می کند. این سوال پیش می آید که چگونه مستاجران را از درگیری با یکدیگر دور نگه داریم؟ مثلاً اگر یکی از آنها تصمیم بگیرد حمام را برای نصف روز قرض بگیرد چه؟

اینجاست که درخواست ها و محدودیت ها وارد عمل می شوند. CPU درخواست فقط برای اهداف برنامه ریزی مورد نیاز است. این چیزی شبیه "لیست آرزوها" از ظرف است و برای انتخاب مناسب ترین گره استفاده می شود. در عین حال CPU محدود کردن را می توان با قرارداد اجاره مقایسه کرد - به محض اینکه واحدی را برای کانتینر انتخاب کردیم، نمی تواند فراتر از حد تعیین شده بروید. و مشکل اینجاست...

نحوه پیاده سازی درخواست ها و محدودیت ها در Kubernetes

Kubernetes از مکانیزم throttling (پرش از چرخه های ساعت) ساخته شده در هسته برای پیاده سازی محدودیت های CPU استفاده می کند. اگر برنامه ای از حد مجاز فراتر رود، throttling فعال می شود (یعنی سیکل های CPU کمتری دریافت می کند). درخواست‌ها و محدودیت‌ها برای حافظه به‌طور متفاوتی سازماندهی می‌شوند، بنابراین شناسایی آنها آسان‌تر است. برای انجام این کار، فقط آخرین وضعیت راه اندازی مجدد غلاف را بررسی کنید: آیا "OOMKilled" است. مهار کردن CPU چندان ساده نیست، زیرا K8s معیارها را تنها بر اساس استفاده در دسترس قرار می دهد، نه توسط cgroups.

درخواست CPU

محدودیت های CPU و throttling تهاجمی در Kubernetes
نحوه اجرای درخواست CPU

برای سادگی، اجازه دهید به عنوان مثال به فرآیند استفاده از یک ماشین با یک CPU 4 هسته ای نگاه کنیم.

K8s از مکانیزم گروه کنترل (cgroups) برای کنترل تخصیص منابع (حافظه و پردازنده) استفاده می کند. یک مدل سلسله مراتبی برای آن موجود است: کودک محدودیت های گروه والدین را به ارث می برد. جزئیات توزیع در یک سیستم فایل مجازی (/sys/fs/cgroup). در مورد پردازنده این است /sys/fs/cgroup/cpu,cpuacct/*.

K8s از فایل استفاده می کند cpu.share برای تخصیص منابع پردازنده در مورد ما، cgroup ریشه 4096 سهم از منابع CPU را دریافت می کند - 100٪ از قدرت پردازنده موجود (1 هسته = 1024؛ این یک مقدار ثابت است). گروه ریشه منابع را متناسب با سهم فرزندان ثبت شده در آن توزیع می کند cpu.share، و آنها نیز به نوبه خود همین کار را با فرزندان خود و غیره انجام می دهند. در یک گره معمولی Kubernetes، cgroup ریشه سه فرزند دارد: system.slice, user.slice и kubepods. دو زیر گروه اول برای توزیع منابع بین بارهای حیاتی سیستم و برنامه های کاربر خارج از K8 استفاده می شوند. اخرین - kubepods - ایجاد شده توسط Kubernetes برای توزیع منابع بین pods.

نمودار بالا نشان می دهد که زیر گروه های اول و دوم هر کدام را دریافت کرده اند 1024 سهام، با زیر گروه kuberpod اختصاص داده شده است 4096 سهام چگونه این امکان وجود دارد: بالاخره گروه ریشه فقط به آن دسترسی دارد 4096 سهام، و مجموع سهام فرزندان او به طور قابل توجهی بیش از این تعداد است (6144)؟ نکته این است که مقدار منطقی است، بنابراین زمانبندی لینوکس (CFS) از آن برای تخصیص متناسب منابع CPU استفاده می کند. در مورد ما، دو گروه اول دریافت می کنند 680 سهام واقعی (16,6٪ از 4096)، و kubepod باقی مانده را دریافت می کند 2736 سهام در صورت توقف، دو گروه اول از منابع تخصیص یافته استفاده نخواهند کرد.

خوشبختانه، زمانبند مکانیزمی برای جلوگیری از هدر رفتن منابع CPU استفاده نشده دارد. این ظرفیت «بیکار» را به یک استخر جهانی منتقل می‌کند، که از آن به گروه‌هایی که به قدرت پردازشگر اضافی نیاز دارند توزیع می‌شود (انتقال به صورت دسته‌ای برای جلوگیری از تلفات گرد انجام می‌شود). روش مشابهی برای همه فرزندان فرزندان اعمال می شود.

این مکانیسم توزیع عادلانه قدرت پردازنده را تضمین می کند و تضمین می کند که هیچ یک از فرآیندها منابع را از دیگران «دزدیده» نمی کند.

محدودیت CPU

علیرغم این واقعیت که تنظیمات محدودیت ها و درخواست ها در K8 شبیه به هم هستند، اجرای آنها کاملاً متفاوت است: گمراه کننده ترین و کمترین قسمت مستند.

K8s درگیر می شود مکانیسم سهمیه CFS برای اجرای محدودیت ها تنظیمات آنها در فایل ها مشخص شده است cfs_period_us и cfs_quota_us در دایرکتوری cgroup (فایل نیز در آنجا قرار دارد cpu.share).

برخلاف cpu.share، سهمیه بر اساس بازه زمانیو نه بر روی قدرت پردازنده موجود. cfs_period_us مدت دوره (دوران) را مشخص می کند - همیشه 100000 میکرو ثانیه (100 میلی ثانیه) است. گزینه ای برای تغییر این مقدار در K8s وجود دارد، اما در حال حاضر فقط در آلفا موجود است. زمان‌بند از دوره برای راه‌اندازی مجدد سهمیه‌های استفاده شده استفاده می‌کند. فایل دوم cfs_quota_us، زمان موجود (سهمیه) را در هر دوره مشخص می کند. توجه داشته باشید که در میکروثانیه نیز مشخص شده است. سهمیه ممکن است از طول دوره تجاوز کند. به عبارت دیگر، ممکن است بیشتر از 100 میلی ثانیه باشد.

بیایید به دو سناریو در ماشین های 16 هسته ای (متداول ترین نوع کامپیوتری که در Omio داریم) نگاه کنیم:

محدودیت های CPU و throttling تهاجمی در Kubernetes
سناریوی 1: 2 رشته و محدودیت 200 میلی ثانیه. بدون گاز

محدودیت های CPU و throttling تهاجمی در Kubernetes
سناریو 2: 10 رشته و محدودیت 200 میلی ثانیه. گلوگاه پس از 20 میلی ثانیه شروع می شود، دسترسی به منابع پردازنده پس از 80 میلی ثانیه دیگر از سر گرفته می شود.

فرض کنید محدودیت CPU را روی آن تنظیم کرده اید 2 هسته ها Kubernetes این مقدار را به 200 میلی ثانیه ترجمه می کند. این بدان معنی است که ظرف می تواند حداکثر 200 میلی ثانیه از زمان CPU را بدون دریچه گاز استفاده کند.

و اینجاست که سرگرمی شروع می شود. همانطور که در بالا ذکر شد، سهمیه موجود 200 میلی‌ثانیه است. اگر به صورت موازی کار می کنید ده رشته ها در یک ماشین 12 هسته ای (به تصویر برای سناریو 2 مراجعه کنید)، در حالی که تمام غلاف های دیگر بیکار هستند، سهمیه فقط در 20 میلی ثانیه تمام می شود (از 10 * 20 میلی ثانیه = 200 میلی ثانیه)، و همه رشته های این غلاف آویزان خواهند شد. » (دریچه گاز) برای 80 میلی ثانیه آینده مواردی که قبلا ذکر شد اشکال زمانبندی، که به دلیل آن گازگیری بیش از حد اتفاق می افتد و کانتینر حتی نمی تواند سهمیه موجود را برآورده کند.

چگونه دریچه گاز را در غلاف ها ارزیابی کنیم؟

فقط وارد پاد شده و اجرا کنید cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods - تعداد کل دوره های زمانبندی؛
  • nr_throttled - تعداد دوره های دریچه گاز در ترکیب nr_periods;
  • throttled_time - زمان دریچه گاز تجمعی در نانوثانیه.

محدودیت های CPU و throttling تهاجمی در Kubernetes

واقعا چه خبر است؟

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

این منجر به خطاهای مختلفی می شود - خرابی در بررسی آمادگی، مسدود شدن کانتینر، قطع شدن اتصال شبکه، وقفه زمانی در تماس های سرویس. این در نهایت منجر به افزایش تاخیر و نرخ خطا بالاتر می شود.

تصمیم و پیامدها

اینجا همه چیز ساده است. ما محدودیت های CPU را رها کردیم و شروع به به روز رسانی هسته سیستم عامل به صورت خوشه ای به آخرین نسخه کردیم که در آن باگ برطرف شد. تعداد خطاها (HTTP 5xx) در خدمات ما بلافاصله به طور قابل توجهی کاهش یافت:

خطاهای HTTP 5xx

محدودیت های CPU و throttling تهاجمی در Kubernetes
خطاهای HTTP 5xx برای یک سرویس مهم

زمان پاسخگویی p95

محدودیت های CPU و throttling تهاجمی در Kubernetes
تأخیر درخواست خدمات بحرانی، صدک 95

هزینه های عملیاتی

محدودیت های CPU و throttling تهاجمی در Kubernetes
تعداد ساعات نمونه صرف شده

گرفتن چیست؟

همانطور که در ابتدای مقاله بیان شد:

می توان با یک آپارتمان مشترک قیاس کرد... Kubernetes به عنوان یک مسکن عمل می کند. اما چگونه می توان مستاجران را از درگیری با یکدیگر دور نگه داشت؟ مثلاً اگر یکی از آنها تصمیم بگیرد حمام را برای نصف روز قرض بگیرد چه؟

این شکار است. یک ظرف بی دقت می تواند تمام منابع CPU موجود در یک دستگاه را بخورد. اگر یک پشته برنامه هوشمند دارید (به عنوان مثال، JVM، Go، Node VM به درستی پیکربندی شده اند)، مشکلی نیست: می توانید در چنین شرایطی برای مدت طولانی کار کنید. اما اگر برنامه ها ضعیف بهینه شده باشند یا اصلا بهینه نشده باشند (FROM java:latest) ممکن است وضعیت از کنترل خارج شود. در Omio ما Dockerfiles پایه خودکار با تنظیمات پیش‌فرض کافی برای پشته زبان اصلی داریم، بنابراین این مشکل وجود نداشت.

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

مراجع

این داستان ماست مطالب زیر کمک زیادی به درک آنچه در حال رخ دادن است کرد:

گزارش اشکال Kubernetes:

آیا در کار خود با مشکلات مشابهی مواجه شده اید یا تجربه ای در ارتباط با دریچه گاز در محیط های تولید کانتینری دارید؟ داستان خود را در نظرات به اشتراک بگذارید!

PS از مترجم

در وبلاگ ما نیز بخوانید:

منبع: www.habr.com

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