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

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

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

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

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 доларів на обчислювальні ресурси щомісяця.

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

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

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

apiVersion: v1
kind: Pod
metadata:
  name: ml-worker-pod
spec:
  restartPolicy: Never
  initContainers:
  # Цей init-контейнер буде перезапускатися при кожному перезапуску на місці.
  - 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-контейнерів для отримання чистого стану

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

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

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 через: