# Kubernetes v1.37: Витіснення планувальника для зміни розміру Podʼів без переззапуску (Alpha)

LLMS index: [llms.txt](/llms.txt)

---

У Kubernetes виділення ресурсів історично було статичним рішенням, прийнятим під час початкового планування та розміщення Podʼа. З просуванням основної функції [in-Place Pod resize](/blog/2025/12/19/kubernetes-v1-35-in-place-Pod-resize-ga/) до General Availability у v1.35, розробники застосунків та оператори кластерів отримали потужну здатність динамічно коригувати виділення CPU та памʼяті запущених контейнерів без руйнівних перезапусків або простою застосунків.

Однак in-place resizing ввів унікальну прогалину в плануванні ресурсів: якщо запущений Pod запитував масштабування ресурсів, що перевищувало доступний запас allocatable ресурсів хост-вузла, Kubelet був змушений позначити запит як `Deferred`. Pod залишався у цьому стані невизначений час, очікуючи, доки ресурси на вузлі не звільняться природним шляхом.

Для закриття цього розриву в плануванні, Kubernetes v1.37 вводить *scheduler preemption для in-place Pod resize* (Alpha), з функціональною можливістю `InPlacePodVerticalScalingSchedulerPreemption`. Ця функція дозволяє планувальнику Kubernetes активно звільняти потужність на повністю завантаженому вузлі шляхом витіснення робочих навантажень з нижчим пріоритетом, уможливлюючи успіх очікуваних in-place масштабувань критичних застосунків з вищим пріоритетом.

## Виклик «deferred» resize {#the-deferred-resize-challenge}

Щоб зрозуміти, чому цей механізм витіснення потрібен, корисно подивитися, як Kubernetes обробляє зміну розміру запущених Podʼів. Коли користувач або контролер (такий як [Vertical Pod Autoscaler](/docs/concepts/workloads/autoscaling/vertical-pod-autoscale/)) оновлює запити ресурсів активного контейнера, Kubelet оцінює, чи базовий вузол має достатньо вільної потужності для задоволення збільшення.

Якщо ресурси вузла повністю використані і не можуть задовольнити нові ліміти, Kubelet встановлює `resizeStatus` контейнера (повідомлений у `status.containerStatuses[]` Podʼа) на `Deferred`. На відміну від запиту на зміну розміру `Infeasible` (який негайно відхиляється, бо він перевищує фізичні межі машини, ліміти namespace або квоти допуску), статус `Deferred` вказує на те, що запит є дійсним, але тимчасово не може бути реалізований, очікуючи, доки потужність вузла не стане доступною.

Перед введенням цього механізму витіснення запит на in-place масштабування Podʼа міг стати постійно заблокованим, якщо вузол був інтенсивно використаний. Навіть коли критичному застосунку (такому як in-memory база даних або real-time веб-сервер) потрібно більше памʼяті для запобігання неминучому збою out-of-memory (OOM), а вузол не мав вільної потужності, resize залишався `Deferred`.

У цьому сценарії оператори кластера мали обмежений вибір:

1. Вручну витіснити Podʼи з нижчим пріоритетом з вузла, щоб звільнити запас ресурсів.
2. Покладатися на кластерний автомасштабувач, який зрештою запустить більший вузол і перепланує Pod. Однак це операція, яка є надзвичайно руйнівною і порушує основну пропозицію цінності «без перезапуску» in-place масштабування.
3. Покладатися на користувацьке рішення автоматичного масштабування, наприклад, кластерний автомасштабувач, який може сам ініціювати операції динамічної зміни розміру вузла.

Оскільки `kube-scheduler` не знав про deferred resize на запущених Podʼах, він не міг використовувати стандартне пріоритетне витіснення для витіснення робочих навантажень з нижчим пріоритетом і звільнення місця для зростання ресурсів запущеного Podʼа з вищим пріоритетом.

## Чому це важливо {#why-this-matters}

В операційних середовищах Kubernetes оператори кластерів прагнуть максимізувати використання ресурсів та ефективність. Поширена стратегія — bin-pack (упакування) невикористаної потужності на ще не повних вузлах робочими навантаженнями з нижчим пріоритетом, такими як пакетні завдання, фонова обробка даних або best-effort задачі.

Без scheduler preemption для in-place resizing це створювало велику операційну дилему. Якщо робочі навантаження з нижчим пріоритетом споживали остаточний запас на вузлі, застосунки з вищим пріоритетом, що запущені на тому самому вузлі, ставали заблокованими (`Deferred`), коли їм потрібно було масштабуватися для обробки раптових стрибків трафіку або сплесків використання памʼяті. Оператори були змушені вибирати між запуском кластерів з низьким використанням та вільним буферним запасом або ризиком того, що критичні робочі навантаження не зможуть масштабуватися, коли це потрібно.

З scheduler preemption для in-place Pod resize ви можете впевнено упакувати невикористаний простір у своїх кластерах робочими навантаженнями з нижчим пріоритетом, не переймаючись тим, що вони деградують Podʼи з вищим пріоритетом або блокують їхні запити на масштабування. Якщо робоче навантаження з високим пріоритетом вимагає in-place resize, що перевищує доступну потужність вузла, планувальник автоматично витісняє Podʼи з нижчим пріоритетом для звільнення запасу. Ви досягаєте високого використання кластера та ефективності витрат, зберігаючи при цьому чутливість та надійність критичних сервісів.

## Архітектурна механіка: Як це працює {#architectural-mechanics-how-it-works}

**Scheduler preemption для in-place Pod resize** інтегрується безпосередньо в основний цикл планування для динамічної та безпечної координації ресурсів.

### Централізоване відстеження планувальником {#centralized-scheduler-tracking}

`kube-scheduler` відстежує в кластері Podʼи, що виконуються зі статусом зміни розміру `Deferred`. Зазвичай Podʼи із заповненим полем `spec.nodeName` вважаються успішно розміщеними й оминають активну чергу планування. У рамках цієї функціональної можливості планувальник перехоплює Podʼи зі статусом `Deferred`, дозволяючи їм залишатися в активній черзі планування спеціально для запуску витіснення. Планувальник здійснює безперервне відстеження цих Podʼів, доки Kubelet не завершить успішно операцію зміни розміру.

### Межа витіснення на одному вузлі {#single-node-preemption-boundary}

На відміну від витіснення при розміщенні, яке аналізує всі вузли в кластері для пошуку оптимального варіанту планування, витіснення для зміни розміру на місці суворо обмежується вузлом, якому наразі призначено Pod. Планувальник визначає відповідні Podʼи з нижчим пріоритетом — «жертви» — на тому самому хості та ініціює їх належне витіснення, звільняючи локальні ресурси. Витіснення суворо обмежується тим самим вузлом, на якому працює Pod із відкладеним зміненням розміру; якщо вузол не може забезпечити змінення розміру навіть після витіснення всіх відповідних робочих навантажень з нижчим пріоритетом, змінення розміру залишається у стані `Deferred`.

### Безпека резервування ресурсів {#resource-reservation-safety}

Щоб запобігти гонитві планувальників і подвійному виділенню ресурсів, планувальник розглядає ресурси, запитувані для зміни розміру, як **вже використані**. Це дозволяє Kubelet запустити зміну розміру, щойно витіснення набуде чинності.

### Розділення зон відповідальності та критичний допуск {#separation-of-concerns-critical-admission}

Коли вузол перебуває під тиском нестачі ресурсів, Kubelet включає локальний механізм, відомий як **critical Pod admission handler** (обробник критичного допуску Podʼів). Під час початкового допуску Podʼа, якщо критичний системний Pod надходить на вузол, в якого бракує вільної потужності, цей локальний обробник може безпосередньо витіснити Podʼи з нижчим пріоритетом на цьому вузлі для гарантування допуску критичного робочного навантаження.

Значною архітектурною перевагою цієї нової функції є суворе розділення зон відповідальності між Kubelet та планувальником. З функціональної можливості `InPlacePodVerticalScalingSchedulerPreemption` **обробник допуску критичних Podʼів Kubelet НЕ виконує локальні перевірки витіснення і НЕ ініціює локальні витіснення для операцій зміни розміру на місці.** Замість цього Kubelet відкладає запит і повністю делегує рішення про витіснення планувальнику. Це гарантує, що єдиний централізований оркестратор керує всією логікою витіснення, повʼязаною зі зміною розміру на місці, поважаючи глобальні пріоритети, Pod Disruption Budgets (PDB) та політики належного завершення роботи.

### Управління конкуруючими оновленнями та гонитвами {#managing-competing-updates-races}

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

## Конфігурація витіснення на рівні вузла {#node-level-preemption-configuration}

Адміністратори та засоби автоматизованого управління (наприклад, засіб автоматичного масштабування кластера) можуть вимкнути витіснення спеціально для зміни розміру без перезапуску на окремих вузлах. Це налаштовується за допомогою нового поля `spec.podPreemptionPolicy` у специфікації вузла:

```yaml
apiVersion: v1
kind: Node
metadata:
  name: batch-workload-node
spec:
  podPreemptionPolicy:
    disableResizePreemption:
      - "cluster-autoscaler.kubernetes.io/disable-preemption"
      - "operator.example.com/policy-override"
```

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

## Спробуйте це! {#try-it-out}

Для використання витіснення планувальника для зміни розміру Podʼа на місці:

* Ваш кластер має працювати на Kubernetes версії **v1.37 або новішій**, як на панелі управління, так і на всіх робочих вузлах.
* Функціональна можливість `InPlacePodVerticalScalingSchedulerPreemption` має бути увімкнена на всіх компонентах панелі управління (`kube-apiserver`, `kube-scheduler`) та `kubelet`.

### Міні-посібник: Спостереження за витісненням планувальника для зміни розміру на місці в дії {#mini-tutorial-observe-resize-preemption-in-action}

Щоб побачити цю функцію в дії локально, ви можете протестувати витіснення планувальника на одно-вузловому кластері `kind` з обмеженим запасом CPU.

#### 1. Створіть kind-кластер з увімкненим витісненням планувальника для зміни розміру на місці {#create-a-kind-cluster-with-scheduler-resize-preemption-enabled}

Створіть конфігураційний файл kind-кластера `kind-config.yaml` з увімкненою функціональною можливістю `InPlacePodVerticalScalingSchedulerPreemption`:

```yaml
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
  InPlacePodVerticalScalingSchedulerPreemption: true
```

Створіть кластер, використовуючи цю конфігурацію, передаючи прапорець `--image`, щоб переконатися, що кластер працює на Kubernetes **v1.37** (або новішій):

```shell
kind create cluster --config kind-config.yaml --image kindest/node:v1.37.0
```


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примітка:</h4>Переконайтеся, що образ вузла, який ви вказуєте, відповідає Kubernetes-кластеру <strong>v1.37</strong> або новішому (такому як <code>kindest/node:v1.37.0</code>). Старіші релізи Kubernetes не підтримують функціональну можливість <code>InPlacePodVerticalScalingSchedulerPreemption</code>.</div>


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

```shell
kubectl get nodes -o custom-columns=NAME:.metadata.name,ALLOCATABLE_CPU:.status.allocatable.cpu
```

У стандартному локальному середовищі `kind` вивід показує **8** доступних CPU ядер:

```shell
NAME                 ALLOCATABLE_CPU
kind-control-plane   8
```

#### 2. Створіть PriorityClasses та розгорніть Podʼи {#create-priorityclasses-and-deploy-pods}

Створіть два PriorityClasses та розгорніть Pod з низьким пріоритетом (що запитує `3` CPU) поруч з Podʼом з високим пріоритетом (що запитує `4` CPU). Разом ці робочі навантаження споживають `7` з `8` доступних CPU ядер, залишаючи `1` CPU вільного доступного запасу на вузлі.

```yaml
# preemption-demo.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
  value: 1000000
  globalDefault: false
  description: "High priority workload"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: low-priority
  value: 1000
  globalDefault: false
  description: "Low priority workload"
---
apiVersion: v1
kind: Pod
metadata:
  name: low-priority-pod
spec:
  priorityClassName: low-priority
  containers:
  - name: worker
    image: nginx
    resources:
      requests:
        cpu: "3"
        memory: "500Mi"
      limits:
        cpu: "3"
        memory: "500Mi"
---
apiVersion: v1
kind: Pod
metadata:
  name: high-priority-pod
spec:
  priorityClassName: high-priority
  containers:
  - name: app
    image: nginx
    resources:
      requests:
        cpu: "4"
        memory: "1Gi"
      limits:
        cpu: "4"
        memory: "1Gi"
```

Збережіть цей маніфест у `preemption-demo.yaml` і застосуйте його:

```shell
kubectl apply -f preemption-demo.yaml
```

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

```shell
kubectl get pods
```

Вивід:

```shell
NAME                READY   STATUS    RESTARTS   AGE
high-priority-pod   1/1     Running   0          9s
low-priority-pod    1/1     Running   0          9s
```

#### 3. Запит на in-place scale-up {#request-an-in-place-scale-up}

Внесіть виправлення до Pod з високим пріоритетом, щоб збільшити його запит на ресурси ЦП з `4` до `6` (+2 одиниці ЦП). Оскільки на вузлі вільний лише `1` ЦП, цей запит на зміну розміру перевищує залишкову доступну ємність:

```shell
kubectl patch pod high-priority-pod --subresource resize --patch \
  '{"spec":{"containers":[{"name":"app", "resources":{"requests":{"cpu":"6"}, "limits":{"cpu":"6"}}}]}}'
```

#### 4. Перевірка події витіснення на Podʼі з низьким пріоритетом {#inspect-the-preemption-event-on-the-low-priority-pod}

Якщо увімкнено `InPlacePodVerticalScalingSchedulerPreemption`, планувальник перехоплює стан зміни розміру `Deferred` у `high-priority-pod` і вибирає `low-priority-pod` для витіснення.

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

```shell
kubectl get events --field-selector involvedObject.name=low-priority-pod
```

У потоці подій (або через `kubectl describe pod low-priority-pod`) ви побачите подію `Preempted`, емітовану планувальником:

```shell
LAST SEEN   TYPE     REASON      OBJECT                 MESSAGE
5s          Normal   Preempted   pod/low-priority-pod   Preempted by pod 97dba925-6b5f-4e2f-99f9-d51c30016586 on node kind-control-plane
5s          Normal   Killing     pod/low-priority-pod   Stopping container worker
```

#### 5. Відстеження життєвого циклу події зміни розміру на Podʼі з високим пріоритетом {#trace-the-resize-event-lifecycle-on-the-high-priority-pod}

Далі перегляньте історію подій на `high-priority-pod`, щоб спостерегти, як зміна розміру прогресувала від deferred до успішного завершення:

```shell
kubectl get events --field-selector involvedObject.name=high-priority-pod
```

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

```shell
LAST SEEN   TYPE      REASON            OBJECT                  MESSAGE
33s         Warning   ResizeDeferred    pod/high-priority-pod   Pod resize OutOfcpu: {"containers":[{"name":"app","resources":{"limits":{"cpu":"6","memory":"1Gi"},"requests":{"cpu":"6","memory":"1Gi"}}}],"generation":2,"error":"Node didnʼt have enough resource: cpu, requested: 6000, used: 3950, capacity: 8000"}
32s         Normal    ResizeStarted     pod/high-priority-pod   Pod resize started: {"containers":[{"name":"app","resources":{"limits":{"cpu":"6","memory":"1Gi"},"requests":{"cpu":"6","memory":"1Gi"}}}],"generation":2}
32s         Normal    ResizeCompleted   pod/high-priority-pod   Pod resize completed: {"containers":[{"name":"app","resources":{"limits":{"cpu":"6","memory":"1Gi"},"requests":{"cpu":"6","memory":"1Gi"}}}],"generation":2}
```

1. **`ResizeDeferred`**: Kubelet початково позначає запит на зміну розміру як deferred (`Warning`) через нестачу запасу CPU на вузлі (`OutOfcpu`).
2. **`ResizeStarted`**: Як тільки планувальник витісняє `low-priority-pod` і потужність звільняється, Kubelet приймає нове виділення і починає актуацію зміни розміру.
3. **`ResizeCompleted`**: Kubelet успішно оновлює cgroup ліміти контейнера через середовище виконання контейнерів без перезапуску Podʼа.

Нарешті, перевірте, що виділений CPU на контейнері відображає новий запит (додавання `{"\n"}` до JSONPath запиту забезпечує завершальний новий рядок у вашому терміналі):

```shell
kubectl get pod high-priority-pod -o jsonpath='{.status.containerStatuses[0].allocatedResources.cpu}{"\n"}'
```

Вивід:

```shell
6
```

Це підтверджує, що зміна розміру без перезапуску Podʼа вдалась.

## Як долучитися {#getting-involved}

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

Якщо ви хочете поділитися своїм досвідом з цією функцією, будь ласка, звʼяжіться зі спільнотою через канали [SIG Scheduling](https://www.kubernetes.dev/community/community-groups/sigs/scheduling) або [SIG Node](https://www.kubernetes.dev/community/community-groups/sigs/node)!
