Kubernetes v1.35: Новий рівень ефективності завдяки перезапуску Podʼів на місці

У версії Kubernetes 1.35 представлено нову потужну функцію, яка забезпечує довгоочікувану можливість: можливість викликати повне перезавантаження Podʼів на місці. Ця функція, Restart All Containers (альфа-версія в 1.35), дозволяє ефективно скинути стан Podʼів порівняно з ресурсоємним підходом, що передбачає видалення та повторне створення всього Podʼа. Ця функція особливо корисна для робочих навантажень AI/ML, дозволяючи розробникам застосунків зосередитися на основній логіці тренування, перекладаючи складні механізми обробки збоїв і відновлення в контейнери sidecars та декларативну конфігурацію Kubernetes. За допомогою RestartAllContainers та інших запланованих вдосконалень Kubernetes продовжує додавати будівельні блоки для створення найбільш гнучких, надійних та ефективних платформ для робочих навантажень AI/ML.

Ця нова функціональність доступна після увімкнення функції RestartAllContainersOnContainerExits. Ця альфа-функція розширює функцію Правила перезапуску контейнерів, яка перейшла в бета-версію в Kubernetes 1.35.

Проблема: коли перезапуску одного контейнера недостатньо, а відтворення подів занадто дороге

Kubernetes вже давно підтримує політики перезапуску на рівні Podʼів (restartPolicy) і, з недавнього часу, на рівні окремих контейнерів. Ці політики чудово підходять для обробки збоїв в одному ізольованому процесі. Однак багато сучасних застосунків мають більш складні взаємозалежності між контейнерами. Наприклад:

  • Контейнер init готує середовище, монтуючи том або створюючи файл конфігурації. Якщо основний контейнер застосунку пошкоджує це середовище, простого перезапуску цього контейнера недостатньо. Необхідно запустити весь процес ініціалізації заново.
  • Watcher sidecar контролює стан системи. Якщо він виявляє невідновлюваний, але повторюваний стан помилки, він повинен ініціювати перезапуск основного контейнера застосунку з чистого аркуша.
  • Sidecar, який управляє віддаленим ресурсом, виходить з ладу. Навіть якщо sidecar перезапускається самостійно, основний контейнер може застрягнути, намагаючись отримати доступ до застарілого або пошкодженого зʼєднання.

У всіх цих випадках бажаною дією є перезапуск не одного контейнера, а всіх. Раніше єдиним способом досягти цього було видалення Podʼа і створення нового за допомогою контролера (наприклад, Job або ReplicaSet). Цей процес є повільним і дорогим, оскільки передбачає залучення планувальника, розподіл ресурсів вузлів та повторну ініціалізацію мережі та сховища.

Ця неефективність стає ще гіршою при обробці великомасштабних робочих навантажень AI/ML (>= 1000 вузлів з одним Podʼом на вузол). Загальною вимогою для цих синхронних робочих навантажень є те, що в разі виникнення збою (наприклад, аварії вузла) всі Podʼи у флотилії повинні бути створені заново, щоб скинути стан перед відновленням навчання, навіть якщо всі інші Podʼи не були безпосередньо уражені збоєм. Одночасне видалення, створення та планування тисяч Podʼів створює величезне вузьке місце. Орієнтовні витрати, повʼязані з цією несправністю, можуть становити 100 000 доларів на місяць у вигляді втрачених ресурсів.

Для обробки цих збоїв у завданнях навчання AI/ML потрібна складна інтеграція, що зачіпає як навчальну платформу, так і Kubernetes, які часто є нестабільними і трудомісткими. Ця функція представляє рішення, властиве Kubernetes, що покращує надійність системи і дозволяє розробникам застосунків зосередитися на основній логіці навчання.

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

Представляємо дію RestartAllContainers

Щоб вирішити цю проблему, у Kubernetes v1.35 додано нову дію до правил перезапуску контейнерів: RestartAllContainers. Коли контейнер завершується у спосіб, що відповідає правилу з цією дією, kubelet ініціює швидкий перезапуск Podʼа на місці.

Цей перезапуск на місці є дуже ефективним, оскільки зберігає найважливіші ресурси Podʼа:

  • UID, IP-адресу та мережевий простір імен Podʼа.
  • Пісочницю Podʼа та будь-які підключені пристрої.
  • Усі томи, включаючи emptyDir та томи, змонтовані з PVC.

Після завершення всіх запущених контейнерів послідовність запуску Podʼа повторно виконується з самого початку. Це означає, що всі init-контейнери запускаються знову по порядку, а потім — sidecar-контейнери та звичайні контейнери, що забезпечує повністю новий старт у відомому надійному середовищі. За винятком ефемерних контейнерів (які завершуються), всі інші контейнери, включаючи ті, що раніше були успішними або невдалими, будуть перезапущені, незалежно від їхніх індивідуальних політик перезапуску.

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

