paint-brush
Як оптимізувати Kubernetes для великих зображень Dockerза@kksudo
155 показання

Як оптимізувати Kubernetes для великих зображень Docker

за Kazakov Kirill10m2024/09/30
Read on Terminal Reader

Надто довго; Читати

🚀 Майстри Kubernetes, ви втомилися годинами чекати, поки ваші вузли розігріються? Уявіть, що цей час можна скоротити до секунд! Ця кардинальна стаття розкриває, як пришвидшити процес розгортання Kubernetes, навіть маючи величезні зображення розміром 3 ГБ і 1000 контейнерів. Відкрийте для себе секрет, який перетворює продуктивність вашого кластера з повільної на надзвукову. Не дозволяйте повільному розігріву гальмувати вас — дізнайтеся, як революціонізувати робочий процес Kubernetes сьогодні!
featured image - Як оптимізувати Kubernetes для великих зображень Docker
Kazakov Kirill HackerNoon profile picture
0-item

Короткий огляд проблеми

Одного разу під час запланованого оновлення кластера k8s ми виявили, що майже всі наші POD (приблизно 500 з 1000) на нових вузлах не можуть запуститися, і хвилини швидко перетворилися на години. Ми активно шукаємо першопричину, але через три години PODS все ще перебував у статусі ContainerCreating .


Kubernetes застряг на ContainerCreating

На щастя, це не було робоче середовище, тому період обслуговування було заплановано на вихідні. Ми мали час дослідити проблему без жодного тиску.

З чого почати пошук першопричини? Хочете дізнатися більше про рішення, яке ми знайшли? Пристебніться та насолоджуйтесь!

Детальніше про проблему

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


Час від часу процес обробки компакт-диска займає до 3 годин, щоб отримати зображення. Однак цього разу він повністю застряг, оскільки кількість PODS під час оновлення EKS (inline, коли ми замінюємо всі вузли в кластері) була занадто великою.


  • Усі наші програми працюють у k8s (на базі EKS ). Щоб заощадити на наших витратах на DEV env, ми використовуємо точкові екземпляри.

  • Ми використовуємо образ AmazonLinux2 для вузлів.

  • У нас є велика кількість гілок функцій (FB) у середовищі розробки, які постійно розгортаються в нашому кластері Kubernetes. Кожен FB має власний набір програм, і кожна програма має власний набір залежностей (всередині зображення).

  • У нашому проекті майже 200 додатків і ця кількість зростає. Кожна програма використовує одне із 7 базових зображень докерів розміром ~2 ГБ. Максимальний загальний розмір заархівованого зображення (в ECR ) становить приблизно 3 ГБ.

  • Усі зображення зберігаються в Amazon Elastic Container Registry (ECR).

  • Ми використовуємо стандартний тип тома gp3 EBS для вузлів.


Виниклі проблеми

  • Розширений час холодного запуску: запуск нового пакета з новим зображенням може зайняти більше 1 години, особливо якщо кілька зображень завантажуються одночасно на одному вузлі.

  • Помилки ErrImagePull: часті помилки ErrImagePull або зависання зі станами ContainerCreating , що вказує на проблеми з отриманням зображення.

  • Високе використання диска: використання диска залишається майже на 100% під час процесу отримання образу, головним чином через інтенсивний дисковий ввід-вивід, необхідний для декомпресії (наприклад, «unpigz»).

  • Проблеми System DaemonSet: деякі системні DaemonSets (наприклад, aws-node або ebs-csi-node ) перейшли в стан «не готовий» через тиск на диск, що вплинуло на готовність вузла.

  • Немає кешу зображень на вузлах: оскільки ми використовуємо точкові екземпляри, ми не можемо використовувати локальний диск для кешування зображень.


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

Після швидкого дослідження ми виявили, що основною проблемою був тиск диска на вузли через процес unpigz . Цей процес відповідає за розпакування зображень докерів. Налаштування за замовчуванням для типу тому gp3 EBS ми не змінювали, тому що не підходять для нашого випадку.


Виправлення для відновлення кластера

Як перший крок ми вирішили зменшити кількість POD на вузлах.

  1. Переводимо нові вузли в стан «Кордон».
  2. Видаліть усі застряглі PODS, щоб зменшити тиск диска
  3. Запустіть один за одним POD, щоб розігріти вузли
  4. Після цього переводимо розігріті вузли в нормальний стан («без кордону»).
  5. Видалено всі вузли в завислому стані
  6. Усі POD успішно почали використовувати кеш зображень Docker


Оригінальний дизайн CI/CD

Основна ідея рішення полягає в тому, щоб розігріти вузли перед початком процесу компакт-диска за допомогою найбільшої частини образу докера (рівня залежностей JS), який використовується як кореневий образ для всіх наших програм. У нас є принаймні 7 типів кореневих зображень із залежностями JS, які пов’язані з типом програми. Отже, давайте розберемо оригінальний дизайн CI/CD.


У нашому конвеєрі CI/CD ми маємо 3 стовпи: Оригінальний конвеєр CI/CD

Оригінальний конвеєр CI/CD:

  1. На кроці Init it: ми готуємо середовище/змінні, визначаємо набір зображень для перебудови тощо...

  2. На кроці Build : ми створюємо зображення та надсилаємо їх до ECR

  3. На кроці Deploy : ми розгортаємо образи на k8s (оновлюємо розгортання тощо)


Детальніше про оригінальний дизайн CICD:

  • Наші функціональні гілки (FB) відгалужені від main гілки. У процесі CI ми завжди аналізуємо набір зображень, які були змінені у FB, і створюємо їх заново. main гілка завжди стабільна, як за визначенням, завжди має бути остання версія базових зображень.
  • Ми окремо створюємо образи докерів залежностей JS (для кожного середовища) і надсилаємо їх до ECR, щоб повторно використовувати їх як кореневий (базовий) образ у файлі Docker. У нас є близько 5–10 типів зображень докера залежностей JS.
  • FB розгортається в кластері k8s в окремому просторі імен, але на загальних вузлах для FB. У FB може бути ~200 додатків із розміром зображення до 3 Гб.
  • У нас є система автоматичного масштабування кластера, яка масштабує вузли в кластері на основі завантаження або очікування PODS з відповідним nodeSelector і допуском.
  • Ми використовуємо точкові екземпляри для вузлів.

Здійснення процесу розминки

Є вимоги до процесу розігріву.

Обов'язкові:

  1. Вирішення проблеми : розглядає та вирішує проблеми ContainerCreating .
  2. Покращена продуктивність : значно скорочується час запуску завдяки використанню попередньо розігрітих базових зображень (залежності JS).

Приємно мати покращення:

  1. Гнучкість : дозволяє легко змінювати тип вузла та його термін служби (наприклад, високий SLA або подовжений час життя).
  2. Прозорість : надає чіткі показники використання та продуктивності.
  3. Економічність : заощаджує кошти, видаляючи VNG одразу після видалення відповідної гілки функції.
  4. Ізоляція : цей підхід гарантує відсутність впливу на інші середовища.

Рішення

Проаналізувавши вимоги та обмеження, ми вирішили запровадити процес розігріву, який би попередньо нагрівав вузли базовими зображеннями кешу JS. Цей процес буде запущений до початку процесу CD, гарантуючи, що вузли готові до розгортання FB, і ми матимемо максимальний шанс потрапити в кеш.


Це вдосконалення ми розділили на три великих кроки:

  1. Створіть набір вузлів (групу віртуальних вузлів) для кожного FB

  2. Додайте базові зображення до сценарію хмарної ініціалізації для нових вузлів

  3. Додайте етап попереднього розгортання, щоб запустити DaemonSet із розділом initContainers , щоб завантажити необхідні образи докерів на вузли перед початком процесу CD.


Оновлений конвеєр CI/CD виглядатиме так: Оновлений конвеєр CI/CD


Оновлений конвеєр CI/CD:

  1. Початковий крок
    1.1.(новий крок) Розгортання ініціалізації : якщо це перший запуск FB, створіть новий особистий набір екземплярів вузлів (у наших термінах це Virtual Node Group або VNG) і завантажте всі базові зображення JS (5–10 зображень). ) від основної гілки. Зробити це цілком справедливо, тому що ми роздвоїли FB з основної гілки. Важливий момент, це не блокуюча операція.
  2. Крок нарощування
  3. Крок перед розгортанням . Завантажте свіжі базові зображення JS із спеціальним тегом FB із ECR.
    3.1.(новий крок) Важливі моменти : це блокуюча операція, оскільки ми повинні зменшити тиск на диск. Одне за іншим ми завантажуємо базові зображення для кожного пов’язаного вузла.
    До речі, дякую за крок « ініціалізація розгортання» , ми вже маємо базові образи докерів з головної гілки, що дає нам великий шанс потрапити в кеш під час першого запуску.
  4. ** Розгорнути
    **На цьому кроці немає змін. Але завдяки попередньому кроці ми вже маємо всі важкі шари зображень докерів на необхідних вузлах.

Ініціалізувати крок розгортання

Створіть новий набір вузлів для кожного FB через виклик API (до сторонньої системи автомасштабування) з нашого конвеєра CI.


Вирішені проблеми:

  1. Ізоляція : кожен FB має власний набір вузлів, що гарантує, що на середовище не впливають інші FB.

  2. Гнучкість : ми можемо легко змінити тип вузла та його термін служби.

  3. Економічність : ми можемо видалити вузли одразу після видалення FB.

  4. Прозорість : ми можемо легко відстежувати використання та продуктивність вузлів (кожен вузол має тег, пов’язаний із FB).

  5. Ефективне використання спотових екземплярів : спотовий екземпляр починається з уже визначених базових зображень, тобто після запуску спотового вузла вже є базові зображення на вузлі (з головної гілки).


Завантажте всі базові зображення JS із головної гілки на нові вузли за допомогою сценарію cloud-init .


Поки зображення завантажуються у фоновому режимі, процес компакт-диска може продовжувати створювати нові зображення без проблем. Крім того, наступні вузли (які будуть створені системою автомасштабування) з цієї групи будуть створені з оновленими даними cloud-init , які вже містять інструкції для завантаження зображень перед запуском.


Вирішені проблеми:

  1. Вирішення проблеми : навантаження на диск зникло, оскільки ми оновили сценарій cloud-init , додавши завантаження базових зображень із головної гілки. Це дозволяє нам отримати доступ до кешу під час першого запуску FB.

  2. Ефективне використання спотових екземплярів : спотовий екземпляр починається з оновлених даних cloud-init . Це означає, що після запуску спотового вузла на вузлі вже є базові зображення (з головної гілки).

  3. Покращена продуктивність : процес CD може продовжувати створювати нові образи без проблем.


Ця дія додала ~17 секунд (виклик API) до нашого конвеєра CI/CD.

Ця дія має сенс лише в перший раз, коли ми запускаємо FB. Наступного разу ми розгорнемо наші програми на вже існуючих вузлах, які вже мають базові образи, які ми доставили під час попереднього розгортання.

Крок перед розгортанням

Нам потрібен цей крок, оскільки зображення FB відрізняються від зображень основної гілки. Нам потрібно завантажити базові зображення FB на вузли перед початком процесу CD. Це допоможе зменшити подовжений час холодного запуску та високе використання диска, які можуть виникнути, коли одночасно завантажується кілька важких образів.


Цілі етапу перед розгортанням

  1. Запобігання тиску на диск : Послідовне завантаження найважчих образів докерів. Після кроку init-deploy ми вже маємо базові зображення на вузлах, що означає, що ми маємо великі шанси отримати кеш звернень.

  2. Підвищення ефективності розгортання : переконайтеся, що вузли попередньо нагріті основними образами докерів, що призведе до швидшого (майже негайного) часу запуску POD.

  3. Підвищення стабільності : мінімізуйте ймовірність виникнення помилок ErrImagePull / ContainerCreating і переконайтеся, що набори системних демонов залишаються в стані «готовості».


На цьому кроці ми додаємо 10–15 хвилин до процесу CD.

Деталі кроку перед розгортанням:

  • На компакт-диску ми створюємо DaemonSet із розділом initContainers .
  • Розділ initContainers виконується перед запуском основного контейнера, забезпечуючи завантаження необхідних зображень до запуску основного контейнера.
  • На компакт-диску ми постійно перевіряємо стан daemonSet. Якщо daemonSet знаходиться в стані «готовий», ми продовжуємо розгортання. В іншому випадку ми чекаємо, поки daemonSet буде готовий.

Порівняння

Порівняння вихідного та оновленого етапів із процесом попереднього нагрівання.

Крок

Ініціалізувати крок розгортання

Крок перед розгортанням

Розгорнути

Загальний час

різниця

Без попереднього розігріву

0

0

11 хв 21 с

11 хв 21 с

0

З підігрівом

8 секунд

58 секунд

25 секунд

1 хв 31 с

-9 хв 50 с


Головне, час «Розгортання» змінено (від першої команди застосування до стану запуску модулів) з 11 хвилин 21 секунди до 25 секунд. Загальний час змінився з 11 хв 21 с до 1 хв 31 с.

Важливий момент, якщо немає базових зображень з основної гілки, то час «Розгортання» буде таким же, як і вихідний час, або трохи більше. Але все одно ми вирішили проблему з тиском диска і часом холодного старту.


Висновок Час витягування

Основну проблему ContainerCreating було вирішено шляхом розігріву. В якості переваги ми значно скоротили час холодного запуску POD.
Тиск на диск зник, тому що ми вже маємо базові зображення на вузлах. Системні daemonSets перебувають у «готовому» та «справному» стані (оскільки немає тиску на диск), і ми не виявили помилок ErrImagePull пов’язаних із цією проблемою.


Можливі рішення та посилання


PS: я хотів би подякувати чудовій технічній команді Justt ( https://www.linkedin.com/company/justt-ai ) за невтомну роботу та справді творчий підхід до будь-якої проблеми, з якою вони стикаються з. Зокрема, я хочу подякувати Ронні Шарабі, чудовому керівнику, який відповідає за чудову роботу, яку виконує команда. Я з нетерпінням чекаю нових і нових чудових прикладів того, як ваша креативність впливає на продукт Justt.


L O A D I N G
. . . comments & more!

About Author

Kazakov Kirill HackerNoon profile picture
Kazakov Kirill@kksudo
DevOps with love. Certified Kubernetes Administrator. I also enjoy riding a motorcycle.

ПОВІСИТИ БИРКИ

ЦЯ СТАТТЯ БУЛА ПРЕДСТАВЛЕНА В...