Перейти до вмісту

Модуль 4.3: Вимоги та ліміти ресурсів

Hands-On Lab Available
K8s Cluster intermediate 30 min
Launch Lab ↗

Opens in Killercoda in a new tab

Складність: [MEDIUM] — Критично для продакшену, впливає на планування

Час на виконання: 35–45 хвилин

Передумови: Модуль 1.1 (Поди), розуміння концепцій CPU та пам’яті


Що ви зможете робити

Розділ «Що ви зможете робити»

Після завершення цього модуля ви зможете:

  • Налаштувати запити та ліміти ресурсів для CPU та пам’яті у специфікаціях Підів
  • Діагностувати проблеми OOMKilled та обмеження CPU, зіставляючи ліміти зі спостережуваною поведінкою
  • Спроєктувати розподіл ресурсів, що балансує продуктивність, вартість та надійність планування
  • Пояснити як запити впливають на планування, а ліміти — на обмеження під час виконання

Чому цей модуль важливий

Розділ «Чому цей модуль важливий»

Запити та ліміти ресурсів контролюють, скільки CPU та пам’яті можуть використовувати ваші контейнери. Без них один контейнер може спожити всі ресурси вузла, залишивши інші поди без ресурсів. Належне управління ресурсами є необхідним для стабільності кластера.

На іспиті CKAD перевіряють:

  • Встановлення запитів та лімітів
  • Розуміння різниці між ними
  • Що відбувається при перевищенні лімітів
  • LimitRanges та ResourceQuotas

Аналогія з орендою квартири

Запити ресурсів — це як гарантоване паркомісце — вам забезпечено цей простір. Ліміти — це як максимальна місткість будівлі — ви можете тимчасово використовувати більше простору, але є жорстка межа. Якщо ви її перевищите (пам’ять), вас виселять (OOMKilled). Якщо будівля заповнена (вузол), нові мешканці (поди) чекають, поки звільниться місце.


Запити проти лімітів

Розділ «Запити проти лімітів»
ТермінЗначенняКоли застосовується
Запит (Request)Гарантований мінімум ресурсівПід час планування
Ліміт (Limit)Максимально дозволені ресурсиПід час виконання
┌─────────────────────────────────────────────────────────────┐
│ Запит ресурсів проти ліміту │
├─────────────────────────────────────────────────────────────┤
│ │
│ Пам'ять: │
│ ├── Запит: 256Mi (гарантовано, для планування) │
│ ├── Фактичне використання може бути від 0 до ліміту │
│ └── Ліміт: 512Mi (жорстка межа, перевищення = OOMKill) │
│ │
│ CPU: │
│ ├── Запит: 100m (гарантовано, для планування) │
│ ├── Може перевищувати запит, якщо вузол має вільну │
│ │ потужність │
│ └── Ліміт: 500m (обмежується, якщо перевищено, НЕ вбиває) │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 0 Запит Факт. Ліміт │ │
│ │ | | | | │ │
│ │ ├───────────┼───────────┼────────────┤ │ │
│ │ │гарантовано│ з можл. │ макс. │ │ │
│ │ │ │ перевищ. │ │ │ │
│ │ └───────────┴───────────┴────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

Встановлення ресурсів

Розділ «Встановлення ресурсів»
apiVersion: v1
kind: Pod
metadata:
name: resource-demo
spec:
containers:
- name: app
image: nginx
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"

Одиниці вимірювання

Розділ «Одиниці вимірювання»

CPU:

ЗначенняОпис
11 ядро CPU
1000m1000 мілікорів = 1 ядро
500m0.5 ядра
100m0.1 ядра (10%)

Пам’ять:

ЗначенняОпис
128Mi128 мебібайтів (на основі 1024)
1Gi1 гібібайт = 1024 Mi
128M128 мегабайтів (на основі 1000)
1G1 гігабайт = 1000 M

Що відбувається при досягненні лімітів

Розділ «Що відбувається при досягненні лімітів»

Перевищення ліміту пам’яті

Розділ «Перевищення ліміту пам’яті»
Контейнер використовує > ліміту → OOMKilled → Контейнер перезапускається
Terminal window
# Перевірити, чи під був OOMKilled
k describe pod my-pod | grep -A5 "Last State"
k get pod my-pod -o jsonpath='{.status.containerStatuses[0].lastState}'

Перевищення ліміту CPU

Розділ «Перевищення ліміту CPU»
Контейнер використовує > ліміту → Обмежується (сповільнюється, НЕ вбивається)

Обмеження CPU непомітне для контейнера — він просто працює повільніше.


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

Клас QoSУмоваПріоритет витіснення
GuaranteedЗапити = Ліміти для всіх контейнерівОстанній (захищений)
BurstableЗапити < Ліміти (або встановлено лише одне)Середній
BestEffortЗапити та ліміти не встановленіПерший (витісняється першим)
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "256Mi" # Те саме, що запит
cpu: "100m" # Те саме, що запит
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi" # Більше за запит
cpu: "500m" # Більше за запит
resources: {} # Ресурси не визначені

Вплив на планування

Розділ «Вплив на планування»

Якщо жоден вузол не має достатньо доступних ресурсів (ємність - виділені запити):

Terminal window
# Перевірити, чому під у стані Pending
k describe pod my-pod
# В подіях буде:
# 0/3 nodes are available: 3 Insufficient cpu.
# або
# 0/3 nodes are available: 3 Insufficient memory.

Перевірка ємності вузла

Розділ «Перевірка ємності вузла»
Terminal window
# Ємність та доступні ресурси вузла
k describe node NODE_NAME | grep -A5 Capacity
k describe node NODE_NAME | grep -A5 Allocatable
# Вже виділені ресурси
k describe node NODE_NAME | grep -A10 "Allocated resources"

Стандартні значення та обмеження на рівні простору імен:

apiVersion: v1
kind: LimitRange
metadata:
name: cpu-memory-limits
spec:
limits:
- default: # Ліміти за замовчуванням, якщо не вказано
cpu: "500m"
memory: "512Mi"
defaultRequest: # Запити за замовчуванням, якщо не вказано
cpu: "100m"
memory: "256Mi"
max: # Максимально дозволено
cpu: "2"
memory: "2Gi"
min: # Мінімально дозволено
cpu: "50m"
memory: "64Mi"
type: Container
Terminal window
# Переглянути LimitRange
k get limitrange
k describe limitrange cpu-memory-limits

Загальні обмеження ресурсів на рівні простору імен:

apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
pods: "10"
Terminal window
# Переглянути використання квоти
k get resourcequota
k describe resourcequota compute-quota

Terminal window
# Встановити ресурси в специфікації пода
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# Перевірити ресурси пода
k get pod POD -o jsonpath='{.spec.containers[*].resources}'
# Перевірити ємність вузла
k describe node NODE | grep -A10 "Allocated"
# Перевірити клас QoS
k get pod POD -o jsonpath='{.status.qosClass}'

  • CPU — стисливий ресурс, пам’ять — ні. Якщо ви перевищите ліміт CPU, вас обмежать. Якщо ви перевищите ліміт пам’яті, вас вб’ють.

  • Запити впливають на планування, ліміти — на час виконання. Під із запитом пам’яті 1Gi не буде заплановано на вузлі, де доступно лише 512Mi, навіть якщо контейнер використовує лише 100Mi.

  • Kubernetes не запобігає перевищенню пам’яті. Якщо всі поди одночасно досягнуть своїх лімітів, вузол вичерпає пам’ять і почне вбивати поди.

  • Синтаксис cpu: 0.1 еквівалентний cpu: 100m (100 мілікорів).


ПомилкаЧому це шкодитьРішення
Ресурси не встановленіПоди BestEffort витісняються першимиЗавжди встановлюйте запити
Запит > ЛімітНекоректно, відхиляєтьсяЗапит повинен бути ≤ Ліміту
Пам’яті занадто малоПостійний OOMKilledПрофілюйте застосунок, збільшіть ліміти
CPU занадто малоЗастосунок працює повільноМоніторте за допомогою k top, коригуйте
Те саме, що ємність вузлаНемає місця для системних подівЗалишайте запас

  1. Яка різниця між запитами та лімітами?

    Відповідь Запити — це гарантовані ресурси, які використовуються для рішень планування. Ліміти — це максимально дозволені ресурси, які застосовуються під час виконання. Запити повинні бути ≤ Лімітів.
  2. Що відбувається, коли контейнер перевищує ліміт пам’яті?

    Відповідь Контейнер отримує OOMKilled (завершується) і може бути перезапущений залежно від політики перезапуску.
  3. Що відбувається, коли контейнер перевищує ліміт CPU?

    Відповідь Контейнер обмежується (сповільнюється), але НЕ вбивається. CPU — стисливий ресурс.
  4. Який клас QoS отримує під, якщо запити дорівнюють лімітам?

    Відповідь Guaranteed. Це клас з найвищим пріоритетом, і поди витісняються останніми.

Завдання: Налаштувати та спостерігати за поведінкою ресурсів.

Частина 1: Базові ресурси

Terminal window
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: resource-demo
spec:
containers:
- name: app
image: nginx
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
EOF
# Перевірити клас QoS
k get pod resource-demo -o jsonpath='{.status.qosClass}'
echo
# Перевірити ресурси
k get pod resource-demo -o jsonpath='{.spec.containers[0].resources}'

Частина 2: Демонстрація OOMKill

Terminal window
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: memory-hog
spec:
containers:
- name: app
image: polinux/stress
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "200M", "--vm-hang", "1"]
resources:
limits:
memory: "100Mi"
EOF
# Спостерігати за OOMKilled
k get pod memory-hog -w
# Перевірити причину
k describe pod memory-hog | grep -A3 "Last State"

Очищення:

Terminal window
k delete pod resource-demo memory-hog

Вправа 1: Базові ресурси (Ціль: 2 хвилини)

Розділ «Вправа 1: Базові ресурси (Ціль: 2 хвилини)»
Terminal window
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: drill1
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
EOF
k get pod drill1 -o jsonpath='{.spec.containers[0].resources}'
echo
k delete pod drill1

Вправа 2: Перевірка класу QoS (Ціль: 2 хвилини)

Розділ «Вправа 2: Перевірка класу QoS (Ціль: 2 хвилини)»
Terminal window
# Guaranteed (запити = ліміти)
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: drill2
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "100m"
memory: "128Mi"
EOF
k get pod drill2 -o jsonpath='{.status.qosClass}'
echo
k delete pod drill2

Вправа 3: Генерація пода з ресурсами (Ціль: 2 хвилини)

Розділ «Вправа 3: Генерація пода з ресурсами (Ціль: 2 хвилини)»
Terminal window
# Використати --dry-run для генерації, потім додати ресурси
k run drill3 --image=nginx --dry-run=client -o yaml > /tmp/drill3.yaml
# Відредагувати для додавання ресурсів (на іспиті використовуйте vim)
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: drill3
spec:
containers:
- name: drill3
image: nginx
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
EOF
k get pod drill3 -o yaml | grep -A8 resources
k delete pod drill3

Вправа 4: Deployment з ресурсами (Ціль: 3 хвилини)

Розділ «Вправа 4: Deployment з ресурсами (Ціль: 3 хвилини)»
Terminal window
cat << 'EOF' | k apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: drill4
spec:
replicas: 2
selector:
matchLabels:
app: drill4
template:
metadata:
labels:
app: drill4
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "100m"
memory: "128Mi"
EOF
k get pods -l app=drill4
k delete deploy drill4

Вправа 5: Перевірка ресурсів вузла (Ціль: 2 хвилини)

Розділ «Вправа 5: Перевірка ресурсів вузла (Ціль: 2 хвилини)»
Terminal window
# Отримати ім'я вузла
NODE=$(k get nodes -o jsonpath='{.items[0].metadata.name}')
# Перевірити ємність
k describe node $NODE | grep -A5 "Capacity:"
# Перевірити доступні ресурси
k describe node $NODE | grep -A5 "Allocatable:"
# Перевірити виділені ресурси
k describe node $NODE | grep -A10 "Allocated resources:"

Вправа 6: LimitRange (Ціль: 4 хвилини)

Розділ «Вправа 6: LimitRange (Ціль: 4 хвилини)»
Terminal window
# Створити простір імен з LimitRange
k create ns drill6
cat << 'EOF' | k apply -n drill6 -f -
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
spec:
limits:
- default:
cpu: "200m"
memory: "256Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
type: Container
EOF
# Створити під без ресурсів
k run drill6-pod --image=nginx -n drill6
# Перевірити, що стандартні значення застосовані
k get pod drill6-pod -n drill6 -o jsonpath='{.spec.containers[0].resources}'
echo
# Очищення
k delete ns drill6

Модуль 4.4: SecurityContexts — Налаштування параметрів безпеки подів та контейнерів.