1. Ефективний перезапуск завдань ML/Batch

Для завдань навчання ML перепланування робочого Pod у разі збою є дорогою операцією, яка марнує цінні обчислювальні ресурси. На кластері навчання з 1000 вузлів накладні витрати на перепланування можуть призвести до втрати [понад 100 000 доларів на обчислювальні ресурси щомісяця](https://docs.google.com/document/d/16zexVooHKPc80F4dVtUjDYK9DOpkVPRNfSv0zRtfFpk/edit?tab=t.0#bookmark=id. qwqcnzf96avw).

За допомогою дій RestartAllContainers ви можете вирішити цю проблему, увімкнувши набагато швидшу гібридну стратегію відновлення: відтворюйте лише «пошкоджені» Podʼи (наприклад, ті, що знаходяться на несправних вузлах), одночасно запускаючи RestartAllContainers для решти справних Podʼів. Тести показують, що це зменшує накладні витрати на відновлення з хвилин до декількох секунд.

Завдяки перезапуску на місці, watcher sidecar може контролювати основний процес навчання. Якщо він зустрічає конкретну помилку, яку можна повторити, watcher може вийти з призначеним кодом, щоб запустити швидке скидання Podʼа робітника, що дозволяє йому перезапуститися з останньої контрольної точки без залучення контролера завдань. Ця функція тепер підтримується Kubernetes.

Детальніше про майбутній розвиток та функції JobSet читайте в KEP-467 JobSet in-place restart.

apiVersion: v1
kind: Pod
metadata:
  name: ml-worker-pod
spec:
  restartPolicy: Never
  initContainers:
  # Цей контейнер ініціалізації буде перезапускатися при кожному перезапуску на місці.
  - name: setup-environment
    image: my-repo/setup-worker:1.0
  - name: watcher-sidecar
    image: my-repo/watcher:1.0
    restartPolicy: Always
    restartPolicyRules:
    - action: RestartAllContainers
      exitCodes:
        operator: In
        # Конкретний код виходу від watcher запускає повний перезапуск Podʼа.
        values: [88]
  containers:
  - name: main-application
    image: my-repo/training-app:1.0

2. Повторний запуск контейнерів init для отримання чистого стану

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

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

3. Обробка великої кількості подібних завдань

Іноді завдання найкраще виконувати у вигляді Podʼів. Кожне завдання вимагає чіткого виконання. Завданням може бути бек-енд ігрової сесії або обробка елементів черги. Якщо швидкість виконання завдань висока, запуск повного циклу створення, планування та ініціалізації Podʼів є надто дорогим, особливо якщо завдання можуть бути короткими. Можливість перезапустити всі контейнери з нуля дозволяє Kubernetes обробляти такі сценарії без використання спеціальних рішень або фреймворків.

Як користуватися

Щоб спробувати цю функцію, ви повинні увімкнути функціональну можливість RestartAllContainersOnContainerExits у компонентах кластера Kubernetes (API-сервер та kubelet), що працюють під управлінням Kubernetes v1.35+. Ця альфа-функція розширює функцію ContainerRestartRules, яка перейшла в бета-версію в v1.35 та є стандартно увімкненою.

Після увімкнення ви можете додати restartPolicyRules до будь-якого контейнера (init, sidecar або звичайного) та використовувати дію RestartAllContainers.

Функція розроблена для зручного використання в наявних застосунках. Однак, якщо застосунок не відповідає певним рекомендаціям, це може спричинити проблеми для застосунку або інструментів спостереження. Увімкнувши цю функцію, переконайтеся, що всі контейнери є такими, що можуть бути повторно запущені, і що зовнішні інструменти готові до повторного запуску контейнерів init. Крім того, під час перезапуску всіх контейнерів kubelet не запускає хуки preStop. Це означає, що контейнери повинні бути створені таким чином, щоб витримувати раптове завершення роботи, не покладаючись на хуки preStop для коректного вимкнення.

Спостереження за перезапуском

Щоб зробити цей процес спостережуваним, до статусу Podʼа додається нова умова AllContainersRestarting. Коли перезапуск ініційовано, ця умова стає True і повертається до False, коли всі контейнери завершили роботу і Pod готовий розпочати свій життєвий цикл заново. Це забезпечує чіткий сигнал для користувачів та інших компонентів кластера про стан Podʼа.

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

Дізнайтеся більше

Ми хочемо почути вашу думку!

Як альфа-функція, RestartAllContainers готова до експериментів, і будь-які випадки використання та відгуки вітаються. Ця функція розробляється спільнотою SIG Node. Якщо ви зацікавлені долучитися, поділитися своїми думками або зробити внесок, будь ласка, приєднуйтеся до нас!

Ви можете зв’язатися з SIG Node через: