Kubernetes: прискоріть ваші послуги через зняття процесорних обмежень

Ще у 2016 році ми в Buffer перейшли на Kubernetes, і зараз близько 60 нод (на AWS) та 1500 контейнерів працюють на нашому k8s-кластері під управлінням удар ногою. Тим не менш, на мікросервіси ми переходили методом спроб і помилок, і навіть після декількох років нашої роботи з k8s ми досі стикаємося з новими проблемами. У цьому пості ми поговоримо про процесорні обмеження: чому ми вважали їх хорошою практикою і чому в результаті вони виявилися не такими хорошими.

Процесорні обмеження та тротлінг

Як і багато інших користувачів Kubernetes, Google дуже рекомендує налаштовувати процесорні обмеження. Без такої установки контейнери в ноді можуть зайняти всі потужності процесора, через що, у свою чергу, важливі Kubernetes-процеси (наприклад kubelet) перестануть реагувати на запити. Таким чином, налаштування процесорних обмежень це хороший спосіб захисту ваших нід.

Процесорні обмеження задають контейнеру максимальний процесорний час, яким він може скористатися за конкретний період (за замовчуванням 100мс), і контейнер ніколи не переступить цю межу. У Kubernetes для тротлінгу контейнера та недопущення перевищення ним межі використовується особливий інструмент CFS QuotaОднак у результаті такі штучні процесорні обмеження занижують продуктивність і збільшують час відгуку ваших контейнерів.

Що може статися, якщо ми не задамо процесорні обмеження?

На жаль, нам самим довелося зіткнутися з цією проблемою. На кожній ноді є процес, що відповідає за управління контейнерами. kubeletі він перестав реагувати на запити. Нода, коли це станеться, перейде у стан NotReady, А контейнери з неї будуть перенаправлені кудись ще й створять ті ж проблеми вже на нових нодах. Чи не ідеальний сценарій, м'яко кажучи.

Прояв проблеми тротлінгу та відгуку

Ключова метрика щодо відстеження контейнерів це trottling, вона показує скільки разів тротили ваш контейнер. Ми з цікавістю звернули увагу на наявність тротлінгу в деяких контейнерах незалежно від того чи було навантаження на граничною процесор чи ні. Для прикладу погляньмо на один з наших основних API:

Kubernetes: прискоріть ваші послуги через зняття процесорних обмежень

Як можна бачити нижче, ми задали обмеження у 800m (0.8 або 80% ядра), і пікові значення у кращому випадку досягають 200m (20% ядра). Здавалося б, до тротлінгу сервісу у нас ще повно процесорних потужностей, проте…

Kubernetes: прискоріть ваші послуги через зняття процесорних обмежень
Ви могли помітити, що навіть при навантаженні на процесор нижче заданих обмежень - значно нижче - все одно спрацьовує тротлінг.

Зіткнувшись із цим, ми незабаром виявили кілька ресурсів (проблема на github, презентація на zadano, пост на omio) про падіння продуктивності та часу відгуку сервісів через тротлінг.

Чому ми спостерігаємо тротлінг при низькому навантаженні процесора? Коротка версія звучить так: "в ядрі Linux є баг, через який спрацьовує необов'язковий тротлінг контейнерів із заданими процесорними обмеженнями". Якщо вас цікавить природа проблеми, ви можете ознайомитись із презентацією (відео и текстовий варіанти) за авторством Дейва Чілука (Dave Chiluk).

Зняття процесорних обмежень (з особливою обережністю)

Після тривалих обговорень, ми вирішили зняти процесорні обмеження з усіх сервісів, які прямо чи опосередковано торкалися критичних важливих для наших користувачів функціонал.

Рішення виявилося непростим, оскільки ми високо цінуємо стабільність нашого кластера. У минулому ми вже експериментували з нестабільністю нашого кластера, і тоді сервіси споживали надто багато ресурсів та гальмували роботу всієї своєї ноди. Тепер же все було дещо інакше: у нас було чітке розуміння того, що ми чекаємо від наших кластерів, а також хороша стратегія реалізації планованих змін.

Kubernetes: прискоріть ваші послуги через зняття процесорних обмежень
Ділове листування з насущного питання.

Як захистити ваші ноди під час зняття обмежень?

Ізолювання «необмежених» сервісів:

У минулому ми вже спостерігали, як деякі ноди потрапляли у стан notReady, в першу чергу через сервіси, які споживали занадто багато ресурсів.

Ми вирішили розмістити такі сервіси в окремі (позначені) ноди, щоб ті не заважали «пов'язаним» сервісам. У результаті завдяки відміткам до деяких нодів і додавання параметра toleration («толератність») до «непов'язаних» сервісів, ми досягли більшого контролю над кластером, і нам стало легше визначати проблеми з нодами. Щоб самостійно провести аналогічні процеси, ви можете ознайомитись з документацією.

Kubernetes: прискоріть ваші послуги через зняття процесорних обмежень

Призначення коректного запиту процесора та пам'яті:

Найбільше ми побоювалися, що процес зжере надто багато ресурсів і перестане відповідати на запити. Оскільки тепер (завдяки Datadog) ми могли чітко спостерігати за всіма сервісами на нашому кластері, я проаналізував кілька місяців роботи тих з них, які ми планували призначити «непов'язаними». Я просто поставив максимальне використання процесора із запасом в 20%, і таким чином виділив місце в ноді на випадок, якщо k8s намагатиметься призначати інші сервіси в ноду.

Kubernetes: прискоріть ваші послуги через зняття процесорних обмежень

Як можна бачити на графіку, максимальне навантаження на процесор досягло 242m CPU ядер (0.242 ядра процесора). За запит процесора достатньо взяти число трохи більше цього значення. Зверніть увагу, що оскільки сервіси орієнтовані користувачів, пікові значення навантаження збігаються з трафіком.

Зробіть те саме з використанням пам'яті і запитами, і вуаля - ви все налаштували! Для більшої безпеки можна додати горизонтальне автоскелювання подів. Таким чином щоразу, коли навантаження на ресурси буде високим, автоскалювання створить нові поди, і кубернети розподілять їх у ноди з вільним місцем. Якщо місце не залишиться в самому кластері, ви можете задати собі оповіщення або налаштувати додавання нових нод через їх автоскалювання.

З мінусів варто зазначити, що ми втратилищільності контейнерів», тобто. числа працюючих в одній ноді контейнерів. Ще у нас може виявитися багато «послаблень» при низькій щільності трафіку, а також є шанс, що ви досягнете високого процесорного навантаження, але з останнім має допомогти автоскалювання нод.

Результати

Я радий опублікувати ці відмінні результати експериментів останніх кількох тижнів, ми вже відзначили значні покращення відгуку серед усіх модифікованих сервісів:

Kubernetes: прискоріть ваші послуги через зняття процесорних обмежень

Найкращого результату ми досягли на нашій головній сторінці (buffer.com), там сервіс прискорився в двадцять двічі!

Kubernetes: прискоріть ваші послуги через зняття процесорних обмежень

Чи виправлено баг ядра Linux?

Так, баг вже виправлений, і фікс додано в ядро дистрибутивів версії 4.19 та вище.

Тим не менш, при прочитанні проблеми kubernetes на github за друге вересня 2020 року ми все ще стикаємося зі згадками деяких Linux-проектів з аналогічним багом. Я вважаю, що в деяких дистрибутивах Linux все ще є ця помилка і зараз ведеться робота над її виправленням.

Якщо ваша версія дистрибутива нижче 4.19, я б порекомендував оновитися до останньої, але вам у будь-якому випадку варто спробувати зняти процесорні обмеження і подивитися чи збережеться тротлінг. Нижче можна ознайомитися з неповним списком керуючих Kubernetes сервісів та Linux дистрибутивів:

  • Debian: фікс інтегрований в останню версію дистрибутива, buster, і виглядає досить свіжим (серпень 2020 року). Деякі попередні версії також можуть бути зафіксовані.
  • Ubuntu: фікс інтегрований в останню версію Ubuntu Focal Fossa 20.04
  • EKS обзавівся фіксом ще в грудні 2019 року. Якщо ваша версія нижче, слід оновити AMI.
  • kops: З червня 2020 року у kops 1.18+ головним чином хоста стане Ubuntu 20.04. Якщо ваша версія kops старша, вам, ймовірно, доведеться почекати фіксу. Ми й самі зараз чекаємо.
  • GKE (Google Cloud): Фікс інтегрований в січні 2020 року, проте проблеми з тротлінгом все ще спостерігаються.

Що робити, якщо фікс виправив проблему з тротлінгом?

Я не впевнений, що проблему повністю вирішено. Коли ми дістанемося версії ядра з фіксом, я протестую кластер і оновлю пост. Якщо хтось уже оновився, я з цікавістю ознайомлюсь із вашими результатами.

Висновок

  • Якщо ви працюєте з Docker-контейнерами під Linux (не важливо Kubernetes, Mesos, Swarm або ще якими), ваші контейнери можуть втрачати у продуктивності через тротлінг;
  • Спробуйте оновитися до останньої версії вашого дистрибутива, сподіваючись, що баг вже пофіксували;
  • Зняття процесорних обмежень вирішить проблему, але це небезпечний прийом, який слід застосовувати з особливою обережністю (краще спочатку оновити ядро ​​та порівняти результати);
  • Якщо ви зняли процесорні обмеження, уважно відстежуйте використання процесора та пам'яті, та переконайтеся, що ваші ресурси процесора перевищують споживання;
  • Безпечним варіантом буде автоскелювання подів для створення нових подів у разі високого навантаження на залізо, щоб kubernetes призначав їх у вільні ноди.

Я сподіваюся, що цей пост допоможе вам покращити продуктивність ваших контейнерних систем.

PS Тут автор листується з читачами та коментаторами (англійською).


Джерело: habr.com

Додати коментар або відгук