Це багатосторінковий друкований вигляд цього розділу. Натисність щоб друкувати.

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

Концепції

Розділ "Концепції" допоможе вам дізнатися про складові системи Kubernetes та абстракції, які використовує Kubernetes для представлення вашого кластера, а також краще зрозуміти, як працює Kubernetes.

1 - Огляд

Kubernetes — це переносима, розширювана та відкрита платформа для управління контейнеризованими робочими навантаженнями та сервісами, яка полегшує як декларативне налаштування, так і автоматизацію. Вона має велику екосистему, яка швидко зростає. Сервіси, підтримка та інструменти Kubernetes широко доступні.

Ця сторінка містить огляд Kubernetes.

Назва Kubernetes (/ˌk(j)uːbərˈnɛtɪs, Кубернетіс) походить від грецького слова, що означає особу, що керує кораблем — керманич1. K8s — це скорочення Kubernetes, яке складається з першої літери «K», потім восьми літер і, нарешті, літери «s». Google зробив Kubernetes проєктом з відкритим кодом у 2014 році. Kubernetes поєднує понад 15 років досвіду Google запуску промислових робочих навантажень у великому масштабі з найкращими ідеями та практиками спільноти.

Чому вам потрібен Kubernetes та що він може робити?

Контейнери — це гарний спосіб обʼєднати та запустити ваші застосунки. У промисловому середовищі вам потрібно керувати контейнерами, які запускають застосунки, і гарантувати відсутність простоїв. Наприклад, якщо контейнер падає, повинен запуститися інший контейнер. Чи не було б легше, якби такою поведінкою займалася система?

Ось саме тут Kubernetes приходить на допомогу! Kubernetes надає вам основу для надійного запуску розподілених систем. Він піклується про масштабування та відмовостійкість для ваших застосунків, забезпечує шаблони розгортання та багато іншого. Наприклад, Kubernetes може легко керувати канарковими розгортаннями (canary deployment) для вашої системи.

Kubernetes пропонує:

Виявлення сервісів та балансування навантаження
Kubernetes може надати доступ до контейнера за допомогою імені DNS або його власної IP-адреси. Якщо навантаження на контейнер зростає, Kubernetes може балансувати навантаження та розподіляти мережевий трафік, що робить роботу розгортання стабільною.
Оркестрування зберігання
Kubernetes дозволяє автоматично монтувати вибрану вами систему зберігання, наприклад локальне сховище, сховища публічних хмарних провайдерів тощо.
Автоматизоване розгортання та відкочування
Ви можете описати бажаний стан розгорнутих контейнерів, використовуючи Kubernetes, і він може змінювати фактичний стан на бажаний із контрольованою швидкістю. Наприклад, ви можете автоматизувати Kubernetes так, щоб він створював нові контейнери для вашого розгортання, видаляв наявні контейнери та передавав усі їхні ресурси новому контейнеру.
Автоматичне пакування
Ви надаєте Kubernetes кластер вузлів, які він може використовувати для виконання контейнеризованих завдань. Ви вказуєте Kubernetes, скільки процесорних потужностей (ЦП) та памʼяті (ОЗП) потрібно кожному контейнеру. Kubernetes може помістити контейнери на ваші вузли, щоб найкращим чином використовувати ресурси.
Самозцілення
Kubernetes перезапускає контейнери, які зазнали збою, замінює контейнери, завершує контейнери, які не відповідають на задану користувачем перевірку стану, і не показує їх клієнтам, доки вони не будуть готові до обслуговування.
Керування секретами та конфігурацією
Kubernetes дозволяє зберігати чутливі дані й керувати ними, зокрема паролі, OAuth-токени та SSH-ключі. Ви можете розгортати та оновлювати секрети й конфігурацію застосунків без перезбирання образів контейнерів і без розкриття секретів у конфігурації вашого стеку.
Пакетне виконання
Окрім сервісів, Kubernetes може керувати вашими пакетними та CI-навантаженнями, за потреби замінюючи контейнери, що зазнали збою.
Горизонтальне масштабування
Масштабуйте застосунок угору та вниз простою командою, через UI або автоматично залежно від використання ЦП.
Подвійний мережевий стек IPv4/IPv6
Виділення адрес IPv4 та IPv6 для Podʼів та Services.
Розширюваність за задумом
Додавайте нові функції до свого кластера Kubernetes без зміни основного коду Kubernetes.

Чим не є Kubernetes?

Kubernetes не є традиційною всеохопною PaaS-платформою (Platform as a Service). Оскільки Kubernetes працює на рівні контейнерів, а не на апаратному рівні, він надає деякі загальноприйняті функції PaaS, такі як розгортання, масштабування, балансування навантаження, і дозволяє користувачам інтегрувати власні рішення для логування, моніторингу та оповіщення. Однак Kubernetes не є монолітним, і ці стандартні рішення є необовʼязковими та підключуваними. Kubernetes надає будівельні блоки для створення платформ для розробників, але зберігає свободу вибору та гнучкість там, де це важливо.

Kubernetes:

  • Не обмежує типи підтримуваних застосунків. Kubernetes прагне підтримувати надзвичайно різноманітні робочі навантаження, включаючи stateless (без збереження стану), stateful (зі збереженням стану) та навантаження з обробки даних. Якщо застосунок може працювати в контейнері, він повинен чудово працювати в Kubernetes.
  • Не розгортає вихідний код і не збирає ваш застосунок. Робочі процеси безперервної інтеграції, доставки та розгортання (CI/CD) визначаються культурою та уподобаннями організації, а також технічними вимогами.
  • Не надає сервіси на рівні застосунків, такі як проміжне програмне забезпечення (middleware, наприклад, шини повідомлень), фреймворки обробки даних (наприклад, Spark), бази даних (наприклад, MySQL), кеші або кластерні системи зберігання (наприклад, Ceph) як вбудовані сервіси. Такі компоненти можуть працювати в Kubernetes та/або бути доступними застосункам, що працюють в Kubernetes, за допомогою переносних механізмів, таких як Open Service Broker.
  • Не диктує рішення для логування, моніторингу або оповіщення. Він забезпечує деякі інтеграції як доказ концепції та механізми збору та експорту метрик.
  • Не надає та не ставить вимог щодо мови/системи конфігурації (наприклад, Jsonnet). Він надає декларативний API, який може використовуватись довільними формами декларативних специфікацій.
  • Не надає та не застосовує жодних комплексних систем конфігурації, обслуговування, управління або самозцілення машин.
  • Крім того, Kubernetes — це не просто система оркестрування. Насправді він усуває необхідність оркестрування. Технічним визначенням оркестрування є виконання визначеного робочого процесу: спочатку зробіть A, потім B, потім C. На відміну від цього, Kubernetes складається з набору незалежних компонованих процесів управління, які безперервно наближають поточний стан до заданого бажаного стану. Не має значення, як ви перейдете від A до C. Централізоване управління також не потрібне. Завдяки цьому система простіша у використанні, потужніша, надійніша, стійкіша та краще розширюється.

Історичний контекст появи Kubernetes

Погляньмо на те, чому Kubernetes настільки корисний, повернувшись в минуле.

Еволюція розгортання

Ера традиційного розгортання:

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

Ера віртуалізованого розгортання:

Як розвʼязання цієї проблеми виникла віртуалізація. Вона дозволяє запускати кілька віртуальних машин (VM) на ЦП одного фізичного сервера. Віртуалізація дозволяє ізолювати застосунки в межах віртуальних машин та надає такий рівень безпеки, що інформація з одного застосунку не може бути вільно доступною в іншому застосунку.

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

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

Ера контейнеризації:

Контейнери подібні до віртуальних машин, але вони мають менш суворі умови ізоляції, що дозволяє застосункам спільно використовувати операційну систему (ОС). Тому контейнери вважаються легшими, ніж віртуальні машини. Так само як і віртуальні машини, контейнер має власну файлову систему, частку ЦП, памʼять, простір процесів тощо. Оскільки вони відокремлені від базової інфраструктури, їх просто переносити між хмарами та дистрибутивами ОС.

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

  • Гнучке створення та розгортання застосунків: підвищено простоту та ефективність створення образу контейнера порівняно з використанням образу віртуальної машини.
  • Безперервна розробка, інтеграція та розгортання: забезпечує надійне та часте створення та розгортання образу контейнера зі швидким та ефективним відкатом (через незмінність образу).
  • Розділення обовʼязків Dev та Ops: створення образів контейнерів застосунків під час збирання/випуску, а не під час розгортання, тим самим застосунки відокремлюються від інфраструктури.
  • Спостережуваність: не тільки відображає інформацію та метрики на рівні ОС, але й стан застосунків та інші сигнали.
  • Узгодженість середовищ розробки, тестування та промислової експлуатації: все працює так само на ноутбуці, як і в хмарі.
  • Переносимість між хмарами та дистрибутивами ОС: працює на Ubuntu, RHEL, CoreOS, у власній інфраструктурі, в основних публічних хмарах і будь-де ще.
  • Управління, орієнтоване на застосунки: підвищує рівень абстракції від запуску ОС на віртуальному обладнанні до запуску застосунків в ОС з використанням логічних ресурсів.
  • Слабко повʼязані, розподілені, еластичні, звільнені мікросервіси: застосунки розбиваються на менші, незалежні частини та можуть бути розгорнуті та керовані динамічно — на відміну від монолітного стека, що працює на одній великій спеціально призначеній для цього машині.
  • Ізоляція ресурсів: передбачувана продуктивність застосунків.
  • Використання ресурсів: висока ефективність та щільність.

Що далі


  1. Керманич — фахівець у навігації, виборі шляху, лоцманському проведенні, вітрах і течіях, глибинах, місцевих умовах… https://uk.wikipedia.org/wiki/Керманич ↩︎

1.1 - Компоненти Kubernetes

Огляд ключових компонентів, з яких складається кластер Kubernetes.

Ця сторінка містить огляд основних компонентів, з яких складається кластер Kubernetes.

Компоненти Kubernetes

Компоненти кластера Kubernetes

Основні компоненти

Кластер Kubernetes складається з панелі управління та одного або декількох робочих вузлів. Ось короткий огляд основних компонентів:

Компоненти панелі управління

Керують загальним станом кластера:

kube-apiserver
Основний серверний компонент, який надає HTTP API Kubernetes.
etcd
Узгоджене та високодоступне сховище ключ-значення для всіх даних API-сервера.
kube-scheduler
Шукає ще не привʼязані до вузла Podʼи та призначає кожен Pod до відповідного вузла.
kube-controller-manager
Запускає контролери для реалізації поведінки API Kubernetes.
cloud-controller-manager (необовʼязково)
Інтегрується з базовими хмарними провайдерами.

Компоненти вузлів

Запускаються на кожному вузлі, підтримуючи запущені Podʼи та надаючи середовище виконання Kubernetes:

kubelet
Забезпечує роботу Podʼів, включно з їхніми контейнерами.
kube-proxy (необовʼязково)
Підтримує мережеві правила на вузлах для реалізації Services.
Середовище виконання контейнерів
Програмне забезпечення, відповідальне за запуск контейнерів. Дивіться Середовище виконання контейнерів, щоб дізнатись більше.
🛇 Цей елемент посилається на сторонній проєкт або продукт, який не є частиною Kubernetes. Докладніше

Ваш кластер може потребувати додаткового програмного забезпечення на кожному вузлі; наприклад, ви можете також запускати systemd на вузлі Linux для контролю локальних компонентів.

Надбудови

Надбудови розширюють функціональність Kubernetes. Ось кілька важливих прикладів:

DNS
Для DNS-розвʼязання на рівні всього кластера.
Вебінтерфейс (Dashboard)
Для керування кластером через вебінтерфейс.
Моніторинг ресурсів контейнерів
Для збирання та зберігання метрик контейнерів.
Логування на рівні кластера
Для збереження логів контейнерів у централізованому сховищі логів.

Гнучкість архітектури

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

Для отримання більш детальної інформації про кожен компонент та різні способи налаштування кластерної архітектури, дивіться сторінку Архітектура кластера.

1.2 - Обʼєкти в Kubernetes

Обʼєкти Kubernetes є постійними сутностями в системі Kubernetes. Kubernetes використовує ці сутності для подання стану вашого кластера. Дізнайтеся про модель обʼєктів Kubernetes та про те, як працювати з цими обʼєктами.

Ця сторінка пояснює, як обʼєкти Kubernetes подаються в API Kubernetes, та як ви можете описати їх у форматі .yaml.

Розуміння обʼєктів Kubernetes

Обʼєкти Kubernetes є постійними сутностями в системі Kubernetes. Kubernetes використовує ці сутності для подання стану вашого кластера. Зокрема, вони можуть описувати:

  • Які контейнеризовані застосунки працюють (і на яких вузлах)
  • Ресурси, доступні для цих застосунків
  • Політики щодо того, як ці застосунки поводяться, такі як політики перезапуску, оновлення та стійкості до відмов

Обʼєкт Kubernetes є "записом про наміри" — після створення обʼєкта система Kubernetes постійно працюватиме, щоб переконатися, що обʼєкт існує. Створюючи обʼєкт, ви фактично повідомляєте системі Kubernetes ваші побажання щодо того, як має виглядати робоче навантаження вашого кластера; це бажаний стан вашого кластера.

Для роботи з обʼєктами Kubernetes — чи то для створення, зміни чи видалення — вам потрібно використовувати API Kubernetes. Наприклад, коли ви використовуєте інтерфейс командного рядка kubectl, CLI виконує необхідні виклики API Kubernetes за вас. Ви також можете використовувати API Kubernetes безпосередньо у ваших власних програмах за допомогою однієї з клієнтських бібліотек.

Специфікація та стан обʼєкта

Майже кожен обʼєкт Kubernetes містить два вкладених поля обʼєкта, які керують конфігурацією обʼєкта: spec та status обʼєкта. Для обʼєктів, які мають spec, вам потрібно встановити його під час створення обʼєкта, надаючи опис характеристик, які ви хочете, щоб ресурс мав: його бажаний стан.

status описує поточний стан обʼєкта, який надається та оновлюється системою Kubernetes та її компонентами. Панель управління Kubernetes постійно та активно керує фактичним станом кожного обʼєкта, щоб він відповідав бажаному стану, який ви вказали.

Наприклад: у Kubernetes Deployment є обʼєктом, який може представляти застосунок, який працює на вашому кластері. При створенні Deployment ви можете встановити spec Deployment, щоб вказати, що ви хочете, щоб працювало три репліки застосунку. Система Kubernetes читає spec Deployment та запускає три екземпляри вашого застосунку, оновлюючи status відповідно до вашого spec. Якщо один із цих екземплярів зазнає збою (зміна стану), система Kubernetes реагує на відмінність між spec та status, вносячи виправлення: у цьому випадку запускаючи екземпляр на заміну.

З докладнішою інформацією про spec, status та metadata обʼєктів Kubernetes звертайтесь до Kubernetes API Conventions.

Опис обʼєктів Kubernetes

Коли ви створюєте обʼєкт Kubernetes, ви повинні визначити spec обʼєкта, який описує бажаний стан обʼєкта, а також деякі основні відомості про обʼєкт, наприклад його назву (name). Коли ви використовуєте API Kubernetes для створення обʼєкта, чи безпосередньо, чи за допомогою kubectl, цей запит до API має містити ці відомості у вигляді JSON в тілі запиту. Найчастіше інформація про обʼєкт подається kubectl у вигляді файлу, який називається маніфестом. За домовленістю, маніфести обʼєктів Kubernetes подаються у форматі YAML (ви також можете використовувати JSON). Інструменти, такі як kubectl, перетворюють інформацію з маніфесту у JSON або інший підтримуваний формат серіалізації, надсилаючи HTTP-запити до API.

Ось приклад маніфесту, який містить обовʼязкові поля обʼєкта та його spec для Kubernetes Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Один зі способів створення Deployment за допомогою маніфесту, подібного до показаного вище, — використання команди kubectl apply в інтерфейсі командного рядка kubectl, передаючи файл .yaml як аргумент. Ось приклад:

kubectl apply -f https://k8s.io/examples/application/deployment.yaml

Вивід буде схожий на цей:

deployment.apps/nginx-deployment created

Обовʼязкові поля

В маніфесті (файл YAML або JSON) обʼєкта Kubernetes, який ви хочете створити, ви повинні вказати значення для таких полів:

  • apiVersion — версія API Kubernetes, яку ви використовуєте для створення обʼєкта
  • kind — тип обʼєкта, який ви хочете створити
  • metadata — дані, які допомагають унікально ідентифікувати обʼєкт, включаючи рядок name, UID та, необовʼязково, namespace
  • spec — опис бажаного стану обʼєкта

Точний формат spec обʼєкта є різним для кожного типу обʼєкта в Kubernetes та містить вкладені поля, що притаманні конкретному типу обʼєкта. Довідник API Kubernetes може допомогти знайти формат spec для всіх типів обʼєктів, які ви можете створити за допомогою Kubernetes.

Наприклад, подивіться на поле spec для обʼєкта Pod. Для кожного Podʼа поле .spec описує Pod та його бажаний стан (наприклад, назву образу контейнера для кожного контейнера всередині цього Podʼа). Інший приклад специфікації обʼєкта — поле spec для обʼєкта StatefulSet. Для StatefulSet поле .spec описує StatefulSet та його бажаний стан. У .spec StatefulSet є шаблон для обʼєктів Pod. Цей шаблон описує Podʼи, які контролер StatefulSet створить, щоб задовольнити специфікацію StatefulSet. Різні типи обʼєктів Kubernetes можуть мати різні .status; дивіться довідник API Kubernetes для отримання відомостей про структуру поля .status та його вміст для кожного типу обʼєкта.

Додаткову інформацію про написання конфігураційних файлів YAML дивіться в Рекомендаціях щодо конфігурації Kubernetes.

Перевірка полів на стороні сервера

Починаючи з Kubernetes v1.25, API-сервер надає перевірку полів на стороні сервера, яка виявляє невідомі та дубльовані поля в обʼєкті. Це надає всю функціональність kubectl --validate на стороні сервера.

kubectl використовує прапорець --validate для встановлення рівня перевірки полів маніфесту. Його значенням може бути ignore, warn та strict, також можна вказати true (еквівалент strict) та false (еквівалент ignore). Типове значення перевірки для kubectl — --validate=true.

Strict
Сувора перевірка, повертає збій під час виявлення помилок.
Warn
Перевірка виконується, але виявлення помилок призводить до виведення попереджень, а не збою.
Ignore
Перевірка на стороні сервера не виконується.

Якщо kubectl не може підʼєднатися до API-сервера, який підтримує перевірку полів, він використовує перевірку на стороні клієнта. Kubernetes v1.27 та пізніші версії завжди пропонуватимуть перевірку полів; версії до v1.27, скоріш за все — ні. Якщо версія Kubernetes на вашому кластері старіша за v1.27, звіртесь з документацією до вашої версії Kubernetes.

Що далі

Якщо ви тільки починаєте свій шлях в Kubernetes, дізнайтесь більше про:

Керування обʼєктами в Kubernetes надає додаткову інформацію про те, як використовувати kubectl для керування обʼєктами. Можливо, вам потрібно буде встановити kubectl, якщо його у вас ще немає.

Щоб дізнатись про API Kubernetes, зверніться до:

Щоб глибше ознайомитися з обʼєктами в Kubernetes, прочитайте інші сторінки цього розділу:

1.2.1 - Управління обʼєктами Kubernetes

Інструмент командного рядка kubectl підтримує кілька різних способів створення та управління обʼєктами Kubernetes. Цей документ надає огляд різних підходів. Для отримання деталей щодо управління обʼєктами за допомогою Kubectl читайте Книгу Kubectl.

Техніки управління

Попередження:

Обʼєкт Kubernetes повинен управлятися лише з використанням однієї техніки. Змішування різних технік для того самого обʼєкта призводить до невизначеної поведінки.
Техніка управлінняДіє наРекомендоване середовищеПідтримувані автори змінКрива навчання
Імперативні командиЖиві обʼєктиПроєкти розробки1+Найлегша
Імперативна конфігурація обʼєктівОкремі файлиПромислові проєкти1Середня
Декларативна конфігурація обʼєктівТеки файлівПромислові проєкти1+Найскладніша

Імперативні команди

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

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

Приклади

Запустіть екземпляр контейнера nginx, створивши обʼєкт Deployment:

kubectl create deployment nginx --image nginx

Компроміси

Переваги порівняно із конфігуруванням обʼєктів:

  • Команди виражені як одне слово дії.
  • Команди вимагають лише одного кроку для внесення змін до кластера.

Недоліки порівняно із конфігуруванням обʼєктів:

  • Команди не інтегруються з процесами перегляду змін.
  • Команди не надають журналу аудиту, повʼязаного зі змінами.
  • Команди не надають джерела записів, окрім самих живих обʼєктів.
  • Команди не надають шаблону для створення нових обʼєктів.

Імперативне конфігурування обʼєктів

В імперативній конфігурації обʼєктів команда kubectl вказує операцію (create, replace тощо), необовʼязкові прапорці та принаймні одне імʼя файлу. Зазначений файл повинен містити повне визначення обʼєкта у форматі YAML або JSON.

Див. API-довідник для отримання докладніших відомостей щодо визначення обʼєктів.

Попередження:

Імперативна команда replace замінює поточну специфікацію на нову, відкидаючи всі зміни обʼєкта, яких немає у файлі конфігурації. Цей підхід не повинен використовуватися із типами ресурсів, специфікації яких оновлюються незалежно від файлу конфігурації. Наприклад, у Services типу LoadBalancer поле externalIPs оновлюється кластером незалежно від файлу конфігурації.

Приклади

Створити обʼєкти, визначені у файлі конфігурації:

kubectl create -f nginx.yaml

Видалити обʼєкти, визначені у двох файлах конфігурації:

kubectl delete -f nginx.yaml -f redis.yaml

Оновити обʼєкти, визначені у файлі конфігурації, перезаписавши живу конфігурацію:

kubectl replace -f nginx.yaml

Компроміси

Переваги порівняно з імперативними командами:

  • Конфігурація обʼєктів може зберігатися у системі контролю версій, такій як Git.
  • Конфігурація обʼєктів може інтегруватися з процесами, такими як перегляд змін перед надсиланням (push) та журнали аудиту.
  • Конфігурація обʼєктів надає шаблон для створення нових обʼєктів.

Недоліки порівняно з імперативними командами:

  • Конфігурація обʼєктів потребує базового розуміння схеми обʼєкта.
  • Конфігурація обʼєктів потребує додаткового кроку у вигляді написання файлу YAML.

Переваги порівняно із декларативною конфігурацією обʼєктів:

  • Поведінка імперативної конфігурації обʼєктів простіша та її легше зрозуміти.
  • Станом на версію Kubernetes 1.5 імперативна конфігурація обʼєктів є більш зрілою.

Недоліки порівняно із декларативною конфігурацією обʼєктів:

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

Декларативна конфігурація обʼєктів

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

Примітка:

Декларативна конфігурація обʼєктів зберігає зміни, внесені іншими авторами, навіть якщо зміни не зливаються назад у файл конфігурації обʼєкта. Це можливо за допомогою використання операції API patch для запису тільки виявлених відмінностей, замість використання операції API replace для заміни всього файлу конфігурації обʼєкта.

Приклади

Обробити всі файли конфігурації обʼєктів у теці configs та створити або внести патчі до живих обʼєктів. Спочатку ви можете використовувати diff, щоб побачити, які зміни будуть внесені, а потім застосувати їх:

kubectl diff -f configs/
kubectl apply -f configs/

Рекурсивно обробити теки:

kubectl diff -R -f configs/
kubectl apply -R -f configs/

Компроміси

Переваги порівняно з імперативною конфігурацією обʼєктів:

  • Зміни, внесені безпосередньо в живі обʼєкти, зберігаються, навіть якщо вони не зливаються назад у файли конфігурації.
  • Декларативна конфігурація обʼєктів краще підтримує роботу з теками та автоматично визначає типи операцій (створення, патч, видалення) для кожного обʼєкта.

Недоліки порівняно з імперативною конфігурацією обʼєктів:

  • Декларативну конфігурацію обʼєктів важче налагоджувати, а її результати важче зрозуміти, коли вони неочікувані.
  • Часткові оновлення за допомогою diffs створюють складні операції злиття та накладання патчів.

Що далі

1.2.2 - Назви та ідентифікатори обʼєктів

Кожен обʼєкт у вашому кластері має Назву, яка є унікальною для цього типу ресурсу. Кожен обʼєкт Kubernetes також має UID, який є унікальним в усьому вашому кластері.

Наприклад, ви можете мати лише один обʼєкт Pod із назвою myapp-1234 в одному й тому самому просторі імен, а також ви можете мати один обʼєкт Pod та один Deployment із назвами myapp-1234 кожен.

Для неунікальних атрибутів, наданих користувачем, Kubernetes надає мітки (labels) та анотації (annotations).

Назви

Наданий клієнтом рядок, який посилається на обʼєкт в URL ресурсу, наприклад /api/v1/pods/some-name.

Водночас лише один обʼєкт певного виду (kind) може мати певну назву. Проте якщо ви видалите обʼєкт, ви можете створити новий обʼєкт з такою самою назвою.

Назви повинні бути унікальними між усіма версіями API того самого ресурсу.

Kubernetes унікально ідентифікує обʼєкти за допомогою комбінації чотирьох атрибутів:

  • Група API (наприклад, apps)
  • Тип ресурсу (наприклад, deployments)
  • Простір імен (для ресурсів із просторами імен)
  • Назва

Хоча ви можете отримати доступ до ресурсу через різні версії API (наприклад, v1 або v1beta1), версія є просто іншим представленням того самого базового обʼєкта. Оскільки версія не є частиною унікальної ідентифікації, ви не можете створити два обʼєкти з однаковою назвою та типом ресурсу в одному просторі імен, використовуючи різні версії API.

Примітка:

У випадках, коли обʼєкти представляють фізичний обʼєкт, наприклад, Node, що представляє фізичний хост, коли хост створюється заново з тією самою назвою без видалення та повторного створення Node, Kubernetes розглядає новий хост як старий, що може призвести до неузгодженостей.

Сервер може згенерувати назву, якщо в запиті на створення ресурсу замість name вказати generateName. Коли використовується generateName, надане значення використовується як префікс назви, до якого сервер додає згенерований суфікс. Навіть якщо назву згенеровано, вона може конфліктувати з наявними назвами, що призведе до відповіді HTTP 409. Це стало набагато менш імовірним в Kubernetes v1.31 і пізніших версіях, оскільки сервер зробить до 8 спроб згенерувати унікальну назву перед тим, як повернути відповідь HTTP 409.

Нижче наведено чотири типи обмежень на назви ресурсів, які часто використовуються.

Назви DNS-піддоменів

Більшість типів ресурсів потребують назви, яку можна використовувати як DNS-піддомен згідно з RFC 1123. Це означає, що назва повинна:

  • містити не більше 253 символів
  • містити лише буквено-цифрові символи, '-' чи '.' в нижньому регістрі
  • починатися буквено-цифровим символом
  • закінчуватися буквено-цифровим символом

Назви міток RFC 1123

Деякі типи ресурсів вимагають, щоб їх назви відповідали стандарту міток DNS, як визначено в RFC 1123. Це означає, що назва повинна:

  • містити не більше 63 символів
  • містити лише буквено-цифрові символи чи '-' в нижньому регістрі
  • починатися алфавітним символом
  • закінчуватися буквено-цифровим символом

Примітка:

Коли увімкнено функціональну можливість RelaxedServiceNameValidation, назви обʼєктів Service можуть починатися з цифри.

Назви міток RFC 1035

Деякі типи ресурсів вимагають, щоб їх назви відповідали стандарту міток DNS, як визначено в RFC 1035. Це означає, що назва повинна:

  • містити не більше 63 символів
  • містити лише буквено-цифрові символи чи '-' в нижньому регістрі
  • починатися алфавітним символом
  • закінчуватися буквено-цифровим символом

Примітка:

Хоча RFC 1123 технічно дозволяє міткам починатися з цифри, поточна реалізація Kubernetes вимагає, щоб мітки як RFC 1035, так і RFC 1123 починалися з літери. Винятком є увімкнення функціональної можливості RelaxedServiceNameValidation для обʼєктів Service, що дозволяє назвам Service починатися з цифри.

Назви сегментів шляху

Деякі типи ресурсів вимагають, щоб їх назви можна було безпечно кодувати як сегмент шляху. Іншими словами, назва не може бути "." або "..", і вона не може містити "/" або "%".

Ось приклад маніфесту для Pod із назвою nginx-demo.

apiVersion: v1
kind: Pod
metadata:
  name: nginx-demo
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

Примітка:

Деякі типи ресурсів мають додаткові обмеження на їх назви.

UID

Рядок, створений системами Kubernetes для унікальної ідентифікації обʼєктів.

Кожен обʼєкт, створений протягом усього життя кластера Kubernetes, має унікальний UID. Він призначений для розрізнення історичних екземплярів схожих сутностей.

UID Kubernetes — це універсальні унікальні ідентифікатори (також відомі як UUID). UUID стандартизовані згідно з ISO/IEC 9834-8 та ITU-T X.667.

Що далі

1.2.3 - Мітки та селектори

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

"metadata": {
  "labels": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

Мітки дозволяють ефективно виконувати запити та спостереження та є ідеальними для використання в інтерфейсах користувача та інтерфейсах командного рядка. Неідентифікувальну інформацію слід записувати за допомогою анотацій.

Мотивація

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

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

Приклади міток:

  • "release" : "stable", "release" : "canary"
  • "environment" : "dev", "environment" : "qa", "environment" : "production"
  • "tier" : "frontend", "tier" : "backend", "tier" : "cache"
  • "partition" : "customerA", "partition" : "customerB"
  • "track" : "daily", "track" : "weekly"

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

Синтаксис та набір символів

Мітки — це пари ключ/значення. Дійсні ключі міток мають два сегменти: необовʼязковий префікс та назву, розділені скісною рискою (/). Сегмент назви є обовʼязковим і має бути не більше 63 символів, починатися та закінчуватися буквено-цифровим символом ([a-z0-9A-Z]), і може містити дефіси (-), підкреслення (_), крапки (.) та буквено-цифрові символи між ними. Префікс є необовʼязковим. Якщо вказаний, префікс повинен бути піддоменом DNS: серією DNS-міток, розділених крапками (.), не довше 253 символів загалом, за якими слідує скісна риска (/).

Якщо префікс відсутній, ключ мітки вважається приватним для користувача. Автоматизовані компоненти системи (наприклад, kube-scheduler, kube-controller-manager, kube-apiserver, kubectl або інші сторонні засоби автоматизації), які додають мітки до обʼєктів користувача, повинні вказати префікс.

Префікси kubernetes.io/ та k8s.io/ є зарезервованими для основних компонентів Kubernetes.

Дійсне значення мітки:

  • повинно бути не більше 63 символів (може бути порожнім),
  • крім порожнього значення, повинно починатися та закінчуватися буквено-цифровим символом ([a-z0-9A-Z]),
  • може містити дефіси (-), підкреслення (_), крапки (.) та буквено-цифрові символи між ними.

Наприклад, ось маніфест для Pod із двома мітками environment: production та app: nginx:

apiVersion: v1
kind: Pod
metadata:
  name: label-demo
  labels:
    environment: production
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

Селектори міток

На відміну від назв та UID, мітки не забезпечують унікальності. Загалом, ми очікуємо, що багато обʼєктів матимуть ті самі мітки.

За допомогою селектора міток клієнт або користувач може ідентифікувати набір обʼєктів. Селектор міток є основним примітивом групування в Kubernetes.

На цей момент API підтримує два типи селекторів: на основі рівності та на основі множини. Селектор міток може складатися з кількох вимог, які розділені комами. У випадку кількох вимог всі повинні бути задоволені, таким чином кома виступає логічним оператором AND (&&).

Семантика порожніх або невизначених селекторів залежить від контексту, і типи API, які використовують селектори, повинні документувати їхню чинність та значення.

Примітка:

Для деяких типів API, таких як ReplicaSets, селектори міток двох екземплярів не повинні перетинатися в одному просторі імен, бо контролер може вважати це конфліктними інструкціями та не зможе визначити, скільки реплік повинно бути присутньо.

Увага:

Для умов на основі рівності і на основі множини немає логічного оператора OR (||). Переконайтеся, що ваші вирази фільтрації структуровані відповідно.

Вимоги на основі рівності

Вимоги на основі рівності чи нерівності дозволяють фільтрувати обʼєкти за ключами та значеннями міток. Відповідні обʼєкти повинні задовольняти всі вказані обмеження міток, хоча вони також можуть мати додаткові мітки. Допускаються три види операторів: =, ==, !=. Перший та другий представляють рівність (equality) і є синонімами, тоді як останній представляє нерівність (inequality). Наприклад:

environment = production
tier != frontend

Перший вибирає всі ресурси з ключем environment та значенням production. Другий вибирає всі ресурси з ключем tier та значеннями, відмінними від frontend, а також усі ресурси без міток із ключем tier. Можна відфільтрувати ресурси в production, виключивши frontend, за допомогою оператора коми: environment=production,tier!=frontend.

Один зі сценаріїв використання вимог на основі рівності — вказування критеріїв вибору вузлів для Podʼів. Наприклад, зразок Podʼа нижче вибирає вузли, де мітка accelerator існує і має значення nvidia-tesla-p100.

apiVersion: v1
kind: Pod
metadata:
  name: cuda-test
spec:
  containers:
    - name: cuda-test
      image: "registry.k8s.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1
  nodeSelector:
    accelerator: nvidia-tesla-p100

Вимоги на основі множини

Вимоги на основі множини дозволяють фільтрувати ключі за набором значень. Підтримуються три види операторів: in, notin та exists (тільки ідентифікатор ключа). Наприклад:

environment in (production, qa)
tier notin (frontend, backend)
partition
!partition
  • Перший приклад вибирає всі ресурси з ключем, рівним environment, та значенням, рівним production або qa.
  • Другий приклад вибирає всі ресурси з ключем, рівним tier, та значеннями, іншими ніж frontend та backend, а також усі ресурси без міток із ключем tier.
  • Третій приклад вибирає всі ресурси, які мають мітку з ключем partition; значення не перевіряються.
  • Четвертий приклад вибирає всі ресурси без мітки з ключем partition; значення не перевіряються.

Так само розділювач-кома діє як оператор AND. Таким чином, фільтрування ресурсів із ключем partition (незалежно від значення) та з environment, відмінним від qa, можна досягти за допомогою partition,environment notin (qa). Селектор міток на основі множини є загальною формою рівності, оскільки environment=production еквівалентно environment in (production); так само для != та notin.

Вимоги на основі множини можна комбінувати з вимогами на основі рівності. Наприклад: partition in (customerA, customerB),environment!=qa.

API

Фільтрація LIST та WATCH

Для операцій list і watch ви можете вказати селектори міток для фільтрації наборів обʼєктів, що повертаються; фільтр задається за допомогою параметра запиту. (Щоб дізнатися докладніше про watch у Kubernetes, прочитайте ефективне виявлення змін). Дозволені обидва типи вимог (тут подано так, як вони зʼявляться у рядку URL-запиту):

  • вимоги на основі рівності: ?labelSelector=environment%3Dproduction,tier%3Dfrontend
  • вимоги на основі множини: ?labelSelector=environment+in+%28production%2Cqa%29%2Ctier+in+%28frontend%29

Обидва стилі селекторів міток можуть бути використані для отримання переліку ресурсів чи спостереження за ними через клієнта REST. Наприклад, спрямовуючись до apiserver за допомогою kubectl та використовуючи вимогу на основі рівності, можна написати:

kubectl get pods -l environment=production,tier=frontend

чи використовуючи вимогу на основі множини:

kubectl get pods -l 'environment in (production),tier in (frontend)'

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

kubectl get pods -l 'environment in (production, qa)'

чи обмежувати негативний збіг за допомогою оператора notin:

kubectl get pods -l 'environment,environment notin (frontend)'

Посилання на набори в обʼєктах API

Деякі обʼєкти Kubernetes, такі як services та replicationcontrollers, також використовують селектори міток для вказівки наборів інших ресурсів, таких як Podʼи.

Service та ReplicationController

Набір Podʼів, на який спрямований service, визначається селектором міток. Так само сукупність Podʼів, якою повинен керувати replicationcontroller, також визначається селектором міток.

Селектори міток для обох обʼєктів визначаються у файлах json або yaml за допомогою мап, і підтримуються лише селектори вимог на основі рівності:

"selector": {
    "component" : "redis",
}

або

selector:
  component: redis

Цей селектор (відповідно у форматі json або yaml) еквівалентний component=redis або component in (redis).

Ресурси, які підтримують вимоги на основі множини

Новіші ресурси, такі як Job, Deployment, ReplicaSet та DaemonSet, також підтримують вимоги на основі множини.

selector:
  matchLabels:
    component: redis
  matchExpressions:
    - { key: tier, operator: In, values: [cache] }
    - { key: environment, operator: NotIn, values: [dev] }

matchLabels — це мапа пар {key,value}. Одна пара {key,value} у мапі matchLabels еквівалентна елементу matchExpressions, де поле key — це "key", оператор — "In", а масив values містить лише "value". matchExpressions — це список вимог селектора Podʼів. Допустимі оператори включають In, NotIn, Exists та DoesNotExist. Масив values повинен бути непорожнім у випадку In та NotIn. Усі вимоги, як із matchLabels, так і з matchExpressions, обʼєднуються логічною операцією AND: усі вони повинні бути задоволені для збігу.

Вибір множини вузлів

Одним із варіантів використання вибору за мітками є обмеження множини вузлів, на які може бути заплановано Pod. Дивіться документацію щодо вибору вузла для отримання докладнішої інформації.

Ефективне використання міток

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

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

У наступних прикладах мітка app включена для зручності при ручних запитах та простому використанні CLI. Мітка app.kubernetes.io/name відповідає рекомендованим конвенціям Kubernetes щодо міток і краще підходить для інструментів та автоматизації.

Фронтенд може мати такі мітки:

labels:
  app: guestbook
  app.kubernetes.io/name: guestbook
  tier: frontend

тоді як Redis master та replica можуть мати різні мітки tier, і, можливо, навіть додаткову мітку role:

labels:
  app: guestbook
  app.kubernetes.io/name: guestbook
  tier: backend
  role: master

та

labels:
  app: guestbook
  app.kubernetes.io/name: guestbook
  tier: backend
  role: replica

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

kubectl apply -f examples/guestbook/all-in-one/guestbook-all-in-one.yaml
kubectl get pods -Lapp -Ltier -Lrole
NAME                           READY  STATUS    RESTARTS   AGE   APP         TIER       ROLE
guestbook-fe-4nlpb             1/1    Running   0          1m    guestbook   frontend   <none>
guestbook-fe-ght6d             1/1    Running   0          1m    guestbook   frontend   <none>
guestbook-fe-jpy62             1/1    Running   0          1m    guestbook   frontend   <none>
guestbook-redis-master-5pg3b   1/1    Running   0          1m    guestbook   backend    master
guestbook-redis-replica-2q2yf  1/1    Running   0          1m    guestbook   backend    replica
guestbook-redis-replica-qgazl  1/1    Running   0          1m    guestbook   backend    replica
my-nginx-divi2                 1/1    Running   0          29m   nginx       <none>     <none>
my-nginx-o0ef1                 1/1    Running   0          29m   nginx       <none>     <none>
kubectl get pods -lapp=guestbook,role=replica
NAME                           READY  STATUS   RESTARTS  AGE
guestbook-redis-replica-2q2yf  1/1    Running  0         3m
guestbook-redis-replica-qgazl  1/1    Running  0         3m

Оновлення міток

Іноді вам може знадобитися змінити мітки наявних Podʼів та інших ресурсів перед створенням нових ресурсів. Це можна зробити за допомогою kubectl label. Наприклад, якщо ви хочете позначити всі свої NGINX Podʼи як рівень фронтенду, виконайте:

kubectl label pods -l app=nginx tier=fe
pod/my-nginx-2035384211-j5fhi labeled
pod/my-nginx-2035384211-u2c7e labeled
pod/my-nginx-2035384211-u3t6x labeled

Спочатку фільтруються всі Podʼи з міткою "app=nginx", а потім вони позначаються як "tier=fe". Щоб переглянути Podʼи, які ви позначили, виконайте:

kubectl get pods -l app=nginx -L tier
NAME                        READY     STATUS    RESTARTS   AGE       TIER
my-nginx-2035384211-j5fhi   1/1       Running   0          23m       fe
my-nginx-2035384211-u2c7e   1/1       Running   0          23m       fe
my-nginx-2035384211-u3t6x   1/1       Running   0          23m       fe

Це виводить всі Podʼи "app=nginx", з додатковим стовпчиком міток рівня Podʼів (вказаним за допомогою -L або --label-columns).

Додаткову інформацію див. у kubectl label.

Що далі

1.2.4 - Простори імен

У Kubernetes простори імен (namespaces) забезпечують механізм для ізоляції груп ресурсів у межах одного кластера. Назви ресурсів повинні бути унікальними в межах простору імен, але не між просторами імен. Засноване на просторах імен обмеження застосовується лише до обʼєктів, які входять до простору імен (наприклад, Deployments, Services тощо), а не до обʼєктів, що поширюються на весь кластер (наприклад, StorageClass, Nodes, PersistentVolumes тощо).

Коли використовувати кілька просторів імен

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

Простори імен задають область дії для назв. Назви ресурсів повинні бути унікальними в межах простору імен, але не між просторами імен. Простори імен не можуть бути вкладені один в одного, і кожен ресурс Kubernetes може бути лише в одному просторі імен.

Простори імен — це спосіб розділити ресурси кластера між кількома користувачами (за допомогою квот ресурсів).

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

Примітка:

Для промислового кластера розгляньте можливість не використовувати простір імен default. Замість цього створюйте і використовуйте інші простори імен.

Початкові простори імен

Після запуску в Kubernetes є чотири початкові простори імен:

default
Kubernetes включає цей простір імен, щоб ви могли почати використовувати новий кластер без попереднього створення простору імен.
kube-node-lease
Цей простір імен містить обʼєкти Lease, повʼязані з кожним вузлом. Лізинги вузлів дозволяють kubelet надсилати пульс, щоб панель управління могла виявити відмову вузла.
kube-public
Цей простір імен може бути прочитаний усіма клієнтами (включаючи тих, які не автентифіковані). Цей простір імен в основному призначений для внутрішнього використання кластером, у випадку, коли деякі ресурси повинні бути видимими та доступними публічно по всьому кластеру. Публічний аспект цього простору імен — це лише домовленість, яка не є обовʼязковою.
kube-system
Простір імен для обʼєктів, створених системою Kubernetes.

Робота з просторами імен

Створення та видалення просторів імен описано в документації з адміністрування просторів імен.

Примітка:

Уникайте створення просторів імен із префіксом kube-, оскільки він зарезервований для системних просторів імен Kubernetes.

Перегляд просторів імен

Ви можете переглянути поточні простори імен у кластері за допомогою:

kubectl get namespace
NAME              STATUS   AGE
default           Active   1d
kube-node-lease   Active   1d
kube-public       Active   1d
kube-system       Active   1d

Встановлення простору імен для запиту

Щоб встановити простір імен для поточного запиту, використовуйте прапорець --namespace.

Наприклад:

kubectl run nginx --image=nginx --namespace=<insert-namespace-name-here>
kubectl get pods --namespace=<insert-namespace-name-here>

Встановлення обраного простору імен

Ви можете назавжди зберегти простір імен для всіх подальших команд kubectl у цьому контексті.

kubectl config set-context --current --namespace=<insert-namespace-name-here>
# Перевірте його
kubectl config view --minify | grep namespace:

Простори імен та DNS

Під час створення Service створюється відповідний DNS-запис. Цей запис має форму <service-name>.<namespace-name>.svc.cluster.local, що означає, що якщо контейнер використовує тільки <service-name>, він буде розвʼязаний у сервіс, локальний для простору імен. Це корисно для використання тієї самої конфігурації в кількох просторах імен, таких як Development, Staging та Production. Якщо вам потрібно досягти обʼєкта в іншому просторі імен, вам слід використовувати повне кваліфіковане доменне імʼя (FQDN).

Отже, усі назви просторів імен повинні бути дійсними DNS-мітками згідно з RFC 1123.

Попередження:

Якщо створити простори імен із тими самими назвами, що й публічні домени верхнього рівня (TLD), Services у цих просторах імен можуть мати короткі імена DNS, які перетинаються з публічними записами DNS. Робоче навантаження з будь-якого простору імен, яке виконує DNS-запит без крапки в кінці, буде перенаправлено на ці сервіси, які матимуть пріоритет над публічним DNS.

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

Не всі обʼєкти належать до простору імен

Більшість ресурсів Kubernetes (наприклад, Pods, Services, Deployments та інші) перебувають у певних просторах імен. Однак самі ресурси просторів імен не належать до жодного простору імен. А ресурси низького рівня, такі як Nodes та PersistentVolumes, не перебувають в жодному просторі імен.

Щоб переглянути, які ресурси Kubernetes є в просторі імен, а які — ні:

# В просторі імен
kubectl api-resources --namespaced=true

# Не в просторі імен
kubectl api-resources --namespaced=false

Автоматичне додавання міток

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.22

Панель управління Kubernetes встановлює незмінну мітку kubernetes.io/metadata.name для всіх просторів імен. Значення мітки — це назва простору імен.

Що далі

1.2.5 - Анотації

Ви можете використовувати анотації Kubernetes, щоб додати довільні неідентифікувальні метадані до обʼєктів. Клієнти, такі як інструменти та бібліотеки, можуть отримувати ці метадані.

Додавання метаданих до обʼєктів

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

Анотації, подібно до міток, є парами ключ/значення:

"metadata": {
  "annotations": {
    "key1" : "value1",
    "key2" : "value2"
  }
}

Примітка:

Ключі та значення в мапі повинні бути рядками. Іншими словами, ви не можете використовувати числові, булеві, спискові або інші типи як для ключів, так і для їх значень.

Ось кілька прикладів інформації, яку можна записати в анотаціях:

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

  • Інформація про збірку, реліз чи образ, така як часові мітки, ідентифікатори релізу, гілка git, номери PR, хеші образу та адреса реєстру.

  • Вказівники на репозиторії логування, моніторингу, аналітики чи аудиту.

  • Інформація про клієнтську бібліотеку чи інструмент, яку можна використовувати для налагодження: наприклад, назва, версія та інформація про збірку.

  • Інформація про походження користувача чи інструменту/системи, така як URL-адреси повʼязаних обʼєктів з інших компонентів екосистеми.

  • Метадані легких інструментів розгортання: наприклад, конфігурація чи контрольні точки.

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

  • Директиви від кінцевого користувача до реалізації щодо зміни поведінки чи використання нестандартних функцій.

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

Синтаксис та набір символів

Анотації — це пари ключ/значення. Допустимі ключі анотацій мають два сегменти: необовʼязковий префікс і назва, розділені скісною рискою (/). Назва є обовʼязковою та має містити не більше 63 символів, починаючись та закінчуючись буквено-цифровим символом ([a-z0-9A-Z]), і може містити дефіси (-), підкреслення (_), крапки (.) та буквено-цифрові символи між ними. Префікс є необовʼязковим. Якщо вказано, префікс повинен бути піддоменом DNS: серією DNS-міток, розділених крапками (.), загальною довжиною не більше 253 символів, за якою слідує скісна риска (/).

Якщо префікс відсутній, ключ анотації вважається приватним для користувача. Автоматизовані компоненти системи (наприклад, kube-scheduler, kube-controller-manager, kube-apiserver, kubectl чи інші автоматизовані засоби від сторонніх розробників), які додають анотації до обʼєктів кінцевого користувача, повинні вказати префікс.

Префікси kubernetes.io/ та k8s.io/ зарезервовані для основних компонентів Kubernetes.

Значення анотацій не мають жодних обмежень щодо набору символів — на відміну від значень міток, значення анотацій можуть містити будь-які рядки, включаючи спеціальні символи, пробіли та структуровані дані, такі як JSON або YAML. Якщо ви плануєте зберігати двійкові дані (наприклад, CBOR), проєкт Kubernetes рекомендує кодувати їх у форматі base64. Однак загальний розмір всіх анотацій на одному обʼєкті (ключі та значення разом) не повинен перевищувати 256 КіБ.

Наприклад, ось маніфест для Pod, який має анотацію imageregistry: https://hub.docker.com/:

apiVersion: v1
kind: Pod
metadata:
  name: annotations-demo
  annotations:
    imageregistry: "https://hub.docker.com/"
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

Що далі

1.2.6 - Селектори полів

Селектори полів дозволяють вам вибирати обʼєкти Kubernetes на основі значень одного або кількох полів ресурсу. Ось кілька прикладів запитів для селекторів полів:

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

Ця команда kubectl вибирає всі Podʼи, для яких значення поля status.phase дорівнює Running:

kubectl get pods --field-selector status.phase=Running

Примітка:

Селектори полів, по суті, є фільтрами ресурсів. Типово селектори/фільтри не застосовуються, а це означає, що вибрані всі ресурси вказаного типу. Це робить запити kubectl get pods та kubectl get pods --field-selector "" еквівалентними.

Підтримувані поля

Підтримувані селектори полів варіюються залежно від типу ресурсу Kubernetes. Усі типи ресурсів підтримують поля metadata.name та metadata.namespace. Використання непідтримуваних селекторів полів призводить до помилки. Наприклад:

kubectl get ingress --field-selector foo.bar=baz
Error from server (BadRequest): Unable to find "ingresses" that match label selector "", field selector "foo.bar=baz": "foo.bar" is not a known field selector: only "metadata.name", "metadata.namespace"

Список підтримуваних полів

ВидПоля
Podspec.nodeName
spec.restartPolicy
spec.schedulerName
spec.serviceAccountName
spec.hostNetwork
status.phase
status.podIP
status.podIPs
status.nominatedNodeName
EventinvolvedObject.kind
involvedObject.namespace
involvedObject.name
involvedObject.uid
involvedObject.apiVersion
involvedObject.resourceVersion
involvedObject.fieldPath
reason
reportingComponent
source
type
Servicespec.clusterIP
spec.type
Secrettype
Namespacestatus.phase
ReplicaSetstatus.replicas
ReplicationControllerstatus.replicas
Jobstatus.successful
Nodespec.unschedulable
CertificateSigningRequestspec.signerName

Поля власних ресурсів

Усі власні типи ресурсів підтримують поля metadata.name та metadata.namespace.

Крім того, поле spec.versions[*].selectableFields у CustomResourceDefinition оголошує, які інші поля власного ресурсу можна використовувати у селекторах полів. Дивіться статтю поля, які можна вибрати для власних ресурсів для отримання додаткових відомостей про те, як використовувати селектори полів з CustomResourceDefinitions.

Підтримувані оператори

Ви можете використовувати оператори =, == та != з селекторами полів (= та == означають те саме). Наприклад, ця команда kubectl вибирає всі Services Kubernetes, які не перебувають у просторі імен default:

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default

Примітка:

Оператори на основі множини (in, notin, exists) не підтримуються для селекторів полів.

Ланцюжки селекторів

Як і селектори міток та інші селектори, селектори полів можна обʼєднувати в ланцюжок у вигляді списку, розділеного комами. Ця команда kubectl вибирає всі Podʼи, для яких значення status.phase не дорівнює Running, а поле spec.restartPolicy дорівнює Always:

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

Кілька типів ресурсів

Ви можете використовувати селектори полів для кількох типів ресурсів. Ця команда kubectl вибирає всі Statefulsets та Services, які не перебувають у просторі імен default:

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default

1.2.7 - Власники та залежності

У Kubernetes деякі обʼєкти є власниками інших обʼєктів. Наприклад, ReplicaSet є власником групи Podʼів. Ці підлеглі обʼєкти є залежними від свого власника.

Власність відрізняється від механізму міток та селекторів, який також використовують деякі ресурси. Наприклад, розгляньте Service, який створює обʼєкти EndpointSlice. Service використовує мітки, щоб панель управління могла визначити, які обʼєкти EndpointSlice використовуються для цього Service. Крім міток, кожен EndpointSlice, керований від імені Service, має посилання на власника. Посилання на власника допомагають різним частинам Kubernetes уникати втручання в обʼєкти, якими вони не керують.

Посилання на власника в специфікаціях обʼєктів

Залежні обʼєкти мають поле metadata.ownerReferences, яке містить посилання на їх власника. Дійсне посилання на власника складається з назви обʼєкта та UID в межах того ж простору імен, що й залежний обʼєкт. Kubernetes автоматично встановлює значення цього поля для обʼєктів, які є залежностями інших обʼєктів, таких як ReplicaSets, DaemonSets, Deployments, Jobs та CronJobs, та ReplicationControllers. Ви також можете налаштувати ці звʼязки вручну, змінивши значення цього поля. Однак зазвичай цього не потрібно робити, і можна дозволити Kubernetes автоматично керувати цими звʼязками.

Залежні обʼєкти також мають поле ownerReferences.blockOwnerDeletion, яке має булеве значення і контролює, чи можуть певні залежні обʼєкти блокувати збір сміття, що видаляє їх власника. Kubernetes автоматично встановлює це поле в true, якщо контролер (наприклад, контролер Deployment) встановлює значення поля metadata.ownerReferences. Ви також можете встановити значення поля blockOwnerDeletion вручну, щоб контролювати, які залежні обʼєкти блокують збір сміття.

Контролер допуску Kubernetes контролює доступ користувачів до зміни цього поля для залежних ресурсів на основі прав видалення власника. Цей контроль перешкоджає несанкціонованим користувачам затримувати видалення обʼєкта-власника.

Примітка:

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

Залежні обʼєкти рівня кластера можуть вказувати тільки власників рівня кластера. У v1.20+, якщо залежний обʼєкт рівня кластера вказує як власника тип у межах простору імен, вважається, що він має нерозвʼязне посилання на власника, і його не можна прибрати збором сміття.

У v1.20+, якщо збирач сміття виявляє недійсний ownerReference між просторами імен або залежний обʼєкт рівня кластера з ownerReference, що посилається на тип у межах простору імен, повідомляється про попереджувальну подію з причиною OwnerRefInvalidNamespace та involvedObject недійсного залежного елемента. Ви можете перевірити наявність такого роду подій (Event), запустивши kubectl get events -A --field-selector=reason=OwnerRefInvalidNamespace.

Власність та завершувачі

Коли ви наказуєте Kubernetes видалити ресурс, API-сервер дозволяє контролеру, що керує ресурсом, обробити будь-які правила завершувача для ресурсу. Завершувачі запобігають випадковому видаленню ресурсів, які вашому кластеру можуть ще бути потрібні для коректної роботи. Наприклад, якщо ви намагаєтеся видалити PersistentVolume, який все ще використовується Podʼом, видалення не відбувається негайно, оскільки PersistentVolume має завершувач kubernetes.io/pv-protection. Замість цього том залишається в стані Terminating до тих пір, поки Kubernetes не очистить завершувач, що відбувається тільки після того, як PersistentVolume більше не привʼязаний до Podʼа.

Kubernetes також додає завершувачі до ресурсу-власника, коли ви використовуєте або каскадне видалення на передньому плані або каскадне видалення сиріт. Під час видалення на передньому плані додається завершувач foreground, так що контролер повинен видалити залежні ресурси, які також мають ownerReferences.blockOwnerDeletion=true, перш ніж він видалить власника. Якщо ви вказуєте політику видалення покинутих ресурсів (сиріт), Kubernetes додає завершувач orphan, так що контролер ігнорує залежні ресурси після того, як він видаляє обʼєкт-власника.

Що далі

1.2.8 - Рекомендовані мітки

Ви можете візуалізувати обʼєкти Kubernetes і керувати ними не лише за допомогою kubectl і панелі керування (dashboard), а й за допомогою інших інструментів. Загальний набір міток дозволяє інструментам працювати між собою, описуючи обʼєкти спільним чином, який можуть розуміти всі інструменти.

Крім підтримки інструментів, рекомендовані мітки описують застосунки так, що до них можна робити запити.

Метадані організовані навколо концепції застосунку. Kubernetes не є платформою як сервіс (PaaS) і не має та не навʼязує формального поняття застосунку. Замість цього застосунки є неформальними та описуються метаданими. Визначення того, що включає застосунок, є гнучким.

Примітка:

Це рекомендовані мітки. Вони полегшують управління застосунками, але не обовʼязкові для будь-яких основних інструментів.

Спільні мітки та анотації мають спільний префікс: app.kubernetes.io. Мітки без префіксу є приватними для користувачів. Спільний префікс забезпечує, що спільні мітки не перешкоджають роботі власних міток користувачів.

Мітки

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

КлючОписПрикладТип
app.kubernetes.io/nameНазва застосункуmysqlрядок
app.kubernetes.io/instanceУнікальна назва, що ідентифікує екземпляр застосункуmysql-abcxyzрядок
app.kubernetes.io/versionПоточна версія застосунку (наприклад, SemVer 1.0, хеш ревізії тощо)5.7.21рядок
app.kubernetes.io/componentКомпонент всередині архітектуриdatabaseрядок
app.kubernetes.io/part-ofНазва застосунку вищого рівня, частиною якого є цей застосунокwordpressрядок
app.kubernetes.io/managed-byІнструмент, який використовується для управління роботою застосункуHelmрядок

Щоб проілюструвати ці мітки в дії, розгляньте такий обʼєкт StatefulSet:

# Це уривок
apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxyz
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
    app.kubernetes.io/managed-by: Helm

Застосунки та екземпляри застосунків

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

Назва застосунку та назва екземпляра записуються окремо. Наприклад, у WordPress є app.kubernetes.io/name — wordpress, тоді як назва екземпляра представлена як app.kubernetes.io/instance зі значенням wordpress-abcxyz. Це дозволяє ідентифікувати застосунок та його екземпляр. Кожен екземпляр застосунку повинен мати унікальну назву.

Приклади

Щоб проілюструвати різні способи використання цих міток, наведено приклади різної складності.

Простий Stateless Service

Розгляньте випадок простого Stateless Service, розгорнутого за допомогою обʼєктів Deployment та Service. Наведені нижче два уривки представляють, як можуть бути використані мітки у їх найпростішій формі.

Deployment використовується для нагляду за Podʼами, які виконують сам застосунок.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: myservice
    app.kubernetes.io/instance: myservice-abcxyz
...

Service використовується для відкриття доступу до застосунку.

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: myservice
    app.kubernetes.io/instance: myservice-abcxyz
...

Вебзастосунок із базою даних

Розгляньмо трохи складніший застосунок: вебзастосунок (WordPress) із базою даних (MySQL), встановлений за допомогою Helm. Наведені нижче уривки ілюструють початок обʼєктів, які використовуються для розгортання цього застосунку.

Початок цього Deployment використовується для WordPress:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: wordpress
    app.kubernetes.io/instance: wordpress-abcxyz
    app.kubernetes.io/version: "4.9.4"
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: wordpress
...

Service використовується для відкриття доступу до WordPress:

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: wordpress
    app.kubernetes.io/instance: wordpress-abcxyz
    app.kubernetes.io/version: "4.9.4"
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: server
    app.kubernetes.io/part-of: wordpress
...

MySQL представлено як StatefulSet з метаданими як для нього, так і для застосунку, до якого він належить:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxyz
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
...

Service використовується для відкриття доступу до MySQL як частини WordPress:

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxyz
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
...

Зверніть увагу, що StatefulSet та Service для MySQL містять інформацію як про MySQL, так і про WordPress, ширший застосунок.

1.2.9 - Версії зберігання

API-сервер Kubernetes зберігає обʼєкти, покладаючись на etcd-сумісне сховище (часто це сам etcd). Кожен обʼєкт серіалізується за допомогою певної версії типу API; наприклад, представлення v1 ConfigMap. Kubernetes використовує термін версія зберігання для опису того, як зберігається обʼєкт у вашому кластері.

API Kubernetes також покладається на автоматичне перетворення; наприклад, якщо у вас є HorizontalPodAutoscaler, то ви можете взаємодіяти з цим HorizontalPodAutoscaler, використовуючи будь-яку комбінацію версій v1 та v2 API HorizontalPodAutoscaler. Kubernetes відповідає за перетворення кожного API-запиту, щоб клієнти не бачили, яка версія насправді серіалізується.

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

Той самий API може мати кілька версій зберігання, які API-сервер може перетворювати на схему обʼєкта. Один обʼєкт, що є частиною цього ресурсу, повинен мати лише одну версію зберігання в будь-який момент часу. Це означає, що API-сервер знає бінарне кодування обʼєктів і може виконувати динамічне перетворення між всіма збереженими версіями до представлення обʼєкта у API.

Версія обʼєкта є повністю відокремленою від версії зберігання. Наприклад, API-обʼєкти v1alpha1 та v1beta1 для того самого ресурсу будуть закодовані однаково в сховищі, доки версія зберігання не буде оновлена між двома обʼєктами.

Відповідність версій зберігання ресурсам

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

Зчитування з API-сервера конвертуватиме збережені дані в API-представлення обʼєкта. Це дозволяє старим версіям зберігання зберігатися необмежений час, доки не відбудуться оновлення обʼєкта. З іншого боку, записи конвертуватимуть збережений обʼєкт у нове представлення після оновлення.

Версії зберігання для власних ресурсів

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

Однак для власних ресурсів певну версію ресурсу необхідно встановити як версію зберігання. Схема, визначена цією конкретною версією власного ресурсу, буде використовуватися як кодування ресурсу на рівні сховища. Див. розширений набір функцій CRD для отримання детальнішої інформації про налаштування API та керування версіями.

Наприклад, див. це CustomResourceDefinition для crontabs:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  # перелік версій, що підтримуються цим CustomResourceDefinition
  versions:
  - name: v1beta1
    # Кожна версія може бути увімкнена/вимкнена за допомогою прапорця Served.
    served: true
    # Одна і тільки одна версія повинна бути позначена як версія зберігання.
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
          time:
            type: string
  conversion:
    strategy: None
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

Визначення API v1beta1 використовується як версія зберігання, тобто будь-які оновлення або створення crontabs зберігатимуться разом зі схемою обʼєкта API v1beta1. У цьому випадку це фактично означатиме, що обʼєкт API v1 ніколи не зможе зберігати поле time, оскільки воно не є частиною визначення сховища. Ця схема використовується на рівні сховища як двійкове кодування самого обʼєкта. Спроба встановити дві версії як версію, що зберігається, одночасно вважається недійсною, оскільки це означатиме, що дві схеми даних вважатимуться дійсними способами зберігання обʼєктів одночасно.

Після зміни версії, яка використовується для зберігання, ця версія API буде використовуватися для зберігання будь-яких нових або оновлених CR. Спостереження за обʼєктом або його отримання лише конвертуватиме обʼєкт зі старої версії зберігання, не змінюючи його. Тільки оновлення або створення матиме ефект і використовуватиме щойно визначену версію зберігання.

Як версії зберігання впливають на шифрування в стані спокою

Існують інструменти для шифрування сховища в стані спокою, особливо для Secrets кластера. Це додає додатковий рівень захисту від витоку даних, оскільки фактично збережені дані в кластері зашифровані. Це означає, що API-сервер фактично розшифровує дані під час їх отримання зі сховища. API-сервер повинен мати ключ для цієї версії зберігання, щоб правильно декодувати обʼєкт.

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

Міграція на іншу версію зберігання

Кілька версій зберігання для одного ресурсу можуть створювати проблеми для адміністраторів кластера. Адміністратор кластера не може видаляти старі версії API для CRD, які можуть не підтримуватися, доки не переконається, що всі обʼєкти більше не використовують повʼязану з ним версію зберігання. Через велику кількість обʼєктів та непрозоре уявлення про те, які з них є новими, а які все ще підтримуються старими версіями зберігання, важко визначити, коли версію можна безпечно видалити. Якщо версію видалено передчасно, обʼєкт може взагалі не вдатися прочитати.

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

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

1.3 - API Kubernetes

API Kubernetes дозволяє запитувати та змінювати стан обʼєктів у Kubernetes. Основа панелі управління Kubernetes — це API-сервер та HTTP API, який він надає. Користувачі, різні частини вашого кластера та зовнішні компоненти взаємодіють між собою через API-сервер.

Основою панелі управління Kubernetes є API-сервер. API-сервер надає HTTP API, який дозволяє кінцевим користувачам, різним частинам вашого кластера та зовнішнім компонентам взаємодіяти між собою.

API Kubernetes дозволяє вам запитувати та змінювати стан обʼєктів API у Kubernetes (наприклад: Pods, Namespaces, ConfigMaps та Events).

Більшість операцій можна виконати за допомогою інтерфейсу командного рядка kubectl або інших інструментів командного рядка, таких як kubeadm, які, своєю чергою, використовують API. Однак ви також можете отримати доступ до API безпосередньо за допомогою викликів REST. Kubernetes надає набір клієнтських бібліотек для тих, хто хоче писати застосунки з використанням API Kubernetes.

Кожен кластер Kubernetes публікує специфікацію API, які він обслуговує. Kubernetes використовує два механізми для публікації цих специфікацій API; обидва корисні для забезпечення автоматичної сумісності. Наприклад, інструмент kubectl отримує та кешує специфікацію API для увімкнення функціонала автозавершення командного рядка та інших функцій. Нижче наведено два підтримувані механізми:

  • Discovery API надає інформацію про API Kubernetes: назви API, ресурси, версії та операції, які підтримуються. Цей термін є специфічним для Kubernetes, оскільки це окремий API, відмінний від Kubernetes OpenAPI. Він задуманий як короткий огляд доступних ресурсів і не описує детальну схему ресурсів. Довідкові відомості про схеми ресурсів дивіться в документі OpenAPI.
  • Документ Kubernetes OpenAPI надає (повні) схеми OpenAPI v2.0 та v3.0 для всіх точок доступу API Kubernetes. OpenAPI v3 є бажаним методом доступу до OpenAPI, оскільки він надає повніше й точніше подання API. Цей варіант містить всі можливі API-шляхи, так само як і всі ресурси, що використовуються та створюються для кожної операції на кожній точці доступу. Тут також є компоненти розширюваності, які підтримує кластер. Дані містять повну специфікацію та значно перевищують за обсягом те, що надає Discovery API.

Discovery API

Kubernetes публікує через Discovery API перелік усіх підтримуваних версій груп і ресурсів. Для кожного ресурсу він містить таке:

  • Назва
  • Область дії: кластер або простір імен
  • URL точки доступу та підтримувані дієслова
  • Альтернативні назви
  • Група, версія, вид (kind)

API доступний як в агрегованому, так і в неагрегованому вигляді. Агреговане виявлення обслуговує дві точки доступу (endpoint), тоді як неагреговане — окрему точку доступу для кожної версії групи.

Агреговане виявлення

This is a stable feature in Kubernetes, and has been since the 1.30 release. You can no longer toggle this feature (the associated feature gate has been removed).

Kubernetes надає стабільну підтримку агрегованого виявлення, публікуючи всі ресурси, які підтримує кластер, через дві точки доступу (/api та /apis). Надсилання запиту до цієї точки доступу різко зменшує кількість запитів, потрібних для отримання даних виявлення з кластера. Ви можете отримати доступ до даних, надсилаючи запити до відповідних точок доступу із заголовком Accept, що вказує на ресурс агрегованого виявлення: Accept: application/json;v=v2;g=apidiscovery.k8s.io;as=APIGroupDiscoveryList.

Якщо тип ресурсу не вказано в заголовку Accept, стандартною відповіддю для точок доступу /api та /apis буде документ неагрегованого виявлення.

Документ виявлення для вбудованих ресурсів можна знайти в репозиторії Kubernetes GitHub. Цей документ GitHub можна використовувати як довідник базового набору доступних ресурсів, якщо кластер Kubernetes недоступний для запитів.

Точка доступу також підтримує ETag і кодування protobuf.

Неагреговане виявлення

Без агрегації виявлення інформація публікується рівнями, де кореневі точки доступу публікують дані виявлення для підлеглих документів.

Список усіх версій груп, підтримуваних кластером, публікується в точках доступу /api та /apis. Наприклад:

{
  "kind": "APIGroupList",
  "apiVersion": "v1",
  "groups": [
    {
      "name": "apiregistration.k8s.io",
      "versions": [
        {
          "groupVersion": "apiregistration.k8s.io/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "apiregistration.k8s.io/v1",
        "version": "v1"
      }
    },
    {
      "name": "apps",
      "versions": [
        {
          "groupVersion": "apps/v1",
          "version": "v1"
        }
      ],
      "preferredVersion": {
        "groupVersion": "apps/v1",
        "version": "v1"
      }
    },
    ...
  ]
}

Для отримання документа виявлення для кожної версії групи за адресою /apis/<group>/<version> (наприклад, /apis/rbac.authorization.k8s.io/v1alpha1) потрібні додаткові запити. Ці точки доступу оголошують список ресурсів, що обслуговуються певною версією групи. Ці точки доступу використовуються командою kubectl для отримання списку ресурсів, підтримуваних кластером.

Визначення інтерфейсу OpenAPI

Докладніше про специфікації OpenAPI дивіться у документації OpenAPI.

Kubernetes працює як з OpenAPI v2.0, так і з OpenAPI v3.0. OpenAPI v3 є бажаним методом доступу до OpenAPI, оскільки він пропонує повніше (без втрат) представлення ресурсів Kubernetes. Через обмеження OpenAPI версії 2 певні поля видалено з опублікованого OpenAPI, включаючи, але не обмежуючись, default, nullable, oneOf.

OpenAPI V2

API-сервер Kubernetes надає агреговану специфікацію OpenAPI v2 через точку доступу /openapi/v2. Ви можете запросити формат відповіді, використовуючи такі заголовки запиту:

Дійсні значення заголовків запиту для запитів OpenAPI v2
ЗаголовокМожливі значенняПримітки
Accept-Encodinggzipцей заголовок можна не надавати
Acceptapplication/com.github.proto-openapi.spec.v2@v1.0+protobufголовним чином для внутрішньокластерного використання
application/jsonтипово
*обслуговує application/json

Попередження:

Правила валідації, опубліковані як частина схем OpenAPI, можуть бути неповними, і зазвичай такими вони й є. Додаткова перевірка відбувається на API-сервері. Якщо вам потрібна точна і повна перевірка, kubectl apply --dry-run=server виконує всі застосовні перевірки (а також активує перевірку часу допуску).

OpenAPI V3

This is a stable feature in Kubernetes, and has been since the 1.27 release. You can no longer toggle this feature (the associated feature gate has been removed).

Kubernetes підтримує публікацію опису своїх API у форматі OpenAPI v3.

Надається точка доступу /openapi/v3 для перегляду списку всіх доступних груп/версій. Ця точка повертає лише JSON. Ці групи/версії вказані в такому форматі:

{
    "paths": {
        ...,
        "api/v1": {
            "serverRelativeURL": "/openapi/v3/api/v1?hash=CC0E9BFD992D8C59AEC98A1E2336F899E8318D3CF4C68944C3DEC640AF5AB52D864AC50DAA8D145B3494F75FA3CFF939FCBDDA431DAD3CA79738B297795818CF"
        },
        "apis/admissionregistration.k8s.io/v1": {
            "serverRelativeURL": "/openapi/v3/apis/admissionregistration.k8s.io/v1?hash=E19CC93A116982CE5422FC42B590A8AFAD92CDE9AE4D59B5CAAD568F083AD07946E6CB5817531680BCE6E215C16973CD39003B0425F3477CFD854E89A9DB6597"
        },
        ....
    }
}

Відносні URL вказують на незмінний опис OpenAPI для поліпшення кешування на стороні клієнта. Також API-сервер встановлює відповідні заголовки кешування HTTP (Expires на 1 рік вперед та Cache-Control на immutable). При використанні застарілого URL API-сервер повертає перенаправлення на найновіший URL.

API-сервер Kubernetes публікує специфікацію OpenAPI v3 для кожної версії групи Kubernetes через точку доступу /openapi/v3/apis/<group>/<version>?hash=<hash>.

Дивіться таблицю нижче для прийнятних заголовків запиту.

Дійсні значення заголовків запиту для запитів OpenAPI v3
ЗаголовокМожливі значенняПримітки
Accept-Encodinggzipцей заголовок можна не надавати
Acceptapplication/com.github.proto-openapi.spec.v3@v1.0+protobufголовним чином для внутрішньокластерного використання
application/jsonтипово
*обслуговує application/json

Реалізація Golang для отримання OpenAPI V3 надається в пакунку k8s.io/client-go/openapi3.

Kubernetes 1.37 публікує OpenAPI v2.0 та v3.0; найближчим часом підтримка 3.1 не планується.

Серіалізація Protobuf

Kubernetes реалізує альтернативний формат серіалізації на основі Protobuf, який призначений головним чином для комунікації всередині кластера. Докладніше про цей формат читайте в пропозиції дизайну серіалізації Kubernetes Protobuf та файлах мови опису інтерфейсу (IDL) для кожної схеми, які розташовані в пакунках Go, які визначають обʼєкти API.

Постійне зберігання

Kubernetes зберігає серіалізований стан обʼєктів, записуючи їх у etcd.

Групи API та версіювання

Щоб полегшити видалення полів або перебудову представлень ресурсів, Kubernetes підтримує кілька версій API, кожна з різним API-шляхом, таким як /api/v1 або /apis/rbac.authorization.k8s.io/v1alpha1.

Версіювання робиться на рівні API, а не на рівні ресурсу чи поля, щоб забезпечити чіткий, послідовний погляд на ресурси та поведінку системи, а також для можливості керування доступом до застарілих та/або експериментальних API.

Щоб полегшити еволюцію та розширення свого API, Kubernetes реалізує групи API, які можна увімкнути або вимкнути.

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

Наприклад, припустимо, є дві версії API, v1 та v1beta1, для того самого ресурсу. Якщо ви спочатку створили обʼєкт, використовуючи версію v1beta1 його API, ви можете згодом читати, оновлювати чи видаляти цей обʼєкт, використовуючи або версію API v1beta1, або версію v1, поки версія v1beta1 не буде застарілою та видаленою. На цьому етапі ви можете продовжувати отримувати доступ та модифікувати обʼєкт, використовуючи API версії v1.

Зміни в API

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

Загалом нові ресурси API та нові поля ресурсів можна додавати часто й регулярно. Видалення ресурсів чи полів вимагає дотримання політики застарівання API.

Kubernetes твердо зобовʼязується підтримувати сумісність з офіційними API Kubernetes, як тільки вони досягнуть загальної доступності (GA), як правило, у версії API v1. Крім того, Kubernetes підтримує сумісність з даними, що зберігаються через бета-версії API офіційних API Kubernetes, і гарантує, що дані можуть бути перетворені та доступні через версії GA API, коли функція стане стабільною.

Якщо ви використовуєте бета-версію API, вам слід перейти до наступної бета- або стабільної версії API, як тільки це API стане стабільним (GA). Найкращий час для цього — під час періоду застарівання бета-API, оскільки обʼєкти одночасно доступні через обидві версії API. Як тільки бета-API завершить свій період застарівання і більше не буде обслуговуватися, слід використовувати версію API, яка його замінює.

Примітка:

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

Дивіться довідник з версій API для отримання докладнішої інформації про визначення рівня API.

Розширення API

API Kubernetes можна розширити одним з двох способів:

  1. Власні ресурси дозволяють декларативно визначити, як API-сервер повинен надавати вибраний вами ресурс API.
  2. Ви також можете розширити API Kubernetes, реалізовуючи шар агрегації.

Що далі

1.4 - Інструмент командного рядка kubectl

kubectl є основним інструментом командного рядка для взаємодії з кластером Kubernetes. Ця сторінка надає огляд kubectl та його ролі в екосистемі Kubernetes.

Kubernetes надає інструмент командного рядка для взаємодії з панеллю управління кластера Kubernetes за допомогою API Kubernetes.

Інструмент kubectl взаємодіє з вашим кластером через API Kubernetes. kubectl шукає файл налаштувань з назвою config у теці $HOME/.kube. Ви можете вказати інші файли kubeconfig, встановивши змінну середовища KUBECONFIG або використавши прапорець --kubeconfig.

Роль kubectl

kubectl є основним інструментом для створення, перегляду, оновлення та видалення обʼєктів Kubernetes. Він доповнює компоненти Kubernetes, які працюють у вашому кластері, та API Kubernetes, який реалізують ці компоненти. Незалежно від того, чи запускаєте ви kubectl з вашого ноутбука, чи з Podʼа всередині кластера, він надсилає запити до API-сервера. Інші клієнти, такі як клієнтські бібліотеки та вебпанелі, такі як Headlamp, також взаємодіють через той самий API.

Як працює kubectl

Інструмент kubectl підключається до API-сервера та автентифікується за допомогою відомостей про кластер, користувача та контекст, визначених у вашому файлі kubeconfig. Коли ви запускаєте kubectl ззовні кластера, він використовує файл kubeconfig для пошуку адреси API-сервера та облікових даних. Коли kubectl працює всередині Podʼа (наприклад, у CI/CD конвеєрі), він може використовувати автентифікацію всередині кластера на основі токена ServiceAccount, змонтованого в Podʼі.

Коли ви запускаєте команду, kubectl перетворює ваші наміри на один або кілька HTTP-запитів до API Kubernetes. API-сервер перевіряє кожен запит, застосовує його до стану кластера, збереженого в etcd, і повертає результат. Це означає, що кожна дія kubectl, чи то створення Deployment, чи читання логів, проходить тим самим шляхом через API.

Оскільки ваш kubeconfig може визначати кілька кластерів, користувачів і контекстів, ви можете використовувати kubectl для перемикання між кластерами без повторного налаштування середовища. Використовуйте kubectl config use-context, щоб змінити активний контекст.

Що можна робити з kubectl

Інструмент kubectl підтримує багато операцій, які можна умовно розділити на такі категорії:

  • Керування ресурсами — Створення, оновлення та видалення обʼєктів, таких як Podʼи, Deployments та Services. Використовуйте kubectl apply для декларативного керування за допомогою конфігураційних файлів.
  • Перевірка стану кластера — Перегляд списків та описів обʼєктів, подій та використання ресурсів.
  • Налагодження — Перегляд логів контейнерів, виконання команд всередині запущеного контейнера або перенаправлення портів до Podʼа.
  • Операції з кластером — Очищення вузлів для обслуговування, позначення вузлів як непридатних для планування, щоб запобігти появі нових навантажень, та керування конфігурацією кластера.
  • Скрипти та автоматизація — Форматування виводу у JSON, YAML або власні колонки за допомогою JSONPath для використання у скриптах та конвеєрах.

Для ознайомлення з синтаксисом, довідником команд та прикладами, дивіться довідку kubectl.

Декларативне vs імперативне керування

Для робочих навантажень у промисловому середовищі рекомендується використовувати декларативне керування обʼєктами за допомогою kubectl apply з конфігураційними файлами з контролем версій. Декларативне керування допомагає відстежувати зміни, співпрацювати та інтегруватися з GitOps-процесами. Імперативні команди (такі як kubectl create або kubectl run) корисні для розробки та експериментів, але їх важче відтворювати та перевіряти.

Розширення kubectl за допомогою втулків

Ви можете розширювати kubectl за допомогою втулків, які додають нові команди. Втулки є автономними бінарними файлами, які дотримуються конвенції іменування kubectl-<назва-втулка>. Спільнота Kubernetes підтримує багато втулків, і ви можете керувати ними за допомогою менеджера втулків Krew.

Сумісність версій

Інструмент kubectl підтримує різницю версій плюс-мінус одну мінорну версію відносно панелі управління кластера. Наприклад, kubectl v1.32 працює з панелями управління в кластерах версій v1.31, v1.32 та v1.33. Використання сумісної версії запобігає непередбачуваній поведінці. Дивіться Політику версійної розбіжності для отримання деталей.

Що далі

2 - Архітектура кластера

Архітектурні концепції в основі Kubernetes.

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

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

Цей документ описує різні компоненти, які вам потрібні для повноцінного та працездатного кластера Kubernetes.

Панель управління (kube-apiserver, etcd, kube-controller-manager, kube-scheduler) та кілька вузлів. Кожен вузол запускає kubelet та kube-proxy.

Схема 1. Компоненти кластера Kubernetes.

Про цю архітектуру

На схемі 1 представлено приклад еталонної архітектури кластера Kubernetes. Фактичний розподіл компонентів може змінюватися залежно від конкретних налаштувань кластера та вимог.

На схемі на кожному вузлі запущено компонент kube-proxy. Вам потрібен компонент мережевого проксі на кожному вузлі, щоб гарантувати, що Service API та повʼязана з ним поведінка доступні у вашій кластерній мережі. Втім, деякі мережеві втулки надають власну, сторонню реалізацію проксі-сервера. Коли ви використовуєте такий мережевий втулок, вузлу не потрібно запускати kube-proxy.

Компоненти панелі управління

Компоненти панелі управління приймають глобальні рішення щодо кластера (наприклад, планування), а також виявляють та реагують на події в кластері (наприклад, запуск нового Podʼа, коли умова поля replicas в Deployment не виконується).

Компоненти панелі управління можуть запускатися на будь-якій машині у кластері. Однак для спрощення скрипти налаштування зазвичай запускають всі компоненти панелі управління на одній машині та не запускають контейнерів користувача на цій машині. Див. Створення кластерів з високою доступністю за допомогою kubeadm для прикладу налаштування панелі управління, яка працює на кількох машинах.

kube-apiserver

API-сервер є компонентом панелі управління Kubernetes, який надає доступ до API Kubernetes. API-сервер є фронтендом для панелі управління Kubernetes.

Основна реалізація API-сервера Kubernetes — kube-apiserver. kube-apiserver спроєктований для горизонтального масштабування, тобто масштабується за допомогою розгортання додаткових екземплярів. Ви можете запустити кілька екземплярів kube-apiserver та балансувати трафік між ними.

etcd

Узгоджене та високодоступне сховище ключ-значення, яке використовується як сховище Kubernetes для всіх даних кластера.

Якщо ваш кластер Kubernetes використовує etcd як основне сховище, переконайтеся, що у вас є план резервного копіювання даних.

Ви можете знайти докладну інформацію про etcd в офіційній документації.

kube-scheduler

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

Під час вибору вузла враховуються такі фактори: індивідуальна і колективна потреба в ресурсах, обмеження за апаратним/програмним забезпеченням і політиками, характеристики affinity та anti-affinity, локальність даних, взаємний вплив робочих навантажень і граничні терміни виконання.

kube-controller-manager

Компонент панелі управління, який запускає процеси контролерів.

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

Існує багато різних типів контролерів. Деякі приклади:

  • Контролер вузлів: відповідає за виявлення та реагування, коли вузли виходять з ладу.
  • Контролер Job: відстежує обʼєкти Job, що представляють одноразові завдання, а потім створює Podʼи для виконання цих завдань до завершення.
  • Контролер EndpointSlice: заповнює обʼєкти EndpointSlice (для забезпечення звʼязку між Services та Podʼами).
  • Контролер ServiceAccount: створює стандартні ServiceAccounts для нових просторів імен.

Наведений вище список не є вичерпним.

cloud-controller-manager

Компонент панелі управління Kubernetes, що вбудовує логіку управління, специфічну для хмари. Cloud controller manager дозволяє звʼязувати ваш кластер з API хмарного провайдера та відокремлює компоненти, що взаємодіють із хмарною платформою, від компонентів, які взаємодіють тільки з вашим кластером.

cloud-controller-manager запускає лише ті контролери, які специфічні для вашого хмарного провайдера. Якщо ви використовуєте Kubernetes у себе на власних потужностях або в навчальному середовищі всередині вашого ПК, кластер не матиме cloud-controller-manager.

Як і kube-controller-manager, cloud-controller-manager обʼєднує кілька логічно незалежних циклів управління в один бінарний файл, який ви запускаєте як один процес. Ви можете масштабувати його горизонтально (запускати більше однієї копії) для покращення продуктивності або для підвищення стійкості до збоїв.

Такі контролери можуть мати залежності від хмарного провайдера:

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

Компоненти вузлів

Компоненти вузлів працюють на кожному вузлі, підтримуючи роботу Podʼів та забезпечуючи середовище виконання Kubernetes.

kubelet

Агент, запущений на кожному вузлі кластера. Забезпечує запуск і роботу контейнерів в Podʼах.

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

kube-proxy (опційно)

kube-proxy є мережевим проксі, що запущений на кожному вузлі кластера і реалізує частину концепції Kubernetes Service.

kube-proxy підтримує мережеві правила на вузлах. Ці правила дозволяють мережеву взаємодію з вашими Podʼами з мережевих сеансів усередині чи поза межами кластера.

kube-proxy використовує шар фільтрації пакетів операційної системи, за його наявності. В іншому випадку kube-proxy скеровує трафік самостійно.

Якщо ви використовуєте мережевий втулок, який самостійно реалізує пересилання пакетів для Services та надає еквівалентну поведінку kube-proxy, то вам не потрібно запускати kube-proxy на вузлах вашого кластера.

Середовище виконання контейнерів

Основний компонент, який дозволяє Kubernetes ефективно запускати контейнери. Він відповідає за керування виконанням і життєвим циклом контейнерів у середовищі Kubernetes.

Kubernetes підтримує середовища виконання контейнерів, такі як containerd, CRI-O, та будь-яку іншу реалізацію Kubernetes CRI (інтерфейс виконання контейнерів).

Надбудови

Надбудови використовують ресурси Kubernetes (DaemonSet, Deployment тощо) для реалізації функцій кластера. Оскільки вони надають функції на рівні кластера, ресурси, що належать надбудовам, розміщуються в просторі імен kube-system.

Вибрані надбудови описані нижче; для розширеного списку доступних надбудов дивіться Надбудови.

DNS

Хоча інші надбудови не є строго необхідними, всі кластери Kubernetes повинні мати кластерний DNS, оскільки багато прикладів залежать від нього.

Кластерний DNS — це DNS-сервер, який доповнює інші DNS-сервери у вашому середовищі та обслуговує DNS-записи для сервісів Kubernetes.

Контейнери, запущені за допомогою Kubernetes, автоматично включають цей DNS-сервер до своїх DNS-пошуків.

Вебінтерфейс (Dashboard)

Dashboard — це універсальний вебінтерфейс для кластерів Kubernetes. Він дозволяє користувачам керувати застосунками, що працюють у кластері, та усувати проблеми з ними, а також із самим кластером.

Моніторинг ресурсів контейнерів

Моніторинг ресурсів контейнерів записує загальні метрики часових рядів для контейнерів у центральну базу даних та надає інтерфейс для перегляду цих даних.

Логування на рівні кластера

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

Мережеві втулки

Мережеві втулки — це програмні компоненти, які реалізують специфікацію інтерфейсу мережі контейнерів (CNI). Вони відповідають за виділення IP-адрес Podʼам та забезпечення їх звʼязку між собою у кластері.

Варіації архітектури

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

Варіанти розгортання панелі управління

Компоненти панелі управління можна розгортати кількома способами:

Традиційне розгортання
Компоненти панелі управління працюють безпосередньо на виділених машинах або віртуальних машинах, часто керованих як служби systemd.
Статичні Podʼи
Компоненти панелі управління розгортаються як статичні Podʼи, керовані kubelet на певних вузлах. Це поширений підхід, який використовують такі інструменти, як kubeadm.
Самостійне розміщення
Панель управління працює як Podʼи у самому кластері Kubernetes, якими керують Deployments та StatefulSets або інші примітиви Kubernetes.
Керовані сервіси Kubernetes
Хмарні провайдери часто абстрагують панель управління, керуючи її компонентами як частиною послуги, яку вони надають.

Розміщення робочих навантажень

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

  • У менших або розробницьких кластерах компоненти панелі управління та робочі навантаження користувачів можуть працювати на тих самих вузлах.
  • Великі промислові кластери часто виділяють певні вузли для компонентів панелі управління, відокремлюючи їх від робочих навантажень користувачів.
  • Деякі організації запускають критично важливі надбудови або інструменти моніторингу на вузлах панелі управління.

Інструменти управління кластером

Такі інструменти, як kubeadm, kops та Kubespray, пропонують різні підходи до розгортання та управління кластерами, кожен з яких має свій метод розташування та управління компонентами.

Налаштування та розширюваність

Архітектура Kubernetes дозволяє значне налаштування:

  • Власні планувальники можна розгортати паралельно зі стандартним планувальником Kubernetes або замінювати його повністю.
  • API-сервери можна розширювати за допомогою CustomResourceDefinitions та API Aggregation.
  • Хмарні провайдери можуть глибоко інтегруватися з Kubernetes за допомогою cloud-controller-manager.

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

Що далі

Дізнайтеся більше про:

2.1 - Вузли

Kubernetes виконує ваше робоче навантаження шляхом розміщення контейнерів у Podʼах для запуску на вузлах. Вузол може бути віртуальною або фізичною машиною, залежно від кластера. Кожен вузол керується панеллю управління і містить необхідні служби для запуску Podʼів.

Зазвичай в кластері є кілька вузлів; в умовах навчання чи обмежених ресурсів може бути всього один вузол.

До компонентів вузла належать kubelet, середовище виконання контейнерів та kube-proxy.

Управління

Є два основних способи додавання вузлів до API-сервера:

  1. kubelet на вузлі самостійно реєструється в панелі управління.
  2. Ви (або інший користувач) вручну додаєте обʼєкт Node.

Після створення обʼєкта Node або якщо kubelet на вузлі самостійно реєструється, панель управління перевіряє, чи новий обʼєкт Node є дійсним. Наприклад, якщо ви спробуєте створити вузол із таким JSON-маніфестом:

{
  "kind": "Node",
  "apiVersion": "v1",
  "metadata": {
    "name": "10.240.79.157",
    "labels": {
      "name": "my-first-k8s-node"
    }
  }
}

Kubernetes внутрішньо створює обʼєкт Node (його представлення). Kubernetes перевіряє, чи kubelet, що відповідає полю metadata.name обʼєкта Node, зареєструвався в API-сервері. Якщо вузол є справним (тобто всі необхідні служби працюють), то він може запускати Podʼи. В іншому випадку цей вузол ігнорується для будь-якої діяльності кластера, доки він не стане справним.

Примітка:

Kubernetes зберігає обʼєкт для недійсного вузла та продовжує перевіряти, чи він стає справним.

Ви або контролер маєте явно видалити обʼєкт Node, щоб припинити цю перевірку його справності.

Назва обʼєкта Node повинна бути дійсним імʼям DNS-піддомену.

Унікальність назв вузлів

Назва ідентифікує Node. Два вузли не можуть мати однакову назву одночасно. Kubernetes також припускає, що ресурс із такою самою назвою — це той самий обʼєкт. У випадку вузла припускається неявно, що екземпляр, який використовує ту саму назву, матиме той самий стан (наприклад, мережеві налаштування, вміст кореневого диска) та атрибути, такі як мітки вузла. Це може призвести до невідповідностей, якщо екземпляр був змінений без зміни його назви. Якщо вузол потрібно замінити або значно оновити, наявний обʼєкт Node потрібно спочатку видалити з API-сервера, а після оновлення додати знову.

Самореєстрація вузлів

Коли прапорець kubelet --register-node є true (типово), kubelet спробує зареєструвати себе в API-сервері. Це бажаний підхід, який використовується більшістю дистрибутивів.

Для самореєстрації kubelet запускається з такими параметрами:

  • --kubeconfig — Шлях до облікових даних для автентифікації на API-сервері.

  • --cloud-provider — Як взаємодіяти з хмарним провайдером для отримання метаданих про себе.

  • --register-node — Автоматична реєстрація в API-сервері.

  • --register-with-taints — Реєстрація вузла з заданим списком taints (розділених комами <ключ>=<значення>:<ефект>).

    Нічого не відбувається, якщо register-node є false.

  • --node-ip — Необовʼязковий розділений комами список IP-адрес вузла. Можна вказати лише одну адресу для кожного сімейства адрес. Наприклад, у кластері з одним стеком IPv4 ви встановлюєте це значення як IPv4-адресу, яку повинен використовувати kubelet для вузла. Див. Налаштування подвійного стека IPv4/IPv6 для отримання відомостей про запуск кластера з подвійним стеком.

    Якщо ви не вказали цей аргумент, kubelet використовує стандартну IPv4-адресу вузла, якщо є; якщо у вузла немає адреси IPv4, тоді kubelet використовує стандартну IPv6-адресу вузла.

  • --node-labels — Мітки для додавання при реєстрації вузла в кластері (див. обмеження міток, що накладаються втулком допуску NodeRestriction).

  • --node-status-update-frequency — Вказує, як часто kubelet публікує свій статус вузла на API-сервері.

Коли увімкнено режим авторизації вузла та втулок допуску NodeRestriction, kubeletʼи мають право створювати/змінювати лише свій власний ресурс Node.

Примітка:

Як зазначено в розділі Унікальність назв вузлів, коли потрібно оновити конфігурацію вузла, доброю практикою є повторна реєстрація вузла в API-сервері. Наприклад, якщо kubelet перезапускається з новим набором --node-labels, але використовується та сама назва Node, зміна не набуде чинності, оскільки мітки встановлюються (або змінюються) лише під час реєстрації вузла в API-сервері.

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

Ручне адміністрування вузлів

Ви можете створювати та змінювати обʼєкти Node за допомогою kubectl.

Коли ви хочете створювати обʼєкти Node вручну, встановіть прапорець kubelet --register-node=false.

Ви можете змінювати обʼєкти Node незалежно від налаштувань --register-node. Наприклад, ви можете встановлювати мітки на наявному вузлі або позначати його як непридатний для планування.

Ви можете встановити необовʼязкові ролі для вузлів, додавши одну або кілька міток node-role.kubernetes.io/<role>: <role> до вузла, де символи <role> обмежуються правилами синтаксису для міток.

Kubernetes ігнорує значення мітки для ролей вузлів; за домовленістю, ви можете задати для неї той самий рядок, який ви використовували для ролі вузла у ключі мітки.

Ви можете використовувати мітки на вузлах разом із селекторами вузлів на Podʼах для управління плануванням. Наприклад, ви можете дозволити Podʼу запускатися лише на підмножині доступних вузлів.

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

Щоб позначити вузол як непридатний для планування, виконайте:

kubectl cordon $NODENAME

Див. Безпечне очищення вузла для деталей.

Примітка:

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

Статус вузла

Статус вузла містить таку інформацію:

Ви можете використовувати kubectl, щоб переглядати статус вузла та інші деталі:

kubectl describe node <вставте-назву-вузла-тут>

Див. Статус вузла для отримання додаткової інформації.

Пульс вузлів

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

Для вузлів існують дві форми пульсу:

Контролер вузлів

Контролер вузлів — це компонент панелі управління Kubernetes, який керує різними аспектами роботи вузлів.

У контролера вузлів є кілька ролей у житті вузла. По-перше, він призначає блок CIDR вузлу при його реєстрації (якщо призначення CIDR увімкнено).

По-друге, він підтримує актуальність внутрішнього списку вузлів контролера зі списком доступних машин хмарного провайдера. Під час роботи в хмарному середовищі, коли вузол несправний, контролер вузлів запитує в хмарного провайдера, чи ще доступна віртуальна машина (VM) для цього вузла. Якщо ні, контролер вузлів видаляє вузол зі свого списку вузлів.

По-третє, він відстежує справність вузлів. Контролер вузлів відповідає за:

  • У випадку, якщо вузол стає недоступним, оновлення умови Ready у полі .status вузла. У цьому випадку контролер вузлів встановлює умову Ready в Unknown.
  • Якщо вузол залишається недоступним: запуск виселення, ініційоване API для всіх Podʼів на недосяжному вузлі. Типово контролер вузлів чекає 5 хвилин між позначенням вузла як Unknown та поданням першого запиту на виселення.

Стандартно контролер вузлів перевіряє стан кожного вузла кожні 5 секунд. Цей період можна налаштувати за допомогою прапорця --node-monitor-period у компоненті kube-controller-manager.

Обмеження швидкості виселення

У більшості випадків контролер вузлів обмежує швидкість виселення до --node-eviction-rate (типово 0,1) за секунду, тобто він не виселятиме Podʼи більш ніж з 1 вузла за 10 секунд.

Поведінка виселення змінюється, коли вузол у певній зоні доступності стає несправним. Контролер вузлів перевіряє, яка частина вузлів в зоні є несправною (умова Ready — Unknown або False) у той самий час:

  • Якщо частка несправних вузлів становить принаймні --unhealthy-zone-threshold (типово 0,55), тоді швидкість виселення зменшується.
  • Якщо кластер малий (тобто має не більше ніж --large-cluster-size-threshold вузлів — типово 50), тоді виселення припиняється.
  • В іншому випадку швидкість виселення зменшується до --secondary-node-eviction-rate (типово 0,01) в секунду.

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

Однією з ключових причин розподілу вузлів за зонами доступності є можливість переміщення навантаження в справні зони, коли одна ціла зона виходить з ладу. Таким чином, якщо всі вузли в зоні несправні, контролер вузлів виселяє Podʼи зі звичайною швидкістю --node-eviction-rate. Крайній випадок — коли всі зони повністю несправні (жоден вузол в кластері не є справним). У такому випадку контролер вузлів припускає, що існує якась проблема зі зʼєднанням між панеллю управління та вузлами, і не виконує жодних виселень. (Якщо стався збій і деякі вузли відновились, контролер вузлів виселяє Podʼи з решти вузлів, які є несправними або недосяжними).

Контролер вузлів також відповідає за виселення Podʼів, що працюють на вузлах із taints NoExecute, якщо ці Podʼи не толерують цей taint. Контролер вузлів також додає taints, відповідні проблемам вузла, таким як недосяжність вузла або неготовність вузла. Це означає, що планувальник не розміщуватиме Podʼи на несправних вузлах.

Відстеження місткості ресурсів

Обʼєкти Node відстежують інформацію про ресурсну місткість вузла: наприклад, кількість доступної памʼяті та кількість процесорів. Вузли, які реєструються самостійно, повідомляють про свою місткість під час реєстрації. Якщо ви додаєте вузол вручну, то вам потрібно встановити інформацію про місткість вузла при його додаванні.

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

Примітка:

Якщо ви хочете явно зарезервувати ресурси для не-Pod процесів, дивіться резервування ресурсів для системних служб.

Топологія вузла

This is a stable feature in Kubernetes, and has been since the 1.27 release. You can no longer toggle this feature (the associated feature gate has been removed).

Якщо ви увімкнули функціональну можливість TopologyManager, то kubelet може використовувати підказки топології при прийнятті рішень щодо призначення ресурсів. Див. Керування політиками топології на вузлі для отримання додаткової інформації.

Що далі

Дізнайтеся більше про таке:

2.2 - Звʼязок між вузлами та панеллю управління

Цей документ описує шляхи звʼязку між API-сервером та кластером Kubernetes. Мета — надати користувачам можливість налаштувати своє встановлення та посилити конфігурацію мережі так, щоб кластер можна було запускати в ненадійній мережі (або на повністю публічних IP-адресах у хмарному середовищі).

Звʼязок вузла з панеллю управління

У Kubernetes існує шаблон API "hub-and-spoke". Усе використання API з боку вузлів (або Podʼів, які на них працюють) завершується на API-сервері. Інші компоненти панелі управління не призначені для надання віддалених сервісів. API-сервер налаштований для прослуховування віддалених підключень на захищеному порту HTTPS (зазвичай 443) з однією або кількома увімкненими формами автентифікації клієнта. Рекомендується увімкнути одну чи кілька форм авторизації, особливо якщо анонімні запити або токени службового облікового запису дозволені.

Вузли повинні бути забезпечені публічним кореневим сертифікатом для кластера, щоб вони могли безпечно підключатися до API-сервера разом з дійсними обліковими даними клієнта. Добрий підхід — надавати kubelet облікові дані клієнта у формі клієнтського сертифіката. Див. завантаження TLS kubelet для автоматичного надання клієнтських сертифікатів kubelet.

Podʼи, які бажають підʼєднатися до API-сервера, можуть це зробити безпечно, використовуючи службовий обліковий запис (service account), так що Kubernetes автоматично вставлятиме публічний кореневий сертифікат та дійсний токен на предʼявника у Pod під час його створення. Service kubernetes (у просторі імен default) налаштована з віртуальною IP-адресою, яка перенаправляється (через kube-proxy) на точку доступу HTTPS на API-сервері.

Компоненти панелі управління також взаємодіють з API-сервером через захищений порт.

У результаті типовий режим роботи для зʼєднань від вузлів та Podʼів, що працюють на вузлах, до панелі управління є захищеним і може працювати через ненадійні та/або публічні мережі.

Звʼязок панелі управління з вузлом

Існують два основних шляхи звʼязку панелі управління (API-сервера) з вузлами. Перший — від API-сервера до процесу kubelet, який працює на кожному вузлі в кластері. Другий — від API-сервера до будь-якого вузла, Podʼа чи Service через проксі API-сервера.

Звʼязок API-сервера з kubelet

Зʼєднання від API-сервера до kubelet використовуються для:

  • Отримання логів для Podʼів.
  • Приєднання (зазвичай через kubectl) до запущених Podʼів.
  • Забезпечення функціональності перенаправлення портів kubelet.

Ці зʼєднання завершуються на точці доступу HTTPS kubelet. Стандартно API-сервер не перевіряє сертифікат обслуговування kubelet, що робить зʼєднання вразливим до атаки "людина посередині" і небезпечним для використання через ненадійні та/або публічні мережі.

Щоб перевірити це зʼєднання, використовуйте прапорець --kubelet-certificate-authority, щоб надати API-серверу пакет кореневих сертифікатів для перевірки сертифіката обслуговування kubelet.

Якщо це неможливо, скористайтеся тунелюванням SSH між API-сервером та kubelet, якщо необхідно, щоб уникнути підключення через ненадійну або публічну мережу.

Нарешті, автентифікацію та/або авторизацію Kubelet потрібно ввімкнути, щоб захистити API kubelet.

Звʼязок API-сервера з вузлами, Podʼами та Service

Зʼєднання від API-сервера до вузла, Podʼа чи Service типово використовують прості HTTP-зʼєднання і, отже, не автентифікуються й не шифруються. Їх можна використовувати через захищене зʼєднання HTTPS, підставивши https: перед назвою вузла, Podʼа чи Service в URL API, але вони не будуть перевіряти сертифікат, наданий точкою доступу HTTPS, та не надаватимуть облікові дані клієнта. Таким чином, хоча зʼєднання буде зашифрованим, воно не гарантує цілісності. Ці зʼєднання поки не є безпечними для використання через ненадійні або публічні мережі.

Тунелі SSH

Kubernetes підтримує тунелі SSH, щоб захистити канали звʼязку від панелі управління до вузлів. У цій конфігурації API-сервер ініціює тунель SSH до кожного вузла в кластері (підключаючись до SSH-сервера, який прослуховує порт 22) і передає весь трафік, призначений для kubelet, вузла, Podʼа чи Service, через тунель. Цей тунель гарантує, що трафік не виходить за межі мережі, в якій працюють вузли.

Примітка:

Тунелі SSH наразі застарілі, тому не варто їх використовувати, якщо ви не знаєте, що робите. Служба Konnectivity — це заміна для цього каналу звʼязку.

Служба Konnectivity

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.18

Як заміна тунелям SSH, служба Konnectivity надає проксі на рівні TCP для звʼязку панелі управління з кластером. Служба Konnectivity складається з двох частин: сервера Konnectivity в мережі панелі управління та агентів Konnectivity в мережі вузлів. Агенти Konnectivity ініціюють зʼєднання з сервером Konnectivity та підтримують мережеві підключення. Після ввімкнення служби Konnectivity весь трафік від панелі управління до вузлів проходить через ці зʼєднання.

Для налаштування служби Konnectivity у своєму кластері скористайтеся завданням служби Konnectivity.

Що далі

2.3 - Контролери

У робототехніці та автоматизації control loop (цикл управління) — це нескінченний цикл, який регулює стан системи.

Ось один приклад циклу управління — термостат у кімнаті.

Коли ви встановлюєте температуру, це означає, що ви повідомляєте термостату про бажаний стан. Фактична температура в кімнаті — це поточний стан. Термостат діє так, щоб наблизити поточний стан до бажаного стану, вмикаючи чи вимикаючи відповідне обладнання.

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

Шаблон контролера

Контролер відстежує принаймні один тип ресурсу Kubernetes. Ці обʼєкти мають поле spec, яке представляє бажаний стан. Контролер(и) для цього ресурсу відповідають за наближення поточного стану до цього бажаного стану.

Контролер може виконати дію самостійно; частіше в Kubernetes контролер надсилає повідомлення API-серверу, які мають корисні побічні ефекти. Нижче ви побачите приклади цього.

Управління через API-сервер

Контролер Job — це приклад вбудованого контролера Kubernetes. Вбудовані контролери управляють станом, взаємодіючи з API-сервером кластера.

Job — це ресурс Kubernetes, який запускає Pod або, можливо, кілька Podʼів, щоб виконати завдання і потім зупинитися.

(Після процесу планування, обʼєкти Pod стають частиною бажаного стану для kubelet).

Коли контролер Job бачить нове завдання, він переконується, що десь у вашому кластері kubeletʼи на наборі вузлів виконують потрібну кількість Podʼів, щоб виконати роботу. Контролер Job не запускає жодних Podʼів або контейнерів самостійно. Замість цього контролер Job вказує API-серверу створити або видалити Podʼи. Інші компоненти панелі управління реагують на нову інформацію (є нові Podʼи для планування та запуску), і зрештою робота виконується.

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

Контролери також оновлюють обʼєкти, які їх конфігурують. Наприклад, коли роботу для завдання виконано, контролер Job оновлює обʼєкт Job, щоб позначити його як Finished.

(Це трошки схоже на те, як деякі термостати вимикають світловий індикатор, щоб сповістити, що ваша кімната тепер має температуру, яку ви встановили).

Безпосереднє управління

На відміну від Job, деякі контролери повинні вносити зміни за межами вашого кластера.

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

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

(Фактично є контролер, що горизонтально масштабує вузли у вашому кластері).

Важливим тут є те, що контролер вносить певні зміни, щоб досягти вашого бажаного стану, а потім повідомляє поточний стан назад на API-сервер вашого кластера. Інші цикли управління можуть спостерігати за цими даними та виконувати свої дії.

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

Бажаний стан порівняно з поточним

Kubernetes дивиться на системи з погляду cloud-native і здатний обробляти постійні зміни.

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

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

Дизайн

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

Корисно мати прості контролери, а не один великий набір взаємозалежних циклів управління. Контролери можуть виходити з ладу, тому Kubernetes спроєктовано з урахуванням цього.

Примітка:

Може існувати кілька контролерів, які створюють або оновлюють один і той самий вид обʼєктів. За лаштунками контролери Kubernetes переконуються, що вони звертають увагу лише на ресурси, повʼязані з ресурсом, який ними керує.

Наприклад, ви можете мати ресурси Deployments і Jobs; обидва ці ресурси створюють Podʼи. Контролер Job не видаляє Podʼи, які створив ваш Deployment, оскільки є інформація (мітки), яку контролери можуть використовувати для розрізнення цих Podʼів.

Способи запуску контролерів

Kubernetes постачається з набором вбудованих контролерів, які працюють всередині kube-controller-manager. Ці вбудовані контролери забезпечують важливі основні функції.

Контролер Deployment та контролер Job — це приклади контролерів, які постачаються разом з Kubernetes (контролери, вбудовані в систему). Kubernetes дозволяє вам запускати стійку панель управління, так що, якщо будь-який з вбудованих контролерів не зможе виконувати завдання, інша частина панелі управління візьме на себе його обовʼязки.

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

Що далі

2.4 - Лізинг

Часто в розподілених системах є потреба в лізингу, який забезпечує механізм блокування спільних ресурсів та координації дій між членами групи. У Kubernetes концепцію лізингу представлено обʼєктами Lease в групі API coordination.k8s.io, які використовуються для критичних для системи можливостей, таких як пульс вузлів (heartbeats) та вибір лідера на рівні компонентів.

Пульс вузлів

Kubernetes використовує API Lease для передачі пульсу (heartbeats) kubelet вузла на API-сервер Kubernetes. Для кожного Node існує обʼєкт Lease з відповідною назвою в просторі імен kube-node-lease. Під капотом кожен сигнал пульсу kubelet — це запит на оновлення цього обʼєкта Lease, який оновлює поле spec.renewTime для обʼєкта Lease. Панель управління Kubernetes використовує відмітку часу цього поля для визначення доступності цього вузла.

Дивіться Обʼєкти Lease вузлів для отримання додаткових деталей.

Вибір лідера

Kubernetes також використовує лізинги, щоб у будь-який момент часу працював лише один екземпляр компонента. Це використовується компонентами панелі управління, такими як kube-controller-manager та kube-scheduler в конфігураціях з високою доступністю, де лише один екземпляр компонента має активно працювати, тоді як інші екземпляри перебувають в режимі очікування.

Прочитайте координовані вибори лідера, щоб дізнатися про те, як Kubernetes використовує Lease API для вибору екземпляра компонента, який буде виконувати роль лідера.

Звільнення блокування вибору лідера kube controller manager під час виходу

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.36; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the ControllerManagerReleaseLeaderElectionLockOnExit feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональна можливість ControllerManagerReleaseLeaderElectionLockOnExit увімкнена, kube-controller-manager активно звільняє блокування вибору лідера під час переходу лідерства, замість того, щоб чекати закінчення терміну дії блокування. Це дозволяє швидше обрати нового лідера, зменшуючи затримку переходу лідерства.

Ідентичність API-сервера

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.26; (стандартно увімкнено)

Починаючи з Kubernetes v1.26, кожен kube-apiserver використовує API Lease для публікації своєї ідентичності для решти системи. Хоча це само по собі не є особливо корисним, це забезпечує механізм для клієнтів для визначення кількості екземплярів kube-apiserver, які керують панеллю управління Kubernetes. Наявність лізингів kube-apiserver уможливлює майбутні функції, які можуть потребувати координації між усіма kube-apiserver.

Ви можете перевірити лізинги, якими володіє кожен kube-apiserver, перевіривши обʼєкти лізингу в просторі імен kube-system з назвою apiserver-<sha256-hash>. Або ви можете використовувати селектор міток apiserver.kubernetes.io/identity=kube-apiserver:

kubectl -n kube-system get lease -l apiserver.kubernetes.io/identity=kube-apiserver
NAME                                        HOLDER                                                                           AGE
apiserver-07a5ea9b9b072c4a5f3d1c3702        apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05        5m33s
apiserver-7be9e061c59d368b3ddaf1376e        apiserver-7be9e061c59d368b3ddaf1376e_84f2a85d-37c1-4b14-b6b9-603e62e4896f        4m23s
apiserver-1dfef752bcb36637d2763d1868        apiserver-1dfef752bcb36637d2763d1868_c5ffa286-8a9a-45d4-91e7-61118ed58d2e        4m43s

Хеш SHA256, який використовується в назвах лізингу, базується на імені хоста операційної системи, яке бачить цей API-сервер. Кожен kube-apiserver повинен бути налаштований на використання імені хоста, унікального в межах кластера. Нові екземпляри kube-apiserver, які використовують те саме імʼя хоста, перехоплюють наявні лізинги за допомогою нового ідентифікатора власника, замість того щоб створювати нові обʼєкти лізингу. Ви можете перевірити імʼя хоста, яке використовує kube-apiserver, перевіривши значення мітки kubernetes.io/hostname:

kubectl -n kube-system get lease apiserver-07a5ea9b9b072c4a5f3d1c3702 -o yaml
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  creationTimestamp: "2023-07-02T13:16:48Z"
  labels:
    apiserver.kubernetes.io/identity: kube-apiserver
    kubernetes.io/hostname: master-1
  name: apiserver-07a5ea9b9b072c4a5f3d1c3702
  namespace: kube-system
  resourceVersion: "334899"
  uid: 90870ab5-1ba9-4523-b215-e4d4e662acb1
spec:
  holderIdentity: apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05
  leaseDurationSeconds: 3600
  renewTime: "2023-07-04T21:58:48.065888Z"

Прострочені лізинги від kube-apiserver, які вже не існують, прибираються новими kube-apiserver через 1 годину.

Ви можете вимкнути лізинги ідентичності API-сервера, вимкнувши функціональну можливість APIServerIdentity.

Робочі навантаження

Ваші власні робочі навантаження можуть визначити своє власне використання лізингів. Наприклад, ви можете запускати власний контролер, де основний член чи лідер виконує операції, яких не виконують інші члени групи. Ви визначаєте лізинг так, щоб репліки контролера могли вибирати чи обирати лідера, використовуючи API Kubernetes для координації. Якщо ви використовуєте лізинг, гарною практикою є визначати назву лізингу, яка очевидно повʼязана з продуктом чи компонентом. Наприклад, якщо у вас є компонент з назвою Example Foo, використовуйте лізинг з назвою example-foo.

Якщо оператор кластера чи інший кінцевий користувач може розгортати кілька екземплярів компонента, виберіть префікс назви та механізм (такий як хеш від назви Deployment), щоб уникнути конфліктів назв лізингів.

Ви можете використовувати інший підхід, якщо він досягає того самого результату: відсутність конфліктів між різними програмними продуктами.

2.5 - Cloud Controller Manager

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.11

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

Cloud-controller-manager — це компонент панелі управління Kubernetes, що вбудовує логіку управління, специфічну для хмари. Cloud controller manager дозволяє звʼязувати ваш кластер з API хмарного провайдера та відокремлює компоненти, що взаємодіють із хмарною платформою, від компонентів, які взаємодіють тільки з вашим кластером.

Відокремлюючи логіку сумісності між Kubernetes і базовою хмарною інфраструктурою, компонент cloud-controller-manager дає змогу хмарним провайдерам випускати функції з іншою швидкістю порівняно з основним проєктом Kubernetes.

Cloud-controller-manager структурується за допомогою механізму втулків, який дозволяє різним хмарним провайдерам інтегрувати свої платформи з Kubernetes.

Дизайн

Компоненти Kubernetes

Cloud-controller-manager працює в панелі управління як реплікований набір процесів (зазвичай це контейнери в Podʼах). Кожен cloud-controller-manager реалізує кілька контролерів в одному процесі.

Примітка:

Ви також можете запускати cloud-controller-manager як надбудову Kubernetes, а не як частину панелі управління.

Функції cloud-controller-manager

Контролери всередині cloud-controller-manager включають:

Контролер вузлів

Контролер вузлів відповідає за оновлення обʼєктів Node при створенні нових серверів у вашій хмарній інфраструктурі. Контролер вузлів отримує інформацію про хости, які працюють у вашому обліковому записі у хмарному провайдері. Контролер вузлів виконує такі функції:

  1. Оновлення обʼєкта Node відповідним унікальним ідентифікатором сервера, отриманим з API хмарного провайдера.
  2. Додавання до обʼєкта Node анотацій та міток з інформацією, специфічною для хмари, такою як регіон, в якому розгорнуто вузол, та доступні йому ресурси (CPU, памʼять тощо).
  3. Отримання імені хоста та мережевих адрес.
  4. Перевірка стану вузла. Якщо вузол перестає відповідати, цей контролер перевіряє за допомогою API вашого хмарного провайдера, чи сервер був деактивований / видалений / завершений. Якщо вузол був видалений з хмари, контролер видаляє обʼєкт Node з вашого кластера Kubernetes.

Деякі реалізації від хмарних провайдерів розділяють це на контролер вузлів та окремий контролер життєвого циклу вузла.

Контролер маршрутів

Контролер маршрутів відповідає за налаштування маршрутів у хмарі відповідним чином, щоб контейнери на різних вузлах вашого кластера Kubernetes могли взаємодіяти один з одним.

Залежно від хмарного провайдера контролер маршрутів може також виділяти блоки IP-адрес для мережі Podʼів.

Контролер Service

Services інтегруються з компонентами хмарної інфраструктури, такими як керовані балансувальники навантаження, IP-адреси, мережеве фільтрування пакетів та перевірка стану цільового обʼєкта. Контролер Service взаємодіє з API вашого хмарного провайдера для налаштування балансувальників навантаження та інших компонентів інфраструктури, коли ви оголошуєте ресурс Service, який їх потребує.

Авторизація

У цьому розділі розглядаються доступи, яких потребує cloud-controller-manager для різних обʼєктів API для виконання своїх операцій.

Контролер вузлів

Контролер вузлів працює тільки з обʼєктами Node. Він вимагає повного доступу для читання та модифікації обʼєктів Node.

v1/Node:

  • get
  • list
  • create
  • update
  • patch
  • watch
  • delete

Контролер маршрутів

Контролер маршрутів відстежує створення обʼєктів Node та налаштовує маршрути відповідним чином. Він вимагає доступу Get до обʼєктів Node.

v1/Node:

  • get

Контролер Service

Контролер Service відстежує події create, update та delete обʼєктів Service, а потім відповідним чином налаштовує балансувальники навантаження для цих Services.

Для доступу до Service потрібні доступи list та watch. Для оновлення Service потрібен доступ patch та update до підресурсу status.

v1/Service:

  • list
  • get
  • watch
  • patch
  • update

Інші

Реалізація ядра cloud-controller-manager потребує доступу до створення Event, а для забезпечення безпечної роботи — доступу до створення ServiceAccounts.

v1/Event:

  • create
  • patch
  • update

v1/ServiceAccount:

  • create

RBAC ClusterRole для cloud-controller-manager виглядає так:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cloud-controller-manager
rules:
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - '*'
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - services/status
  verbs:
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - serviceaccounts
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - persistentvolumes
  verbs:
  - get
  - list
  - update
  - watch

Що далі

  • Адміністрування cloud-controller-manager містить інструкції щодо запуску cloud-controller-manager та управління ним.

  • Щодо оновлення панелі управління з високою доступністю для використання cloud-controller-manager див. Міграція реплікованої панелі управління для використання cloud-controller-manager.

  • Хочете знати, як реалізувати свій власний cloud-controller-manager або розширити наявний проєкт?

    • Cloud-controller-manager використовує інтерфейси Go, зокрема, інтерфейс CloudProvider, визначений у cloud.go з kubernetes/cloud-provider, щоб дозволити підключати реалізації для будь-якої хмари.
    • Реалізація загальних контролерів, виділених у цьому документі (Node, Route та Service), а також допоміжний каркас разом із загальним інтерфейсом хмарного провайдера є частиною ядра Kubernetes. Реалізації, специфічні для хмарних провайдерів, знаходяться поза ядром Kubernetes і реалізують інтерфейс CloudProvider.
    • Для отримання додаткової інформації щодо розробки втулків див. Розробка cloud-controller-manager.

2.6 - Про cgroup v2

У Linux контрольні групи обмежують ресурси, які виділяються процесам.

kubelet та середовище виконання контейнерів повинні співпрацювати з cgroups для забезпечення управління ресурсами для Podʼів та контейнерів, що включає запити та обмеження CPU/памʼяті для контейнеризованих робочих навантажень.

Є дві версії cgroups у Linux: cgroup v1 і cgroup v2. cgroup v2 — це нове покоління API cgroup в Linux.

Що таке cgroup v2?

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.25

cgroup v2 — це наступне покоління API Linux cgroup. cgroup v2 надає єдину систему управління з розширеними можливостями управління ресурсами.

cgroup v2 пропонує кілька поліпшень порівняно з cgroup v1, таких як:

  • Один уніфікований дизайн ієрархії в API
  • Безпечне делегування піддерева контейнерам
  • Нові можливості, такі як Pressure Stall Information
  • Розширене управління розподілом ресурсів та ізоляція між різними ресурсами
    • Обʼєднаний облік різних типів виділення памʼяті (мережева памʼять, памʼять ядра і т. д.)
    • Облік відкладених змін ресурсів, таких як запис кешу сторінок

Деякі можливості Kubernetes використовують виключно cgroup v2 для поліпшення управління ресурсами та ізоляцією. Наприклад, можливість MemoryQoS покращує якість обслуговування памʼяті та покладається на примітиви cgroup v2.

Використання cgroup v2

Рекомендованим способом використання cgroup v2 є використання дистрибутиву Linux, який стандартно вмикає та використовує cgroup v2.

Щоб перевірити, чи ваш дистрибутив використовує cgroup v2, див. Визначення версії cgroup на вузлах Linux.

Вимоги

cgroup v2 має наступні вимоги:

  • Дистрибутив ОС вмикає cgroup v2
  • Версія ядра Linux — 5.8 або пізніше
  • Середовище виконання контейнерів підтримує cgroup v2. Наприклад:
  • kubelet та середовище виконання контейнерів налаштовані на використання cgroup-драйвера systemd

Підтримка cgroup v2 дистрибутивами Linux

Для ознайомлення зі списком дистрибутивів Linux, які використовують cgroup v2, див. документацію cgroup v2

  • Container Optimized OS (з версії M97)
  • Ubuntu (з версії 21.10, рекомендовано 22.04+)
  • Debian GNU/Linux (з Debian 11 bullseye)
  • Fedora (з версії 31)
  • Arch Linux (з квітня 2021)
  • RHEL та схожі дистрибутиви (з версії 9)

Щоб перевірити, чи ваш дистрибутив використовує cgroup v2, див. документацію вашого дистрибутиву або скористайтеся інструкціями в Визначенні версії cgroup на вузлах Linux.

Також можна увімкнути cgroup v2 вручну у вашому дистрибутиві Linux, змінивши аргументи завантаження ядра. Якщо ваш дистрибутив використовує GRUB, systemd.unified_cgroup_hierarchy=1 повинно бути додано в GRUB_CMDLINE_LINUX в /etc/default/grub, а потім виконайте sudo update-grub. Однак рекомендованим підходом є використання дистрибутиву, який вже стандартно вмикає cgroup v2.

Міграція на cgroup v2

Щоб перейти на cgroup v2, переконайтеся, що ваша система відповідає вимогам, а потім оновіть її до версії ядра, яка стандартно вмикає cgroup v2.

Kubelet автоматично виявляє, що ОС працює з cgroup v2 і діє відповідно без додаткової конфігурації.

При переході на cgroup v2 не повинно бути помітної різниці в використанні, якщо користувачі не звертаються безпосередньо до файлової системи cgroup чи на вузлі, чи зсередини контейнерів.

cgroup v2 використовує інший API, ніж cgroup v1, тому, якщо є застосунки, які безпосередньо отримують доступ до файлової системи cgroup, вони повинні бути оновлені до новіших версій, які підтримують cgroup v2. Наприклад:

  • Деякі сторонні агенти моніторингу та безпеки можуть залежати від файлової системи cgroup. Оновіть ці агенти до версій, які підтримують cgroup v2.
  • Якщо ви використовуєте cAdvisor як окремий DaemonSet для моніторингу Podʼів та контейнерів, оновіть його до v0.43.0 чи новіше.
  • Якщо ви розгортаєте Java-застосунки, віддайте перевагу використанню версій, які повністю підтримують cgroup v2:
  • Якщо ви використовуєте пакунок uber-go/automaxprocs, переконайтеся, що ви використовуєте версію v1.5.1 чи вище.
  • Якщо ви розгортаєте застосунки на Node.js, віддавайте перевагу версіям, які виявляють обмеження памʼяті cgroup v2. Node.js читає обмеження памʼяті cgroup v2 (через libuv) починаючи з Node.js v20.3.0. Лінія випуску v18 ненадійно виявляє обмеження памʼяті cgroup v2. Версії без цієї підтримки можуть читати загальну памʼять хоста замість обмеження, застосованого до Podʼа, що може призвести до неправильного розміру heap і завершення роботи через нестачу памʼяті (OOM). У версіях, яких це стосується, встановіть розмір heap явно, наприклад за допомогою прапорця --max-old-space-size.

Визначення версії cgroup на вузлах Linux

Версія cgroup залежить від використаного дистрибутиву Linux та версії cgroup, налаштованої в ОС. Щоб перевірити, яка версія cgroup використовується вашим дистрибутивом, виконайте команду stat -fc %T /sys/fs/cgroup/ на вузлі:

stat -fc %T /sys/fs/cgroup/

Для cgroup v2 вивід — cgroup2fs.

Для cgroup v1 вивід — tmpfs.

Визнання застарілим cgroup v1

СТАН ФУНКЦІОНАЛУ: Deprecated since Kubernetes v1.35

Kubernetes оголосив cgroup v1 застарілим. Видалення відбудеться відповідно до політики застарівання Kubernetes.

Kubelet більше стандартно не буде запускатися на вузлі cgroup v1. Щоб вимкнути це налаштування, адміністратор кластера повинен встановити failCgroupV1 на false у файлі конфігурації kubelet.

Що далі

2.7 - Самозцілення Kubernetes

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

Можливості самозцілення

  • Перезапуски на рівні контейнера: Якщо контейнер всередині Podʼа виходить з ладу, Kubernetes перезапускає його відповідно до restartPolicy.

  • Заміна реплік: Якщо Pod у Deployment або StatefulSet виходить з ладу, Kubernetes створює Pod на заміну, щоб підтримувати вказану кількість реплік. Якщо Pod, який є частиною DaemonSet, виходить з ладу, панель управління створює Pod на заміну для запуску на тому самому вузлі.

  • Відновлення постійного сховища: Якщо на вузлі працює Pod з підключеним PersistentVolume (PV), і вузол виходить з ладу, Kubernetes може повторно приєднати том до нового Podʼа на іншому вузлі.

  • Балансування навантаження для Services: Якщо Pod, що перебуває за Service, виходить з ладу, Kubernetes автоматично видаляє його з точок доступу Service, щоб направляти трафік лише до справних Podʼів.

Ось деякі з ключових компонентів, які забезпечують самозцілення Kubernetes:

  • kubelet: Забезпечує роботу контейнерів і перезапускає ті, які вийшли з ладу.

  • Контролери Deployment (через ReplicaSet), ReplicaSet, StatefulSet і DaemonSet: Підтримують бажану кількість реплік Podʼа.

  • Контролер PersistentVolume: Керує приєднанням і відʼєднанням томів для робочих навантажень зі збереженням стану.

Візьміть до уваги

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

  • Помилки застосунків: Kubernetes може перезапускати контейнери, але проблеми в самому застосунку необхідно вирішувати окремо.

Що далі

2.8 - Збір сміття

Збір сміття — це загальний термін для різних механізмів, які Kubernetes використовує для очищення ресурсів кластера. Це дозволяє очищати ресурси, такі як:

Власники та залежності

Багато обʼєктів в Kubernetes повʼязані один з одним через посилання на власника. Посилання на власника повідомляють панелі управління, які обʼєкти залежать від інших. Kubernetes використовує посилання на власника для того, щоб дати панелі управління та іншим клієнтам API можливість очищати повʼязані ресурси перед видаленням обʼєкта. У більшості випадків Kubernetes автоматично керує посиланнями на власника.

Власність відрізняється від механізму міток та селекторів, які також використовують деякі ресурси. Наприклад, розгляньте Service, який створює обʼєкти EndpointSlice. Service використовує мітки, щоб дозволити панелі управління визначити, які обʼєкти EndpointSlice використовуються для цього Service. Окрім міток, кожен обʼєкт EndpointSlice, керований від імені Service, має посилання на власника. Посилання на власника допомагають різним частинам Kubernetes уникати втручання в обʼєкти, якими вони не керують.

Примітка:

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

Залежні обʼєкти рівня кластера можуть вказувати тільки власників рівня кластера. У v1.20+, якщо залежний обʼєкт рівня кластера вказує як власника тип у межах простору імен, вважається, що він має нерозвʼязне посилання на власника, і його не можна прибрати збором сміття.

У v1.20+, якщо збирач сміття виявляє недійсний ownerReference між просторами імен або залежний обʼєкт рівня кластера з ownerReference, що посилається на тип у межах простору імен, повідомляється про попереджувальну подію з причиною OwnerRefInvalidNamespace та involvedObject недійсного залежного елемента. Ви можете перевірити наявність такого роду подій, запустивши kubectl get events -A --field-selector=reason=OwnerRefInvalidNamespace.

Каскадне видалення

Kubernetes перевіряє та видаляє обʼєкти, які більше не мають посилань на власника, наприклад, Podʼи, які залишилися після видалення ReplicaSet. Коли ви видаляєте обʼєкт, ви можете контролювати, чи автоматично видаляє Kubernetes залежні обʼєкти, у процесі, що називається каскадним видаленням. Існують два типи каскадного видалення:

  • Каскадне видалення на передньому плані
  • Каскадне видалення у фоні

Ви також можете контролювати те, як і коли збір сміття видаляє ресурси, які мають посилання на власника, використовуючи завершувачі Kubernetes.

Каскадне видалення на передньому плані

У каскадному видаленні на передньому плані обʼєкт власника, який видаляється, спочатку потрапляє в стан deletion in progress. У цьому стані з обʼєктом власника відбувається таке:

  • API-сервер Kubernetes встановлює поле metadata.deletionTimestamp обʼєкта на час, коли обʼєкт був позначений на видалення.
  • API-сервер Kubernetes також встановлює поле metadata.finalizers в foregroundDeletion.
  • Обʼєкт залишається видимим через API Kubernetes, поки не буде завершений процес видалення.

Після того як обʼєкт власника потрапляє в стан deletion in progress, контролер видаляє відомі йому залежні обʼєкти. Після видалення всіх відомих залежних обʼєктів контролер видаляє обʼєкт власника. На цьому етапі обʼєкт більше не є видимим в API Kubernetes.

Під час каскадного видалення на передньому плані єдиними залежними обʼєктами, які блокують видалення власника, є ті, що мають поле ownerReference.blockOwnerDeletion=true і перебувають у кеші контролера збору сміття. Кеш контролера збору сміття може не містити обʼєкти, тип ресурсу яких не вдається успішно перелічити / відстежувати, або обʼєкти, які створюються одночасно з видаленням обʼєкта-власника. Дивіться Використання каскадного видалення на передньому плані, щоб дізнатися більше.

Каскадне видалення у фоні

Під час фонового каскадного видалення API-сервер Kubernetes негайно видаляє обʼєкт-власник, а контролер збирача сміття (ваш власний або стандартний) очищає залежні обʼєкти у фоновому режимі. Якщо існує завершувач, він гарантує, що обʼєкти не будуть видалені, доки не будуть виконані всі необхідні завдання з очищення. Типово Kubernetes використовує каскадне видалення у фоні, якщо ви не використовуєте видалення вручну на передньому плані або не вибираєте залишити залежні обʼєкти.

Дивіться Використання каскадного видалення у фоні, щоб дізнатися більше.

Залишені залежні обʼєкти

Коли Kubernetes видаляє обʼєкт власника, залишені залежні обʼєкти називаються залишеними обʼєктами. Типово Kubernetes видаляє залежні обʼєкти. Щоб дізнатися, як перевизначити цю поведінку, дивіться Видалення власників та залишені залежності.

Збір сміття невикористаних контейнерів та образів

kubelet виконує збір сміття невикористаних образів кожні пʼять хвилин та невикористаних контейнерів кожну хвилину. Ви повинні уникати використання зовнішніх інструментів для збору сміття, оскільки вони можуть порушити поведінку kubelet та видаляти контейнери, які повинні існувати.

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

Життєвий цикл образу контейнера

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

  • HighThresholdPercent
  • LowThresholdPercent

Використання дискового простору вище встановленого значення HighThresholdPercent викликає збір сміття, який видаляє образи в порядку їх останнього використання, починаючи з найстаріших. Kubelet видаляє образи до тих пір, поки використання дискового простору не досягне значення LowThresholdPercent.

Збір сміття для невикористаних контейнерних образів

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

Щоб налаштувати параметр, встановіть значення поля imageMaximumGCAge у файлі конфігурації kubelet.

Значення вказується як тривалість. Дивіться визначення тривалості в глосарії для отримання докладних відомостей.

Наприклад, ви можете встановити значення поля конфігурації 12h45m, що означає 12 годин 45 хвилин.

Примітка:

Ця функція не відстежує використання образів між перезапусками kubelet. Якщо kubelet перезапускається, вік відстежуваного образу скидається, що призводить до того, що kubelet очікує повний проміжок часу imageMaximumGCAge перш ніж вважати образ придатним для збору сміття на основі його віку.

Збір сміття контейнерів

Kubelet збирає сміття невикористаних контейнерів на основі таких змінних, які ви можете визначити:

  • MinAge: мінімальний вік, за якого kubelet може видаляти контейнер. Щоб вимкнути, встановіть значення 0.
  • MaxPerPodContainer: максимальна кількість мертвих контейнерів, які може мати кожен Pod. Щоб вимкнути, встановіть значення менше 0.
  • MaxContainers: максимальна кількість мертвих контейнерів у кластері. Щоб вимкнути, встановіть значення менше 0.

Крім цих змінних, kubelet збирає сміття невизначених та видалених контейнерів, як правило, починаючи з найстарших.

MaxPerPodContainer та MaxContainers можуть потенційно конфліктувати одне з одним в ситуаціях, де збереження максимальної кількості контейнерів на кожен Pod (MaxPerPodContainer) перевищить допустиму загальну кількість глобальних мертвих контейнерів (MaxContainers). У цій ситуації kubelet коригує MaxPerPodContainer для вирішення конфлікту. У найгіршому випадку може бути знижений MaxPerPodContainer до 1 та виселені найдавніші контейнери. Крім того, контейнери, які належать Podʼам, які були видалені, видаляються, коли стають старшими за MinAge.

Примітка:

Kubelet збирає сміття лише для контейнерів, якими він керує.

Налаштування збору сміття

Ви можете налаштовувати збір сміття ресурсів, налаштовуючи параметри, специфічні для контролерів, що керують цими ресурсами. На наступних сторінках показано, як налаштовувати збір сміття:

Що далі

2.9 - Проксі змішаних версій

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.36; (стандартно увімкнено)

У Kubernetes 1.37 включено бета-функцію, яка дозволяє API-серверу проксіювати запити ресурсів до інших рівноправних API-серверів. Це також дозволяє клієнтам отримати цілісне уявлення про ресурси, що обслуговуються в усьому кластері, за допомогою виявлення. Це корисно, коли існують кілька API-серверів, що працюють на різних версіях Kubernetes в одному кластері (наприклад, під час тривалого впровадження нового релізу Kubernetes).

Це дозволяє адміністраторам кластерів налаштовувати високодоступні кластери, які можна оновлювати безпечніше:

  1. гарантуючи, що контролери, які покладаються на виявлення для показу повного списку ресурсів для важливих завдань, завжди отримують повний огляд усіх ресурсів. Ми називаємо це повним виявленням у масштабі кластера — Peer-aggregated discovery.
  2. направляючи запити на ресурси (зроблені під час оновлення) на відповідний kube-apiserver. Це проксіювання запобігає появі у користувачів несподіваних помилок 404 Not Found, спричинених процесом оновлення. Цей механізм називається Mixed Version Proxy.

Активація Peer-aggregated Discovery та Mixed Version Proxy

Переконайтеся, що функціональну можливість UnknownVersionInteroperabilityProxy увімкнено, коли ви запускаєте API-сервер:

kube-apiserver \
--feature-gates=UnknownVersionInteroperabilityProxy=true \
# обовʼязкові аргументи командного рядка для цієї функції
--peer-ca-file=<шлях до сертифіката CA kube-apiserver>
--proxy-client-cert-file=<шлях до сертифіката агрегатора проксі>,
--proxy-client-key-file=<шлях до ключа агрегатора проксі>,
--requestheader-client-ca-file=<шлях до сертифіката CA агрегатора>,
# requestheader-allowed-names може бути встановлено як порожнє значення, щоб дозволити будь-яке Common Name
--requestheader-allowed-names=<дійсні значення Common Name для перевірки сертифіката клієнта проксі>,

# необовʼязкові прапорці для цієї функції
--peer-advertise-ip=`IP цього kube-apiserver, який повинні використовувати рівноправні сервери для проксіювання запитів`
--peer-advertise-port=`порт цього kube-apiserver, який повинні використовувати рівноправні сервери для проксіювання запитів`

# …і інші прапорці як зазвичай

Транспорт та автентифікація проксі між API-серверами

  • Вихідний kube-apiserver повторно використовує наявні прапорці автентифікації клієнта API-сервера --proxy-client-cert-file та --proxy-client-key-file для представлення своєї ідентичності, яку перевіряє рівноправний сервер (цільовий kube-apiserver). Цільовий API-сервер перевіряє це підключення на основі конфігурації, яку ви вказуєте за допомогою аргументу командного рядка --requestheader-client-ca-file.

  • Для автентифікації сертифікатів цільового сервера вам слід налаштувати пакет сертифікатів центру сертифікації, вказавши аргумент командного рядка --peer-ca-file вихідному API-серверу.

Налаштування для зʼєднання з рівноправним API-сервером

Для встановлення мережевого розташування kube-apiserver, яке рівноправні сервери будуть використовувати для проксіювання запитів, використовуйте аргументи командного рядка --peer-advertise-ip та --peer-advertise-port для kube-apiserver або вказуйте ці поля в конфігураційному файлі API-сервера. Якщо ці прапорці не вказані, рівноправні сервери будуть використовувати значення з аргументів командного рядка --advertise-address або --bind-address для kube-apiserver. Якщо ці прапорці теж не встановлені, використовується типовий інтерфейс хоста.

Peer-aggregated discovery

Коли ви вмикаєте цю функцію, запити на виявлення автоматично вмикаються для надання комплексного документа виявлення (з переліком усіх ресурсів, що обслуговуються будь-яким apiserver у кластері) стандартно.

Якщо ви хочете запросити документ non peer-aggregated discovery, ви можете вказати це, додавши до запиту на виявлення такий заголовок Accept:

application/json;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList;profile=nopeer

Примітка:

Peer-aggregated discovery підтримується тільки для запитів Aggregated Discovery до точки доступу /apis, а не для запитів Unaggregated (Legacy) Discovery.

Проксіювання змішаних версій

Коли ви вмикаєте проксіювання змішаних версій, шар агрегації завантажує спеціальний фільтр, який виконує такі дії:

  • Коли запит ресурсу надходить до API-сервера, який не може обслуговувати цей API (чи то тому, що його версія старіша за появу цього API, чи тому, що API вимкнено на API-сервері), API-сервер намагається надіслати запит до рівноправного API-сервера, який може обслуговувати затребуваний API. Це відбувається шляхом ідентифікації груп API / версій / ресурсів, які локальний сервер не розпізнає, і спроби проксіювати ці запити до рівноправного API-сервера, який може обробити запит.
  • Якщо рівноправний API-сервер не відповідає, то вихідний API-сервер відповідає помилкою 503 ("Service Unavailable").

Як це працює під капотом

Коли API-сервер отримує запит ресурсу, він спочатку перевіряє, які API-сервери можуть обслуговувати затребуваний ресурс. Ця перевірка відбувається за допомогою документа non peer-aggregated discovery.

  • Якщо ресурс є в документі non peer-aggregated discovery, отриманому від API-сервера, що отримав запит (наприклад, GET /api/v1/pods/some-pod), запит обробляється локально.

  • Якщо ресурс у запиті (наприклад, GET /apis/resource.k8s.io/v1beta1/resourceclaims) не знайдено в документі non peer-aggregated discovery, отриманому з API-сервера, який намагається обробити запит (API-сервер обробки), ймовірно, через те, що API resource.k8s.io/v1beta1 був введений в новій версії Kubernetes, а API-сервер обробки працює на старій версії, яка його не підтримує, тоді API-сервер обробки знаходить рівноправні API-сервери, які обслуговують відповідну групу API / версію / ресурс (у цьому випадку resource.k8s.io/v1beta1/resourceclaims) перевіряючи документи non peer-aggregated discovery усіх рівноправних API-серверів. Потім API-сервер обробки проксіює запит до одного з відповідних рівноправних kube-apiserver, які знають про запитуваний ресурс.

  • Якщо для цієї групи API / версії / ресурсу немає відомого рівноправного сервера, API-сервер обробки передає запит до власного ланцюжка обробників, який повинен зрештою повернути відповідь 404 ("Not Found").

  • Якщо API-сервер обробки визначив і вибрав рівноправний API-сервер, але той не відповідає (через проблеми з підключенням до мережі або стан гонитви між отриманням запиту та реєстрацією контролером інформації про рівноправний сервер у панелі управління), API-сервер обробки відповідає помилкою 503 ("Service Unavailable").

3 - Контейнери

Технологія для пакування застосунку разом із його залежностями часу виконання.

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

Слово контейнер є дуже перевантаженим терміном. Щоразу, коли ви використовуєте це слово, перевіряйте, чи ваша аудиторія використовує те саме визначення.

Кожен контейнер, який ви запускаєте, є повторюваним; стандартизація завдяки включеним залежностям означає, що ви отримуєте однакову поведінку, де б ви його не запускали.

Контейнери відокремлюють застосунки від базової інфраструктури хоста. Це полегшує розгортання в різних хмарних або ОС-середовищах.

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

Образи контейнерів

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

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

Середовище виконання контейнерів

Основний компонент, який дозволяє Kubernetes ефективно запускати контейнери. Він відповідає за керування виконанням і життєвим циклом контейнерів у середовищі Kubernetes.

Kubernetes підтримує середовища виконання контейнерів, такі як containerd, CRI-O, та будь-яку іншу реалізацію Kubernetes CRI (інтерфейс виконання контейнерів).

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

Ви також можете використовувати RuntimeClass для запуску різних Podʼів з однаковим середовищем виконання контейнерів, але з різними налаштуваннями.

3.1 - Образи контейнерів

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

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

Ця сторінка надає огляд концепції образів контейнерів.

Примітка:

Якщо ви шукаєте образи контейнерів для випуску Kubernetes (наприклад, v1.37, остання мінорна версія), відвідайте розділ Завантаження Kubernetes.

Назви образів

Образам контейнерів зазвичай дається назва, така як pause, example/mycontainer або kube-apiserver. Образ також може містити імʼя хоста реєстру; наприклад: fictional.registry.example/imagename, а також, можливо, номер порту; наприклад: fictional.registry.example:10443/imagename.

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

Після частини назви образу ви можете додати теґ або дайджест (так само, як під час використання команд на кшталт docker чи podman). Теґи дозволяють ідентифікувати різні версії одного ряду образів. Дайджест — унікальний ідентифікатор конкретної версії образу. Дайджести є хешами, які визначаються вмістом образу, і вони не змінюються. Теґи можна переносити на інші образи, а дайджести залишаються незмінними.

Теґи образів складаються з малих і великих літер, цифр, підкреслень (_), крапок (.) та дефісів (-). Довжина теґу може бути до 128 символів, а сам теґ має відповідати такому шаблону: [a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}. Дізнатись більше та знайти регулярний вираз для перевірки можна в OCI Distribution Specification. Якщо ви не вказуєте теґ, Kubernetes вважає, що ви маєте на увазі теґ latest.

Дайджест образу складається з назви алгоритму хешу (наприклад, sha256) і значення хешу. Наприклад: sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07. Додаткову інформацію про формат дайджесту можна знайти в OCI Image Specification.

Деякі приклади назв образів, які може використовувати Kubernetes:

  • busybox — тільки назва образу, без теґу або дайджесту. Kubernetes використовує публічний реєстр Docker і теґ latest. Те саме, що й docker.io/library/busybox:latest.
  • busybox:1.32.0 — назва образу з теґом. Kubernetes використовує публічний реєстр Docker. Те саме, що й docker.io/library/busybox:1.32.0.
  • registry.k8s.io/pause:latest — назва образу із власним реєстром і теґом latest.
  • registry.k8s.io/pause:3.5 — назва образу із власним реєстром і теґом, відмінним від latest.
  • registry.k8s.io/pause@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 — назва образу з дайджестом.
  • registry.k8s.io/pause:3.5@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 — назва образу з теґом і дайджестом. Для завантаження використовується лише дайджест.

Оновлення образів

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

Політика завантаження образів

imagePullPolicy для контейнера та теґ образу впливають на те, коли kubelet намагається завантажити (отримати) вказаний образ.

Ось список значень, які можна встановити для imagePullPolicy та їхній вплив:

IfNotPresent
образ завантажується лише тоді, коли він ще не присутній локально.
Always
кожного разу, коли kubelet запускає контейнер, kubelet звертається до середовища виконання контейнерів для завантаження образу. Середовище виконання контейнерів звертається до реєстру, розвʼязує теґ або назву образу в дайджест і завантажує будь-які шари, які ще не кешовані локально. Якщо всі шари вже присутні, середовище виконання контейнерів використовує кешований образ без повторного завантаження. Сам kubelet не перевіряє, чи образ кешований локально; він завжди делегує це середовищу виконання контейнерів.
Never
kubelet не намагається отримати образ. Якщо образ вже присутній локально, kubelet намагається запустити контейнер; в іншому випадку запуск закінчується невдачею. Див. попередньо завантажені образи для отримання деталей.

Семантика кешування середовища виконання контейнерів робить ефективним навіть imagePullPolicy: Always, якщо реєстр доступний. Середовище виконання контейнерів може помітити, що шари образів вже існують на вузлі, так що їх не потрібно знову завантажувати.

Примітка:

Ви повинні уникати використання теґу :latest при розгортанні контейнерів в промисловому середовищі, оскільки так важче відстежити, яка версія образу запущена, і складніше виконати відкат.

Замість цього використовуйте значущий теґ, такий як v1.42.0, і/або дайджест.

Щоб переконатися, що Pod завжди використовує ту саму версію образу контейнера, ви можете вказати дайджест образу; замініть <image-name>:<tag> на <image-name>@<digest> (наприклад, image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2).

При використанні теґів образів, якщо реєстр образів змінив код, який представляє теґ вашого образу, ви могли б отримати суміш Podʼів, що запускають старий і новий код. Дайджест образу унікально ідентифікує конкретну версію образу, тому Kubernetes запускає той самий код кожного разу, коли він запускає контейнер із вказаними назвою образу та дайджестом. Зазначення образу за допомогою дайджесту фіксує код, який ви запускаєте, так що зміна в реєстрі не може призвести до такого змішаного використання версій.

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

Стандартна політика завантаження образів

Коли ви (чи контролер) подаєте новий Pod API-серверу, ваш кластер встановлює поле imagePullPolicy при виконанні певних умов:

  • якщо ви не вказуєте поле imagePullPolicy, але вказуєте дайджест образу контейнера, imagePullPolicy автоматично встановлюється в IfNotPresent.
  • якщо ви не вказуєте поле imagePullPolicy, а теґ образу контейнера — :latest, imagePullPolicy автоматично встановлюється в Always;
  • якщо ви не вказуєте ні поле imagePullPolicy, ні теґ образу контейнера, imagePullPolicy автоматично встановлюється в Always;
  • якщо ви не вказуєте поле imagePullPolicy, але вказуєте теґ образу контейнера, який не є :latest, imagePullPolicy автоматично встановлюється в IfNotPresent.

Примітка:

Значення imagePullPolicy контейнера завжди встановлюється при першому створенні обʼєкта і не оновлюється, якщо теґ чи дайджест образу пізніше зміниться.

Наприклад, якщо ви створите Deployment з образом, теґ якого не є :latest, і пізніше оновите образ цього Deployment до теґу :latest, поле imagePullPolicy не зміниться на Always. Вам слід вручну змінити політику завантаження для будь-якого обʼєкта після його початкового створення.

Обовʼязкове завантаження образів

Якщо ви хочете завжди виконувати завантаження, ви можете зробити одне з такого:

  • Встановіть imagePullPolicy контейнера в Always.
  • Не вказуйте imagePullPolicy і використовуйте :latest як теґ образу; Kubernetes встановить політику в Always під час подання Podʼа.
  • Не вказуйте ні imagePullPolicy, ні теґ образу; Kubernetes встановить політику в Always під час подання Podʼа.
  • Увімкніть контролер допуску AlwaysPullImages.

ImagePullBackOff

Коли kubelet починає створювати контейнери для Podʼа за допомогою середовища виконання контейнерів, можливо, контейнер перебуває у стані Waiting з причини ImagePullBackOff.

Статус ImagePullBackOff означає, що контейнер не може запуститися через те, що Kubernetes не може завантажити образ контейнера (з причин, таких як неправильна назва образу або спроба завантаження з приватного реєстру без imagePullSecret). Частина BackOff вказує на те, що Kubernetes буде продовжувати пробувати завантажити образ зі збільшенням інтервалів між спробами.

Kubernetes збільшує інтервал між кожною спробою до тих пір, поки не досягне вбудованого обмеження, яке становить 300 секунд (5 хвилин).

Завантаження образу відповідно до класу середовища виконання

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.29; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the RuntimeClassInImageCriApi feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

У Kubernetes є альфа-підтримка для виконання завантаження образів на основі RuntimeClass Podʼа.

Якщо ви увімкнете функціональну можливість RuntimeClassInImageCriApi, kubelet вказує образи контейнерів кортежем із назви образу та обробника середовища виконання, а не лише назвою чи дайджестом образу. Ваше середовище виконання контейнерів може адаптувати свою поведінку залежно від обраного обробника. Завантаження образів на основі класу середовища виконання буде корисним для контейнерів, що використовують віртуальні машини, таких як контейнери Windows Hyper-V.

Послідовне та паралельне завантаження образів

Типово kubelet завантажує образи послідовно. Іншими словами, kubelet надсилає лише один запит на завантаження образу до служби образів одночасно. Інші запити на завантаження образів мають чекати, поки завершиться обробка поточного запиту.

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

Якщо ви хочете увімкнути паралельне завантаження образів, ви можете встановити поле serializeImagePulls у значення false в конфігурації kubelet. Якщо serializeImagePulls встановлено в false, запити на завантаження образів будуть надсилатись до служби образів негайно, і кілька образів буде завантажено одночасно.

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

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

Максимальна кількість паралельних завантажень образів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.35

Коли serializeImagePulls встановлено в значення false, kubelet стандартно не обмежує максимальну кількість образів, які завантажуються одночасно. Якщо ви хочете обмежити кількість паралельних завантажень образів, ви можете встановити поле maxParallelImagePulls у конфігурації kubelet. Якщо maxParallelImagePulls дорівнює n, тільки n образів можуть завантажуватися одночасно, і будь-яке завантаження образу за межами n буде чекати, поки завершиться принаймні одне завантаження образу, яке вже триває.

Обмеження кількості паралельних завантажень образів допоможе уникнути зайвого використання пропускної здатності мережі або дискових операцій вводу/виводу, коли увімкнено паралельне завантаження образів.

Ви можете встановити maxParallelImagePulls на додатне число, більше або рівне 1. Якщо ви встановите maxParallelImagePulls більше або рівне 2, ви повинні встановити serializeImagePulls в значення false. Kubelet не буде запущено з недійсними налаштуваннями maxParallelImagePulls.

Мультиархітектурні образи з індексами образів

Окрім надання бінарних образів, реєстр контейнерів також може обслуговувати індекс образу контейнера. Індекс образу може посилатися на кілька маніфестів образу для версій контейнера, специфічних для архітектури. Ідея полягає в тому, що ви можете мати імʼя для образу (наприклад: pause, example/mycontainer, kube-apiserver) та дозволити різним системам отримувати правильний бінарний образ для використання на їхній архітектурі машини.

Проєкт Kubernetes зазвичай створює образи контейнерів для своїх випусків із назвами, що містять суфікс -$(ARCH). Для зворотної сумісності генеруйте старіші образи із суфіксами. Наприклад, образ pause буде мультиархітектурним образом із маніфестами для всіх підтримуваних архітектур, а pause-amd64 — зворотно сумісною версією для старіших конфігурацій або файлів YAML із жорстко закодованими назвами образів із суфіксами.

Використання приватних реєстрів

Приватні реєстри можуть вимагати автентифікації для виявлення та/або завантаження образів з них. Облікові дані можна надати кількома способами:

Ці варіанти пояснюються докладніше нижче.

Зазначення imagePullSecrets в Podʼі

Примітка:

Це рекомендований підхід для запуску контейнерів на основі образів з приватних реєстрів.

У Kubernetes підтримується вказівка ключів реєстру образів контейнерів у Podʼі. Усі imagePullSecrets мають бути Secrets, які існують у тому самому Namespace, що й Pod. Ці Secrets мають бути типу kubernetes.io/dockercfg або kubernetes.io/dockerconfigjson.

Налаштування вузлів для автентифікації в приватному реєстрі

Конкретні інструкції щодо встановлення облікових даних залежать від середовища виконання контейнерів та реєстру, який ви вибрали для використання. Вам слід звертатися до документації щодо вашого рішення для отримання найточнішої інформації.

Для прикладу конфігурування приватного реєстру образів контейнерів дивіться завдання Завантаження образу з приватного реєстру. У цьому прикладі використовується приватний реєстр в Docker Hub.

Постачальник облікових даних Kubelet для отримання автентифікованих образів

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

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

Докладніше дивіться Налаштування постачальника облікових даних образу kubelet.

Тлумачення файлу config.json

Тлумачення config.json відрізняється між оригінальною реалізацією Docker та тлумаченням Kubernetes. У Docker ключі auths можуть вказувати тільки кореневі URL-адреси, тоді як Kubernetes дозволяє використовувати URL-адреси із шаблонами glob, а також шляхи, які відповідають префіксу. Єдиним обмеженням є те, що шаблони glob (*) повинні включати крапку (.) для кожного піддомену. Кількість піддоменів, що мають збіг, повинна дорівнювати кількості шаблонів glob (*.), наприклад:

  • *.kubernetes.io не збігатиметься з kubernetes.io, але збігатиметься з abc.kubernetes.io
  • *.*.kubernetes.io не збігатиметься з abc.kubernetes.io, але збігатиметься з abc.def.kubernetes.io
  • prefix.*.io збігатиметься з prefix.kubernetes.io
  • *-good.kubernetes.io збігатиметься з prefix-good.kubernetes.io

Це означає, що файл config.json, подібний до цього, є дійсним:

{
    "auths": {
        "my-registry.example/images": { "auth": "…" },
        "*.my-registry.example/images": { "auth": "…" }
    }
}

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

  • my-registry.example/images
  • my-registry.example/images/my-image
  • my-registry.example/images/another-image
  • sub.my-registry.example/images/my-image

Однак ці назви образів контейнерів не матимуть збігу:

  • a.sub.my-registry.example/images/my-image
  • a.b.sub.my-registry.example/images/my-image

Kubelet виконує операції завантаження образів послідовно для кожного знайденого набору облікових даних. Це означає, що можливі кілька записів у config.json для різних шляхів:

{
    "auths": {
        "my-registry.example/images": {
            "auth": "…"
        },
        "my-registry.example/images/subpath": {
            "auth": "…"
        }
    }
}

Тепер, якщо контейнер вказує образ my-registry.example/images/subpath/my-image, то kubelet спробує завантажити його з обох джерел автентифікації, якщо завантаження з одного з них виявиться невдалим.

Попередньо завантажені образи

Примітка:

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

Типово kubelet намагається отримати кожен образ з вказаного реєстру. Однак якщо властивість imagePullPolicy контейнера встановлена на IfNotPresent або Never, то використовується локальний образ (переважно або виключно, відповідно).

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

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

Подібно до використання постачальника облікових даних kubelet, попередньо завантажені образи також підходять для запуску статичних Podʼів, які залежать від образів, розміщених у приватному реєстрі.

Примітка:

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

Доступ до попередньо завантажених образів може бути авторизований згідно з перевіркою облікових даних для завантаження образів.

Перевірка облікових даних під час завантаження образів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

Якщо увімкнено функціональну можливість KubeletEnsureSecretPulledImages, Kubernetes перевірятиме облікові дані для кожного образу, який потребує їх для завантаження, навіть якщо цей образ вже присутній на вузлі. Ця перевірка гарантує, що образи із запиту Podʼа, які не були успішно завантажені за допомогою наданих облікових даних, повинні бути повторно завантажені з реєстру. Крім того, при повторному завантаженні образів з тими самими обліковими даними, які раніше призвели до успішного завантаження образів, не потрібно повторно завантажувати їх з реєстру, а вони перевіряються локально без доступу до реєстру (за умови, що образ доступний локально). Це контролюється полем imagePullCredentialsVerificationPolicy у конфігурації Kubelet.

Ця конфігурація контролює, коли потрібно перевіряти облікові дані для завантаження образів, якщо образ вже присутній на вузлі:

  • NeverVerify: Імітує поведінку, коли цю функціональну можливість вимкнено. Якщо образ присутній локально, облікові дані для завантаження образу не перевірятимуться.
  • NeverVerifyPreloadedImages: Образи, завантажені за межами kubelet, не перевірятимуться, але всі інші образи будуть перевірятися. Це стандартна поведінка.
  • NeverVerifyAllowListedImages: Образи, завантажені поза kubelet і згадані у списку preloadedImagesVerificationAllowlist, вказаному у конфігурації kubelet, не перевірятимуться.
  • AlwaysVerify: Всі образи будуть перевірятися перед тим, як їх буде використано.

Ця перевірка застосовується до попередньо завантажених образів, образів, завантажених за допомогою Secrets рівня вузла, та образів, завантажених за допомогою Secrets рівня Podʼа.

Примітка:

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

Перше увімкнення KubeletEnsureSecretPulledImages

Коли функція KubeletEnsureSecretPulledImages вмикається вперше, чи то через оновлення kubelet, чи то явним увімкненням цієї функції, і якщо kubelet має доступ до будь-яких образів у цей момент, усі вони вважатимуться попередньо завантаженими. Це відбувається тому, що в цьому випадку kubelet не має записів про образи, що завантажуються. Kubelet зможе почати створювати записи про завантаження образів лише тоді, коли образ завантажується вперше.

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

Зверніть увагу, що видалення теки, що містить записи про завантаження образів, матиме той самий ефект при перезапуску kubelet, зокрема всі образи, які зараз кешовані на вузлах середовищем виконання контейнерів, вважатимуться попередньо завантаженими.

Створення Secret з Docker-конфігом

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

kubectl create secret docker-registry <name> \
  --docker-server=<docker-registry-server> \
  --docker-username=<docker-user> \
  --docker-password=<docker-password> \
  --docker-email=<docker-email>

Якщо у вас вже є файл облікових даних Docker, то замість наведеної вище команди ви можете імпортувати файл облікових даних як Secret Kubernetes. Створення Secret на основі наявних облікових даних Docker пояснює, як це зробити.

Це особливо корисно, якщо ви використовуєте кілька приватних реєстрів контейнерів, оскільки kubectl create secret docker-registry створює Secret, який працює лише з одним приватним реєстром.

Примітка:

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

Посилання на imagePullSecrets у Podʼі

Тепер ви можете створювати Podʼи, які посилаються на цей Secret, додавши розділ imagePullSecrets до визначення Podʼа. Кожен елемент у масиві imagePullSecrets може посилатися тільки на Secret у тому ж просторі імен.

Наприклад:

cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: foo
  namespace: awesomeapps
spec:
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
  imagePullSecrets:
    - name: myregistrykey
EOF

cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF

Це слід робити для кожного Podʼа, який використовує приватний реєстр.

Однак налаштування цього поля можна автоматизувати, вказавши розділ imagePullSecrets у ресурсі ServiceAccount. Дивіться Додавання ImagePullSecrets до службового облікового запису для докладних інструкцій.

Ви можете використовувати це разом із .docker/config.json на рівні вузла. Облікові дані буде обʼєднано.

Сценарії використання

Існує кілька рішень для конфігурації приватних реєстрів. Ось деякі типові сценарії використання та рекомендовані рішення.

  1. Кластер, в якому використовуються лише непропрієтарні (наприклад, відкриті) образи. Немає потреби приховувати образи.
    • Використовуйте відкриті образи з відкритого реєстру.
      • Конфігурація не потрібна.
      • Деякі хмарні провайдери автоматично кешують або дзеркалюють відкриті образи, що підвищує доступність та скорочує час їх отримання.
  2. Кластер, в якому використовуються деякі пропрієтарні образи, які повинні бути прихованими від тих, хто поза компанією, але доступними для всіх користувачів кластера.
    • Використовуйте розміщений приватний реєстр.
      • Можлива ручна конфігурація на вузлах, які повинні мати доступ до приватного реєстру.
    • Або запустіть внутрішній приватний реєстр за вашим фаєрволом з відкритим доступом на читання.
      • Не потрібна конфігурація Kubernetes.
    • Використовуйте розміщений сервіс реєстру образів контейнерів, який контролює доступ до образів.
      • Він працюватиме краще з автомасштабуванням вузлів, ніж ручна конфігурація вузла.
    • Або на кластері, де зміна конфігурації вузла незручна, використовуйте imagePullSecrets.
  3. Кластер із пропрієтарними образами, кілька з яких вимагають строгого контролю доступу.
    • Переконайтеся, що контролер допуску AlwaysPullImages активний. У протилежному випадку всі Podʼи потенційно мають доступ до всіх образів.
    • Перемістіть конфіденційні дані в ресурс Secret, а не додавайте їх в образ.
  4. Багатокористувацький кластер, де кожен орендар потребує власного приватного реєстру.
    • Переконайтеся, що контролер допуску AlwaysPullImages активний. У протилежному випадку всі Podʼи всіх орендарів потенційно мають доступ до всіх образів.
    • Запустіть приватний реєстр з обовʼязковою авторизацією.
    • Створіть облікові дані реєстру для кожного орендаря, збережіть їх у Secret та внесіть Secret у кожний простір імен орендаря.
    • Орендар додає цей Secret до imagePullSecrets кожного простору імен.

Якщо вам потрібен доступ до декількох реєстрів, ви можете створити Secret для кожного реєстру окремо.

Застарілий вбудований постачальник облікових даних kubelet

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

Існувало три вбудовані реалізації інтеграції постачальника облікових даних kubelet: ACR (Azure Container Registry), ECR (Elastic Container Registry) та GCR (Google Container Registry).

Починаючи з версії Kubernetes v1.26 застарілий механізм було видалено, тому вам потрібно або:

  • налаштувати постачальника облікових даних образів kubelet на кожному вузлі; або
  • вказати облікові дані для завантаження образів, використовуючи imagePullSecrets та принаймні один Secret.

Що далі

3.2 - Середовище контейнера

Ця сторінка описує ресурси, доступні контейнерам в середовищі контейнера.

Середовище контейнера

Середовище контейнера Kubernetes надає кілька важливих ресурсів контейнерам:

  • Файлову систему, яка є комбінацією образу та одного чи декількох томів.
  • Інформацію про сам контейнер.
  • Інформацію про інші обʼєкти в кластері.

Інформація про контейнер

hostname контейнера — це назва Podʼа, в якому він працює. Вона доступна через команду hostname або виклик функції gethostname у бібліотеці libc.

Назва та простір імен Podʼа доступні як змінні середовища через downward API.

Змінні середовища, визначені користувачем у визначенні Podʼа, також доступні для контейнера, так само як будь-які статично визначені змінні середовища в образі контейнера.

Інформація про кластер

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

Для сервісу з назвою foo, який експонує набір Podʼів, кожен з яких виконує контейнер із назвою bar, визначаються такі змінні:

FOO_SERVICE_HOST=<хост, на якому працює сервіс>
FOO_SERVICE_PORT=<порт, на якому працює сервіс>

Сервіси мають виділені IP-адреси, які доступні для контейнера через DNS, якщо увімкнено надбудову DNS.

Що далі

3.3 - Клас виконання

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.20

Ця сторінка описує ресурс RuntimeClass та механізм вибору середовища виконання.

RuntimeClass — це функція для вибору конфігурації середовища виконання контейнерів. Конфігурація середовища виконання контейнерів використовується для запуску контейнерів у Podʼі.

Обґрунтування

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

Ви також можете використовувати RuntimeClass для запуску різних Podʼів з однаковим середовищем виконання, але з різними налаштуваннями.

Налаштування

  1. Налаштуйте реалізацію CRI на вузлах (залежить від середовища виконання).
  2. Створіть відповідні ресурси RuntimeClass.

1. Налаштуйте реалізацію CRI на вузлах

Конфігурації, доступні через RuntimeClass, залежать від конкретної реалізації інтерфейсу середовища виконання контейнерів (CRI). Для отримання інструкцій щодо налаштування перегляньте відповідну документацію (нижче) для вашої реалізації CRI.

Примітка:

Стандартно RuntimeClass передбачає однорідну конфігурацію вузла в усьому кластері (що означає, що всі вузли налаштовані однаковим чином щодо середовищ виконання контейнерів). Щоб підтримувати різнорідні конфігурації вузлів, див. Планування нижче.

Кожна конфігурація має відповідний handler, на який посилається RuntimeClass. Handler повинен бути дійсним імʼям DNS-мітки.

2. Створіть відповідні ресурси RuntimeClass

Кожна конфігурація, налаштована на кроці 1, повинна мати асоційований handler, який ідентифікує конфігурацію. Для кожного handler створіть обʼєкт RuntimeClass.

Ресурс RuntimeClass наразі має всього 2 значущі поля: назва RuntimeClass (metadata.name) та handler (handler). Визначення обʼєкта виглядає так:

# RuntimeClass визначений у групі API node.k8s.io
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  # Назва, за якою посилатимуться на RuntimeClass.
  # RuntimeClass — ресурс без простору імен.
  name: myclass
# Назва відповідної конфігурації CRI
handler: myconfiguration

Назва обʼєкта RuntimeClass повинна бути дійсним імʼям DNS-піддомену.

Примітка:

Рекомендується обмежити операції запису RuntimeClass (create/update/patch/delete), щоб вони були доступні тільки адміністраторам кластера. Зазвичай це типове налаштування. Докладніше див. Огляд авторизації.

Використання

Після налаштування RuntimeClass для кластера ви можете вказати runtimeClassName в специфікації Podʼа, щоб використовувати його. Наприклад:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  # ...

Це доручить kubelet використовувати вказаний RuntimeClass для запуску цього Podʼа. Якщо зазначений RuntimeClass не існує або CRI не може виконати відповідний handler, Pod увійде в термінальну фазу Failed. Шукайте відповідну подію для отримання повідомлення про помилку.

Якщо runtimeClassName не вказано, буде використовуватися стандартний RuntimeHandler, що еквівалентно поведінці за вимкненої функції RuntimeClass.

Конфігурація CRI

Докладніше про налаштування CRI див. у Встановлення CRI.

containerd

Обробники середовища виконання налаштовуються через конфігурацію containerd за шляхом /etc/containerd/config.toml. Дійсні обробники налаштовуються в розділі runtimes:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]

Докладніше див. у документації з конфігурації containerd:

CRI-O

Обробники середовища виконання налаштовуються через конфігурацію CRI-O за шляхом /etc/crio/crio.conf. Дійсні обробники налаштовуються в таблиці crio.runtime:

[crio.runtime.runtimes.${HANDLER_NAME}]
  runtime_path = "${PATH_TO_BINARY}"

Докладніше див. у документації з конфігурації CRI-O.

Планування

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.16

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

Щоб гарантувати, що Podʼи потрапляють на вузли, які підтримують конкретний RuntimeClass, цей набір вузлів повинен мати спільні мітки, які потім обираються полем runtimeclass.scheduling.nodeSelector. NodeSelector RuntimeClass обʼєднується з nodeSelector Podʼа під час допуску, фактично беручи перетин множин вузлів, обраних кожним із них. Якщо виникає конфлікт, Pod буде відхилено.

Якщо підтримувані вузли мають taints, що не дають запускатися на вузлі Podʼам з іншим RuntimeClass, ви можете додати tolerations до RuntimeClass. Як і з nodeSelector, tolerations обʼєднуються з tolerations Podʼа під час допуску, фактично беручи обʼєднання множин вузлів, які толерує кожен із них.

Щоб дізнатися більше про налаштування селектора вузла і tolerations, див. Призначення Podʼів вузлам.

Накладні витрати Podʼів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

Ви можете вказати ресурси overhead, що повʼязані із запуском Podʼа. Вказівка накладних витрат дозволяє кластеру (включаючи планувальник) враховувати це при прийнятті рішень про Podʼи та ресурси.

Накладні витрати Podʼа визначаються через поле overhead в RuntimeClass. За допомогою цього поля ви можете вказати накладні витрати запуску Podʼів, що використовують цей RuntimeClass, та забезпечити облік цих накладних витрат в Kubernetes.

Що далі

3.4 - Хуки життєвого циклу контейнера

На цій сторінці описано, як контейнери, керовані kubelet, можуть використовувати фреймворк хуків життєвого циклу контейнера для запуску коду, викликаного подіями під час управління їх життєвим циклом.

Огляд

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

Хуки контейнера

Контейнерам доступні два хуки:

PostStart

Цей хук виконується відразу після створення контейнера. Він працює одночасно з ENTRYPOINT контейнера (головним процесом), що означає, що хук може виконуватися до, під час або після запуску головного процесу.

До обробника не передаються жодні параметри.

Примітка:

Хоча хук працює одночасно з процесом контейнера, він може затримувати оновлення статусу контейнера; контейнер може не переходити в стан Running до завершення роботи хука.

PreStop

Цей хук викликається негайно перед тим, як контейнер буде завершено через запит API або подію управління, таку як невдача проби на живучість/запуску, витіснення, конкуренція за ресурси та інші. Виклик хука PreStop не вдасться, якщо контейнер вже перебуває у стані terminated або completed, і хук повинен завершити виконання до того, як може бути відправлений сигнал TERM для зупинки контейнера. Відлік пільгового періоду припинення Podʼа починається до виконання хука PreStop, тому, незалежно від результату обробника, контейнер врешті-решт завершиться протягом пільгового періоду припинення Podʼа. Жодні параметри не передаються обробнику.

Докладний опис поведінки припинення роботи Podʼів можна знайти в Припинення роботи Podʼа.

StopSignal

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

Детальніший опис поведінки завершення за допомогою власних сигналів зупинки можна знайти в розділі Сигнали зупинки.

Реалізації обробника хуків

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

  • Exec — Виконує конкретну команду, таку як pre-stop.sh, всередині cgroups та namespaces контейнера. Ресурси, спожиті командою, зараховуються на рахунок контейнера.
  • HTTP — Виконує HTTP-запит до конкретного endpoint в контейнері.
  • Sleep — Призупиняє контейнер на вказаний час.

Виконання обробника хука

Коли викликається хук управління життєвим циклом контейнера, система управління Kubernetes виконує обробник відповідно до дії хука: httpGet, tcpSocket (застаріло) та sleep виконуються процесом kubelet, а exec виконується в контейнері.

Виклик обробника хука PostStart ініціюється при створенні контейнера, тобто точка входу контейнера ENTRYPOINT і хук PostStart спрацьовують одночасно. (Це означає, що, як правило, не має сенсу використовувати HTTP-хук для PostStart, оскільки немає гарантії, що процес контейнера буде повністю запущений, коли хук запуститься.) Якщо хук PostStart виконується занадто довго або зависає, це може завадити контейнеру перейти у стан running.

Хуки PreStop не викликаються асинхронно від сигналу зупинки контейнера; хук повинен завершити своє виконання до того, як може бути відправлений сигнал TERM. Якщо хук PreStop зависає під час виконання, фаза Podʼа буде Terminating і залишиться такою, поки Pod не буде вбито після закінчення його terminationGracePeriodSeconds. Цей період припинення роботи застосовується до загального часу, необхідного як для виконання хука PreStop, так і для зупинки контейнера. Якщо, наприклад, terminationGracePeriodSeconds дорівнює 60, і хук займає 55 секунд для завершення, а контейнер зупиняється нормально через 10 секунд після отримання сигналу, то контейнер буде вбитий перш, ніж він зможе завершити роботу, оскільки terminationGracePeriodSeconds менше, ніж загальний час (55+10), необхідний для цих двох подій.

Якщо хук PostStart або PreStop завершується збоєм, контейнер буде вбито.

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

Гарантії доставки хуків

Доставка хука має відбуватися щонайменше один раз, що означає, що хук може бути викликано кілька разів для будь-якої події, такої як для PostStart чи PreStop. Реалізація хука повинна правильно це обробляти.

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

Налагодження обробників хуків

Логи обробника хука не відображаються в подіях Podʼа. Якщо обробник зазнає збою з будь-якої причини, він розсилає подію. Для PostStart це подія FailedPostStartHook, а для PreStop це подія FailedPreStopHook. Щоб самостійно згенерувати подію FailedPostStartHook, змініть файл lifecycle-events.yaml, замінивши команду postStart на "badcommand", і застосуйте його. Ось приклад виводу події, який ви побачите після виконання kubectl describe pod lifecycle-demo:

Events:
  Type     Reason               Age              From               Message
  ----     ------               ----             ----               -------
  Normal   Scheduled            7s               default-scheduler  Successfully assigned default/lifecycle-demo to ip-XXX-XXX-XX-XX.us-east-2...
  Normal   Pulled               6s               kubelet            Successfully pulled image "nginx" in 229.604315ms
  Normal   Pulling              4s (x2 over 6s)  kubelet            Pulling image "nginx"
  Normal   Created              4s (x2 over 5s)  kubelet            Created container lifecycle-demo-container
  Normal   Started              4s (x2 over 5s)  kubelet            Started container lifecycle-demo-container
  Warning  FailedPostStartHook  4s (x2 over 5s)  kubelet            Exec lifecycle hook ([badcommand]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(30229739-9651-4e5a-9a32-a8f1688862db)" failed - error: command 'badcommand' exited with 126: , message: "OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: \"badcommand\": executable file not found in $PATH: unknown\r\n"
  Normal   Killing              4s (x2 over 5s)  kubelet            FailedPostStartHook
  Normal   Pulled               4s               kubelet            Successfully pulled image "nginx" in 215.66395ms
  Warning  BackOff              2s (x2 over 3s)  kubelet            Back-off restarting failed container

Що далі

4 - Робочі навантаження

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

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

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

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

Kubernetes надає кілька вбудованих ресурсів робочого навантаження:

  • Deployment та ReplicaSet (що є заміною застарілого типу ресурсу ReplicationController). Deployment є хорошим вибором для керування робочим навантаженням, яке не зберігає стану, де будь-який Pod у Deployment є взаємозамінним і може бути замінений, якщо це потрібно.
  • StatefulSet дозволяє вам запускати один або кілька повʼязаних Podʼів, які відстежують стан певним чином. Наприклад, якщо ваше робоче навантаження зберігає дані в постійному сховищі, ви можете запустити StatefulSet, який поєднує кожен Pod з PersistentVolume. Ваш код, який працює в Podʼах цього StatefulSet, може реплікувати дані на інші Podʼи того самого StatefulSet, щоб покращити загальну стійкість.
  • DaemonSet визначає Podʼи, які надають можливості, що є локальними для вузлів. Кожного разу, коли ви додаєте вузол до свого кластера, який відповідає специфікації в DaemonSet, панель управління планує Pod для цього DaemonSet на новому вузлі. Кожен Pod в DaemonSet виконує роботу, схожу на роботу системного демона на класичному сервері Unix/POSIX. DaemonSet може бути фундаментальним для роботи вашого кластера, як, наприклад, втулок для роботи мережі кластера, він може допомогти вам керувати вузлом, або надати необовʼязкову поведінку, яка покращує платформу контейнерів, яку ви використовуєте.
  • Job та CronJob надають різні способи визначення завдань, які виконуються до завершення та зупиняються. Ви можете використовувати Job для визначення завдання, яке виконується до завершення, тільки один раз. Ви можете використовувати CronJob для запуску того самого Job кілька разів згідно з розкладом.

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

Розміщення робочих навантажень

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Хоча стандартні ресурси робочого навантаження (наприклад, Deployments та Jobs) керують життєвим циклом Podʼів, ви можете мати складні вимоги до планування, де групи Podʼів мають розглядатися як єдине ціле.

Workload API дозволяє визначати PodGroupTemplates для групування Podʼів та застосування до них розширених політик планування, таких як групове планування. Контролери створюють обʼєкти PodGroup з цих шаблонів під час виконання, а Pods посилаються на свій PodGroup через поле spec.schedulingGroup. Це особливо корисно для пакетної обробки та робочих навантажень машинного навчання, де необхідне розміщення "все або нічого".

Що далі

Окрім читання про кожен вид API для керування робочими навантаженнями, ви можете дізнатися, як виконувати конкретні завдання:

Щоб дізнатись про механізми Kubernetes для відокремлення коду від конфігурації, відвідайте сторінку Конфігурація.

Є дві допоміжні концепції, які пояснюють, як Kubernetes керує Podʼами застосунків:

  • Збір сміття прибирає обʼєкти з вашого кластера після видалення їхнього ресурсу-власника.
  • Контролер time-to-live after finished видаляє Jobs після того, як минув визначений час з моменту їх завершення.

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

4.1 - Podʼи

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

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

Окрім контейнерів застосунків, Pod може містити init-контейнери, які запускаються під час старту Podʼа. Ви також можете впровадити ефемерні контейнери для налагодження запущеного Podʼа.

Що таке Pod?

Примітка:

Вам потрібно встановити середовище виконання контейнерів на кожному вузлі кластера, щоб Podʼи могли там працювати.

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

Pod схожий на набір контейнерів зі спільними просторами імен та спільними томами файлової системи.

Podʼи в кластері Kubernetes використовуються двома основними способами:

  • Podʼи, що запускають один контейнер. Модель «один контейнер на Pod» є найпоширенішим сценарієм використання Kubernetes. У цьому випадку Pod можна розглядати як обгортку навколо одного контейнера; Kubernetes керує Podʼами, а не контейнерами безпосередньо.
  • Podʼи, що запускають кілька контейнерів, які мають працювати разом. Pod може інкапсулювати застосунок, який складається з кількох розміщених разом контейнерів, що тісно повʼязані та мають спільно використовувати ресурси. Ці розміщені разом контейнери утворюють єдину цілісну одиницю.

Групування в одному Podʼі кількох контейнерів, які розміщені разом і керуються спільно, — відносно складний сценарій використання. Використовуйте цей підхід лише в тих конкретних випадках, коли ваші контейнери тісно повʼязані.

Для забезпечення реплікації (з метою підвищення відмовостійкості або розширення потужності) не потрібно запускати кілька контейнерів; якщо вам потрібно кілька реплік, дивіться Керування навантаженням.

Використання Podʼів

Нижче наведено приклад Podʼа, який складається з контейнера, що запускає образ nginx:1.14.2.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

Для створення Podʼа, показаного вище, виконайте таку команду:

kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml

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

Ресурси робочих навантажень для керування Podʼами

Зазвичай вам не потрібно створювати Podʼи напряму, навіть одиничні Podʼи. Натомість створюйте їх за допомогою ресурсів робочих навантажень, таких як Deployment або Job. Якщо ваші Podʼи потребують відстеження стану, розгляньте використання ресурсу StatefulSet.

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

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

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

Робота з Podʼами

Ви навряд чи створюватимете окремі Podʼи напряму в Kubernetes, навіть одиничні Podʼи. Це тому, що Podʼи спроєктовано як відносно ефемерні, одноразові сутності. Коли Pod створено (безпосередньо вами або опосередковано контролером), новий Pod планується до запуску на вузлі вашого кластера. Pod залишається на цьому вузлі, доки не завершить виконання, доки обʼєкт Pod не буде видалено, доки Pod не буде виселено через брак ресурсів або доки вузол не зазнає збою.

Примітка:

Перезапуск контейнера в Podʼі не слід плутати з перезапуском Podʼа. Pod — це не процес, а середовище для запуску контейнерів. Pod існує до тих пір, поки його не видалено.

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

Операційна система Podʼа

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.25

Ви маєте встановити значення поля .spec.os.name як windows або linux, щоб визначити операційну систему, яку потребують контейнери в цьому Podʼі. Наразі це єдині операційні системи, які підтримує Kubernetes. У майбутньому цей список може бути розширено.

kubelet відмовляється запускати Pod, якщо значення .spec.os.name не збігається з операційною системою вузла. Однак у Kubernetes v1.37 значення .spec.os.name не впливає на те, як kube-scheduler обирає вузол для запуску Podʼа. У будь-якому кластері, де вузли працюють під керуванням більш ніж однієї операційної системи, слід правильно встановити мітку kubernetes.io/os на кожному вузлі та визначати Podʼи з nodeSelector на основі мітки операційної системи. Якщо ви не вкажете nodeSelector на основі ОС, kube-scheduler може призначити Pod на вузол, ОС якого не підходить для його контейнерів. Стандарти безпеки Podʼів також використовують поле .spec.os.name, щоб уникнути застосування політик, які не стосуються цієї операційної системи.

Podʼи та контролери

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

Ось кілька прикладів ресурсів робочих навантажень, які керують одним чи більше Podʼами:

Визначення групи планування

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Стандартно Kubernetes планує кожний Pod окремо. Однак деякі тісно повʼязані застосунки потребують одночасного планування групи Podʼів для коректної роботи.

Ви можете повʼязати Pod з PodGroup за допомогою поля групи планування (spec.schedulingGroup). Це повідомляє kube-scheduler, що Pod належить до певної групи, і дає йому змогу застосовувати скоординовані рішення щодо розміщення на рівні групи одразу для всієї групи.

Шаблони Podʼів

Контролери ресурсів робочих навантажень створюють Podʼи із шаблону Podʼа (pod template) та керують цими Podʼами від вашого імені.

PodTemplate — це специфікації для створення Podʼів, які входять до складу ресурсів робочих навантажень, таких як Deployments, Jobs та DaemonSets.

Кожен контролер ресурсу робочого навантаження використовує PodTemplate всередині обʼєкта робочого навантаження для створення фактичних Podʼів. PodTemplate є частиною бажаного стану будь-якого ресурсу робочого навантаження, який ви використовуєте для запуску вашого застосунку.

Коли ви створюєте Pod, ви можете додати змінні середовища до шаблону Podʼа для контейнерів, які запускаються в Podʼі.

Нижче наведено маніфест простого Job із template, який запускає один контейнер. Контейнер в цьому Podʼі виводить повідомлення, а потім призупиняється.

apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    # Це шаблон Podʼа
    spec:
      containers:
      - name: hello
        image: busybox:1.28
        command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
      restartPolicy: OnFailure
    # Кінець шаблону Podʼа

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

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

Кожен ресурс робочого навантаження реалізує власні правила для обробки змін у шаблоні Podʼа. Якщо ви хочете дізнатися більше саме про StatefulSet, прочитайте розділ Стратегія оновлення у навчальному матеріалі «Основи StatefulSet».

На вузлах kubelet безпосередньо не спостерігає за жодними деталями шаблонів Podʼів та оновлень і не керує ними; ці деталі абстраговані. Ця абстракція та розділення відповідальностей спрощує семантику системи та робить можливим розширення поведінки кластера без зміни наявного коду.

Оновлення та заміна Podʼів

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

Kubernetes не забороняє вам керувати Podʼами напряму. Ви можете оновлювати деякі поля запущеного Podʼа на місці. Однак операції оновлення Podʼа, такі як patch та replace, мають деякі обмеження:

  • Більшість метаданих Podʼа є незмінними. Наприклад, ви не можете змінити поля namespace, name, uid або creationTimestamp.
  • Якщо metadata.deletionTimestamp встановлено, новий запис не може бути доданий до списку metadata.finalizers.
  • Оновлення Podʼа не можуть змінювати поля, крім spec.containers[*].image, spec.initContainers[*].image, spec.activeDeadlineSeconds, spec.terminationGracePeriodSeconds, spec.tolerations або spec.schedulingGates. Для spec.tolerations ви можете додавати лише нові записи.
  • Під час оновлення поля spec.activeDeadlineSeconds дозволені два типи оновлень:
    1. присвоєння незаданому полю додатного числа;
    2. зміна значення поля з додатного числа на менше невідʼємне число.

Субресурси Podʼів

Наведені вище правила оновлення застосовуються до звичайних оновлень Podʼів, але інші поля Podʼа можна оновлювати через субресурси.

  • Resize: Субресурс resize дозволяє оновлювати ресурси контейнерів (spec.containers[*].resources). Дивіться Зміна розміру ресурсів контейнера для більш детальної інформації.
  • Ефемерні контейнери: Субресурс ephemeralContainers дозволяє додавати ефемерні контейнери до Podʼа. Дивіться Ефемерні контейнери для більш детальної інформації.
  • Status: Субресурс status дозволяє оновлювати статус Podʼа. Зазвичай його використовують лише kubelet та інші системні контролери.
  • Binding: Субресурс binding дозволяє встановити spec.nodeName Podʼа за допомогою запиту Binding. Зазвичай його використовує лише планувальник.

Покоління Podʼа

  • Поле metadata.generation є унікальним. Воно автоматично встановлюється системою таким чином, що нові Podʼи мають значення metadata.generation, рівне 1, і кожне оновлення змінюваних полів у специфікації Podʼа збільшуватиме значення metadata.generation на 1.
СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.35
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.35. It was first available in the v1.33 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate PodObservedGenerationTracking, Kubernetes ignores it but does not report any error.

  • observedGeneration — це поле, яке фіксується в розділі status обʼєкта Pod. kubelet встановлює status.observedGeneration, щоб повʼязати стан Podʼа з його поточним статусом. Поле status.observedGeneration Podʼа відображатиме metadata.generation Podʼа в момент, коли повідомляється про статус Podʼа.

Примітка:

Полем status.observedGeneration керує kubelet, і зовнішні контролери не повинні його змінювати.

Різні поля статусу можуть бути повʼязані або з metadata.generation поточного циклу синхронізації, або з metadata.generation попереднього циклу синхронізації. Ключова відмінність полягає в тому, чи зміна в spec відображається безпосередньо в status, чи є опосередкованим результатом запущеного процесу.

Безпосередні оновлення статусу

Для полів статусу, у яких безпосередньо відображається виділена специфікація, observedGeneration буде повʼязано з поточним metadata.generation (покоління N).

Ця поведінка застосовується до:

  • Статусу Resize: Статус операції зміни розміру ресурсу.
  • Виділених ресурсів: Ресурси, виділені Podʼу після зміни розміру.
  • Ефемерних контейнерів: Коли додається новий ефемерний контейнер, і він перебуває у стані Waiting.

Опосередковані оновлення статусу

Для полів статусу, які є опосередкованим результатом виконання специфікації, observedGeneration буде повʼязано з metadata.generation попереднього циклу синхронізації (покоління N-1).

Ця поведінка застосовується до:

  • Образу контейнера: ContainerStatus.ImageID показує образ з попереднього покоління, поки новий образ не буде завантажено, а контейнер не буде оновлено.
  • Фактичних ресурсів: Під час незавершеної зміни розміру фактичні ресурси, що використовуються, все ще належать запиту попереднього покоління.
  • Стану контейнера: Під час незавершеної зміни розміру стан контейнера з політикою, що вимагає перезапуску, відображає запит попереднього покоління.
  • activeDeadlineSeconds, terminationGracePeriodSeconds та deletionTimestamp: Вплив цих полів на статус Podʼа є результатом раніше отриманої специфікації.

Спільні ресурси та комунікація

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

Зберігання в Podʼах

Pod може визначати набір спільних томів. Всі контейнери в Podʼі можуть отримати доступ до спільних томів, що дозволяє цим контейнерам спільно використовувати дані. Також томи дають змогу постійним даним у Podʼі зберегтися, якщо один із його контейнерів доведеться перезапустити. Дивіться розділ Зберігання для отримання додаткової інформації про те, як Kubernetes реалізує спільне зберігання та робить його доступним для Podʼів.

Мережеві можливості Podʼа

Кожному Podʼу присвоюється унікальна IP-адреса для кожного сімейства адрес. Кожен контейнер у Podʼі використовує спільний мережевий простір імен, що включає IP-адресу та мережеві порти. Всередині Podʼа (і тільки там) контейнери, що належать до Podʼа, можуть спілкуватися один з одним за допомогою localhost. Коли контейнери в Podʼі спілкуються із сутностями поза межами Podʼа, вони повинні координувати використання спільних мережевих ресурсів (таких як порти). Всередині Podʼа контейнери використовують спільну IP-адресу та простір портів і можуть знаходити один одного через localhost. Контейнери в Podʼі також можуть спілкуватися між собою, використовуючи стандартні засоби міжпроцесної комунікації, такі як семафори SystemV або спільну памʼять POSIX. Контейнери в різних Podʼах мають різні IP-адреси і не можуть спілкуватися за допомогою IPC на рівні ОС без спеціальної конфігурації. Контейнери, які хочуть взаємодіяти з контейнером, що працює в іншому Podʼі, можуть використовувати IP-мережу для спілкування.

Контейнери в межах Podʼа сприймають імʼя хоста системи як імʼя, що збігається з налаштованим name для Podʼа. Більше інформації про це можна знайти в розділі Мережа в кластері.

Налаштування безпеки Podʼів

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

Для базової конфігурації безпеки слід дотримуватися стандарту безпеки Podʼів Baseline та запускати контейнери з правами, відмінними від root. Ви можете налаштувати прості контексти безпеки:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: ["sh", "-c", "sleep 1h"]

Щоб дізнатися про розширені налаштування контексту безпеки, зокрема про можливості (capabilities), профілі seccomp та детальні параметри безпеки, перейдіть до розділу Концепції безпеки.

Запити та обмеження ресурсів

Коли ви визначаєте Pod, ви можете за бажанням вказати, скільки кожного ресурсу потрібно контейнеру. Найчастіше вказують CPU та памʼять (RAM).

Коли ви вказуєте запит ресурсу (request) для контейнерів у Podʼі, kube-scheduler використовує цю інформацію щоб визначити, на якому вузлі розмістити Pod. Коли ви вказуєте обмеження ресурсу (limit) для контейнера, kubelet забезпечує дотримання цих обмежень, щоб запущений контейнер не міг використовувати більше цього ресурсу, ніж встановлене вами обмеження.

Обмеження CPU застосовуються через тротлінг CPU. Коли контейнер наближається до свого обмеження CPU, ядро обмежує його доступ до CPU. Обмеження памʼяті застосовуються ядром через завершення процесів за нестачі памʼяті (OOM kills), коли контейнер перевищує своє обмеження.

Примітка:

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

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

Статичні Podʼи

Статичні Podʼи керуються безпосередньо демоном kubelet на конкретному вузлі, без спостереження за ними з боку API-сервера. У той час як більшістю Podʼів керує панель управління (наприклад, Deployment), для статичних Podʼів kubelet безпосередньо наглядає за кожним статичним Podʼом (та перезапускає його, якщо він зазнає збою).

Статичні Podʼи завжди привʼязані до одного kubelet на конкретному вузлі. Основне призначення статичних Podʼів — запуск самостійно розміщеної панелі управління: іншими словами, використання kubelet для нагляду за окремими компонентами панелі управління.

За докладнішою інформацією звертайтесь до розділу Статичні Podʼи.

Podʼи з кількома контейнерами

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

Podʼи в кластері Kubernetes використовуються двома основними способами:

  • Podʼи, що запускають один контейнер. Модель «один контейнер на Pod» є найпоширенішим сценарієм використання Kubernetes. У цьому випадку Pod можна розглядати як обгортку навколо одного контейнера; Kubernetes керує Podʼами, а не контейнерами безпосередньо.
  • Podʼи, що запускають кілька контейнерів, які мають працювати разом. Pod може інкапсулювати застосунок, який складається з кількох розміщених разом контейнерів, що тісно повʼязані та мають спільно використовувати ресурси. Ці розміщені разом контейнери утворюють єдину цілісну одиницю сервісу — наприклад, один контейнер надає публічний доступ до даних, збережених у спільному томі, тоді як окремий sidecar-контейнер оновлює ці файли. Pod обгортає ці контейнери, ресурси зберігання та ефемерну мережеву ідентичність в єдину одиницю.

Наприклад, у вас може бути контейнер, який виконує функції вебсервера для файлів у спільному томі, та окремий sidecar-контейнер, який оновлює ці файли з віддаленого джерела, як показано на цій діаграмі:

Діаграма створення Podʼа

Деякі Podʼи мають init-контейнери та контейнери застосунку. Стандартно init-контейнери запускаються та завершують роботу до запуску контейнерів застосунку.

У вас також можуть бути sidecar-контейнери, які надають допоміжні сервіси основному Podʼу застосунку (наприклад, service mesh).

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.33
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.33. It was first available in the v1.28 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate SidecarContainers, Kubernetes ignores it but does not report any error.

Стандартно увімкнена функціональна можливість SidecarContainers дозволяє вказати restartPolicy: Always для init-контейнерів. Встановлення політики перезапуску Always гарантує, що контейнери, для яких ви її встановили, вважатимуться sidecar-контейнерами, що працюють протягом усього життєвого циклу Podʼа. Контейнери, які ви явно визначаєте як sidecar-контейнери, запускаються перед основним Podʼом застосунку та працюють, доки Pod не завершить роботу.

Проби контейнерів

Проба — це діагностика, яку kubelet періодично виконує для контейнера. Для виконання діагностики kubelet може використовувати різні дії:

  • ExecAction (виконується за допомогою середовища виконання контейнерів)
  • TCPSocketAction (перевіряється безпосередньо kubelet)
  • HTTPGetAction (перевіряється безпосередньо kubelet)

Ви можете дізнатися більше про проби в документації про життєвий цикл Podʼа.

Що далі

  • Дізнайтесь про життєвий цикл Podʼа.
  • Дізнайтесь про PodDisruptionBudget та як ви можете використовувати його для керування доступністю застосунку під час порушень.
  • Pod є ресурсом найвищого рівня в REST API Kubernetes. Визначення обʼєкта Pod детально описує цей обʼєкт.
  • Допис в блозі The Distributed System Toolkit: Patterns for Composite Containers пояснює типові конфігурації Podʼів з більш ніж одним контейнером.
  • Дізнайтесь про Обмеження топології розміщення Podʼів
  • Прочитайте про Розширену конфігурацію Podʼів, щоб дізнатися про цю тему докладніше. На цій сторінці висвітлюються аспекти конфігурації Podʼів, що виходять за межі основних положень, зокрема:
    • PriorityClasses
    • RuntimeClasses
    • розширені способи налаштування планування: того, як Kubernetes вирішує, на якому вузлі має працювати Pod.

Для розуміння контексту того, чому Kubernetes обгортає загальний API Pod іншими ресурсами (такими як StatefulSets або Deployments), ви можете ознайомитися з попередніми розробками, зокрема:

4.1.1 - Життєвий цикл Podʼа

Ця сторінка описує життєвий цикл Podʼа. Podʼи проходять визначений життєвий цикл, починаючи з фази Pending, переходячи до Running, якщо принаймні один з основних контейнерів успішно запускається, а потім до фази Succeeded або Failed, залежно від того, чи завершився будь-який контейнер у Podʼі зі збоєм.

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

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

Час життя Podʼа

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

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

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

Ви можете використати готовність Podʼів до планування, щоб затримати планування Podʼа, доки не буде видалено всі його шлюзи планування (scheduling gates). Наприклад, ви можете визначити набір Podʼів, але запустити планування лише після того, як всі Podʼи будуть створені.

Podʼи та відновлення після збоїв

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

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

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

У Kubernetes використовується абстракція вищого рівня, яка називається контролер, яка виконує роботу з керування відносно одноразовими екземплярами Podʼів.

Конкретний Pod (визначений UID) ніколи не «переплановується» на інший вузол; натомість цей Pod може бути замінений новим, майже ідентичним Podʼом. Якщо ви створюєте Pod-замінник, він може навіть мати ту саму назву (як у .metadata.name), що й старий Pod, але замінник матиме інший .metadata.uid, ніж старий Pod.

Kubernetes не гарантує, що заміну наявного Podʼа буде заплановано на той самий вузол, де був старий Pod, який замінюється.

Повʼязаний час життя

Коли говорять, що щось має такий самий час життя, як і Pod, наприклад том, це означає, що ця річ існує стільки часу, скільки існує цей конкретний Pod (саме з цим UID). Якщо цей Pod буде видалений з будь-якої причини, і навіть якщо буде створений ідентичний замінник, повʼязана річ (у цьому прикладі — том) також буде знищена і створена знову.

Багатоконтейнерний Pod, який містить sidecar-контейнер для завантаження файлів і вебсервер. Pod використовує ефемерний том emptyDir як спільне сховище для контейнерів.

Малюнок 1.

Багатоконтейнерний Pod, який містить sidecar-контейнер для завантаження файлів і вебсервер. Pod використовує ефемерний том emptyDir як спільне сховище для контейнерів.

Фаза Podʼа

Поле status Podʼа — це обʼєкт PodStatus, який має поле phase.

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

Кількість і значення фаз Podʼа суворо контролюються. Окрім того, що задокументовано тут, не слід нічого припускати про Podʼи з певним значенням phase.

Ось можливі значення для phase:

ЗначенняОпис
PendingPod прийнято кластером Kubernetes, але один чи кілька контейнерів ще не налаштовано й не підготовлено до запуску. Це включає час, який Pod витрачає на очікування планування, а також час, який витрачається на завантаження образів контейнерів з мережі.
RunningPod привʼязаний до вузла, і всі контейнери створені. Принаймні один контейнер все ще працює або перебуває у процесі запуску чи перезапуску.
SucceededВсі контейнери в Podʼі завершили роботу успішно і не будуть перезапущені.
FailedВсі контейнери в Podʼі завершили роботу, і принаймні один контейнер завершився зі збоєм. Іншими словами, контейнер вийшов зі статусом, відмінним від нуля, або його роботу завершила система, і контейнер не налаштований на автоматичний перезапуск.
UnknownЗ якоїсь причини не вдалося отримати стан Podʼа. Ця фаза, як правило, виникає через помилку у звʼязку з вузлом, де повинен виконуватися Pod.

Примітка:

Якщо Podʼу раз у раз не вдається запуститися, у полі Status деяких команд kubectl може зʼявитися CrashLoopBackOff. Аналогічно, під час видалення Podʼа у полі Status деяких команд kubectl може зʼявитися Terminating.

Не плутайте Status, поле відображення kubectl, призначене для зручності користувача, з phase Podʼа. Фаза Podʼа є явною частиною моделі даних Kubernetes і API Pod.

  NAMESPACE               NAME               READY   STATUS             RESTARTS   AGE
  alessandras-namespace   alessandras-pod    0/1     CrashLoopBackOff   200        2d9h

Pod отримує час на коректне завершення, який стандартно становить 30 секунд. Ви можете використовувати прапорець --force для примусового завершення роботи Podʼа.

Починаючи з Kubernetes 1.27, kubelet переводить видалені Podʼи в термінальну фазу (Failed або Succeeded залежно від статусів завершення роботи контейнерів Podʼа) перед їх видаленням з API-сервера, за винятком двох випадків:

Якщо вузол виходить з ладу або втрачає звʼязок з рештою кластера, Kubernetes застосовує політику встановлення phase всіх Podʼів на втраченому вузлі у Failed.

Стани контейнера

Окрім фази Podʼа загалом, Kubernetes відстежує стан кожного контейнера всередині Podʼа. Ви можете використовувати хуки життєвого циклу контейнера, щоб запускати події на певних етапах життєвого циклу контейнера.

Як тільки планувальник призначає Pod вузлу, kubelet починає створювати контейнери для цього Podʼа, використовуючи середовище виконання контейнерів. Існує три можливі стани контейнера: Waiting (Очікування), Running (Виконання) та Terminated (Завершено).

Щоб перевірити стан контейнерів Podʼа, ви можете використовувати kubectl describe pod <name-of-pod>. Вивід показує стан для кожного контейнера в межах цього Podʼа.

Кожен стан має конкретне значення:

Waiting

Якщо контейнер не перебуває ні в стані Running, ні в стані Terminated, то він перебуває в стані Waiting. Контейнер у стані Waiting усе ще виконує операції, необхідні для завершення запуску: наприклад, завантажує образ контейнера з реєстру образів контейнерів або застосовує дані Secret. Коли ви використовуєте kubectl для опитування Podʼа із контейнером, який перебуває в стані Waiting, ви також бачите поле Reason, яке стисло пояснює, чому контейнер перебуває в цьому стані.

Running

Статус Running вказує на те, що виконання контейнера відбувається без проблем. Якщо було налаштовано хук postStart, він уже виконався і завершився. Коли ви використовуєте kubectl для опитування Podʼа із контейнером, який перебуває в стані Running, ви також бачите інформацію про те, коли контейнер перейшов у стан Running.

Terminated

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

Якщо у контейнера є налаштований хук preStop, цей хук запускається перед тим, як контейнер перейде в стан Terminated.

Як Podʼи вирішують проблеми з контейнерами

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

  1. Початковий збій: Kubernetes намагається негайно перезапустити контейнер на основі restartPolicy Podʼа.
  2. Повторні збої: Після початкового збою Kubernetes застосовує експоненційну затримку для наступних перезапусків, описану в restartPolicy. Це запобігає перевантаженню системи швидкими повторними спробами перезапуску.
  3. Стан CrashLoopBackOff: Це означає, що для певного контейнера зараз діє механізм затримки, бо цей контейнер перебуває в циклі збоїв: він раз у раз зазнає збою та перезапускається.
  4. Скидання затримки: Якщо контейнер успішно працює протягом певного часу (наприклад, 10 хвилин), Kubernetes скидає затримку, розглядаючи будь-який новий збій як перший.

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

Іншими словами, коли контейнер потрапляє в цикл збоїв, Kubernetes застосовує експоненційну затримку, про яку було згадано в політиці перезапуску контейнера. Цей механізм не дає несправному контейнеру перевантажувати систему безперервними невдалими спробами запуску.

CrashLoopBackOff може бути спричинений такими проблемами:

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

Щоб розібратися у причинах проблеми CrashLoopBackOff, користувач може:

  1. Перевірити логи: Використовуйте kubectl logs <name-of-pod>, щоб перевірити логи контейнера. Часто це найпряміший спосіб діагностики проблеми, що спричиняє збої.
  2. Перевірити події: Використовуйте kubectl describe pod <name-of-pod> для перегляду подій для Podʼа, які можуть надати підказки про проблеми конфігурації або ресурсів.
  3. Перевірити конфігурацію: Переконайтеся, що конфігурація Podʼа, включаючи змінні середовища та змонтовані томи, є правильною і що всі необхідні зовнішні ресурси доступні.
  4. Перевірити обмеження ресурсів: Переконайтеся, що контейнеру виділено достатньо CPU та памʼяті. Іноді збільшення ресурсів у визначенні Podʼа може вирішити проблему.
  5. Налагодити застосунок: У коді застосунку можуть бути помилки або неправильні налаштування. Запуск цього образу контейнера локально або в середовищі розробки може допомогти діагностувати проблеми, специфічні для застосунку.

Перезапуск контейнера

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

Перезапуск контейнера та відмовостійкість

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

Політика перезапуску контейнера на рівні Podʼа

spec Podʼа має поле restartPolicy із можливими значеннями Always, OnFailure та Never. Стандартне значення — Always.

restartPolicy для Podʼа застосовується до контейнерів застосунків в Podʼі та до звичайних init-контейнерів. Sidecar-контейнери не враховують поле restartPolicy на рівні Podʼа: у Kubernetes sidecar визначається як запис всередині initContainers, у якого restartPolicy на рівні контейнера встановлено в Always. Для init-контейнерів, які завершують роботу з помилкою, kubelet виконує їх перезапуск, якщо restartPolicy на рівні Podʼа — OnFailure або Always:

  • Always: автоматично перезапускає контейнер після будь-якого завершення.
  • OnFailure: перезапускає контейнер тільки після його завершення з помилкою (код виходу відмінний від нуля).
  • Never: ніколи автоматично не перезапускає контейнер, що завершив роботу.
Порівняння поведінки перезапуску

Наступна таблиця показує, як контейнери поводяться за різних політик перезапуску та кодів виходу:

Код виходуrestartPolicy: AlwaysrestartPolicy: OnFailurerestartPolicy: NeverSidecar-контейнери
0 (Успіх)ПерезапускаєтьсяНе перезапускаєтьсяНе перезапускаєтьсяЗавжди перезапускається
Ненульовий (Збій)ПерезапускаєтьсяПерезапускаєтьсяНе перезапускаєтьсяЗавжди перезапускається

Примітка:

Поведінка перезапуску особливо важлива під час вибору між Deployments та Jobs:

  • Deployments зазвичай використовують restartPolicy: Always (єдине дозволене значення) для безперервної роботи застосунків.
  • Jobs зазвичай використовують restartPolicy: OnFailure або restartPolicy: Never для належного оброблення завдань пакетної обробки.
  • Sidecar-контейнери є init-контейнерами, які завжди перезапускаються незалежно від restartPolicy Podʼа, оскільки вони мають власну політику перезапуску на рівні контейнера restartPolicy: Always.
Приклади сценаріїв

Ось конкретні приклади, що демонструють різну поведінку перезапуску:

Приклад 1: Вебсервер з restartPolicy: Always (типово для Deployments)

apiVersion: v1
kind: Pod
metadata:
  name: web-server
spec:
  restartPolicy: Always  # Контейнер перезапускається незалежно від коду виходу
  containers:
  - name: nginx
    image: nginx:1.14.2
    # Якщо цей контейнер аварійно завершить роботу або завершиться з будь-якої причини, його буде перезапущено

Приклад 2: Завдання пакетної обробки з restartPolicy: OnFailure

apiVersion: batch/v1
kind: Job
metadata:
  name: data-processor
spec:
  template:
    spec:
      restartPolicy: OnFailure  # Перезапускається лише за ненульового коду виходу
      containers:
      - name: processor
        image: busybox:1.28
        command: ['sh', '-c', 'echo "Processing data..."; exit 0']
        # Код виходу 0: Job успішно завершено, перезапуск не відбувається
        # Код виходу 1+: Контейнер перезапускається для повторної спроби завдання

Приклад 3: Одноразове завдання з restartPolicy: Never

apiVersion: v1
kind: Pod
metadata:
  name: migration-task
spec:
  restartPolicy: Never  # Ніколи не перезапускається, незалежно від коду виходу
  containers:
  - name: migrate
    image: busybox:1.28
    command: ['sh', '-c', 'echo "Running migration..."; exit 1']
    # Навіть із кодом виходу 1 (збій) контейнер не буде перезапущено
    # Pod залишиться в стані Failed
Sidecar-контейнери та політики перезапуску

Sidecar-контейнери мають особливу поведінку перезапуску, яка відрізняється від звичайних контейнерів застосунків:

  • Sidecar-контейнери ігнорують restartPolicy на рівні Podʼа: Вони використовують власне поле restartPolicy на рівні контейнера, яке завжди встановлено в Always
  • Незалежний життєвий цикл: Sidecar-контейнери можуть перезапускатися незалежно від основного контейнера застосунку
  • Постійна робота: Sidecar-контейнери продовжують працювати протягом усього часу життя Podʼа, надаючи допоміжні сервіси

Приклад: Pod із sidecar-контейнером

apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecar
spec:
  restartPolicy: OnFailure  # Застосовується лише до основного контейнера
  initContainers:
  - name: logging-sidecar    # Це sidecar-контейнер
    image: fluent/fluent-bit:1.8
    restartPolicy: Always    # Sidecar завжди перезапускається незалежно від коду виходу
    # Надає сервіси логування протягом усього часу життя Podʼа
  containers:
  - name: main-app          # Дотримується політики перезапуску на рівні Podʼа
    image: nginx:1.14.2
    # Перезапускається лише у разі помилки (ненульовий код виходу) через політику OnFailure Podʼа

Примітка:

Хоча основний контейнер застосунку дотримується політики restartPolicy: OnFailure на рівні Podʼа, sidecar-контейнер буде перезапущений незалежно від його коду виходу, оскільки sidecar-контейнери завжди мають restartPolicy: Always на рівні контейнера.

Коли kubelet обробляє перезапуск контейнера згідно з налаштованою політикою перезапуску, це стосується лише перезапусків, які призводять до заміни контейнерів всередині того ж Podʼа та на тому ж вузлі. Після завершення контейнерів у Podʼі kubelet перезапускає їх із затримкою, що зростає експоненційно (10 с, 20 с, 40 с, …), і обмеженою пʼятьма хвилинами (300 секунд). Якщо контейнер виконується протягом 10 хвилин без проблем, kubelet скидає таймер затримки перезапуску для цього контейнера. У розділі Sidecar-контейнери та життєвий цикл Podʼа пояснюється поведінка init containers, коли для них вказано поле restartPolicy.

Політика та правила перезапуску окремих контейнерів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

Якщо у вашому кластері увімкнено функціональну можливість ContainerRestartRules, ви можете вказати restartPolicy та restartPolicyRules для окремих контейнерів, щоб перевизначити політику перезапуску Podʼа. Політика перезапуску контейнера та правила застосовуються до контейнерів застосунків в Podʼі та до звичайних init-контейнерів.

Нативний для Kubernetes sidecar-контейнер має restartPolicy на рівні контейнера, встановлену в Always.

Перезапуски контейнерів відбуватимуться з такою ж експоненційною затримкою, як і за політики перезапуску Podʼа, описаної вище. Підтримувані політики перезапуску контейнерів:

  • Always: автоматично перезапускає контейнер після будь-якого завершення.
  • OnFailure: перезапускає контейнер тільки після його завершення з помилкою (код виходу відмінний від нуля).
  • Never: ніколи автоматично не перезапускає контейнер, що завершив роботу.

Крім того, для окремих контейнерів можна вказати restartPolicyRules. Якщо вказано поле restartPolicyRules, то також повинно бути вказано restartPolicy контейнера. restartPolicyRules визначають список правил, які застосовуються під час завершення контейнера. Кожне правило складатиметься з умови та дії. Підтримувана умова — exitCodes, яка порівнює код виходу контейнера зі списком заданих значень. Підтримувана дія — Restart, що означає, що контейнер буде перезапущено. Правила будуть оцінюватися по порядку. За першого збігу буде застосовано дію. Якщо жодна з умов правил не збіглася, Kubernetes повернеться до налаштованої restartPolicy контейнера.

Наприклад, Pod з політикою перезапуску OnFailure, який має контейнер try-once. Це дозволяє Podʼу перезапускати лише певні контейнери:

apiVersion: v1
kind: Pod
metadata:
  name: on-failure-pod
spec:
  restartPolicy: OnFailure
  containers:
  - name: try-once-container    # Цей контейнер буде запущено лише один раз, оскільки політика перезапуску — Never.
    image: registry.k8s.io/busybox:1.27.2
    command: ['sh', '-c', 'echo "Only running once" && sleep 10 && exit 1']
    restartPolicy: Never
  - name: on-failure-container  # Цей контейнер буде перезапущено у разі збою.
    image: registry.k8s.io/busybox:1.27.2
    command: ['sh', '-c', 'echo "Keep restarting" && sleep 1800 && exit 1']

Pod з політикою перезапуску Always, який має init-контейнер, що виконується лише один раз. Якщо init-контейнер зазнає збою, Pod зазнає збою. Це дозволяє Podʼу завершитися зі збоєм, якщо ініціалізація не вдалася, але продовжувати працювати після успішної ініціалізації:

apiVersion: v1
kind: Pod
metadata:
  name: fail-pod-if-init-fails
spec:
  restartPolicy: Always
  initContainers:
  - name: init-once      # Цей init-контейнер спробує запуститися лише один раз. Якщо він зазнає збою, Pod зазнає збою.
    image: registry.k8s.io/busybox:1.27.2
    command: ['sh', '-c', 'echo "Failing initialization" && sleep 10 && exit 1']
    restartPolicy: Never
  containers:
  - name: main-container # Цей контейнер завжди перезапускатиметься після успішної ініціалізації.
    image: registry.k8s.io/busybox:1.27.2
    command: ['sh', '-c', 'sleep 1800 && exit 0']

Pod з політикою перезапуску Never, який має контейнер, що ігнорує одні коди виходу та перезапускається на конкретних кодах виходу. Це корисно для розрізнення між помилками, після яких контейнер можна перезапустити, і тими, які не дозволяють зробити перезапуск:

apiVersion: v1
kind: Pod
metadata:
  name: restart-on-exit-codes
spec:
  restartPolicy: Never
  containers:
  - name: restart-on-exit-codes
    image: registry.k8s.io/busybox:1.27.2
    command: ['sh', '-c', 'sleep 60 && exit 0']
    restartPolicy: Never     # Політика перезапуску контейнера повинна бути вказана, якщо вказані правила
    restartPolicyRules:      # Перезапускати контейнер, лише якщо він завершується з кодом 42
    - action: Restart
      exitCodes:
        operator: In
        values: [42]

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

Перезапуск усіх контейнерів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.36; (стандартно увімкнено)

Якщо у вашому кластері увімкнено функціональну можливість RestartAllContainersOnContainerExits, ви можете вказати RestartAllContainers як дію в restartPolicyRules на рівні контейнера. Коли завершення контейнера відповідає правилу з цією дією, весь Pod завершується і перезапускається на місці.

Такий перезапуск «на місці» є ефективнішим способом скидання стану Podʼа порівняно з повним видаленням і повторним створенням. Це особливо цінно для робочих навантажень, де перепланування є дорогим, наприклад, для пакетних завдань або завдань навчання AI/ML.

Як працюють перезапуски Podʼів на місці

Коли спрацьовує дія RestartAllContainers, kubelet виконує такі кроки:

  1. Швидке завершення: Усі запущені контейнери в Podʼі завершуються. Налаштований параметр terminationGracePeriodSeconds не враховується, а налаштовані хуки preStop не виконуються. Це забезпечує швидке вимкнення.
  2. Збереження ресурсів Podʼа: Основні ресурси Podʼа зберігаються:
    • UID Podʼа, IP-адреса та мережевий простір імен
    • Пісочниця Podʼа та будь-які підключені пристрої
    • Усі томи, включаючи томи emptyDir та змонтовані томи
  3. Оновлення статусу Podʼа: Статус Podʼа оновлюється: стан PodRestartInPlace встановлюється в True. Це робить процес перезапуску спостережуваним.
  4. Повна послідовність перезапуску: Після завершення роботи всіх контейнерів стан PodRestartInPlace встановлюється в False, і Pod починає стандартний процес запуску:
    • Init-контейнери запускаються повторно по черзі.
    • Запускаються sidecar-контейнери та звичайні контейнери.

Ключовим аспектом цієї можливості є те, що всі контейнери перезапускаються, включаючи ті, які раніше успішно завершилися або зазнали збою. Дія RestartAllContainers перевизначає будь-яку налаштовану restartPolicy на рівні контейнера або Podʼа.

Цей механізм корисний у сценаріях, коли всім контейнерам потрібно почати з чистого аркуша, наприклад:

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

Розглянемо робоче навантаження, де sidecar-спостерігач відповідає за перезапуск основного застосунку з відомого справного стану, якщо той натрапляє на помилку. Спостерігач може завершитися з певним кодом, щоб ініціювати повний перезапуск робочого Podʼа на місці.

apiVersion: v1
kind: Pod
metadata:
  name: ml-worker
spec:
  restartPolicy: Never # The pod itself should not restart unless explicitly told to.
  initContainers:
  - name: setup-environment
    image: registry.k8s.io/busybox:1.27.2
    command: ['sh', '-c', 'echo "Setting up environment"']
    # This init container runs once to prepare the environment.
    # It will run again after a RestartAllContainers action.
  - name: watcher-sidecar
    image: registry.k8s.io/busybox:1.27.2
    # In a real-world scenario, this would be a dedicated watcher image.
    # This command simulates the watcher exiting with a special code.
    command: ['sh', '-c', 'sleep 60; exit 88']
    restartPolicy: Always
    restartPolicyRules:
    - action: RestartAllContainers
      exitCodes:
        # Exit code 88 triggers a full pod restart.
        operator: In
        values: [88]
  containers:
  - name: main-application
    image: registry.k8s.io/busybox:1.27.2
    command: ['sh', '-c', 'echo "Application is running"; sleep 3600']

У цьому прикладі:

  • Загальна політика перезапуску Podʼа — Never.
  • watcher-sidecar виконує команду, а потім завершується з кодом 88.
  • Код завершення відповідає правилу, що запускає дію RestartAllContainers.
  • Потім весь Pod, включаючи init-контейнер setup-environment і контейнер main-application, перезапускається на місці. Pod зберігає свій UID, пісочницю, IP-адресу та томи.

Зменшена затримка перезапуску контейнера

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.33; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the ReduceDefaultCrashLoopBackOffDecay feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

З увімкненою альфа-функціональною можливістю ReduceDefaultCrashLoopBackOffDecay затримка повторних спроб запуску контейнера у вашому кластері починатиметься з 1 с (замість 10 с) і експоненційно зростатиме у 2 рази з кожним перезапуском до максимальної затримки 60 с (замість 300 с, тобто 5 хвилин).

Якщо ви використовуєте цю можливість з альфа-можливістю KubeletCrashLoopBackOffMax (описано нижче), окремі вузли можуть мати різні значення максимальної затримки.

Налаштовувана затримка перезапуску контейнера

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

З увімкненою функціональною можливістю KubeletCrashLoopBackOffMax ви можете змінити максимальну затримку між повторними спробами запуску контейнера, яка стандартно становить 300 секунд (5 хвилин). Ця конфігурація встановлюється для кожного вузла за допомогою конфігурації kubelet. У вашій конфігурації kubelet у полі crashLoopBackOff встановіть значення поля maxContainerRestartPeriod між "1s" і "300s". Як описано вище у Політиці перезапуску контейнерів, затримки на цьому вузлі все одно починатимуться з 10 секунд і збільшуватимуться експоненційно у 2 рази з кожним перезапуском, але тепер вони будуть обмежені вашим налаштованим максимумом. Якщо значення maxContainerRestartPeriod, яке ви налаштували, менше початкового стандартного значення у 10 секунд, початкову затримку буде встановлено на налаштоване максимальне значення.

Дивіться такі приклади налаштування kubelet:

# затримки перезапуску контейнерів починаються з 10 секунд і збільшуються
# у 2 рази з кожним перезапуском, максимум до 100 с
kind: KubeletConfiguration
crashLoopBackOff:
    maxContainerRestartPeriod: "100s"
# затримки між перезапусками контейнера завжди будуть дорівнювати 2 с
kind: KubeletConfiguration
crashLoopBackOff:
    maxContainerRestartPeriod: "2s"

Якщо ви використовуєте цю можливість разом з альфа-можливістю ReduceDefaultCrashLoopBackOffDecay (описано вище), стандартні значення кластера для початкової та максимальної затримки будуть уже не 10 і 300 секунд, а 1 і 60 секунд. Конфігурація для кожного вузла має пріоритет над стандартними значеннями, встановленими ReduceDefaultCrashLoopBackOffDecay, навіть якщо це призведе до того, що вузол матиме довшу максимальну затримку, ніж інші вузли у кластері.

Стани Podʼа

Pod має PodStatus, який містить масив PodConditions, через які Pod пройшов або не пройшов. Kubelet керує такими PodConditions:

  • PodScheduled: Pod заплановано на вузол.
  • PodReadyToStartContainers: пісочницю Podʼа успішно створено, мережу налаштовано, томи зберігання змонтовано, а будь-які динамічні ресурси (якщо їх запитано) виділено.
  • ContainersReady: всі контейнери в Podʼі готові.
  • Initialized: всі init-контейнери успішно завершили виконання.
  • Ready: Pod може обслуговувати запити і його слід додати до пулів балансування навантаження всіх відповідних Services.
  • DisruptionTarget: Pod скоро буде завершено через порушення (наприклад, витіснення, виселення або збір сміття).
  • PodResizePending: було запитано зміну розміру Podʼа, але її неможливо застосувати. Дивіться Статус зміни розміру Podʼа.
  • PodResizeInProgress: Pod перебуває в процесі зміни розміру. Дивіться Статус зміни розміру Podʼа.
Назва поляОпис
typeНазва цього стану Podʼа.
statusВказує, чи застосовується цей стан, з можливими значеннями "True", "False" або "Unknown".
lastProbeTimeМітка часу останньої перевірки стану Podʼа.
lastTransitionTimeМітка часу останнього переходу Podʼа з одного статусу в інший.
reasonМашиночитаний текст у форматі UpperCamelCase, який вказує причину останнього переходу стану.
messageЗрозуміле людині повідомлення з подробицями щодо останнього переходу статусу.

Готовність Podʼа

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.14

Ваш застосунок може внести додатковий зворотний звʼязок або сигнали в PodStatus: готовність Podʼа. Щоб використовувати це, встановіть readinessGates в spec Podʼа, щоб вказати список додаткових станів, які kubelet оцінює для визначення готовності Podʼа.

Шлюзи готовності (readiness gates) визначаються поточним станом полів status.condition Podʼа. Якщо Kubernetes не може знайти такий стан у полі status.conditions Podʼа, статус цього стану стандартно вважається "False".

Наприклад:

kind: Pod
...
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
status:
  conditions:
    - type: Ready                              # вбудований стан Podʼа
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"        # додатковий стан Podʼа
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
...

Стани Podʼа, які ви додаєте, повинні мати назви, що відповідають формату ключа міток Kubernetes.

Статус для готовності Podʼа

Команда kubectl patch не підтримує зміну статусу обʼєкта накладанням патчів. Щоб встановити ці status.conditions для Podʼа, застосунки та оператори повинні використовувати дію PATCH. Ви можете використовувати клієнтську бібліотеку Kubernetes для написання коду, який встановлює власні стани Podʼа для готовності Podʼа.

Pod, який використовує власні стани, оцінюється як готовий лише тоді, коли істинні обидва такі твердження:

  • Всі контейнери в Podʼі готові.
  • Усі стани, вказані в readinessGates, мають значення True.

Коли контейнери Podʼа перебувають у стані Ready, але принаймні один власний стан відсутній або має значення False, kubelet встановлює стан Podʼа в ContainersReady.

Готовність Podʼа до запуску контейнерів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.37

Примітка:

На ранніх етапах розробки цей стан називали PodHasNetwork.

Після того, як Pod заплановано на вузол, kubelet має його допустити, а всі необхідні томи зберігання мають бути змонтовані. Щойно ці фази завершаться, kubelet працює із середовищем виконання контейнерів (використовуючи Container Runtime Interface (CRI)), щоб налаштувати пісочницю середовища виконання та мережу для Podʼа. Якщо Pod використовує Динамічне виділення ресурсів, ці ресурси також виділяються під час цієї фази. Стан PodReadyToStartContainers додається до поля status.conditions Podʼа.

Kubelet встановлює цей стан в False, коли виявляє, що Pod не має пісочниці середовища виконання з налаштованою мережею. Це трапляється в таких випадках:

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

Після створення пісочниці, налаштування мережі, монтування томів та (якщо запитано) динамічного виділення ресурсів kubelet встановлює стан PodReadyToStartContainers в True. Завантаження образу та створення контейнера відбуваються після цього.

Для Podʼа з init-контейнерами kubelet встановлює стан Initialized в True після успішного завершення init-контейнерів (що відбувається після успішного створення пісочниці та налаштування мережі втулком середовища виконання). Для Podʼа без init-контейнерів kubelet встановлює стан Initialized в True перед початком створення пісочниці та налаштування мережі.

Зміна розміру Podʼів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.35
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.35. It was first available in the v1.27 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate InPlacePodVerticalScaling, Kubernetes ignores it but does not report any error.

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.36; (стандартно увімкнено)

Kubernetes підтримує зміну ресурсів CPU та памʼяті, виділених для Podʼів, після їх створення. (Для інших ресурсів інфраструктури потрібно використовувати інші методи, специфічні для цих ресурсів.) Існує два основні підходи до зміни розміру CPU та памʼяті:

Зміна розміру Podʼа на місці

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

Якщо ви вказали ресурси на рівні Podʼа, ви також можете змінювати їх на місці. Для отримання додаткової інформації див. Зміна розміру CPU та памʼяті, призначених Podʼам.

Щоб виконати зміну розміру на місці, оновіть бажаний стан Podʼа за допомогою субресурсу /resize. Kubelet спробує застосувати нові значення ресурсів до контейнерів, що працюють. Стани Podʼа PodResizePending та PodResizeInProgress (описані в розділі Стани Podʼа) вказують на статус операції зміни розміру. Для отримання додаткової інформації про статус зміни розміру див. Статус зміни розміру контейнера.

Основні моменти, які слід враховувати під час зміни розміру на місці:

  • На місці можна змінювати розмір лише ресурсів CPU та памʼяті.
  • Клас якості обслуговування (QoS) Podʼа визначається під час створення і не може бути змінений шляхом зміни розміру.
  • Ви можете налаштувати, чи потрібно перезапускати контейнер для зміни розміру, використовуючи resizePolicy у специфікації контейнера.

Детальні інструкції щодо зміни розміру на місці див. Зміна розміру CPU та памʼяті, призначених контейнерам.

Зміна розміру шляхом запуску Podʼів-замінників

Більш cloud native підхід до зміни ресурсів Podʼа полягає у використанні ресурсу робочого навантаження, який ним керує (наприклад, Deployment або StatefulSet). Коли ви оновлюєте специфікації ресурсів у шаблоні Podʼа, контролер робочого навантаження створює нові Podʼи з оновленими ресурсами та завершує старі Podʼи відповідно до своєї стратегії оновлення.

Цей підхід:

  • Працює з будь-якою версією Kubernetes.
  • Може змінювати будь-яку специфікацію Podʼа, а не тільки ресурси.
  • Призводить до заміни Podʼа, тому вам слід спроєктувати робоче навантаження таким чином, щоб воно могло обробляти заплановані порушення. Розгляньте можливість використання PodDisruptionBudget для контролю доступності.
  • Вимагає, щоб вашими Podʼами керував ресурс робочого навантаження.

Ви також можете використовувати VerticalPodAutoscaler для автоматичного керування рекомендаціями та оновленнями ресурсів Podʼів.

Проби контейнерів

Kubernetes дозволяє визначати проби для безперервного моніторингу стану контейнерів у Podʼі. Проба — це діагностика, яку періодично виконує kubelet для контейнера. Для виконання діагностики kubelet або виконує код всередині контейнера, або виконує мережевий запит.

На основі результатів проб Kubernetes може перезапускати несправні контейнери або припиняти надсилання трафіку до контейнерів, які не готові.

Kubelet може за бажанням виконувати три типи проб для запущених контейнерів і реагувати на них, кожен із цих типів має своє призначення. Для механізмів проб (exec, grpc, httpGet, tcpSocket), полів конфігурації та детальних рекомендацій щодо використання дивіться Проби життєздатності, готовності та запуску.

Проба запуску

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

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

Якщо проба запуску не вдається, kubelet завершує контейнер, і до контейнера застосовується його політика перезапуску.

Проба життєздатності

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

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

Проба готовності

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

Якщо проба готовності повертає невдалий результат, контролер EndpointSlice видаляє IP-адресу Podʼа з EndpointSlices усіх Services, які відповідають Podʼу.

Проби готовності виконуються в контейнері протягом усього його життєвого циклу.

Завершення роботи Podʼів

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

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

Зазвичай під час такого коректного завершення роботи Podʼа kubelet надсилає запити до середовища виконання контейнерів, щоб спробувати зупинити контейнери в Podʼі, спочатку надсилаючи сигнал TERM (також відомий як SIGTERM) основному процесу в кожному контейнері з тайм-аутом періоду коректного завершення. Запити на зупинку контейнерів обробляються середовищем виконання контейнерів асинхронно. Порядок обробки цих запитів не гарантується. Багато середовищ виконання контейнерів враховують значення STOPSIGNAL, визначене в образі контейнера і, якщо воно відрізняється, надсилають значення STOPSIGNAL, визначене в образі контейнера, замість TERM. Після закінчення періоду коректного завершення сигнал KILL надсилається всім залишковим процесам, а потім Pod видаляється з API-сервера. Якщо kubelet або сервіс керування середовищем виконання контейнерів перезапускається під час очікування завершення процесів, кластер повторює спробу спочатку, включаючи повний початковий період коректного завершення.

Сигнали зупинки

Сигнал зупинки, який використовується для завершення роботи контейнера, може бути визначено в образі контейнера за допомогою інструкції STOPSIGNAL. Якщо в образі контейнера не визначено жодного сигналу зупинки, для завершення роботи контейнера буде використано стандартний сигнал середовища виконання контейнерів (SIGTERM як для containerd, так і для CRI-O).

Визначення власних сигналів зупинки

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.33; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the ContainerStopSignals feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Якщо увімкнено функціональну можливість ContainerStopSignals, ви можете налаштувати власний сигнал зупинки для ваших контейнерів з життєвого циклу контейнера. Щоб визначати сигнали зупинки в життєвому циклі контейнера, поле spec.os.name Podʼа має бути заданим. Перелік допустимих сигналів залежить від ОС, на яку заплановано Pod. Для Podʼів, запланованих на вузли Windows, ми підтримуємо тільки SIGTERM і SIGKILL як допустимі сигнали.

Ось приклад специфікації Podʼа, що визначає власний сигнал зупинки:

spec:
  os:
    name: linux
  containers:
    - name: my-container
      image: container-image:latest
      lifecycle:
        stopSignal: SIGUSR1

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

Порядок завершення роботи Podʼа

Порядок завершення роботи Podʼа на прикладі:

  1. Ви використовуєте інструмент kubectl, щоб вручну видалити певний Pod, зі стандартним періодом коректного завершення (30 секунд).

  2. Pod в API-сервері оновлюється: записується час, після якого Pod вважається «мертвим», разом із періодом коректного завершення. Якщо ви використовуєте kubectl describe для перевірки Podʼа, який ви видаляєте, цей Pod показується як «Terminating». На вузлі, де працює Pod: щойно kubelet бачить, що Pod позначено як такий, що завершує роботу (встановлено тривалість коректного вимкнення), kubelet розпочинає локальний процес вимкнення Podʼа.

    1. Якщо один із контейнерів Podʼа має визначений хук preStop і terminationGracePeriodSeconds у специфікації Podʼа не встановлено в 0, kubelet виконує цей хук всередині контейнера. Стандартне значення terminationGracePeriodSeconds — 30 секунд.

      Якщо хук preStop все ще виконується після закінчення періоду коректного завершення, kubelet запитує невелике одноразове подовження цього періоду на 2 секунди.

      Примітка:

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

      Є спеціальний порядок, якщо для Podʼа вказані будь-які sidecar-контейнери. В іншому випадку контейнери в Podʼі отримують сигнал TERM у різний час і в довільному порядку. Якщо порядок завершення роботи має значення, розгляньте можливість використання хука preStop для синхронізації (або перейдіть на використання sidecar-контейнерів).

  3. Одночасно з тим, як kubelet розпочинає коректне вимкнення Podʼа, панель управління оцінює, чи слід вилучити цей Pod, що вимикається, з обʼєктів EndpointSlice, де ці обʼєкти представляють Service із налаштованим селектором. ReplicaSets та інші ресурси робочого навантаження більше не вважають такий Pod дійсною реплікою, що обслуговує запити.

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

    Будь-які точки доступу, які представляють Podʼи, що завершують роботу, не вилучаються негайно з EndpointSlices, а статус, який вказує на стан завершення, надається через API EndpointSlice. Точки доступу, що завершують роботу, завжди мають статус ready зі значенням false (для зворотної сумісності з версіями до 1.26), тому балансувальники навантаження не використовуватимуть їх для звичайного трафіку.

    Якщо потрібне очищення трафіку на Podʼі, що завершує роботу, фактичну готовність можна перевірити за станом serving. Докладніше про те, як реалізувати очищення зʼєднань, можна дізнатися в навчальному матеріалі Порядок завершення роботи Podʼів та Endpointʼів

  4. kubelet забезпечує завершення та вимкнення Podʼа.

    1. Коли період коректного завершення спливає, а в Podʼі все ще працює якийсь контейнер, kubelet ініціює примусове вимкнення. Середовище виконання контейнерів надсилає SIGKILL усім процесам, які ще працюють у будь-якому контейнері в Podʼі. Kubelet також прибирає прихований контейнер pause, якщо це середовище виконання контейнерів його використовує.
    2. kubelet переводить Pod у термінальну фазу (Failed або Succeeded залежно від кінцевого стану його контейнерів).
    3. kubelet ініціює примусове видалення обʼєкта Pod з API-сервера, встановлюючи період коректного завершення в 0 (негайне видалення).
    4. API-сервер видаляє API-обʼєкт Podʼа, який після цього більше не видимий для жодного клієнта.

Примусове завершення Podʼів

Увага:

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

Стандартно всі видалення є коректними протягом 30 секунд. Команда kubectl delete підтримує опцію --grace-period=<seconds>, яка дозволяє перевизначити стандартне значення власним.

Встановлення періоду коректного завершення в 0 примусово та негайно видаляє Pod з API-сервера. Якщо Pod все ще працював на вузлі, це примусове видалення змушує kubelet негайно розпочати прибирання.

Використовуючи kubectl, ви повинні вказати додатковий прапорець --force разом із --grace-period=0, щоб виконати примусове видалення.

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

Увага:

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

Якщо вам потрібно примусово видалити Podʼи, які є частиною StatefulSet, дивіться документацію для видалення Podʼів з StatefulSet.

Завершення роботи Podʼа та sidecar-контейнери

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

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

Аналогічно, якщо Pod має хук preStop, який перевищує період коректного завершення, може статися аварійне завершення. Загалом, якщо ви використовували хуки preStop для керування порядком завершення без sidecar-контейнерів, тепер ви можете видалити їх і дозволити kubelet автоматично керувати завершенням роботи sidecar-контейнерів.

Збір сміття Podʼів

Для Podʼів, що завершилися зі збоєм, обʼєкти API залишаються в API кластера, поки людина чи контролер явно їх не видалять.

Збирач сміття Podʼів (PodGC), який є контролером панелі управління, прибирає завершені Podʼи (з фазою Succeeded або Failed), коли кількість Podʼів перевищує налаштований поріг (визначений параметром terminated-pod-gc-threshold в kube-controller-manager). Це запобігає витоку ресурсів під час створення та завершення Podʼів з часом.

Крім того, PodGC очищує будь-які Podʼи, які відповідають одній з таких умов:

  1. є осиротілими Podʼами — привʼязаними до вузла, якого вже не існує,
  2. є незапланованими Podʼами у стані завершення,
  3. є Podʼами у стані завершення, привʼязаними до неготового вузла з taint node.kubernetes.io/out-of-service.

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

Поведінка Podʼа під час перезапуску kubelet

Якщо ви перезапускаєте kubelet, Podʼи (та їхні контейнери) продовжують працювати навіть під час перезапуску. Коли на вузлі працюють Podʼи, зупинка або перезапуск kubelet на цьому вузлі не призводить до того, що kubelet зупиняє всі локальні Podʼи перед тим, як зупинитися сам. Щоб зупинити Podʼи на вузлі, ви можете використовувати kubectl drain.

Виявлення перезапусків kubelet

СТАН ФУНКЦІОНАЛУ: Deprecated since Kubernetes v1.35; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the ChangeContainerStatusOnKubeletRestart feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли kubelet запускається, він перевіряє, чи є вже вузол із привʼязаними Podʼами. Якщо стан Ready вузла залишається незмінним, тобто стан не змінився з true на false, Kubernetes виявляє це як перезапуск kubelet. (Можна перезапустити kubelet іншими способами, наприклад, щоб виправити помилку вузла, але в таких випадках Kubernetes вибирає безпечний варіант і розглядає це як зупинку kubelet, а потім його запуск).

Коли kubelet перезапускається, статусами контейнерів керують по-різному залежно від налаштування функціональної можливості:

  • Стандартно kubelet не змінює статуси контейнерів після перезапуску. Контейнери, для яких було встановлено ready: true, залишаються готовими.

    Якщо ви зупините kubelet на достатньо довгий час, щоб він не пройшов серію перевірок пульсу вузла, а потім зачекаєте, перш ніж знову запустити kubelet, Kubernetes може почати виселяти Podʼи з цього вузла. Однак, навіть якщо виселення Podʼів починається, Kubernetes не позначає окремі контейнери в цих Podʼах як ready: false. Виселення на рівні Podʼа відбувається після того, як панель управління додає вузлу taint node.kubernetes.io/not-ready (через провал перевірок пульсу).

  • У Kubernetes 1.37 ви можете вибрати стару поведінку, за якої kubelet завжди змінює значення контейнерів ready після перезапуску kubelet на false.

    Ця стара поведінка довгий час була стандартною, але спричиняла проблеми для користувачів Kubernetes, особливо у великих розгортаннях. Хоча функціональна можливість дозволяє тимчасово повернутися до цієї старої поведінки, проєкт Kubernetes рекомендує подавати звіт про помилку, якщо ви зіткнулися з проблемами. Функціональну можливість ChangeContainerStatusOnKubeletRestart буде видалено в майбутньому.

Що далі

4.1.2 - Стани Podʼа

У Kubernetes багато обʼєктів мають стани (conditions). Стан — це показник певного аспекту фактичного становища того, що представляє обʼєкт. Podʼи мають стани, і стани Podʼів у Kubernetes є важливим аспектом, що дозволяє контролерам (а також тим, хто займається усуненням несправностей) оцінювати справність Podʼів.

Фаза Podʼа надає високорівневий огляд того, на якому етапі життєвого циклу перебуває Pod, але одне значення не може показати повну картину. Наприклад, Pod може перебувати у фазі Running, але не бути готовим до обслуговування трафіку. Стани Podʼа доповнюють фазу, відстежуючи незалежно один від одного різні аспекти статусу Podʼа, наприклад, чи було його заплановано, чи готові його контейнери, чи триває зміна розміру, або чи буде робота Podʼа незабаром порушена через taint.

Структура стану Podʼа

Статус Podʼа включає масив PodConditions, які вказують, чи пройшов Pod певні контрольні точки.

Кожен елемент масиву PodCondition має такі поля:

Поля PodCondition
ПолеОпис
typeНазва стану Podʼа.
statusВказує, чи застосовний цей стан, з можливими значеннями "True", "False" або "Unknown".
lastProbeTimeЧас останньої перевірки стану Podʼа.
lastTransitionTimeЧас останнього переходу Podʼа з одного стану в інший.
reasonТекст у форматі UpperCamelCase, придатний для машинного зчитування, що вказує причину останнього переходу стану.
messageПовідомлення, придатне для сприйняття людиною, що містить детальну інформацію про останній перехід стану.
observedGeneration.metadata.generation Podʼа на момент запису стану. Див. Генерація Podʼа.

Вбудовані стани Podʼа

Kubernetes керує такими станами Podʼа:

Стани життєвого циклу: встановлюються під час проходження Podʼом свого життєвого циклу, приблизно в такому порядку: PodScheduled, PodReadyToStartContainers, Initialized, ContainersReady, Ready.

Інші стани: встановлюються у відповідь на конкретні операції або події: DisruptionTarget, PodResizePending, PodResizeInProgress.

Крім вбудованих станів, ви можете визначати власні стани за допомогою шлюзів готовності Podʼа (readiness gates).

Стани життєвого циклу Podʼа

У міру проходження Podʼом свого життєвого циклу kubelet встановлює такі стани приблизно в такому порядку:

  1. PodScheduled: Pod було заплановано на вузол.
  2. PodReadyToStartContainers: пісочницю Podʼа було успішно створено та налаштовано мережу. Пісочницю та мережу налаштовують середовище виконання контейнерів та втулок CNI.
  3. Initialized: всі init-контейнери успішно завершилися. Для Podʼа без init-контейнерів встановлюється в True перед створенням пісочниці.
  4. ContainersReady: всі контейнери в Podʼі готові. Готовність контейнера визначається його пробою готовності, якщо вона налаштована.
  5. Ready: Pod здатний обслуговувати запити і повинен бути доданий до пулів балансування навантаження всіх відповідних Services. Podʼи, які не є Ready, видаляються з точок доступу Service.

Примітка:

Стан Ready залежить не лише від ContainersReady. Якщо Pod вказує readinessGates, всі ці власні стани також повинні бути True, щоб Pod був Ready. Див. Готовність Podʼа для деталей.

Ви можете перевірити стани Podʼа за допомогою kubectl:

kubectl get pod <pod-name> -o yaml

Цей вивід показує, як виглядає поле status.conditions для запущеного Podʼа:

status:
  conditions:
    - type: PodScheduled
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-03-29T08:52:21Z"
      observedGeneration: 1
    - type: PodReadyToStartContainers
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-04-11T06:02:16Z"
      observedGeneration: 1
    - type: Initialized
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-03-29T08:52:21Z"
      observedGeneration: 1
    - type: ContainersReady
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-04-11T06:02:45Z"
      observedGeneration: 1
    - type: Ready
      status: "True"
      lastProbeTime: null
      lastTransitionTime: "2026-04-11T06:02:45Z"
      observedGeneration: 1

PodReadyToStartContainers

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.37
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.37. It was first available in the v1.28 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate PodReadyToStartContainersCondition, Kubernetes ignores it but does not report any error.

Примітка:

Під час ранньої розробки цей стан називався PodHasNetwork.

Після того, як Pod заплановано на вузол, kubelet має його допустити, а всі необхідні томи зберігання мають бути змонтовані. Після завершення цих фаз kubelet працює із середовищем виконання контейнерів (використовуючи Container Runtime Interface, CRI), щоб налаштувати пісочницю середовища виконання та мережу для Podʼа. Стан PodReadyToStartContainers додається до поля status.conditions Podʼа.

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

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

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

Для Podʼа з init-контейнерами kubelet встановлює стан Initialized в True після успішного завершення init-контейнерів (що відбувається після успішного створення пісочниці та налаштування мережі втулком середовища виконання). Для Podʼа без init-контейнерів kubelet встановлює стан Initialized в True перед початком створення пісочниці та налаштування мережі.

Інші стани Podʼа

Такі стани не є частиною нормального проходження життєвого циклу Podʼа. Вони встановлюються у відповідь на конкретні операції або події.

DisruptionTarget

Спеціальний стан Podʼа DisruptionTarget додається для того, щоб вказати, що Pod буде видалено через порушення. Поле reason цього стану додатково вказує одну з таких причин завершення Podʼа:

PreemptionByScheduler
Pod підлягає витісненню планувальником, щоб звільнити місце для нового Podʼа з вищим пріоритетом. Для отримання додаткової інформації див. Пріоритет та витіснення Podʼів.
DeletionByTaintManager
Pod підлягає видаленню менеджером Taint (який є частиною контролера життєвого циклу вузла в kube-controller-manager) через NoExecute taint, який Pod не толерує; див. виселення на основі taint.
EvictionByEvictionAPI
Pod підлягає виселенню за допомогою API Kubernetes.
DeletionByPodGC
Pod, який привʼязаний до вузла, що більше не існує, підлягає видаленню за допомогою механізму збору сміття Podʼів.
TerminationByKubelet
Pod було завершено kubelet через виселення внаслідок тиску на вузол, коректне вимкнення вузла або витіснення заради системно критичних Podʼів.

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

Примітка:

Процес порушення роботи Podʼа може бути зупинено. Панель управління може повторно спробувати продовжити порушення роботи того самого Podʼа, але це не гарантовано. В результаті стан DisruptionTarget може бути додано до Podʼа, але цей Pod може фактично не бути видаленим. У такій ситуації через деякий час стан порушення роботи Podʼа буде очищено.

Разом з очищенням Podʼів, механізм збору сміття Podʼів (PodGC) також позначає їх як невдалі, якщо вони перебувають у нетермінальній фазі (див. також збір сміття Podʼів).

Під час використання Job (або CronJob) ви можете використовувати ці стани порушення роботи Podʼів як частину політики збоїв Podʼа вашого Job.

Для отримання додаткової інформації див. Порушення.

PodResizePending та PodResizeInProgress

Kubelet оновлює стани Podʼа, щоб вказати статус запиту на зміну розміру:

  • type: PodResizePending: Kubelet не може негайно виконати запит. Поле message надає пояснення, чому.
    • reason: Infeasible: Запитана зміна розміру неможлива на поточному вузлі (наприклад, запит більше ресурсів, ніж є на вузлі).
    • reason: Deferred: Запитана зміна розміру наразі неможлива, але може стати можливою пізніше (наприклад, якщо інший Pod буде видалено). Kubelet повторно спробує зміну розміру.
  • type: PodResizeInProgress: Kubelet прийняв зміну розміру та виділив ресурси, але зміни ще застосовуються. Зазвичай це триває недовго, але може зайняти більше часу залежно від типу ресурсу та поведінки середовища виконання. Будь-які помилки під час виконання повідомляються у полі message (разом з reason: Error).

Якщо запитана зміна розміру є Deferred, kubelet періодично повторно намагатиметься виконати зміну розміру, наприклад, коли інший Pod буде видалено або зменшено масштаб.

Докладніше див. Зміна розміру CPU та памʼяті, призначених контейнерам.

Розширена готовність Podʼів

Ваш застосунок може додавати додаткові відгуки або сигнали в .status Podʼа; це відомо як розширена готовність Podʼів. Щоб використовувати це, встановіть readinessGates у spec Podʼа, щоб вказати список додаткових станів, які kubelet оцінює для готовності Podʼа. Потім ви реалізуєте або встановлюєте контролер, який керує цими власними станами, і kubelet використовує це як додаткове вхідне значення для визначення готовності Podʼа.

Шлюзи готовності (readiness gates) визначаються поточним станом полів status.condition для Podʼа. Якщо Kubernetes не може знайти такий стан у полі status.conditions Podʼа, його статус стандартно дорівнює "False".

kind: Pod
...
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
status:
  conditions:
    - type: Ready                              # вбудована PodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"        # додаткова PodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
...

Стани Podʼа, які ви додаєте, повинні мати назви, що відповідають формату ключів міток Kubernetes.

Статус готовності Podʼа

Щоб встановити ці status.conditions для Podʼа, застосунки та оператори повинні використовувати дію PATCH на субресурсі статусу Podʼа. Ви можете використовувати kubectl patch з --subresource=status, або бібліотеку клієнта Kubernetes, щоб написати код, який встановлює власні стани Podʼа для готовності Podʼа.

Pod, який використовує власні стани, вважається готовим тільки тоді, коли виконуються обидва такі твердження:

  • Всі контейнери в Podʼі готові.
  • Усі стани, зазначені в readinessGates, мають значення True.

Коли контейнери Podʼа готові, але принаймні один власний стан відсутній або має значення False, kubelet встановлює стан Ready Podʼа в status: "False" з reason: ReadinessGatesNotReady.

Що далі

4.1.3 - Init-контейнери

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

Ви можете вказати init-контейнери в специфікації Podʼа разом із масивом containers (який описує контейнери застосунку).

У Kubernetes sidecar-контейнер — це контейнер, який запускається перед основним контейнером застосунку і продовжує працювати. Цей документ стосується init-контейнерів — контейнерів, які виконуються до завершення під час ініціалізації Podʼа.

Що таке init-контейнери

У Podʼі може бути кілька контейнерів, які виконують застосунки, але також може бути один чи кілька init-контейнерів, які виконуються до того, як стартують контейнери застосунків.

Init-контейнери точно такі самі, як і звичайні контейнери, за винятком того, що:

  • Init-контейнери завжди виконуються до завершення.
  • Кожен init-контейнер повинен успішно завершити свою роботу, перш ніж почне роботу наступний.

Якщо init-контейнер Podʼа зазнає збою, kubelet повторно перезапускає цей init-контейнер, доки він не завершиться успішно. Однак якщо restartPolicy Podʼа дорівнює Never, і init-контейнер зазнає збою під час запуску цього Podʼа, Kubernetes вважає весь Pod невдалим.

Щоб вказати init-контейнер для Podʼа, додайте поле initContainers у специфікацію Podʼа, у вигляді масиву обʼєктів container (аналогічно полю containers застосунку та його вмісту). Дивіться Container в довідці API для отримання докладнішої інформації.

Статус init-контейнерів повертається у полі .status.initContainerStatuses у вигляді масиву статусів контейнерів (аналогічно полю .status.containerStatuses).

Відмінності від звичайних контейнерів

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

Звичайні init-контейнери (іншими словами, виключаючи sidecar-контейнери) не підтримують поля lifecycle, livenessProbe, readinessProbe чи startupProbe. Init-контейнери повинні успішно завершити свою роботу перед тим, як Pod може бути готовий; sidecar-контейнери продовжують працювати протягом життєвого циклу Podʼа і підтримують деякі проби. Дивіться sidecar-контейнер для отримання додаткової інформації про ці контейнери.

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

Відмінності від sidecar-контейнерів

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

Init-контейнери запускаються і завершують свою роботу послідовно, і основний контейнер не починає свою роботу, доки всі init-контейнери успішно не завершать свою роботу.

Init-контейнери не підтримують lifecycle, livenessProbe, readinessProbe чи startupProbe, у той час, як sidecar-контейнери підтримують всі ці проби, щоб керувати своїм життєвим циклом.

Init-контейнери використовують ті самі ресурси (CPU, памʼять, мережу), що й основні контейнери застосунків, але не взаємодіють з ними напряму. Однак вони можуть використовувати спільні томи для обміну даними.

Використання init-контейнерів

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

  • Init-контейнери можуть містити утиліти або власний код для налаштування, які відсутні в образі застосунку. Наприклад, немає потреби створювати образ FROM іншого образу лише для використання інструменту, такого як sed, awk, python чи dig під час налаштування.
  • Ролі створення та розгортання образу застосунку можуть працювати незалежно одна від одної без необхідності спільного створення єдиного образу застосунку.
  • Init-контейнери можуть працювати з іншим поданням файлової системи, ніж контейнери застосунків у тому самому Podʼі. Отже, їм можна надати доступ до Secrets, до яких контейнери застосунків не мають доступу.
  • Оскільки init-контейнери завершують свою роботу, перш ніж будь-які контейнери застосунків розпочнуть свою роботу, вони пропонують механізм блокування або затримки запуску контейнера застосунку до виконання певних умов. Після того, як умови виконані, всі контейнери застосунків у Podʼі можуть стартувати паралельно.
  • Init-контейнери можуть безпечно виконувати утиліти або власний код, який інакше зробив би образ контейнера застосунку менш безпечним. Відокремлюючи непотрібні інструменти, ви можете обмежити поверхню атаки вашого образу контейнера застосунку.

Приклади

Ось кілька ідей, як використовувати init-контейнери:

  • Очікування на створення Service, використовуючи команду оболонки у вигляді одного рядка, наприклад:

    for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
    
  • Реєстрація Podʼа на віддаленому сервері зі значеннями, отриманими з Downward API за допомогою команди, подібної цій:

    curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
    
  • Очікування певного часу перед запуском контейнера застосунку за допомогою команди, подібної цій:

    sleep 60
    
  • Клонування репозиторію Git у том

  • Додавання значень у файл конфігурації та виклик інструменту шаблонування для динамічного створення файлу конфігурації для основного контейнера застосунку. Наприклад, додайте значення POD_IP у конфігурацію та створіть основний файл конфігурації застосунку за допомогою Jinja.

Init-контейнери в роботі

У цьому прикладі визначається простий Pod, який має два init-контейнери. Перший чекає на myservice, а другий — на mydb. Як тільки обидва init-контейнери завершаться, Pod запускає контейнер застосунку зі свого розділу spec.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

Ви можете запустити цей Pod, виконавши:

kubectl apply -f myapp.yaml

Вивід буде подібний до цього:

pod/myapp-pod created

І перевірте його статус за допомогою:

kubectl get -f myapp.yaml

Вивід буде подібний до цього:

NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

або докладніше:

kubectl describe -f myapp.yaml

Вивід буде подібний до цього:

Name:          myapp-pod
Namespace:     default
[...]
Labels:        app.kubernetes.io/name=MyApp
Status:        Pending
[...]
Init Containers:
  init-myservice:
[...]
    State:         Running
[...]
  init-mydb:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Containers:
  myapp-container:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Events:
  FirstSeen    LastSeen    Count    From                      SubObjectPath                           Type          Reason        Message
  ---------    --------    -----    ----                      -------------                           --------      ------        -------
  16s          16s         1        {default-scheduler }                                              Normal        Scheduled     Successfully assigned myapp-pod to 172.17.4.201
  16s          16s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulling       pulling image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulled        Successfully pulled image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Created       Created container init-myservice
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container init-myservice

Щоб переглянути логи init-контейнерів у цьому Podʼі, виконайте:

kubectl logs myapp-pod -c init-myservice # Огляд першого init-контейнера
kubectl logs myapp-pod -c init-mydb      # Огляд другого init-контейнера

На цей момент ці init-контейнери будуть чекати виявлення Services з назвами mydb та myservice.

Ось конфігурація, яку ви можете використовувати для того, щоб ці Services зʼявилися:

---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

Щоб створити Services mydb та myservice:

kubectl apply -f services.yaml

Вивід буде подібний до цього:

service/myservice created
service/mydb created

Потім ви побачите, що ці init-контейнери завершаться, і Pod myapp-pod переходить у стан Running:

kubectl get -f myapp.yaml

Вивід буде подібний до цього:

NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

Цей простий приклад повинен дати вам натхнення для створення ваших власних init-контейнерів. У розділі Що далі є посилання на більш детальний приклад.

Докладний опис поведінки

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

Кожен init-контейнер має успішно завершитися перед тим, як буде запущений наступний контейнер. Якщо контейнер не вдалося запустити через помилку середовища виконання або він завершується з помилкою, його перезапускають згідно з політикою перезапуску Podʼа (restartPolicy). Однак якщо політика перезапуску Podʼа (restartPolicy) встановлена на Always, init-контейнери використовують політику перезапуску OnFailure.

Pod не може бути Ready, поки всі init-контейнери не завершаться успішно. Порти init-контейнерів не агрегуються в Service. Pod, що ініціалізується, перебуває в стані Pending, але повинен мати умову Initialized, встановлену на false.

Якщо Pod перезапускається або був перезапущений, всі init-контейнери мають виконатися знову.

Зміни в специфікації init-контейнерів обмежуються полем образу контейнера. Безпосередня зміна поля image init-контейнера не призводить до перезапуску Podʼа або його перестворення. Якщо Pod ще не було запущено, ці зміни можуть вплинути на те, як він завантажиться.

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

Оскільки init-контейнери можуть бути перезапущені, повторно виконані або виконані заново, код init-контейнерів повинен бути ідемпотентним. Зокрема, код, що записує дані в будь-який том emptyDir, має бути підготовлений до того, що файл виводу вже може існувати.

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

Використовуйте activeDeadlineSeconds в Podʼі, щоб запобігти нескінченним збоям init-контейнерів. Активний крайній термін охоплює init-контейнери. Однак рекомендується використовувати activeDeadlineSeconds лише якщо команди розгортають свій застосунок як Job, оскільки activeDeadlineSeconds впливає навіть після завершення роботи init-контейнера. Pod, який вже працює правильно, буде завершено через activeDeadlineSeconds, якщо ви його налаштуєте.

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

Спільне використання ресурсів всередині контейнерів

З урахуванням порядку виконання init-контейнерів, sidecar-контейнерів і контейнерів застосунків застосовуються такі правила використання ресурсів:

  • Найвищий запит чи обмеження будь-якого конкретного ресурсу, визначеного у всіх init-контейнерах, вважається ефективним запитом/обмеженням ініціалізації. Якщо для будь-якого ресурсу не вказано обмеження, це вважається найвищим обмеженням.
  • Ефективний запит/обмеження Podʼа для ресурсу — це більше з:
    • суми запитів/обмежень усіх контейнерів застосунків для ресурсу
    • ефективного запиту/обмеження ініціалізації для ресурсу
  • Планування виконується на основі ефективних запитів/обмежень, що означає, що init-контейнери можуть резервувати ресурси для ініціалізації, які не використовуються протягом життя Podʼа.
  • Рівень якості обслуговування (QoS), ефективний рівень QoS Podʼа, — це рівень QoS як для init-контейнерів, так і для контейнерів застосунків.

Квоти та обмеження застосовуються на основі ефективного запиту та обмеження Podʼа.

Init-контейнери та cgroups Linux

У Linux розподіл ресурсів для контрольних груп (cgroups) рівня Podʼа ґрунтується на ефективному запиті та обмеженні Podʼа, так само як і для планувальника.

Причини перезапуску Podʼа

Pod може перезапускатися, що призводить до повторного виконання init-контейнерів, з таких причин:

  • Перезапускається контейнер інфраструктури Podʼа. Це трапляється рідко, і це мав би зробити хтось із root-доступом до вузлів.
  • Всі контейнери в Podʼі завершуються, коли restartPolicy встановлено в Always, що примушує до перезапуску, а запис про завершення init-контейнера було втрачено через збір сміття.

Pod не буде перезапущено, коли змінюється образ init-контейнера, або запис про завершення init-контейнера було втрачено через збір сміття. Це стосується Kubernetes v1.20 і пізніших версій. Якщо ви використовуєте старішу версію Kubernetes, ознайомтеся з документацією версії, яку ви використовуєте.

Що далі

Дізнайтеся більше про таке:

4.1.4 - Sidecar-контейнери

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.33
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.33. It was first available in the v1.28 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate SidecarContainers, Kubernetes ignores it but does not report any error.

Sidecar-контейнери — це додаткові контейнери, які працюють разом з основним контейнером застосунку всередині одного Podʼа. Ці контейнери використовуються для розширення чи покращення функціональності основного контейнера застосунку, надаючи додаткові сервіси чи функціональність, таку як логування, моніторинг, безпеку або синхронізацію даних, не змінюючи безпосередньо код основного застосунку.

Зазвичай в одному Podʼі є лише один контейнер застосунку. Наприклад, якщо у вас є вебзастосунок, який потребує локального вебсервера, локальний вебсервер буде sidecar-контейнером, а сам вебзастосунок — це контейнер застосунку.

Sidecar-контейнери в Kubernetes

Kubernetes реалізує sidecar-контейнери як особливий випадок init-контейнерів; sidecar-контейнери залишаються запущеними після запуску Podʼа. У цьому документі термін звичайні init-контейнери використовується для чіткого посилання на контейнери, які працюють лише під час запуску Podʼа.

За умови, що у вашому кластері увімкнено функціональну можливість SidecarContainers (стандартно активна починаючи з Kubernetes v1.29), ви можете вказати restartPolicy для контейнерів, вказаних у полі initContainers Podʼа. Ці sidecar-контейнери, які перезапускаються, є незалежними від інших init-контейнерів та від основних контейнерів застосунку у тому ж Podʼі, і можуть бути запущені, зупинені або перезапущені без впливу на основний контейнер застосунку та інші init-контейнери.

Ви також можете запустити Pod із декількома контейнерами, які не позначені як init-контейнери або sidecar. Це доцільно, якщо контейнери в межах Podʼа необхідні для його роботи в цілому, але вам не потрібно керувати тим, які контейнери спочатку запускаються або зупиняються. Ви також можете це зробити, якщо вам потрібно підтримувати старі версії Kubernetes, які не підтримують поле restartPolicy на рівні контейнера.

Приклад застосунку

Нижче наведено приклад Deployment з двома контейнерами, один з яких є sidecar:

Примітка:

У цьому прикладі sidecar-контейнер навмисно визначений в initContainers з restartPolicy: Always. Kubernetes розглядає такі контейнери як sidecar-контейнери, які продовжують працювати впродовж усього терміну існування Podʼа.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: alpine:latest
          command: ['sh', '-c', 'while true; do echo "logging" >> /opt/logs.txt; sleep 1; done']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          # Setting restartPolicy: Always makes this a sidecar container.
          restartPolicy: Always
          command: ['sh', '-c', 'tail -F /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      volumes:
        - name: data
          emptyDir: {}

Sidecar-контейнери та життєвий цикл Podʼа

Якщо init-контейнер створено з параметром restartPolicy, встановленим на Always, він розпочне роботу і залишиться запущеним протягом усього життя Podʼа. Це може бути корисно для запуску допоміжних сервісів, відокремлених від основних контейнерів застосунку.

Якщо для цього init-контейнера вказано readinessProbe, результат буде використовуватися для визначення стану ready Podʼа.

Оскільки ці контейнери визначені як init-контейнери, вони користуються тими самими гарантіями порядку та послідовності, що й звичайні init-контейнери, що дозволяє змішувати sidecar-контейнери зі звичайними init-контейнерами у складних потоках ініціалізації Podʼа.

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

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

Jobs із sidecar-контейнерами

Якщо ви визначаєте Job, який використовує sidecar у вигляді init-контейнерів у стилі Kubernetes, sidecar-контейнер у кожному Podʼі не заважає завершенню Job після завершення основного контейнера.

Ось приклад Job із двома контейнерами, один з яких — це sidecar-контейнер:

apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    spec:
      containers:
        - name: myjob
          image: alpine:latest
          command: ['sh', '-c', 'echo "logging" > /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          # Setting restartPolicy: Always makes this a sidecar container.
          restartPolicy: Always
          command: ['sh', '-c', 'tail -F /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      restartPolicy: Never
      volumes:
        - name: data
          emptyDir: {}

Відмінності від контейнерів застосунків

Sidecar-контейнери працюють поруч з контейнерами застосунку в одному Podʼі. Однак вони не виконують основну логіку застосунку; замість цього вони надають допоміжну функціональність основному застосунку.

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

Sidecar-контейнери спільно використовують з основним контейнером ті самі простори імен мережі та сховища. Це спільне розташування дозволяє їм тісно взаємодіяти та спільно використовувати ресурси.

З погляду Kubernetes коректне завершення sidecar-контейнера є менш важливим. Якщо інші контейнери використали весь виділений час для коректного завершення роботи, то sidecar-контейнери отримають сигнал SIGTERM, а потім SIGKILL, перш ніж вони встигнуть коректно завершити роботу. Отже, коди виходу, відмінні від 0 (0 означає успішний вихід), для sidecar-контейнерів є нормальним явищем під час завершення роботи Podʼа, і зовнішні інструменти зазвичай мають їх ігнорувати.

Відмінності від init-контейнерів

Sidecar-контейнери працюють поруч з основним контейнером, розширюючи його функціональність та надаючи додаткові сервіси.

Sidecar-контейнери працюють паралельно з основним контейнером застосунку. Вони активні протягом усього життєвого циклу Podʼа та можуть бути запущені та зупинені незалежно від основного контейнера. На відміну від init-контейнерів, sidecar-контейнери підтримують проби, щоб контролювати їхній життєвий цикл.

Sidecar-контейнери можуть взаємодіяти безпосередньо з основними контейнерами застосунків, оскільки вони, так само як і init-контейнери, завжди спільно використовують ту саму мережу і за бажанням також можуть спільно використовувати томи (файлові системи).

Init-контейнери зупиняються до того, як основний контейнер застосунку розпочне роботу, тож init-контейнер не може обмінюватись повідомленнями з контейнером застосунку в Podʼі. Будь-які дані передаються лише в один бік (наприклад, init-контейнер може залишити інформацію у томі emptyDir).

Зміна образу sidecar-контейнера не призведе до перезапуску Podʼа, але спровокує перезапуск контейнера.

Спільне використання ресурсів всередині контейнерів

З урахуванням порядку виконання init-контейнерів, sidecar-контейнерів і контейнерів застосунків застосовуються такі правила використання ресурсів:

  • Найвищий запит чи обмеження будь-якого конкретного ресурсу, визначеного у всіх init-контейнерах, вважається ефективним запитом/обмеженням ініціалізації. Якщо для будь-якого ресурсу не вказано обмеження, це вважається найвищим обмеженням.
  • Ефективний запит/обмеження Podʼа для ресурсу — це сума накладних витрат Podʼа та більшого з:
    • суми запитів/обмежень усіх контейнерів, що не є init-контейнерами (контейнерів застосунків і sidecar-контейнерів), для ресурсу
    • ефективного запиту/обмеження ініціалізації для ресурсу
  • Планування виконується на основі ефективних запитів/обмежень, що означає, що init-контейнери можуть резервувати ресурси для ініціалізації, які не використовуються протягом життя Podʼа.
  • Рівень якості обслуговування (QoS), ефективний рівень QoS Podʼа, — це рівень QoS для всіх init-контейнерів, sidecar-контейнерів і контейнерів застосунків.

Квоти та обмеження застосовуються на основі ефективного запиту та обмеження Podʼа.

Sidecar-контейнери та cgroups Linux

У Linux розподіл ресурсів для контрольних груп (cgroups) рівня Podʼа ґрунтується на ефективному запиті та обмеженні Podʼа, так само як і для планувальника.

Що далі

4.1.5 - Ефемерні контейнери

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.25

Ця сторінка надає огляд ефемерних контейнерів: особливого типу контейнера, який працює тимчасово в наявному Pod для виконання дій, ініційованих користувачем, таких як усунення несправностей. Ефемерні контейнери використовуються для огляду сервісів, а не для створення застосунків.

Розуміння ефемерних контейнерів

Podʼи є фундаментальним елементом застосунків Kubernetes. Оскільки Podʼи призначені бути одноразовими та замінними, ви не можете додати контейнер до Podʼа після його створення. Замість цього ви зазвичай видаляєте та замінюєте Podʼи у контрольований спосіб, використовуючи Deployments.

Іноді, однак, необхідно оглянути стан наявного Podʼа, наприклад, для усунення несправностей, коли важко відтворити помилку. У цих випадках ви можете запустити ефемерний контейнер у наявному Podʼі, щоб оглянути його стан та виконати довільні команди.

Що таке ефемерний контейнер?

Ефемерні контейнери відрізняються від інших контейнерів тим, що вони не мають гарантій щодо ресурсів або виконання, і їх ніколи автоматично не перезапускають, тому вони не підходять для створення застосунків. Ефемерні контейнери описуються за допомогою того самого ContainerSpec, що й звичайні контейнери, але багато полів несумісні та заборонені для ефемерних контейнерів.

  • В ефемерних контейнерів не може бути портів, тому такі поля, як ports, livenessProbe, readinessProbe, заборонені.
  • Виділення ресурсів для Podʼа незмінне, тому встановлення resources заборонене.
  • Для повного списку дозволених полів дивіться довідкову документацію EphemeralContainer.

Ефемерні контейнери створюються за допомогою спеціального обробника ephemeralcontainers в API замість того, щоб додавати їх безпосередньо до pod.spec, тому не можна додати ефемерний контейнер за допомогою kubectl edit.

Як і звичайний контейнер, ефемерний контейнер не можна змінити або видалити після додавання до Podʼа.

Примітка:

Ефемерні контейнери не підтримуються статичними Podʼами.

Використання ефемерних контейнерів

Ефемерні контейнери корисні для інтерактивного усунення несправностей, коли kubectl exec недостатній, оскільки контейнер зазнав збою або образ контейнера не містить засобів налагодження.

Зокрема, образи distroless дозволяють вам розгортати мінімальні образи контейнерів, які зменшують поверхню атаки та вплив помилок і вразливостей. Оскільки образи distroless не включають оболонку або будь-які засоби налагодження, складно налагоджувати образи distroless, використовуючи лише kubectl exec.

Під час використання ефемерних контейнерів корисно увімкнути спільний простір імен процесів (process namespace sharing), щоб ви могли переглядати процеси в інших контейнерах.

Що далі

4.1.6 - Проби життєздатності, готовності та запуску

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

На основі результатів проб Kubernetes може перезапускати несправні контейнери або припиняти надсилання трафіку до контейнерів, які не готові.

Типи проб

Kubelet може за потреби виконувати три види проб у запущених контейнерах і реагувати на їхні результати; кожна з них має власне призначення:

Проба запуску

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

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

Проба життєздатності

Проби життєздатності (Liveness) визначають, коли слід перезапустити контейнер. Наприклад, проби життєздатності можуть виявити ситуацію взаємного блокування, коли застосунок працює, але не може просуватися далі. Перезапуск контейнера в такому стані допомагає забезпечити більшу доступність застосунку, незважаючи на наявність помилок.

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

Увага:

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

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

Проба готовності

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

Якщо проба готовності повертає неуспішний стан, контролер EndpointSlice видаляє IP-адресу Podʼа з EndpointSlices усіх Services, що відповідають цьому Podʼу.

Проби готовності виконуються в контейнері протягом всього його життєвого циклу.

Примітка:

Якщо ви хочете мати можливість завершувати обробку запитів під час видалення Podʼа, вам не обовʼязково потрібна проба готовності; коли Pod видаляється, відповідна точка доступу в EndpointSlice оновить свої стани: стан готовності точки доступу буде встановлено на false, тому балансувальники навантаження не використовуватимуть Pod для звичайного трафіку. Дивіться Завершення роботи Podʼа для отримання додаткової інформації про те, як kubelet обробляє видалення Podʼа.

Коли використовувати кожну пробу

Коли слід використовувати пробу запуску?

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

Якщо ваш контейнер зазвичай запускається більше ніж \( initialDelaySeconds + failureThreshold \times periodSeconds \), слід вказати пробу запуску, яка перевіряє ту саму точку доступу, що й проба життєздатності. Стандартне значення для periodSeconds становить 10 секунд. Потім слід встановити failureThreshold достатньо високим, щоб дозволити контейнеру запуститися, не змінюючи стандартне значення для проби життєздатності. Це допомагає захиститися від взаємних блокувань.

Коли слід використовувати пробу життєздатності?

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

Якщо ви хочете, щоб ваш контейнер був завершений і перезапущений у разі невдачі проби, тоді використовуйте пробу життєздатності та встановіть restartPolicy на Always або OnFailure.

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

Коли слід використовувати пробу готовності?

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

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

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

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

Механізми перевірки

Існує чотири різні способи перевірки контейнера за допомогою проб. Кожна проба повинна визначати точно один із цих чотирьох механізмів:

exec
Виконує вказану команду всередині контейнера. Діагностика вважається успішною, якщо команда завершується з кодом стану 0.
grpc
Виконує віддалений виклик процедури за допомогою gRPC. Цільовий обʼєкт повинен реалізовувати перевірки стану gRPC. Діагностика вважається успішною, якщо status відповіді дорівнює SERVING. Для отримання додаткової інформації див. gRPC-проби.
httpGet
Виконує запит HTTP GET до IP-адреси Podʼа на вказаному порту та шляху. Діагностика вважається успішною, якщо відповідь має код стану від 200 до 399 включно. Для отримання додаткової інформації див. HTTP-проби.
tcpSocket
Виконує перевірку TCP на IP-адресу Podʼа на вказаному порту. Діагностика вважається успішною, якщо порт відкритий. Якщо віддалена система (контейнер) закриває зʼєднання відразу після його відкриття, це також вважається справним станом. Для отримання додаткової інформації див. TCP-проби.

Увага:

На відміну від інших механізмів, реалізація проби exec передбачає створення/відгалуження кількох процесів під час кожного виконання. В результаті, у випадку кластерів з високою щільністю Podʼів, меншими інтервалами initialDelaySeconds, periodSeconds, налаштування будь-якої проби з механізмом exec може призвести до збільшення навантаження на CPU вузла. У таких сценаріях розгляньте можливість використання альтернативних механізмів проб, щоб уникнути цих накладних витрат.

Результати проб

Kubelet оцінює результат виконання кожної проби та діє відповідно. Кожна проба має один із трьох результатів:

Success
Контейнер пройшов діагностику.
Failure
Контейнер не пройшов діагностику. Для проб життєздатності та запуску kubelet завершує контейнер, і до нього застосовується його політика перезапуску. Для проб готовності kubelet позначає контейнер як неготовий, і Pod перестає отримувати трафік від відповідних Services.
Unknown
Діагностика не вдалася (жодних дій не слід виконувати, kubelet здійснить подальші перевірки).

Якщо контейнер не надає певну пробу, kubelet завжди вважає результат Success. Для проб готовності, зокрема, результат вважається Failure до закінчення початкової затримки.

Поля конфігурації

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

apiVersion: v1
kind: Pod
metadata:
  name: probe-example
spec:
  containers:
  - name: app
    image: registry.k8s.io/e2e-test-images/agnhost:2.40
    ports:
    - containerPort: 8080
    startupProbe:
      httpGet:
        path: /healthz
        port: 8080
      failureThreshold: 30
      periodSeconds: 10
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 5
      timeoutSeconds: 3
      failureThreshold: 3
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      periodSeconds: 5
initialDelaySeconds
Кількість секунд після запуску контейнера, перш ніж будуть ініційовані проби запуску, життєздатності або готовності. Якщо визначена проба запуску, затримки проб життєздатності та готовності не починаються, поки проба запуску не буде успішною. У деяких старих версіях Kubernetes initialDelaySeconds може ігноруватися, якщо periodSeconds було встановлено на значення більше, ніж initialDelaySeconds. Однак у поточних версіях initialDelaySeconds завжди враховується, і проба не почнеться до закінчення цієї початкової затримки. Стандартне значення — 0 секунд. Мінімальне значення — 0.
periodSeconds
Як часто (у секундах) виконувати пробу. Стандартно — 10 секунд. Мінімальне значення — 1. Поки контейнер не готовий, проба готовності може виконуватися в інший час, ніж налаштований інтервал periodSeconds. Це робиться для того, щоб Pod став готовим швидше.
timeoutSeconds
Кількість секунд, після яких проба вважається такою, що перевищила час очікування. Стандартне значення — 1 секунда. Мінімальне значення — 1.
successThreshold
Мінімальна кількість послідовних успішних спроб, щоб проба вважалася успішною після невдачі. Стандартне значення — 1. Для проб життєздатності та запуску має бути 1. Мінімальне значення — 1.
failureThreshold
Після того, як проба не вдалася failureThreshold разів поспіль, Kubernetes вважає, що загальна перевірка не вдалася: контейнер не готовий/справний/живий. Стандартне значення — 3. Мінімальне значення — 1. У випадку проби запуску або життєздатності, якщо принаймні failureThreshold проб не вдалося, Kubernetes вважає контейнер несправним і запускає перезапуск для цього конкретного контейнера. Kubelet дотримується налаштування terminationGracePeriodSeconds для цього контейнера. Для невдалої проби готовності kubelet продовжує виконувати контейнер, який не пройшов перевірку, і також продовжує виконувати інші проби; оскільки перевірка не вдалася, kubelet встановлює стан Ready для Podʼа у значення false.
terminationGracePeriodSeconds
Налаштуйте період очікування для kubelet між ініціюванням завершення роботи несправного контейнера та примусовою зупинкою цього контейнера середовищем виконання контейнерів. Стандартно успадковується значення на рівні Podʼа для terminationGracePeriodSeconds (30 секунд, якщо не вказано), а мінімальне значення — 1. Див. terminationGracePeriodSeconds на рівні проби для детальнішої інформації.

Увага:

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

terminationGracePeriodSeconds на рівні проби

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.28

У версіях 1.25 і вище користувачі можуть вказати terminationGracePeriodSeconds на рівні проби як частину специфікації проби. Коли встановлені обидва значення terminationGracePeriodSeconds на рівні Podʼа і проби, kubelet використовує значення на рівні проби.

Під час встановлення terminationGracePeriodSeconds зверніть увагу на таке:

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

Наприклад:

spec:
  terminationGracePeriodSeconds: 3600  # рівень Podʼа
  containers:
  - name: test
    image: ...

    ports:
    - name: liveness-port
      containerPort: 8080

    livenessProbe:
      httpGet:
        path: /healthz
        port: liveness-port
      failureThreshold: 1
      periodSeconds: 60
      # Перевизначення значення terminationGracePeriodSeconds на рівні Podʼа
      terminationGracePeriodSeconds: 60

Значення terminationGracePeriodSeconds на рівні проби не може бути встановлено для проб готовності. Воно буде відхилено API-сервером.

Деталі механізму проб

HTTP-проби

HTTP-проби мають додаткові поля, які можна встановити в httpGet:

  • host: Імʼя хоста для підключення, стандартно IP-адреса Podʼа. Ймовірно, ви захочете встановити "Host" у httpHeaders замість цього.
  • scheme: Схема для підключення до хоста (HTTP або HTTPS). Стандартно "HTTP".
  • path: Шлях для доступу до HTTP-сервера. Стандартно "/".
  • httpHeaders: Власні заголовки для встановлення в запиті. HTTP дозволяє повторювані заголовки.
  • port: Назва або номер порту для доступу до контейнера. Номер повинен бути в діапазоні від 1 до 65535.
  • protocol: Протокол для використання в запиті проби. Стандартно HTTP1. Встановіть HTTP2, щоб виконувати пробу через HTTP/2 без шифрування (h2c). Вимагає увімкнення функціональної можливості H2CContainerProbe.

Для HTTP-проби kubelet надсилає HTTP-запит на вказаний порт і шлях для виконання перевірки. Kubelet надсилає пробу на IP-адресу Podʼа, якщо адреса не перевизначена за допомогою необовʼязкового поля host у httpGet. Якщо поле scheme встановлено в HTTPS, kubelet надсилає HTTPS-запит, пропускаючи перевірку сертифіката. У більшості сценаріїв ви не захочете встановлювати поле host. Ось один сценарій, де ви б його встановили. Припустимо, контейнер слухає на 127.0.0.1, а поле hostNetwork Podʼа встановлено в true. Тоді host у httpGet слід встановити на 127.0.0.1. Якщо ваш Pod покладається на віртуальні хости, що, ймовірно, є більш поширеним випадком, ви не повинні використовувати host, а натомість встановити заголовок Host у httpHeaders.

Для HTTP-проби kubelet надсилає два заголовки запиту на додачу до обовʼязкового заголовка Host:

  • User-Agent, який стандартно дорівнює kube-probe/1.37, де 1.37 — це версія kubelet.
  • Accept, який стандартно дорівнює */*.

Ви можете перевизначити ці заголовки, визначивши httpHeaders для проби. Наприклад:

livenessProbe:
  httpGet:
    httpHeaders:
      - name: Accept
        value: application/json

startupProbe:
  httpGet:
    httpHeaders:
      - name: User-Agent
        value: MyUserAgent

Ви також можете видалити ці два заголовки, визначивши їх з порожнім значенням.

livenessProbe:
  httpGet:
    httpHeaders:
      - name: Accept
        value: ""

startupProbe:
  httpGet:
    httpHeaders:
      - name: User-Agent
        value: ""
СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the H2CContainerProbe feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Ви можете налаштувати HTTP-пробу для використання HTTP/2 без шифрування (h2c), встановивши поле protocol на HTTP2. Коли protocol встановлено на HTTP2, kubelet використовує h2c (HTTP/2 через звичайний TCP) замість HTTP/1.1. Типове значення protocol — HTTP1. Встановлення protocol: HTTP2 вимагає scheme: HTTP (типове значення); API-сервер відхиляє комбінацію protocol: HTTP2 з scheme: HTTPS. Для отримання додаткових відомостей див. Використання HTTP/2 без шифрування (h2c) з HTTP-пробами.

Обробка перенаправлень

Коли kubelet перевіряє контейнер за допомогою HTTP, він слідує за перенаправленнями лише у випадку, якщо перенаправлення відбувається на той самий хост. Це включає перенаправлення, які змінюють протокол з HTTP на HTTPS, навіть якщо проба налаштована з scheme: HTTP.

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

Якщо kubelet слідує за перенаправленням і отримує 11 або більше перенаправлень загалом, проба вважається успішною і записується подія ProbeWarning. Наприклад:

Events:
  Type     Reason        Age                     From               Message
  ----     ------        ----                    ----               -------
  Normal   Scheduled     29m                     default-scheduler  Successfully assigned default/httpbin-7b8bc9cb85-bjzwn to daocloud
  Normal   Pulling       29m                     kubelet            Pulling image "docker.io/kennethreitz/httpbin"
  Normal   Pulled        24m                     kubelet            Successfully pulled image "docker.io/kennethreitz/httpbin" in 5m12.402735213s
  Normal   Created       24m                     kubelet            Created container httpbin
  Normal   Started       24m                     kubelet            Started container httpbin
 Warning  ProbeWarning  4m11s (x1197 over 24m)  kubelet            Readiness probe warning: Probe terminated redirects

Увага:

Під час обробки проби httpGet kubelet припиняє читання тіла відповіді після 10KiB. Успішність проби визначається виключно кодом стану відповіді, який знаходиться в заголовках відповіді.

Якщо ви перевіряєте точку доступу, що повертає тіло відповіді більше ніж 10KiB, kubelet все одно позначить пробу як успішну на основі коду стану, але закриє зʼєднання після досягнення межі 10KiB. Це раптове закриття може спричинити появу в логах вашого застосунку помилок connection reset by peer або broken pipe errors, які важко відрізнити від реальних проблем мережі.

Для надійних проб httpGet наполегливо рекомендується використовувати спеціальні точки доступу перевірки стану, які повертають мінімальне тіло відповіді. Якщо ви повинні використовувати наявну точку доступу з великим обсягом даних, розгляньте можливість використання проби exec для виконання запиту HEAD замість цього.

TCP-проби

Для TCP-проби kubelet встановлює зʼєднання проби на вузлі, а не в Podʼі, що означає, що ви не можете використовувати назву сервісу в параметрі host, оскільки kubelet не може її розвʼязати.

gRPC-проби

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.27

Якщо ваш застосунок реалізує Протокол перевірки стану gRPC, ви можете налаштувати Kubernetes для використання його для перевірки запуску, життєздатності або готовності застосунку.

Ось приклад маніфесту:

apiVersion: v1
kind: Pod
metadata:
  name: etcd-with-grpc
spec:
  containers:
  - name: etcd
    image: registry.k8s.io/etcd:3.5.1-0
    command: [ "/usr/local/bin/etcd", "--data-dir",  "/var/lib/etcd", "--listen-client-urls", "http://0.0.0.0:2379", "--advertise-client-urls", "http://127.0.0.1:2379", "--log-level", "debug"]
    ports:
    - containerPort: 2379
    livenessProbe:
      grpc:
        port: 2379
      initialDelaySeconds: 10

Для використання gRPC-проби необхідно налаштувати port. Якщо ви хочете розрізняти проби різних типів і проби для різних функцій, ви можете використовувати поле service. Ви можете встановити service зі значенням liveness і налаштувати вашу точку доступу gRPC Health Checking так, щоб вона відповідала на цей запит інакше, ніж коли service має значення readiness. Це дозволяє використовувати ту саму точку доступу для різних видів перевірки стану контейнера, замість того щоб слухати на двох різних портах. Якщо ви хочете вказати власну назву сервісу та також вказати тип проби, проєкт Kubernetes рекомендує використовувати назву, яка обʼєднує ці значення. Наприклад: myservice-liveness (використовуючи - як роздільник).

Примітка:

На відміну від HTTP або TCP-проб, ви не можете вказати порт перевірки стану за назвою, і ви не можете налаштувати власне імʼя хоста.

Проблеми конфігурації (наприклад: неправильний порт або сервіс, не реалізований протокол перевірки стану) вважаються невдачею проби, подібно до HTTP та TCP-проб.

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GRPCContainerProbeTLS feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Ви можете налаштувати gRPC-пробу для підключення через TLS, встановивши поле mode на TLS. Коли mode встановлено на TLS, kubelet використовує TLS з InsecureSkipVerify (він не перевіряє сертифікат сервера). Типове значення mode — Plaintext. Для отримання додаткових відомостей див. Використання TLS з gRPC-пробами.

Що далі

4.1.7 - Порушення

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

Також це стосується адміністраторів кластера, які хочуть виконувати автоматизовані дії з кластером, такі як оновлення та автомасштабування кластерів.

Добровільні та невідворотні порушення

Podʼи не зникають, поки хтось (людина або контролер) не знищить їх, або не трапиться невідворотна помилка обладнання чи системного програмного забезпечення.

Ми називаємо ці невідворотні випадки невідворотними порушеннями застосунку. Приклади:

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

Окрім вичерпання ресурсів, усі ці ситуації мають бути знайомі більшості користувачів; вони не є специфічними для Kubernetes.

Ми називаємо інші випадки добровільними порушеннями. До них належать як дії, ініційовані власником застосунку, так і ті, які ініціює адміністратор кластера. Типові дії власника застосунку включають:

  • видалення Deployment або іншого контролера, який керує Podʼом
  • оновлення шаблону Podʼа в Deployment, що призводить до перезапуску
  • безпосереднє видалення Podʼа (наприклад, випадково)

Дії адміністратора кластера включають:

  • Очищення вузла для ремонту чи оновлення.
  • Очищення вузла з кластера для зменшення масштабу кластера (дізнайтеся більше про автомасштабування вузлів).
  • Видалення Podʼа з вузла, щоб щось інше помістилося на цей вузол.

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

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

Увага:

Не всі добровільні порушення обмежені бюджетами порушень Podʼів. Наприклад, видалення Deployment чи Pod обходить бюджети порушень Podʼів.

Робота з порушеннями

Ось кілька способів помʼякшення невідворотних порушень:

Частота добровільних порушень різниться. На базовому кластері Kubernetes немає автоматизованих добровільних порушень (тільки ті, які ініціює користувач). Однак адміністратор кластера або провайдер хостингу може запускати деякі додаткові сервіси, які призводять до добровільних порушень. Наприклад, розгортання оновлень програмного забезпечення вузла може призвести до добровільних порушень. Також деякі реалізації автомасштабування кластера (вузла) можуть призводити до добровільних порушень для дефрагментації та ущільнення вузлів. Адміністратор кластера або провайдер хостингу мають задокументувати, якого рівня добровільних порушень, якщо такі є, слід очікувати. Деякі параметри конфігурації, такі як використання PriorityClasses у вашій специфікації Podʼа, також можуть призводити до добровільних (і невідворотних) порушень.

Бюджети порушень Podʼів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.21

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

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

Менеджери кластерів та провайдери хостингу повинні використовувати інструменти, які дотримуються бюджетів порушень Podʼів, викликаючи Eviction API замість прямого видалення Podʼів або Deployments.

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

PDB визначає кількість реплік, яку застосунок може витримати, порівняно з тим, скільки їх має бути. Наприклад, Deployment, який має .spec.replicas: 5, повинен мати 5 Podʼів в будь-який момент часу. Якщо PDB дозволяє бути 4 Podʼам одночасно, то Eviction API дозволить добровільне порушення роботи одного (але не двох) Podʼів одночасно.

Група Podʼів, з яких складається застосунок, визначається за допомогою селектора міток, такого самого, як і той, який використовується контролером застосунку (deployment, stateful-set тощо).

"Передбачена" кількість Podʼів обчислюється з .spec.replicas ресурсу робочого навантаження, який керує цими Podʼами. Панель управління визначає ресурс робочого навантаження, що володіє Podʼом, оглядаючи .metadata.ownerReferences Podʼа.

Невідворотні порушення неможливо запобігти за допомогою PDB; однак вони враховуються в бюджеті.

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

Рекомендується встановлювати політику виселення несправних Podʼів AlwaysAllow у ваших PodDisruptionBudgets для підтримки виселення застосунків, що працюють неправильно, під час очищення вузла. Стандартно очищення чекає, доки Podʼи застосунку стануть справними, перш ніж продовжитися.

Коли Pod виселяється за допомогою API виселення, він коректно завершується з урахуванням налаштування terminationGracePeriodSeconds його PodSpec.

Приклад PodDisruptionBudget

Припустимо, що у кластері є 3 вузли, від node-1 до node-3. У кластері працює кілька застосунків. Один з них має 3 репліки, які спочатку називаються pod-a, pod-b і pod-c. Також показано інший, не повʼязаний з ними Pod без PDB — pod-x. Спочатку Podʼи розташовані так:

node-1node-2node-3
pod-a доступнийpod-b доступнийpod-c доступний
pod-x доступний

Усі 3 Podʼи є частиною Deployment, і вони разом мають PDB, який вимагає, щоб одночасно було доступними принаймні 2 з 3 Podʼів.

Наприклад, припустимо, що адміністратор кластера хоче перезавантажитися в нову версію ядра, щоб виправити помилку в ядрі. Адміністратор кластера спочатку намагається очистити node-1 за допомогою команди kubectl drain. Цей інструмент намагається виселити pod-a і pod-x. Це вдається негайно. Обидва Podʼи одночасно переходять у стан terminating. Це переводить кластер у стан:

node-1 очищуєтьсяnode-2node-3
pod-a завершуєтьсяpod-b доступнийpod-c доступний
pod-x завершується

Deployment помічає, що один з Podʼів завершується, тому він створює заміну під назвою pod-d. Оскільки node-1 закрито для планування (cordoned), заміна опиняється на іншому вузлі. Також щось створило pod-y як заміну для pod-x.

(Примітка: для StatefulSet, pod-a, який міг би мати назву щось на зразок pod-0, повинен був би повністю завершити свою роботу, перш ніж його заміна, яка також має назву pod-0, але має інший UID, може бути створена. В іншому випадку приклад застосовується і до StatefulSet.)

Тепер кластер перебуває у такому стані:

node-1 очищуєтьсяnode-2node-3
pod-a завершуєтьсяpod-b доступнийpod-c доступний
pod-x завершуєтьсяpod-d запускаєтьсяpod-y

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

node-1 очищенийnode-2node-3
pod-b доступнийpod-c доступний
pod-d запускаєтьсяpod-y

На цьому етапі, якщо нетерплячий адміністратор кластера намагається очистити node-2 або node-3, команда очищення буде заблокована, оскільки доступно тільки 2 Podʼи для Deployment, і його PDB вимагає принаймні 2. Після того, як пройде певний час, pod-d стає доступним.

Тепер стан кластера виглядає так:

node-1 очищенийnode-2node-3
pod-b доступнийpod-c доступний
pod-d доступнийpod-y

Тепер адміністратор кластера намагається очистити node-2. Команда drain спробує виселити два Podʼи в певному порядку, скажімо, спочатку pod-b, а потім pod-d. Їй вдасться виселити pod-b. Але коли вона спробує виселити pod-d, то отримає відмову, через те, що це залишить тільки один доступний Pod для Deployment.

Deployment створює заміну для pod-b з назвою pod-e. Оскільки в кластері недостатньо ресурсів для планування pod-e, очищення знову буде заблоковано. Кластер може опинитися в такому стані:

node-1 очищенийnode-2node-3немає вузла
pod-b завершуєтьсяpod-c доступнийpod-e очікує
pod-d доступнийpod-y

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

Ви можете побачити, як Kubernetes змінює частоту випадків порушень відповідно до:

  • скільки реплік потрібно для застосунку
  • скільки часу потрібно для коректного вимкнення екземпляра
  • скільки часу потрібно для запуску нового екземпляра
  • типу контролера
  • місткості ресурсів кластера

Стани порушення роботи Podʼа

This is a stable feature in Kubernetes, and has been since the 1.31 release. You can no longer toggle this feature (the associated feature gate has been removed).

Спеціальний стан Podʼа DisruptionTarget додається для того, щоб вказати, що Pod буде видалено через порушення. Поле reason цього стану додатково вказує одну з таких причин завершення Podʼа:

PreemptionByScheduler
Pod має бути витіснений планувальником, щоб звільнити місце для нового Podʼа з вищим пріоритетом. Докладніше дивіться Витіснення за пріоритетом Podʼа.
DeletionByTaintManager
Pod має бути видалений Менеджером Taint (який є частиною контролера життєвого циклу вузла в kube-controller-manager) через NoExecute taint, який Pod не толерує; див. виселення на основі taint.
EvictionByEvictionAPI
Pod був позначений для виселення за допомогою API Kubernetes.
DeletionByPodGC
Pod, який повʼязаний із вузлом, якого вже не існує, має бути видалений за допомогою збору сміття Podʼів.
TerminationByKubelet
Pod було завершено kubelet через виселення внаслідок тиску на вузол, коректне вимкнення вузла або витіснення заради системно критичних Podʼів.

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

Примітка:

Процес порушення роботи Podʼа може бути зупинено. Панель управління може повторно спробувати продовжити порушення роботи того самого Podʼа, але це не гарантовано. В результаті стан DisruptionTarget може бути додано до Podʼа, але цей Pod може фактично не бути видаленим. У такій ситуації через деякий час стан порушення роботи Podʼа буде очищено.

Разом з очищенням Podʼів механізм збору сміття Podʼів (PodGC) також позначає їх як невдалі, якщо вони перебувають у нетермінальній фазі (див. також збір сміття Podʼів).

Під час використання Job (або CronJob) ви можете використовувати ці стани порушення роботи Podʼів як частину політики збоїв Podʼа вашого Job.

Розділення ролей власника кластера та власника застосунку

Часто корисно розглядати менеджера кластера і власника застосунку як окремі ролі з обмеженим знанням одне про одного. Це розділення обовʼязків може мати сенс у таких сценаріях:

  • коли багато команд розробки застосунків використовують спільний кластер Kubernetes і є природна спеціалізація ролей
  • коли використовуються інструменти або сервіси сторонніх розробників для автоматизації управління кластером

Бюджети порушень Podʼів підтримують це розділення ролей, надаючи інтерфейс між цими ролями.

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

Як виконувати дії, що порушують роботу, у вашому кластері

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

  • Прийняти простій під час оновлення.
  • Перемикнутися на іншу повну репліку кластера.
    • Відсутність простою, але може бути дорогою як для дубльованих вузлів, так і для зусиль людей для оркестрування перемикання.
  • Розробляти застосунки, стійкі до порушень, і використовувати PDB.
    • Відсутність простою.
    • Мінімальне дублювання ресурсів.
    • Дозволяє більше автоматизації адміністрування кластера.
    • Написання застосунків, стійких до порушень, складне, але робота над стійкістю до добровільних порушень здебільшого збігається з роботою над підтримкою автомасштабування та стійкістю до невідворотних порушень.

Що далі

4.1.8 - Імʼя хоста Podʼа

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

Стандартне імʼя хоста Podʼа

Коли створюється Pod, імʼя його хоста (яке спостерігається зсередини Podʼа) походить від значення metadata.name Podʼа. І імʼя хоста, і відповідне повністю кваліфіковане доменне імʼя (FQDN) встановлюються у значення metadata.name (з точки зору Podʼа)

apiVersion: v1
kind: Pod
metadata:
  name: busybox-1
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

Pod, створений цим маніфестом, матиме імʼя хоста та повністю кваліфіковане доменне імʼя (FQDN) встановлені як busybox-1.

Імʼя хоста з полями hostname та subdomain Podʼа

Специфікація Podʼа включає необовʼязкове поле hostname. Коли воно встановлене, це значення має пріоритет над metadata.name Podʼа як імʼя хоста (яке спостерігається зсередини Podʼа). Наприклад, Pod з spec.hostname встановленим як my-host матиме імʼя хоста встановлене як my-host.

Специфікація Podʼа також включає необовʼязкове поле subdomain, яке вказує, що Pod належить до піддомену в межах свого простору імен. Якщо Pod має spec.hostname встановлений як "foo" і spec.subdomain встановлений як "bar" у просторі імен my-namespace, імʼя його хоста стає foo, а його повністю кваліфіковане доменне імʼя (FQDN) стає foo.bar.my-namespace.svc.cluster-domain.example (спостерігається зсередини Podʼа).

Коли встановлені і hostname, і subdomain, DNS-сервер кластера створить записи A та/або AAAA на основі цих полів. Дивіться: Поля hostname та subdomain Podʼа.

Імʼя хоста з полем setHostnameAsFQDN Podʼа

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.22

Коли Pod налаштований на використання повністю кваліфікованого доменного імені (FQDN), його імʼя хоста є коротким імʼям хоста. Наприклад, якщо у вас є Pod з повністю кваліфікованим доменним імʼям busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example, то стандартно команда hostname всередині цього Podʼа повертає busybox-1, а команда hostname --fqdn повертає FQDN.

Коли в специфікації Podʼа встановлено і setHostnameAsFQDN: true, і поле subdomain, kubelet записує FQDN Podʼа як імʼя хоста для простору імен цього Podʼа. У цьому випадку і hostname, і hostname --fqdn повертають FQDN Podʼа.

FQDN Podʼа будується так само, як було визначено раніше. Воно складається з поля spec.hostname Podʼа (якщо вказано) або поля metadata.name, spec.subdomain, назви простору імен та суфікса домену кластера.

Примітка:

У Linux поле імені хоста ядра (поле nodename структури utsname) обмежено 64 символами.

Якщо Pod вмикає цю функцію, а його FQDN довший за 64 символи, він не зможе запуститися. Pod залишатиметься в статусі Pending (відображається як ContainerCreating через kubectl), генеруючи події помилок, такі як "Failed to construct FQDN from Pod hostname and cluster domain".

Це означає, що під час використання цього поля ви повинні переконатися, що сумарна довжина полів metadata.name (або spec.hostname) та spec.subdomain Podʼа не призводить до появи FQDN, що перевищує 64 символи.

Імʼя хоста з полем hostnameOverride Podʼа

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.37; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.37. It was first available in the v1.34 release.

Встановлення значення для hostnameOverride в специфікації Podʼа змушує kubelet безумовно встановлювати як імʼя хоста, так і повністю кваліфіковане доменне імʼя (FQDN) Podʼа у значення hostnameOverride.

Поле hostnameOverride має обмеження довжини 64 символи і повинно відповідати стандарту імен піддоменів DNS, визначеному в RFC 1123.

Приклад:

apiVersion: v1
kind: Pod
metadata:
  name: busybox-2-busybox-example-domain
spec:
  hostnameOverride: busybox-2.busybox.example.domain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

Примітка:

Це впливає лише на імʼя хоста всередині Podʼа; це не впливає на записи A або AAAA Podʼа в DNS-сервері кластера.

Якщо hostnameOverride встановлено разом з полями hostname та subdomain:

  • Імʼя хоста всередині Podʼа перевизначається значенням hostnameOverride.

  • Записи A та/або AAAA Podʼа в DNS-сервері кластера все ще генеруються на основі полів hostname та subdomain.

Примітка: Якщо встановлено hostnameOverride, ви не можете одночасно встановлювати поля hostNetwork та setHostnameAsFQDN. API-сервер явно відхилить будь-який запит на створення, що намагається використати цю комбінацію.

Для деталей щодо поведінки, коли hostnameOverride встановлено в комбінації з іншими полями (hostname, subdomain, setHostnameAsFQDN, hostNetwork), див. таблицю в деталях проєкту KEP-4762.

4.1.9 - Класи якості обслуговування (Quality of Service) Podʼів

Ця сторінка представляє класи якості обслуговування (Quality of Service, QoS) в Kubernetes та пояснює, як Kubernetes присвоює кожному Podʼу клас QoS як наслідок обмежень ресурсів, які ви вказуєте для контейнерів у цьому Podʼі. Kubernetes покладається на цю класифікацію для прийняття рішень про те, які Podʼи виселяти, коли на вузлі бракує доступних ресурсів.

Класи якості обслуговування

Kubernetes класифікує Podʼи, які ви запускаєте, і розподіляє кожен Pod у певний клас якості обслуговування (quality of service, QoS). Kubernetes використовує цю класифікацію, щоб впливати на те, як обробляються різні Podʼи. Kubernetes робить цю класифікацію на основі запитів ресурсів контейнерів у цьому Podʼі, а також того, як ці запити співвідносяться з обмеженнями ресурсів. Це відомо як клас якості обслуговування (QoS). Kubernetes присвоює кожному Podʼу клас QoS на основі запитів та обмежень ресурсів його складових контейнерів. Kubernetes використовує класи QoS, щоб вирішити, які Podʼи виселяти з вузла, що перебуває під тиском на вузол. Можливі класи QoS: Guaranteed, Burstable та BestEffort. Коли на вузлі закінчуються ресурси, Kubernetes спочатку виселяє Podʼи BestEffort, що працюють на цьому вузлі, потім Burstable і нарешті Guaranteed. Коли це виселення спричинене тиском на ресурси, кандидатами на виселення є лише Podʼи, що перевищують запити ресурсів.

Guaranteed

Podʼи, які мають клас Guaranteed, мають найстрогіші обмеження ресурсів і найменшу ймовірність бути виселеними. Гарантується, що їх не буде завершено, доки вони не перевищать свої обмеження або доки не залишиться Podʼів з нижчим пріоритетом, які можна витіснити з вузла. Вони не можуть отримувати ресурси понад вказані обмеження. Ці Podʼи також можуть використовувати ексклюзивні CPU за допомогою політику управління CPU типу static.

Критерії

Щоб Pod отримав клас QoS Guaranteed:

  • Кожен контейнер в Podʼі повинен мати обмеження та запит на памʼять, обидва більші за нуль.
  • Для кожного контейнера у Podʼі обмеження памʼяті повинен дорівнювати запиту памʼяті.
  • Кожен контейнер в Podʼі повинен мати обмеження та запит на CPU, обидва більші за нуль.
  • Для кожного контейнера у Podʼі обмеження CPU повинен дорівнювати запиту CPU.

Якщо ж Pod використовує ресурси на рівні Podʼа:

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.34; (стандартно увімкнено)
  • Pod повинен мати обмеження памʼяті на рівні Podʼа і запит на памʼять, і їхні значення повинні бути рівними.
  • Pod повинен мати обмеження та запит CPU на рівні Podʼа, і їхні значення повинні бути рівними.

Burstable

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

Критерії

Pod отримує клас QoS Burstable, якщо:

  • Pod не відповідає критеріям класу QoS Guaranteed.
  • Принаймні один контейнер у Podʼі має запит або обмеження памʼяті чи CPU, або Pod має запит або обмеження памʼяті чи CPU на рівні Podʼа.

BestEffort

Podʼи в класі BestEffort можуть використовувати ресурси вузла, які не призначені спеціально для Podʼів інших класів QoS. Наприклад, якщо у вас є вузол із 16 ядрами CPU, доступними для kubelet, і ви призначили 4 ядра CPU Podʼу з класом Guaranteed, Pod у класі QoS BestEffort може намагатися використовувати будь-яку кількість із решти 12 ядер CPU.

Kubelet віддає перевагу виселенню Podʼів з класом BestEffort, якщо вузол потрапляє в стан тиску на ресурси.

Критерії

Pod має клас QoS BestEffort, якщо він не відповідає критеріям ані Guaranteed, ані Burstable. Іншими словами, Pod має клас BestEffort лише в тому випадку, якщо жоден з контейнерів у Podʼі не має обмеження або запиту памʼяті, і жоден з контейнерів у Podʼі не має обмеження або запиту CPU, а Pod не має жодних обмежень або запитів щодо памʼяті або CPU на рівні Podʼа. Контейнери в Podʼі можуть запитувати інші ресурси (не CPU чи памʼять) і все одно класифікуватися як BestEffort.

Memory QoS з cgroup v2

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно увімкнено)

Memory QoS використовує контролер памʼяті cgroup v2 для керування тротлінгом і захистом памʼяті в Kubernetes. Ця функція використовує клас QoS Podʼа для визначення, які налаштування cgroup слід застосувати, але вона не впливає на класифікацію Podʼів.

Типова конфігурація kubelet не вмикає тротлінг памʼяті або захист памʼяті. Щоб увімкнути будь-яку з цих поведінок, налаштуйте memoryThrottlingFactor або memoryReservationPolicy відповідно.

Тротлінг памʼяті

Тротлінг памʼяті контролюється полем конфігурації kubelet memoryThrottlingFactor. Його типове значення — nil, що означає, що kubelet не встановлює memory.high. Щоб увімкнути тротлінг памʼяті, встановіть memoryThrottlingFactor у значення більше 0 та менше або рівне 1.

Для Podʼів типу Burstable kubelet використовує memory.high, щоб застосувати тротлінг до виділення памʼяті до того, як робоче навантаження досягне свого жорсткого обмеження (memory.max). Поріг тротлінгу для контейнера обчислюється за такою формулою:

memory.high = requests + memoryThrottlingFactor * (limits - requests)

Наприклад, з memoryThrottlingFactor, встановленим на 0.9, контейнер із запитом 256 MiB і обмеженням 1 GiB отримає memory.high, встановлений приблизно на 947 MiB. Якщо контейнер Burstable не має обмеження памʼяті, kubelet використовує доступну для розподілу памʼять вузла замість обмеження.

Для контейнерів BestEffort, які не мають запиту або обмеження памʼяті, kubelet обчислює memory.high, використовуючи нульовий запит та доступну для розподілу памʼять вузла замість обмеження. Контейнери Guaranteed не отримують memory.high, оскільки їхні запити дорівнюють обмеженням.

Налаштування резервування памʼяті

Резервування памʼяті контролюється через поле конфігурації kubelet memoryReservationPolicy:

  • None (стандартно): kubelet не встановлює memory.min або memory.low для контейнерів і Podʼів. Жодна памʼять не блокується ядром жорстко.
  • TieredReservation: kubelet встановлює багаторівневий захист памʼяті на основі класу QoS Podʼа:
    • Podʼи Guaranteed: memory.min встановлюється на запити памʼяті. Ядро не буде звільняти цю памʼять за жодних обставин.
    • Podʼи Burstable: memory.low встановлюється на запити памʼяті. Ядро переважно зберігає цю памʼять, але може звільнити її за надзвичайного тиску.
    • Podʼи BestEffort: захист памʼяті не встановлюється.

Наприклад, така конфігурація kubelet вмикає тротлінг памʼяті з коефіцієнтом 0.9 та багаторівневий захист памʼяті:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
memoryThrottlingFactor: 0.9
memoryReservationPolicy: TieredReservation

Ви можете налаштовувати кожне поле незалежно. Опустіть memoryThrottlingFactor, щоб kubelet не встановлював memory.high, або встановіть memoryReservationPolicy: None, щоб вимкнути захист памʼяті.

Оскільки захист памʼяті cgroup v2 є ієрархічним, kubelet також налаштовує батьківські cgroup. Він встановлює memory.min на кореневій cgroup kubepods як суму запитів памʼяті для Podʼів Guaranteed та Burstable. Він встановлює memory.low на кореневій cgroup kubepods та cgroup QoS Burstable як суму запитів памʼяті для Podʼів Burstable. Без такого покриття батьківських cgroup захист на рівні Podʼа та контейнера був би неефективним.

Увага:

Для Podʼа Guaranteed запити памʼяті дорівнюють обмеженням памʼяті. З memoryReservationPolicy: TieredReservation memory.min тому дорівнює memory.max. Для робочого навантаження, яке використовує великий кеш сторінок, ядро може бути не в змозі звільнити достатньо кешу сторінок, перш ніж cgroup досягне memory.max, що може призвести до завершення через OOM (OOM kill). Встановіть обмеження памʼяті так, щоб включити достатній запас для кешу сторінок.

Вимкнення або відкочування Memory QoS

Щоб вимкнути Memory QoS, встановіть функціональну можливість MemoryQoS на false, переконайтеся, що memoryReservationPolicy не встановлено або встановлено у None, та перезапустіть kubelet. Під час запуску kubelet скидає memory.min та memory.low на кореневій cgroup kubepods та memory.low на cgroup QoS Burstable до нуля. Значення memory.min та memory.low на рівні Podʼа та контейнера можуть залишатися, але вони неефективні, оскільки відповідний захист батьківських cgroup дорівнює нулю.

Коли Memory QoS вимкнено, kubelet встановлює memory.high на max щоразу, коли середовище виконання контейнерів застосовує конфігурацію ресурсів до нового, перезапущеного або зміненого на місці контейнера. Вже запущений контейнер, який не перезапускається та не змінює розмір, може зберігати своє попереднє значення memory.high до наступного перезапуску або зміни розміру на місці.

Системні вимоги

Для роботи Memory QoS необхідна версія Linux із cgroup v2. Рекомендується використовувати ядро версії 5.9 або вище, оскільки обмеження memory.high у старих версіях ядра може спричинити відому помилку livelock. Якщо функція MemoryQoS увімкнена на старішому ядрі, kubelet записує попередження в лог під час запуску.

Деяка поведінка незалежна від класу QoS

Деяка поведінка є незалежною від класу QoS, який визначає Kubernetes. Наприклад:

  • Будь-який контейнер, що перевищує обмеження ресурсів, буде завершено та перезапущено kubelet без впливу на інші контейнери в цьому Podʼі.

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

  • Запит ресурсів Podʼа дорівнює сумі запитів ресурсів його складових контейнерів, а обмеження Podʼа дорівнює сумі обмежень ресурсів його складових контейнерів.

  • Планувальник kube-scheduler не враховує клас QoS під час вибору того, які Podʼи витіснити. Витіснення може відбуватися, коли кластер не має достатньо ресурсів для запуску всіх визначених вами Podʼів.

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

Що далі

4.1.10 - Група планування

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Ви можете повʼязати Pod з PodGroup, щоб вказати, що Pod належить до групи Pods, які плануються разом. Це дозволяє планувальнику застосовувати політики на рівні групи, такі як групове планування, замість того, щоб розглядати кожен Pod окремо.

Визначення групи планування

Коли увімкнено функціональну можливість GenericWorkload, ви можете встановити поле spec.schedulingGroup у маніфесті Pod. Це поле встановлює звʼязок із конкретним обʼєктом PodGroup у тому самому просторі імен за назвою.

apiVersion: v1
kind: Pod
metadata:
  name: worker-0
  namespace: some-ns
spec:
  schedulingGroup:
    podGroupName: training-worker-0
  containers:
  - name: ml-worker
    image: training:v1

Поле schedulingGroup є незмінним. Після встановлення Pod не можна перемістити до іншого PodGroup.

Поведінка

Коли ви встановлюєте spec.schedulingGroup, планувальник шукає вказаний PodGroup і застосовує визначену в ньому політику планування:

  • Якщо PodGroup використовує політику basic, кожен Pod планується незалежно, використовуючи стандартну поведінку Kubernetes. Групування використовується як мітка на рівні групи.
  • Якщо PodGroup використовує політику gang, Pod входить у життєвий цикл планування "все або нічого". Планувальник намагається розмістити принаймні minCount Pods у групі одночасно; жоден з них не привʼязується до вузлів, якщо мінімум не досягнуто.
СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість CompositePodGroup увімкнено, PodGroup також може вказувати батьківський CompositePodGroup. В ієрархічному робочому навантаженні планування регулюється політиками, визначеними для всього дерева груп (такими як багаторівневе групове планування або обмеження топології).

Відсутні посилання на групи

Якщо Pod посилається на PodGroup, що ще не існує, Pod залишається в стані очікування. Аналогічно, якщо згаданий PodGroup вказує батьківський CompositePodGroup (через spec.parentCompositePodGroupName), який ще не створено, планування не починається, і Pod залишається в стані очікування, доки вся ієрархія груп не існуватиме в кластері.

Планувальник автоматично розглядає Pod повторно, щойно всі необхідні ресурси PodGroup та CompositePodGroup будуть існувати.

Що далі

4.1.11 - Статичні Podʼи

Статичні Podʼи (Static Pods) керуються безпосередньо демоном kubelet на конкретному вузлі, без спостереження за ними з боку API-сервера. На відміну від Podʼів, які керуються панеллю управління (наприклад, Deployment), kubelet стежить за кожним статичним Podʼом і перезапускає його у разі збою.

Статичні Podʼи завжди привʼязані до одного kubelet на конкретному вузлі.

Основне призначення статичних Podʼів — запуск самостійно розміщеної панелі управління: іншими словами, використання kubelet для контролю окремих компонентів панелі управління. Наприклад, kubeadm використовує статичні Podʼи для запуску kube-apiserver, kube-controller-manager, kube-scheduler та etcd на вузлах панелі управління.

Примітка:

Якщо ваш кластер запускає компоненти панелі управління як Podʼи, вони, ймовірно, є статичними Podʼами. Ви можете розпізнати їхні дзеркальні Podʼи в просторі імен kube-system за анотацією kubernetes.io/config.mirror.

Дзеркальні Podʼи

Kubelet автоматично намагається створити дзеркальний Pod на API-сервері Kubernetes для кожного статичного Podʼа. Це означає, що Podʼи, що працюють на вузлі, видимі на API-сервері, але керувати ними звідти не можна. До назв Podʼів буде додано імʼя хоста вузла з дефісом перед ним.

Kubelet передає мітки зі статичного Podʼа до дзеркального Podʼа. Ви можете використовувати ці мітки як зазвичай через селектори.

Якщо ви спробуєте використати kubectl для видалення дзеркального Podʼа з API-сервера, kubelet не видалить статичний Pod. Kubelet створить дзеркальний Pod заново.

Обмеження

Специфікація статичного Podʼа не може посилатися на інші обʼєкти API, такі як ServiceAccount, ConfigMap або Secret.

Статичні Podʼи не підтримують ефемерні контейнери.

Статичні Podʼи чи DaemonSets

Якщо ви запускаєте кластер Kubernetes і використовуєте статичні Podʼи для запуску Podʼів на кожному вузлі, вам, ймовірно, слід натомість використовувати DaemonSet.

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

Статичні Podʼи запускаються kubelet до того, як API-сервер стане доступним, що робить їх придатними для ініціалізації компонентів панелі управління. DaemonSets потребують запущеної панелі управління.

Що далі

4.1.12 - Простори імен користувачів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.36
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.36. It was first available in the v1.28 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate UserNamespacesSupport, Kubernetes ignores it but does not report any error.

Ця сторінка пояснює, як використовуються простори імен користувачів у Podʼах Kubernetes. Простір імен користувачів ізолює користувача, який працює всередині контейнера, від користувача на хості.

Процес, який працює як root у контейнері, може працювати на хості як інший користувач (не root); іншими словами, процес має повні привілеї для операцій всередині простору імен користувачів, але не має привілеїв для операцій за його межами.

Ви можете використовувати цю функцію, щоб зменшити шкоду, яку скомпрометований контейнер може заподіяти хосту або іншим Podʼам на тому самому вузлі. Є кілька вразливостей безпеки, які оцінено як ВИСОКІ або КРИТИЧНІ, і які неможливо було використати, коли простори імен користувачів активні. Очікується, що простори імен користувачів також помʼякшуватимуть деякі майбутні вразливості.

Перш ніж ви розпочнете

Примітка: Цей розділ містить посилання на проєкти сторонніх розробників, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Проєкти вказано в алфавітному порядку. Щоб додати проєкт до цього списку, ознайомтеся з посібником з контенту перед надсиланням змін. Докладніше.

Це функція, доступна лише для Linux, і потребує підтримки в Linux для монтування idmap на використовуваних файлових системах. Це означає:

  • На вузлі файлова система, яку ви використовуєте для /var/lib/kubelet/pods/, або власна тека, яку ви налаштовуєте для цього, повинна підтримувати монтування idmap.
  • Всі файлові системи, які використовуються в томах Podʼа, повинні підтримувати монтування idmap.

На практиці це означає, що вам потрібне ядро Linux принаймні версії 6.3, оскільки tmpfs почав підтримувати монтування idmap у цій версії. Це зазвичай потрібно, оскільки кілька функцій Kubernetes використовують tmpfs (токен ServiceAccount, який монтується стандартно, використовує tmpfs, Secrets також використовують tmpfs тощо).

Деякі популярні файлові системи, які підтримують монтування idmap в Linux 6.3, — це: btrfs, ext4, xfs, fat, tmpfs, overlayfs.

Крім того, середовище виконання контейнерів та його базове середовище виконання OCI повинні підтримувати простори імен користувачів. Підтримку надають такі середовища виконання OCI:

  • crun версії 1.9 або вище (рекомендована версія 1.13+).
  • runc версії 1.2 або вище.

Для використання просторів імен користувачів з Kubernetes також потрібне середовище виконання контейнерів CRI, щоб використовувати цю функцію з Podʼами Kubernetes:

  • containerd: версія 2.0 (і новіші) підтримує простори імен користувачів для контейнерів.
  • CRI-O: версія 1.25 (і новіші) підтримує простори імен користувачів для контейнерів.

Стан підтримки просторів імен користувачів в cri-dockerd відстежується у тікеті на GitHub.

Вступ

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

Pod може увімкнути використання просторів імен користувачів, встановивши поле pod.spec.hostUsers в false.

Kubelet вибере UID/GID хоста, на які зіставляється Pod, і зробить це так, щоб жодні два Podʼи на одному вузлі не використовували однакове зіставлення.

Поля runAsUser, runAsGroup, fsGroup тощо в pod.spec завжди посилаються на користувача всередині контейнера. Ці користувачі використовуватимуться для монтування томів (вказаних у pod.spec.volumes), і тому UID/GID хоста не матимуть жодного впливу на запис/читання з томів, які може монтувати Pod. Іншими словами, inode, створені/зчитані у томах, змонтованих Podʼом, будуть такими самими, як якби Pod не використовував простори імен користувачів.

Таким чином, Pod може легко вмикати та вимикати простори імен користувачів (не впливаючи на права власності на файли томів), а також надавати спільний доступ до томів з Podʼами без просторів імен користувачів, просто вказавши відповідних користувачів всередині контейнера (RunAsUser, RunAsGroup, fsGroup тощо). Це стосується будь-якого тому, який може бути змонтовано в Pod, включно з hostPath (якщо Podʼу дозволено монтувати томи hostPath).

Стандартно дійсні UID/GID, коли цю функцію активовано, лежать у діапазоні 0-65535. Це стосується файлів та процесів (runAsUser, runAsGroup тощо).

Файли з UID/GID за межами цього діапазону вважатимуться такими, що належать до overflow ID, зазвичай 65534 (налаштовано в /proc/sys/kernel/overflowuid та /proc/sys/kernel/overflowgid). Однак змінити ці файли неможливо, навіть якщо працювати як користувач/група 65534.

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

Більшість застосунків, які потребують запуску з правами root, але не мають доступу до інших просторів імен хоста чи ресурсів, повинні продовжувати працювати без змін, якщо простори імен користувачів активовано.

Розуміння просторів імен користувачів для Podʼів

Кілька середовищ виконання контейнерів із їхньою типовою конфігурацією (таких як Docker Engine, containerd, CRI-O) використовують простори імен Linux для ізоляції. Існують інші технології, які також можна використовувати з цими середовищами (наприклад, Kata Containers використовує віртуальні машини замість просторів імен Linux). Ця сторінка стосується середовищ виконання контейнерів, які використовують простори імен Linux для ізоляції.

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

Це означає, що контейнери можуть працювати як root і зіставлятися з користувачем, який не є root, на хості. Всередині контейнера процес буде вважати себе root (і, отже, інструменти типу apt, dnf тощо працюватимуть нормально), тоді як насправді процес не має привілеїв на хості. Ви можете перевірити це, наприклад, дізнавшись, від імені якого користувача працює процес контейнера, виконавши ps aux на хості. Користувач, якого показує ps, не той самий, що і користувач, якого ви бачите, якщо ви виконаєте команду id всередині контейнера.

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

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

Можливості, надані Podʼу, також обмежуються простором імен користувачів Podʼа і здебільшого є недійсними поза ним, а деякі навіть абсолютно нечинні. Ось два приклади:

  • CAP_SYS_MODULE не має жодного ефекту, якщо його надано Podʼу, що використовує простори імен користувачів, Pod не може завантажити модулі ядра.
  • CAP_SYS_ADMIN обмежений простором імен користувачів Podʼа та є недійсним поза його межами.

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

Якщо ви хочете дізнатися більше подробиць про те, що змінюється під час використання просторів імен користувачів, дивіться man 7 user_namespaces.

Налаштування вузла для підтримки просторів імен користувачів

Типово kubelet призначає UID/GID Podʼам вище діапазону 0-65535, ґрунтуючись на припущенні, що файли та процеси хоста використовують UID/GID у цьому діапазоні, що є стандартним для більшості дистрибутивів Linux. Цей підхід запобігає будь-якому перетину між UID/GID хоста та UID/GID Podʼів.

Уникнення перетину є важливим для помʼякшення впливу вразливостей, таких як CVE-2021-25741, де потенційно Pod може читати довільні файли на хості. Якщо UID/GID Podʼа та хоста не перетинаються, те, що може робити Pod, обмежено: UID/GID Podʼа не буде відповідати власнику/групі файлів хоста.

Kubelet може використовувати власний діапазон ідентифікаторів користувачів та груп для Podʼів. Для налаштування власного діапазону вузол повинен мати:

  • Користувача kubelet в системі (ви не можете використовувати інше імʼя користувача тут)
  • Встановлений бінарний файл getsubids (частина shadow-utils), доступний у PATH для бінарного файлу kubelet.
  • Конфігурацію допоміжних UID/GID для користувача kubelet (див. man 5 subuid та man 5 subgid).

Цей параметр лише збирає конфігурацію діапазону UID/GID та не змінює користувача, який виконує kubelet.

Вам потрібно дотримуватися деяких обмежень для допоміжного діапазону ID, який ви призначаєте користувачу kubelet:

  • Допоміжний ідентифікатор користувача, який визначає початок діапазону UID для Podʼів, має бути кратним 65536 і також повинен бути більшим або рівним 65536. Іншими словами, ви не можете використовувати жодний ідентифікатор з діапазону 0-65535 для Podʼів; kubelet накладає це обмеження, щоб ускладнити створення ненавмисно незахищеної конфігурації.

  • Кількість допоміжних ID повинна бути кратною 65536.

  • Кількість допоміжних ID повинна бути щонайменше 65536 x <maxPods>, де <maxPods> — максимальна кількість Podʼів, які можуть запускатися на вузлі.

  • Ви повинні призначити однаковий діапазон як для ідентифікаторів користувача, так і для ідентифікаторів груп. Не має значення, чи мають інші користувачі діапазони ідентифікаторів користувача, що не збігаються з діапазонами ідентифікаторів груп.

  • Жоден з призначених діапазонів не повинен перетинатися з жодним іншим призначенням.

  • Конфігурація допоміжних ID повинна бути лише одним рядком. Іншими словами, ви не можете мати кілька діапазонів.

Наприклад, ви можете визначити /etc/subuid та /etc/subgid, щоб обидва мали такі записи для користувача kubelet:

# Формат такий
#   імʼя:firstID:кількість ідентифікаторів
# де
# - firstID дорівнює 65536 (мінімальне можливе значення)
# - кількість ідентифікаторів дорівнює 110 * 65536
#   (110 — стандартне обмеження кількості Podʼів на вузлі)

kubelet:65536:7208960

Примітка, якщо ви переконфігуровуєте вузол

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

Конфігурацію слід змінювати, коли на вузлі не запущено жодного Podʼа, що використовує простори імен користувачів. Під час зміни на вузлі, де працюють будь-які Podʼи з просторами імен користувачів, вам потрібно спочатку очистити вузол перед застосуванням конфігурації та перезапуском kubelet. Під час очищення вузла памʼятайте, що Podʼи DaemonSet або інші Podʼи, що толерують taint unschedulable, не будуть виселені.

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

Кількість ID для кожного Podʼа

Починаючи з Kubernetes v1.33 можна встановити кількість ID для кожного Podʼа в KubeletConfiguration.

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
userNamespaces:
  idsPerPod: 1048576

Значення idsPerPod (uint32) має бути кратним 65536. Стандартне значення — 65536. Це значення застосовується лише до контейнерів, створених після запуску kubelet з цією KubeletConfiguration. На запущені контейнери ця конфігурація не впливає.

У версіях Kubernetes до v1.33 кількість ID для кожного Podʼа була жорстко встановлена на рівні 65536.

Інтеграція з перевіркою допуску безпеки Podʼів

Для Podʼів Linux, які увімкнули простори імен користувачів, Kubernetes послаблює застосування Стандартів безпеки Podʼа контрольованим способом.

Якщо ви створите Pod, який використовує простори імен користувачів, такі поля не будуть обмежені навіть у контекстах, які застосовують стандарт безпеки Podʼів Baseline чи Restricted. Це не становить проблему безпеки, оскільки root всередині Podʼа з просторами імен користувачів фактично посилається на користувача всередині контейнера, який ніколи не зіставляється з привілейованим користувачем на хості. Ось список полів, які не перевіряються для Podʼів в цих обставинах:

  • spec.securityContext.runAsNonRoot
  • spec.containers[*].securityContext.runAsNonRoot
  • spec.initContainers[*].securityContext.runAsNonRoot
  • spec.ephemeralContainers[*].securityContext.runAsNonRoot
  • spec.securityContext.runAsUser
  • spec.containers[*].securityContext.runAsUser
  • spec.initContainers[*].securityContext.runAsUser
  • spec.ephemeralContainers[*].securityContext.runAsUser

Крім того, якщо Pod перебуває в контексті зі стандартом безпеки Podʼів Baseline, перевірка таких полів також буде помʼякшена:

  • spec.containers[*].securityContext.procMount
  • spec.initContainers[*].securityContext.procMount
  • spec.ephemeralContainers[*].securityContext.procMount

Зі стандартом безпеки Podʼів Restricted Pod все одно повинен використовувати лише стандартний або порожній ProcMount.

Обмеження

Під час використання просторів імен користувачів для Podʼа заборонено використовувати інші простори імен хоста. Зокрема, якщо ви встановите hostUsers: false, ви не маєте права встановлювати жодне з такого:

  • hostNetwork: true
  • hostIPC: true
  • hostPID: true

Жоден контейнер також не може використовувати volumeDevices (блокові томи raw, такі як /dev/sda). Це стосується всіх масивів контейнерів у специфікації Podʼа:

  • containers
  • initContainers
  • ephemeralContainers

Підтримка файлової системи

Podʼи, які використовують простір імен користувачів, потребують, щоб файлова система підтримувала монтування idmap. Деякі файлові системи не підтримують монтування idmap, і тому не можуть використовуватися з просторами імен користувачів. У таких випадках будуть згенеровані такі події. Зверніть увагу, що деталі попередження залежать від середовища виконання контейнерів, яке ви використовуєте.

Warning  Failed 1s kubelet Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: failed to fulfil mount request: failed to set MOUNT_ATTR_IDMAP on ${your mount path} invalid argument (maybe the filesystem used doesn't support idmap mounts on this kernel?): unknown

Томи NFS не можуть бути змонтовані в Pod з простором імен користувачів, оскільки клієнт Linux NFS ще не підтримує монтування idmap. Для поточного списку підтримуваних файлових систем дивіться сторінку керівництва Linux mount_setattr(2).

Метрики та спостережуваність

Kubelet експортує дві метрики prometheus, специфічні для просторів імен користувачів:

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

Що далі

4.1.13 - Downward API

Є два способи надати поля Podʼа та контейнера запущеному контейнеру: змінні середовища та файли, які заповнюються спеціальним типом тому. Разом ці два способи надання полів Podʼа та контейнера називають downward API.

Іноді корисно, щоб контейнер мав інформацію про себе, не перебуваючи занадто повʼязаним із Kubernetes. Downward API дозволяє контейнерам використовувати інформацію про себе чи кластер, не використовуючи клієнт Kubernetes або API-сервер.

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

У Kubernetes є два способи надати поля Podʼа та контейнера запущеному контейнеру:

Разом ці два способи надання полів Podʼа та контейнера називають downward API.

Доступні поля

Через downward API доступні лише деякі поля API Kubernetes. У цьому розділі перелічено поля, які ви можете зробити доступними.

Ви можете передавати інформацію з доступних полів рівня Podʼа, використовуючи fieldRef. На рівні API spec для Podʼа завжди визначає принаймні один Container. Ви можете передавати інформацію з доступних полів рівня Container, використовуючи resourceFieldRef.

Інформація, доступна за допомогою fieldRef

Деякі поля рівня Podʼа ви можете передати контейнеру як змінну середовища або через том downwardAPI. Через обидва механізми доступні такі поля:

metadata.name
назва Podʼа
metadata.namespace
простір імен Podʼа
metadata.uid
унікальний ідентифікатор Podʼа
metadata.annotations['<KEY>']
значення анотації Podʼа з назвою <KEY> (наприклад, metadata.annotations['myannotation'])
metadata.labels['<KEY>']
текстове значення мітки Podʼа з назвою <KEY> (наприклад, metadata.labels['mylabel'])

Така інформація доступна через змінні середовища, але не як поле fieldRef у томі downwardAPI:

spec.serviceAccountName
назва ServiceAccount Podʼа
spec.nodeName
назва вузла, на якому виконується Pod
status.hostIP
основна IP-адреса вузла, до якого призначено Pod
status.hostIPs
IP-адреси — це версія подвійного стека status.hostIP, перша завжди така сама, як і status.hostIP.
status.podIP
основна IP-адреса Podʼа (зазвичай, його IPv4-адреса)
status.podIPs
IP-адреси — це версія подвійного стека status.podIP, перша завжди така сама, як і status.podIP

Така інформація доступна через fieldRef тому downwardAPI, але не як змінні середовища:

metadata.labels
всі мітки Podʼа у форматі label-key="escaped-label-value" з однією міткою на рядок
metadata.annotations
всі анотації Podʼа у форматі annotation-key="escaped-annotation-value" з однією анотацією на рядок

Інформація, доступна за допомогою resourceFieldRef

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

Примітка:

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.35
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.35. It was first available in the v1.27 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate InPlacePodVerticalScaling, Kubernetes ignores it but does not report any error.

Ресурси CPU та памʼяті контейнера можуть бути змінені під час роботи контейнера. Якщо це станеться, буде оновлено том downward API, але змінні середовища не буде оновлено, доки контейнер не буде перезапущено. Докладніші відомості наведено у статті Зміна розміру ресурсів CPU та памʼяті, призначених контейнерам.

resource: limits.cpu
Обмеження CPU контейнера
resource: requests.cpu
Запит CPU контейнера
resource: limits.memory
Обмеження памʼяті контейнера
resource: requests.memory
Запит памʼяті контейнера
resource: limits.hugepages-*
Обмеження hugepages контейнера
resource: requests.hugepages-*
Запит hugepages контейнера
resource: limits.ephemeral-storage
Обмеження ефемерного сховища контейнера
resource: requests.ephemeral-storage
Запит ефемерного сховища контейнера

Резервна інформація для обмежень ресурсів

Якщо обмеження CPU та памʼяті не вказані для контейнера, і ви намагаєтеся надати цю інформацію через downward API, kubelet стандартно надає максимальне доступне для розподілу значення CPU та памʼяті на основі розрахунку ресурсів вузла, доступних для розподілу.

Що далі

Ви можете прочитати про томи downwardAPI.

Ви можете спробувати використовувати downward API щоб надати інформацію рівня контейнера чи Podʼа:

4.1.14 - Розширена конфігурація Podʼів

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

PriorityClasses

PriorityClasses дозволяють вам встановити важливість Podʼів відносно інших Podʼів. Якщо ви призначаєте клас пріоритету Podʼу, Kubernetes встановлює поле .spec.priority для цього Podʼа на основі PriorityClass, який ви вказали (ви не можете встановити .spec.priority безпосередньо). Якщо або коли Pod не може бути запланований, і проблема повʼязана з нестачею ресурсів, kube-scheduler намагається витіснити Podʼи з нижчим пріоритетом, щоб зробити можливим планування Podʼа з вищим пріоритетом.

PriorityClass — це обʼєкт API в межах кластера, який зіставляє назву класу пріоритету з цілочисельним значенням пріоритету. Більші числа вказують на вищий пріоритет.

Визначення PriorityClass

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 10000
globalDefault: false
description: "Клас пріоритету для робочих навантажень із високим пріоритетом"

Вказування пріоритету Podʼа за допомогою PriorityClass

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  priorityClassName: high-priority

Вбудовані PriorityClasses

Kubernetes надає два вбудовані PriorityClasses:

  • system-cluster-critical: для системних компонентів, які є критичними для кластера
  • system-node-critical: для системних компонентів, які є критичними для окремих вузлів. Це найвищий пріоритет, який можуть мати Podʼи в Kubernetes.

Для отримання додаткової інформації див. Пріоритет і витіснення Podʼів.

RuntimeClasses

RuntimeClass дозволяє вказати низькорівневе середовище виконання контейнерів для Podʼа. Це корисно, коли потрібно вказати різні середовища виконання контейнерів для різних типів Podʼів, наприклад, коли потрібні різні рівні ізоляції або функції середовища виконання.

Приклад Podʼа

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  containers:
  - name: mycontainer
    image: nginx

RuntimeClass — це обʼєкт у межах кластера, який представляє середовище виконання контейнерів, доступне на деяких або всіх ваших вузлах.

Адміністратор кластера встановлює та налаштовує конкретні середовища виконання, що підтримують RuntimeClass.

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

Докладнішу інформацію див. у документації RuntimeClass.

Конфігурація контексту безпеки на рівні Podʼа та контейнера

Поле Security context у специфікації Podʼа забезпечує детальний контроль за налаштуваннями безпеки для Podʼів та контейнерів.

securityContext на рівні Podʼа

Деякі аспекти безпеки застосовуються до всього Podʼа; для інших аспектів ви можете встановити стандартне значення без будь-яких перевизначень на рівні контейнера.

Ось приклад використання securityContext на рівні Podʼа:

Приклад Podʼа

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:  # Це стосується всього Podʼа
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: sec-ctx-demo
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: ["sh", "-c", "sleep 1h"]

Контекст безпеки на рівні контейнера

Ви можете вказати контекст безпеки тільки для конкретного контейнера. Ось приклад:

Приклад Podʼа

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-2
spec:
  containers:
  - name: sec-ctx-demo-2
    image: gcr.io/google-samples/node-hello:1.0
    securityContext:
      allowPrivilegeEscalation: false
      runAsNonRoot: true
      runAsUser: 1000
      capabilities:
        drop:
        - ALL
      seccompProfile:
        type: RuntimeDefault

Параметри контексту безпеки

  • Ідентифікатори користувачів та груп: контроль того, під яким користувачем/групою працює контейнер
  • Capabilities: додавання або видалення можливостей Linux
  • Профілі Seccomp: налаштування профілів обчислювальної безпеки
  • Параметри SELinux: налаштування контексту SELinux
  • AppArmor: налаштування профілів AppArmor для додаткового контролю доступу
  • Параметри Windows: налаштування параметрів безпеки, специфічних для Windows

Увага:

Ви також можете використовувати securityContext Podʼа, щоб дозволити привілейований режим у контейнерах Linux. Привілейований режим перевизначає багато інших налаштувань безпеки в securityContext. Уникайте використання цього параметра, якщо ви не можете надати еквівалентні дозволи за допомогою інших полів у securityContext. Ви можете запускати контейнери Windows у подібному привілейованому режимі, встановивши прапорець windowsOptions.hostProcess у контексті безпеки на рівні Podʼа. Детальні відомості та інструкції див. у розділі Створення Windows HostProcess Podʼа.

Для отримання додаткової інформації див. Налаштування контексту безпеки для Podʼа або контейнера.

Вплив на рішення щодо планування Podʼів

Kubernetes надає кілька механізмів для контролю над тим, на яких вузлах плануються ваші Podʼи.

Селектори вузлів

Найпростіша форма обмеження вибору вузлів:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeSelector:
    disktype: ssd

Спорідненість вузлів

Спорідненість вузлів дозволяє вам вказати правила, які обмежують, на яких вузлах може бути запланований ваш Pod. Ось приклад Podʼа, який віддає перевагу роботі на вузлах, позначених як такі, що розташовані на певному континенті, вибираючи на основі значення мітки topology.kubernetes.io/zone.

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - antarctica-east1
            - antarctica-west1
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:3.8

Спорідненість та антиспорідненість Podʼів

Окрім спорідненості вузлів, ви також можете обмежити, на яких вузлах може бути заплановано Pod, на основі міток інших Podʼів, які вже працюють на вузлах. Спорідненість Podʼів дозволяє вам вказати правила щодо розміщення Podʼів відносно інших Podʼів.

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - database
        topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: registry.k8s.io/pause:3.8

Tolerations

Tolerations дозволяють планувати Podʼи на вузли з відповідними taints:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: myapp
    image: nginx
  tolerations:
  - key: "key"
    operator: "Equal"
    value: "value"
    effect: "NoSchedule"

Для отримання додаткової інформації див. Призначення Podʼів до вузлів.

Накладні витрати на Pod

Накладні витрати на Pod дозволяють враховувати ресурси, що споживаються інфраструктурою Podʼа, понад запити та обмеження контейнерів.

---
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kvisor-runtime
handler: kvisor-runtime
overhead:
  podFixed:
    memory: "2Gi"
    cpu: "500m"
---
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: kvisor-runtime
  containers:
  - name: myapp
    image: nginx
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

Що далі

4.2 - Керування навантаженням

Kubernetes надає кілька вбудованих API для декларативного керування вашими робочими навантаженнями та їх компонентами.

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

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

Вбудовані API для керування робочими навантаженнями:

Deployment (і, опосередковано, ReplicaSet), найпоширеніший спосіб запуску застосунку у вашому кластері. Deployment є хорошим вибором для керування робочим навантаженням, яке не зберігає стану, де будь-який Pod у Deployment є взаємозамінним і може бути замінений, якщо це потрібно. (Deployments є заміною застарілого API ReplicationController).

StatefulSet дозволяє вам керувати одним або кількома Podʼами, які виконують один і той самий код застосунку, де Podʼи покладаються на наявність окремої ідентичності. Це відрізняється від Deployment, де очікується, що Podʼи будуть взаємозамінними. Найпоширеніше використання StatefulSet полягає в тому, щоб забезпечити звʼязок між Podʼами та їх постійним сховищем. Наприклад, ви можете запустити StatefulSet, який повʼязує кожен Pod із PersistentVolume. Якщо один з Podʼів у StatefulSet зазнає збою, Kubernetes створює Pod-замінник, підключений до того самого PersistentVolume.

DaemonSet визначає Podʼи, які надають можливості, що є локальними для конкретного вузла; наприклад, драйвер, який дозволяє контейнерам на цьому вузлі отримувати доступ до системи зберігання. Ви використовуєте DaemonSet, коли драйвер або інший сервіс рівня вузла має працювати на вузлі, де він корисний. Кожен Pod в DaemonSet виконує роль, схожу на системний демон на класичному сервері Unix/POSIX. DaemonSet може бути фундаментальним для роботи вашого кластера, наприклад, як втулок, який надає цьому вузлу доступ до мережі кластера, він може допомогти вам керувати вузлом, або надати менш важливі можливості, які покращують платформу контейнерів, яку ви використовуєте. Ви можете запускати DaemonSets (та їхні Podʼи) на кожному вузлі вашого кластера або лише на підмножині (наприклад, встановити драйвер прискорювача GPU лише на вузлах, де встановлено GPU).

Ви можете використовувати Job та/або CronJob для визначення завдань, які виконуються до завершення та зупиняються. Job представляє одноразове завдання, тоді як кожен CronJob повторюється згідно з розкладом.

Інші теми в цьому розділі:

4.2.1 - Deployments

Deployment керує набором Podʼів для запуску робочого навантаження застосунку, зазвичай такого, що не зберігає стан.

Deployment забезпечує декларативні оновлення для Podʼів та ReplicaSets.

Ви описуєте бажаний стан у Deployment, і контролер Deployment змінює фактичний стан на бажаний з контрольованою швидкістю. Ви можете визначити Deployments для створення нових ReplicaSets або для видалення наявних Deployments і перейняття всіх їхніх ресурсів новими Deployments.

Примітка:

Не керуйте ReplicaSets, що належать Deployment. Розгляньте можливість створення тікета в основному репозиторії Kubernetes, якщо ваш випадок використання не врахований нижче.

Сценарії використання

Типові сценарії використання Deployments:

Створення Deployment

Розглянемо приклад Deployment. Він створює ReplicaSet для запуску трьох Podʼів nginx:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

У цьому прикладі:

  • Створюється Deployment з назвою nginx-deployment, назва вказується в полі .metadata.name. Ця назва буде основою для ReplicaSets та Podʼів, які буде створено потім. Докладніше див. у розділі Написання специфікації Deployment.

  • Deployment створює ReplicaSet, який створює три реплікованих Podʼи, кількість зазначено у полі .spec.replicas.

  • Поле .spec.selector визначає, як створений ReplicaSet відшукує Podʼи для керування. У цьому випадку вибирається мітка, визначена в шаблоні Podʼа (app: nginx). Однак можливі складніші правила вибору, якщо сам шаблон Podʼа задовольняє це правило.

    Примітка:

    Поле .spec.selector.matchLabels є мапою пар {key,value}. Одна пара {key,value} у мапі matchLabels еквівалентна елементу matchExpressions, де поле key — "key", operator — "In", а масив values містить лише "value". Всі умови, як від matchLabels, так і від matchExpressions, повинні бути виконані для отримання збігу.
  • Поле .spec.template має такі вкладені поля:

    • Podʼи позначаються міткою app: nginx з поля .metadata.labels.
    • Специфікація шаблону Podʼа, або поле .spec, вказує на те, що Podʼи запускають один контейнер, nginx, який використовує образ nginx з Docker Hub версії 1.14.2.
    • Створюється один контейнер із назвою nginx, вказаною в полі .spec.containers[0].name.

Перш ніж почати, переконайтеся, що ваш кластер Kubernetes працює. Дотримуйтесь наведених нижче кроків для створення наведеного вище Deployment:

  1. Створіть Deployment, виконавши таку команду:

    kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
    
  2. Виконайте kubectl get deployments для перевірки створення Deployment.

    Якщо Deployment все ще створюється, ви побачите вивід, подібний до цього:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   0/3     0            0           1s
    

    Коли ви переглядаєте Deployments у вашому кластері, показуються такі поля:

    • NAME — містить назви Deployments у просторі імен.
    • READY — показує, скільки реплік застосунку доступно користувачам. Значення відповідає шаблону готово/бажано.
    • UP-TO-DATE — показує кількість реплік, які були оновлені для досягнення бажаного стану.
    • AVAILABLE — показує, скільки реплік застосунку доступно користувачам.
    • AGE — показує час впродовж якого застосунок працює.

    Зверніть увагу, що бажана кількість реплік дорівнює 3 відповідно до поля .spec.replicas.

  3. Для перевірки стану розгортання Deployment виконайте kubectl rollout status deployment/nginx-deployment.

    Вивід буде подібний до цього:

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    deployment "nginx-deployment" successfully rolled out
    
  4. Запустіть kubectl get deployments знов через кілька секунд. Вивід буде подібний до цього:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           18s
    

    Зверніть увагу, що Deployment створив усі три репліки, і всі репліки актуальні (вони містять останній шаблон Podʼа) та доступні.

  5. Для перевірки ReplicaSet (rs), створеного Deployment, виконайте kubectl get rs. Вивід буде подібний до цього:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-75675f5897   3         3         3       18s
    

    Вивід ReplicaSet має такі поля:

    • NAME — перелік назв ReplicaSets в просторі імен.
    • DESIRED — показує бажану кількість реплік застосунку, яку ви вказали під час створення Deployment. Це бажаний стан.
    • CURRENT — показує, скільки реплік працює зараз.
    • READY — показує, скільки реплік застосунку доступно користувачам.
    • AGE — показує час впродовж якого застосунок працює.

    Зверніть увагу, що назва ReplicaSet завжди складається як [DEPLOYMENT-NAME]-[HASH]. Ця назва буде основою для назв Podʼів, які буде створено потім.

    Рядок HASH є відповідником мітки pod-template-hash в ReplicaSet.

  6. Для ознайомлення з мітками, автоматично створеними для кожного Podʼа, виконайте kubectl get pods --show-labels. Вивід буде схожим на це:

    NAME                                READY     STATUS    RESTARTS   AGE       LABELS
    nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    

    Створений ReplicaSet гарантує наявність трьох Podʼів nginx.

Примітка:

Ви маєте зазначити відповідний селектор та мітки шаблону Podʼа в Deployment (у цьому випадку app: nginx).

Не допускайте перетину міток або селекторів з іншими контролерами (включаючи інші Deployments та StatefulSets). Kubernetes не завадить вам це зробити, і якщо кілька контролерів мають селектори, що перетинаються, ці контролери можуть конфліктувати та поводитись непередбачувано.

Мітка pod-template-hash

Увага:

Не змінюйте цю мітку.

Мітка pod-template-hash додається контролером Deployment до кожного ReplicaSet, який створює або переймає Deployment.

Ця мітка гарантує, що дочірні ReplicaSets Deployment не перетинаються. Вона генерується шляхом хешування PodTemplate ReplicaSet, і отриманий хеш використовується як значення мітки, яке додається до селектора ReplicaSet, міток шаблону Podʼа, а також до всіх наявних Podʼів, які можуть бути у ReplicaSet.

Оновлення Deployment

Примітка:

Розгортання Deployment запускається тоді й лише тоді, коли змінюється шаблон Podʼа Deployment (тобто .spec.template), наприклад, якщо оновлено мітки чи образи контейнерів шаблону. Інші оновлення, такі як масштабування Deployment, не запускають розгортання.

Дотримуйтеся поданих нижче кроків для оновлення вашого Deployment:

  1. Оновіть Podʼи nginx, щоб використовувати образ nginx:1.16.1 замість nginx:1.14.2.

    kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
    

    або використовуйте таку команду:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    де deployment/nginx-deployment вказує на Deployment, nginx — на контейнер, який буде оновлено, і nginx:1.16.1 — на новий образ та його теґ.

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment image updated
    

    Або ви можете відредагувати (edit) Deployment і змінити .spec.template.spec.containers[0].image з nginx:1.14.2 на nginx:1.16.1:

    kubectl edit deployment/nginx-deployment
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment edited
    
  2. Щоб перевірити статус розгортання, виконайте:

    kubectl rollout status deployment/nginx-deployment
    

    Вивід буде подібний до цього:

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    

    або

    deployment "nginx-deployment" successfully rolled out
    

Отримайте більше деталей про ваш оновлений Deployment:

  • Після успішного розгортання можна переглянути Deployment за допомогою kubectl get deployments. Вивід буде подібний до цього:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           36s
    
  • Виконайте kubectl get rs, щоб перевірити, що Deployment оновив Podʼи, створивши новий ReplicaSet і масштабувавши його до 3 реплік, а також зменшивши розмір старого ReplicaSet до 0 реплік.

    kubectl get rs
    

    Вивід буде подібний до цього:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       6s
    nginx-deployment-2035384211   0         0         0       36s
    
  • Виклик get pods повинен тепер показати лише нові Podʼи:

    kubectl get pods
    

    Вивід буде подібний до цього:

    NAME                                READY     STATUS    RESTARTS   AGE
    nginx-deployment-1564180365-khku8   1/1       Running   0          14s
    nginx-deployment-1564180365-nacti   1/1       Running   0          14s
    nginx-deployment-1564180365-z9gth   1/1       Running   0          14s
    

    Наступного разу, коли вам потрібно буде оновити ці Podʼи, вам достатньо буде знову оновити шаблон Podʼа в Deployment.

    Deployment гарантує, що під час оновлення недоступною буде лише певна кількість Podʼів. Типово він гарантує, що працюватимуть принаймні 75% бажаної кількості Podʼів (максимум 25% недоступних).

    Deployment також гарантує, що понад бажану кількість Podʼів буде створено лише певну кількість. Типово він гарантує, що працюватимуть щонайбільше 125% бажаної кількості Podʼів (максимальне перевищення 25%).

    Наприклад, якщо ви ретельно досліджуєте Deployment вище, ви побачите, що спочатку було створено новий Pod, потім видалено старий Pod і створено ще один новий. Старі Podʼи не прибираються допоки не зʼявиться достатня кількість нових, і не створюються нові Podʼи допоки не буде прибрано достатню кількість старих. Deployment гарантує, що доступні принаймні 3 Podʼи, а загалом доступно щонайбільше 4 Podʼи. У випадку розгортання з 4 репліками кількість Podʼів буде між 3 і 5.

  • Отримайте деталі вашого Deployment:

    kubectl describe deployments
    

    Вивід буде подібний до цього:

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Thu, 30 Nov 2017 10:56:25 +0000
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=2
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
       Containers:
        nginx:
          Image:        nginx:1.16.1
          Port:         80/TCP
          Environment:  <none>
          Mounts:       <none>
        Volumes:        <none>
      Conditions:
        Type           Status  Reason
        ----           ------  ------
        Available      True    MinimumReplicasAvailable
        Progressing    True    NewReplicaSetAvailable
      OldReplicaSets:  <none>
      NewReplicaSet:   nginx-deployment-1564180365 (3/3 replicas created)
      Events:
        Type    Reason             Age   From                   Message
        ----    ------             ----  ----                   -------
        Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set nginx-deployment-2035384211 to 3
        Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 1
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 2
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 2
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 1
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 3
        Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 0
    

    Тут ви бачите, що під час створення Deployment спочатку було створено ReplicaSet (nginx-deployment-2035384211) та масштабовано його до 3 реплік безпосередньо. Коли ви оновили Deployment, було створено новий ReplicaSet (nginx-deployment-1564180365) та масштабовано його до 1, потім Deployment дочекався, коли він запуститься. Потім було зменшено розмір старого ReplicaSet до 2 і масштабовано новий ReplicaSet до 2, таким чином, щоб принаймні 3 Podʼи були доступні, і не більше 4 Podʼів в будь-який момент. Потім було продовжено масштабування нового та старого ReplicaSet, з однаковою стратегією поетапного оновлення. Зрештою у вас буде 3 доступні репліки в новому ReplicaSet, а старий ReplicaSet буде зменшено до 0.

Примітка:

Kubernetes не враховує Podʼи, які завершують роботу, коли розраховує кількість availableReplicas, яка повинна бути між replicas - maxUnavailable та replicas + maxSurge. Внаслідок цього ви можете помітити, що під час розгортання є більше Podʼів, ніж очікувалося, і що загальні ресурси, які використовує Deployment, перевищують replicas + maxSurge до того моменту, поки не мине terminationGracePeriodSeconds для Podʼів, що завершують роботу.

Rollover (або кілька одночасних оновлень)

Кожного разу, коли контролер Deployment виявляє новий Deployment, створюється ReplicaSet для запуску необхідних Podʼів. Якщо Deployment оновлюється, поточний ReplicaSet, який контролює Podʼи, мітки яких відповідають .spec.selector, але шаблон яких не відповідає .spec.template, зменшується. Врешті-решт новий ReplicaSet масштабується до .spec.replicas, а всі старі ReplicaSets масштабуються до 0.

Якщо ви оновите Deployment під час наявного процесу розгортання, Deployment створить новий ReplicaSet відповідно до оновлення, почне масштабувати його і перекине (roll over) ReplicaSet, який масштабував раніше: він додасть його до свого списку старих ReplicaSets і почне зменшувати його масштаб.

Наприклад, припустимо, ви створюєте Deployment для створення 5 реплік nginx:1.14.2, але потім оновлюєте Deployment для створення 5 реплік nginx:1.16.1, коли вже створено тільки 3 репліки nginx:1.14.2. У цьому випадку Deployment негайно починає знищувати 3 Podʼи nginx:1.14.2, які вже створено, і починає створювати Podʼи nginx:1.16.1. Deployment не чекає, доки буде створено 5 реплік nginx:1.14.2, перш ніж змінити напрямок.

Оновлення селектора міток

Зазвичай не рекомендується вносити оновлення до селектора міток, і рекомендується планувати ваші селектори наперед.

Селектор міток Deployment є незмінним після створення; його не можна оновити за допомогою kubectl patch, kubectl edit, kubectl apply або інструментів, таких як helm upgrade.

Якщо вам потрібно змінити селектор, вам доведеться видалити Deployment і створити його заново. Стандартно видалення Deployment також видаляє його запущені Podʼи, що призводить до простою; використовуйте --cascade=orphan, якщо вам потрібно, щоб ці Podʼи продовжували працювати під час повторного створення Deployment (див. наслідки нижче). Будьте дуже обережні та переконайтеся, що ви розумієте такі наслідки:

  • Додавання: Коли ви створюєте новий Deployment з більш вузьким селектором, новий Deployment повинен також мати відповідний шаблон Podʼа. Якщо у вас є наявний маніфест і ви редагуєте його, щоб звузити селектор, вам потрібно відредагувати метадані шаблону Podʼа всередині цього Deployment, додавши нові мітки для відповідності, інакше API-сервер поверне помилку валідації. Це зміна без перетину: новий Deployment не "побачить" старі Podʼи (які не мають нової мітки), тому старий ReplicaSet стане сирітським і буде створено абсолютно новий ReplicaSet.
  • Оновлення значень: Зміна наявного значення в ключі селектора (наприклад, з v1 на v2) призводить до тієї ж поведінки, що й додавання (сирітство та повторне створення).
  • Видалення: Видалення наявного ключа з селектора Deployment не вимагає жодних змін у мітках шаблону Podʼа. Це зміна з перетином: новий, ширший селектор відповідатиме старим Podʼам. Наявні ReplicaSets не стають сирітськими, і новий ReplicaSet не створюється, але зверніть увагу, що видалена мітка все ще існує в будь-яких наявних Podʼах та ReplicaSets. Ви можете прибрати її, запустивши розгортання Deployment.

Відкочування Deployment

Іноді вам може знадобитися відкотити Deployment, наприклад, коли Deployment нестабільний, зокрема зациклений на збоях (crash looping). Типово вся історія розгортань Deployment зберігається в системі, щоб ви могли відкотити його в будь-який час (ви можете змінити це, змінивши обмеження історії ревізій).

Примітка:

Ревізія Deployment створюється, коли запускається розгортання Deployment. Це означає, що нова ревізія створюється тільки тоді, коли змінюється шаблон Podʼа Deployment (.spec.template), наприклад, якщо ви оновлюєте мітки або образи контейнера в шаблоні. Інші оновлення, такі як масштабування Deployment, не створюють ревізію Deployment, щоб ви могли одночасно використовувати ручне або автоматичне масштабування. Це означає, що під час відкочування до попередньої ревізії повертається лише частина шаблону Podʼа Deployment.
  • Припустимо, ви припустилися помилки під час оновлення Deployment, вказавши назву образу як nginx:1.161 замість nginx:1.16.1:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.161
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment image updated
    
  • Розгортання застрягає. Ви можете перевірити це, перевіривши стан розгортання:

    kubectl rollout status deployment/nginx-deployment
    

    Вивід буде подібний до цього:

    Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
    
  • Натисніть Ctrl-C, щоб зупинити спостереження за станом розгортання. Докладніше про розгортання, що застрягли, читайте тут.

  • Ви бачите, що кількість старих реплік (додаючи кількість реплік від nginx-deployment-1564180365 та nginx-deployment-2035384211) — 3, а кількість нових реплік (від nginx-deployment-3066724191) — 1.

    kubectl get rs
    

    Вивід буде подібний до цього:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       25s
    nginx-deployment-2035384211   0         0         0       36s
    nginx-deployment-3066724191   1         1         0       6s
    
  • При перегляді створених Podʼів ви бачите, що 1 Pod, створений новим ReplicaSet, застряг у циклі завантаження образу.

    kubectl get pods
    

    Вивід буде подібний до цього:

    NAME                                READY     STATUS             RESTARTS   AGE
    nginx-deployment-1564180365-70iae   1/1       Running            0          25s
    nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
    nginx-deployment-1564180365-hysrc   1/1       Running            0          25s
    nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s
    

    Примітка:

    Контролер Deployment автоматично зупиняє невдале розгортання та припиняє масштабування нового ReplicaSet. Це залежить від параметрів rollingUpdate (maxUnavailable), які ви вказали. Стандартно Kubernetes встановлює значення 25%.
  • Отримайте опис Deployment:

    kubectl describe deployment
    

    Вивід буде подібний до цього:

    Name:           nginx-deployment
    Namespace:      default
    CreationTimestamp:  Tue, 15 Mar 2016 14:48:04 -0700
    Labels:         app=nginx
    Selector:       app=nginx
    Replicas:       3 desired | 1 updated | 4 total | 3 available | 1 unavailable
    StrategyType:       RollingUpdate
    MinReadySeconds:    0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.161
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    ReplicaSetUpdated
    OldReplicaSets:     nginx-deployment-1564180365 (3/3 replicas created)
    NewReplicaSet:      nginx-deployment-3066724191 (1/1 replicas created)
    Events:
      FirstSeen LastSeen    Count   From                    SubObjectPath   Type        Reason              Message
      --------- --------    -----   ----                    -------------   --------    ------              -------
      1m        1m          1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 1
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 1
    

    Для виправлення цього вам потрібно відкотитися до попередньої ревізії Deployment, яка є стабільною.

Перевірка історії розгортання Deployment

Виконайте наведені нижче кроки, щоб перевірити історію розгортань:

  1. По-перше, перевірте ревізії цього Deployment:

    kubectl rollout history deployment/nginx-deployment
    

    Вивід буде подібний до цього:

    deployments "nginx-deployment"
    REVISION    CHANGE-CAUSE
    1           <none>
    2           <none>
    3           <none>
    

    CHANGE-CAUSE копіюється з анотації Deployment kubernetes.io/change-cause до його ревізій під час створення. Ви можете вказати повідомлення CHANGE-CAUSE:

    • Анотуючи Deployment командою kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="image updated to 1.16.1"
    • Ручним редагуванням маніфесту ресурсу.
    • Використовуючи інструменти, які автоматично встановлюють анотації.

Примітка:

У старих версіях Kubernetes можна було використовувати прапорець --record з командами kubectl для автоматичного заповнення поля CHANGE-CAUSE. Цей прапорець є застарілим і буде видалений у майбутніх версіях.
  1. Щоб переглянути деталі кожної ревізії, виконайте:

    kubectl rollout history deployment/nginx-deployment --revision=2
    

    Вивід буде подібний до цього:

    deployments "nginx-deployment" revision 2
      Labels:       app=nginx
              pod-template-hash=1159050644
      Containers:
       nginx:
        Image:      nginx:1.16.1
        Port:       80/TCP
         QoS Tier:
            cpu:      BestEffort
            memory:   BestEffort
        Environment Variables:      <none>
      No volumes.
    

Відкочування до попередньої ревізії

Виконайте наведені нижче кроки, щоб відкотити Deployment з поточної версії на попередню версію, яка є версією 2.

  1. Тепер ви вирішили скасувати поточне розгортання та повернутися до попередньої ревізії:

    kubectl rollout undo deployment/nginx-deployment
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment rolled back
    

    Замість цього ви можете виконати відкочування до певної ревізії, вказавши її параметром --to-revision:

    kubectl rollout undo deployment/nginx-deployment --to-revision=2
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment rolled back
    

    Для отримання додаткових відомостей про команди, повʼязані з розгортаннями, читайте kubectl rollout.

    Deployment тепер повернуто до попередньої стабільної ревізії. Як ви можете бачити, контролер Deployment генерує подію DeploymentRollback щодо відкочування до ревізії 2.

  2. Перевірте, чи відкочування був успішним і Deployment працює як очікується, виконавши:

    kubectl get deployment nginx-deployment
    

    Вивід буде подібний до цього:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           30m
    
  3. Отримайте опис Deployment:

    kubectl describe deployment nginx-deployment
    

    Вивід буде подібний до цього:

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Sun, 02 Sep 2018 18:17:55 -0500
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=4
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.16.1
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   nginx-deployment-c4747d96c (3/3 replicas created)
    Events:
      Type    Reason              Age   From                   Message
      ----    ------              ----  ----                   -------
      Normal  ScalingReplicaSet   12m   deployment-controller  Scaled up replica set nginx-deployment-75675f5897 to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 0
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-595696685f to 1
      Normal  DeploymentRollback  15s   deployment-controller  Rolled back deployment "nginx-deployment" to revision 2
      Normal  ScalingReplicaSet   15s   deployment-controller  Scaled down replica set nginx-deployment-595696685f to 0
    

Масштабування Deployment

Ви можете масштабувати Deployment за допомогою такої команди:

kubectl scale deployment/nginx-deployment --replicas=10

Вивід буде подібний до цього:

deployment.apps/nginx-deployment scaled

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

kubectl autoscale deployment/nginx-deployment --min=10 --max=15 --cpu-percent=80%

Вивід буде подібний до цього:

deployment.apps/nginx-deployment scaled

Пропорційне масштабування

Deployments RollingUpdate підтримують виконання кількох версій застосунку одночасно. Коли ви або автомасштабувальник масштабуєте Deployment RollingUpdate, який перебуває посеред розгортання (у процесі або призупиненого), контролер Deployment балансує додаткові репліки в наявних активних ReplicaSets (ReplicaSets з Podʼами), щоб помʼякшити ризик. Це називається пропорційним масштабуванням.

Наприклад, ви використовуєте Deployment з 10 репліками, maxSurge=3 та maxUnavailable=2.

  • Переконайтеся, що у вашому Deployment працює 10 реплік.

    kubectl get deploy
    

    Вивід буде подібний до цього:

    NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment     10        10        10           10          50s
    
  • Ви оновлюєте образ, який, як виявляється, неможливо знайти в межах кластера.

    kubectl set image deployment/nginx-deployment nginx=nginx:sometag
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment image updated
    
  • Оновлення образу розпочинає нове розгортання з ReplicaSet nginx-deployment-1989198191, але воно блокується через вимогу maxUnavailable, згадану вище. Перевірте стан розгортання:

    kubectl get rs
    

    Вивід буде подібний до цього:

    NAME                          DESIRED   CURRENT   READY     AGE
    nginx-deployment-1989198191   5         5         0         9s
    nginx-deployment-618515232    8         8         8         1m
    
  • Потім надходить новий запит на масштабування для Deployment. Автомасштабувальник збільшує репліки Deployment до 15. Контролер Deployment повинен вирішити, куди додати цих нових 5 реплік. Якби ви не використовували пропорційне масштабування, всі 5 реплік були б додані в новий ReplicaSet. З пропорційним масштабуванням ви розподіляєте додаткові репліки між всіма ReplicaSets. Більші частки йдуть в ReplicaSets з найбільшою кількістю реплік, а менші частки йдуть в ReplicaSets з меншою кількістю реплік. Залишки додаються до ReplicaSet з найбільшою кількістю реплік. ReplicaSets з нульовою кількістю реплік не масштабуються.

У нашому прикладі вище 3 репліки додаються до старого ReplicaSet, а 2 репліки — до нового ReplicaSet. Процес розгортання повинен зрештою перемістити всі репліки в новий ReplicaSet, за умови, що нові репліки стають справними. Для підтвердження цього виконайте:

kubectl get deploy

Вивід буде подібний до цього:

NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment     15        18        7            8           7m

Статус розгортання підтверджує, як репліки були додані до кожного ReplicaSet.

kubectl get rs

Вивід буде подібний до цього:

NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-1989198191   7         7         0         7m
nginx-deployment-618515232    11        11        11        7m

Призупинення та відновлення розгортання Deployment

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

  • Наприклад, для створеного Deployment:

    Отримайте деталі Deployment:

    kubectl get deploy
    

    Вивід буде подібний до цього:

    NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx     3         3         3            3           1m
    

    Отримайте стан розгортання:

    kubectl get rs
    

    Вивід буде подібний до цього:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         1m
    
  • Призупиніть розгортання за допомогою такої команди:

    kubectl rollout pause deployment/nginx-deployment
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment paused
    
  • Потім оновіть образ в Deployment:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment image updated
    
  • Зверніть увагу, що нове розгортання не розпочалося:

    kubectl rollout history deployment/nginx-deployment
    

    Вивід буде подібний до цього:

    deployments "nginx"
    REVISION  CHANGE-CAUSE
    1   <none>
    
  • Отримайте стан розгортання, щоб перевірити, що наявний ReplicaSet не змінився:

    kubectl get rs
    

    Вивід буде подібний до цього:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         2m
    
  • Ви можете робити стільки оновлень, скільки вам потрібно, наприклад, оновіть ресурси, які будуть використовуватися:

    kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment resource requirements updated
    

    Початковий стан Deployment до призупинення його розгортання продовжить працювати, але нові оновлення до Deployment не матимуть жодного впливу, поки розгортання Deployment призупинено.

  • У кінці відновіть розгортання Deployment і спостерігайте, як новий ReplicaSet зʼявляється з усіма новими оновленнями:

    kubectl rollout resume deployment/nginx-deployment
    

    Вивід буде подібний до цього:

    deployment.apps/nginx-deployment resumed
    
  • Спостерігайте за статусом розгортання, доки воно не завершиться.

    kubectl get rs --watch
    

    Вивід буде подібний до цього:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   2         2         2         2m
    nginx-3926361531   2         2         0         6s
    nginx-3926361531   2         2         1         18s
    nginx-2142116321   1         2         2         2m
    nginx-2142116321   1         2         2         2m
    nginx-3926361531   3         2         1         18s
    nginx-3926361531   3         2         1         18s
    nginx-2142116321   1         1         1         2m
    nginx-3926361531   3         3         1         18s
    nginx-3926361531   3         3         2         19s
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         20s
    
  • Отримайте статус останнього розгортання:

    kubectl get rs
    

    Вивід буде подібний до цього:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         28s
    

Примітка:

Ви не можете відкотити призупинене розгортання Deployment, доки ви його не відновите.

Статус Deployment

Deployment переходить через різні стани протягом свого життєвого циклу. Він може бути в процесі, коли виконується розгортання нового ReplicaSet, може бути завершеним або не зможе просуватися.

Deployment в процесі

Kubernetes позначає Deployment як в процесі (progressing), коли виконується одне з таких завдань:

  • Deployment створює новий ReplicaSet.
  • Deployment масштабує вгору свій новий ReplicaSet.
  • Deployment масштабує вниз свої старі ReplicaSets.
  • Нові Podʼи стають готовими або доступними (готові принаймні MinReadySeconds).

Коли розгортання стає "в процесі" (progressing), контролер Deployment додає стан з такими атрибутами до .status.conditions Deployment:

  • type: Progressing
  • status: "True"
  • reason: NewReplicaSetCreated | reason: FoundNewReplicaSet | reason: ReplicaSetUpdated

Ви можете відстежувати хід розгортання за допомогою kubectl rollout status.

Завершений Deployment

Kubernetes позначає Deployment як завершений (complete), коли він має такі характеристики:

  • Всі репліки, повʼязані з Deployment, були оновлені до останньої версії, яку ви вказали. Це означає, що всі запитані вами оновлення були завершені.
  • Всі репліки, повʼязані з Deployment, доступні.
  • Не працюють жодні старі репліки для Deployment.

Коли розгортання стає "завершеним" (complete), контролер Deployment встановлює стан з такими атрибутами в .status.conditions Deployment:

  • type: Progressing
  • status: "True"
  • reason: NewReplicaSetAvailable

Цей стан Progressing буде зберігати значення статусу "True" до тих пір, поки не буде запущено нове розгортання. Стан залишається незмінним навіть тоді, коли змінюється доступність реплік (що натомість впливає на стан Available).

Ви можете перевірити, чи завершено Deployment, використовуючи kubectl rollout status. Якщо розгортання завершилося успішно, kubectl rollout status повертає код виходу нуль (успіх).

kubectl rollout status deployment/nginx-deployment

Вивід буде подібний до цього:

Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out

і код виходу з kubectl rollout дорівнює 0 (успіх):

echo $?
0

Невдалий Deployment

Ваш Deployment може застрягти під час спроби розгорнути свій найновіший ReplicaSet і ніколи не завершитися. Це може статися через такі чинники:

  • Недостатні квоти
  • Збій проб readiness
  • Помилки завантаження образів
  • Недостатні дозволи
  • Діапазони обмежень (limit ranges)
  • Неправильна конфігурація середовища виконання застосунку

Один із способів виявлення цього стану — це вказати параметр крайнього терміну у специфікації вашого Deployment: (.spec.progressDeadlineSeconds). .spec.progressDeadlineSeconds вказує кількість секунд, протягом яких контролер Deployment чекатиме, перш ніж вказати (в статусі Deployment), що прогрес розгортання зупинився.

Ця команда kubectl встановлює специфікацію з progressDeadlineSeconds, щоб змусити контролер повідомляти про відсутність прогресу розгортання для Deployment після 10 хвилин:

kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'

Вивід буде подібний до цього:

deployment.apps/nginx-deployment patched

Після того як крайній термін закінчився, контролер Deployment додає DeploymentCondition з такими атрибутами до .status.conditions Deployment:

  • type: Progressing
  • status: "False"
  • reason: ProgressDeadlineExceeded

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

Дивіться Домовленості API Kubernetes для отримання додаткової інформації щодо станів у статусі.

Примітка:

Kubernetes не вживає ніяких заходів щодо Deployment, що застряг, окрім повідомлення про стан у статусі з reason: ProgressDeadlineExceeded. Оркестратори вищого рівня можуть використовувати це і відповідним чином діяти, наприклад, відкотити Deployment до його попередньої версії.

Примітка:

Якщо ви призупините розгортання Deployment, Kubernetes не перевірятиме прогрес відносно вказаного вами крайнього терміну. Ви можете безпечно призупинити розгортання Deployment в середині процесу розгортання та відновити його, не активуючи стан перевищення крайнього терміну.

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

kubectl describe deployment nginx-deployment

Вивід буде подібний до цього:

<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

Якщо ви виконаєте kubectl get deployment nginx-deployment -o yaml, статус Deployment схожий на це:

status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: Replica set "nginx-deployment-4262182780" is progressing.
    reason: ReplicaSetUpdated
    status: "True"
    type: Progressing
  - lastTransitionTime: 2016-10-04T12:25:42Z
    lastUpdateTime: 2016-10-04T12:25:42Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
      object-counts, requested: pods=1, used: pods=3, limited: pods=2'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
  observedGeneration: 3
  replicas: 2
  unavailableReplicas: 2

Зрештою, коли крайній термін прогресу Deployment буде перевищений, Kubernetes оновить статус і причину стану Progressing:

Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     False   ProgressDeadlineExceeded
  ReplicaFailure  True    FailedCreate

Ви можете вирішити проблему недостатньої квоти, зменшивши масштаб вашого Deployment, зменшивши масштаб інших контролерів, які ви можете виконувати, або збільшивши квоту у вашому просторі імен. Якщо ви задовольните умови квоти і контролер Deployment завершить розгортання, ви побачите, що статус Deployment оновлюється успішним станом (status: "True" та reason: NewReplicaSetAvailable).

Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable

type: Available з status: "True" означає, що у вас є мінімальна доступність Deployment. Мінімальна доступність визначається параметрами, вказаними в стратегії розгортання. type: Progressing з status: "True" означає, що ваш Deployment або перебуває посеред розгортання і прогресує, або він успішно завершив свій прогрес, і доступна мінімально необхідна нова кількість реплік (див. причину стану для конкретики — у нашому випадку reason: NewReplicaSetAvailable означає, що Deployment завершено).

Ви можете перевірити, чи Deployment не зміг просуватися, за допомогою kubectl rollout status. kubectl rollout status повертає код виходу, відмінний від нуля, якщо Deployment перевищив крайній термін виконання.

kubectl rollout status deployment/nginx-deployment

Вивід буде подібний до цього:

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline

і код виходу з kubectl rollout дорівнює 1 (позначає помилку):

echo $?
1

Операції з невдалим Deployment

Всі дії, які застосовуються до завершеного Deployment, також застосовуються до невдалого Deployment. Ви можете масштабувати його вгору/вниз, відкотити на попередню ревізію або навіть призупинити, якщо вам потрібно застосувати кілька змін у шаблоні Podʼа Deployment.

Політика очищення

Ви можете встановити поле .spec.revisionHistoryLimit у Deployment, щоб вказати, скільки старих ReplicaSets для цього Deployment ви хочете зберегти. Решту прибере збір сміття у фоновому режимі. Типове значення становить 10.

Примітка:

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

Очищення починається лише після того, як Deployment досягне завершеного стану. Якщо ви встановите .spec.revisionHistoryLimit у 0, будь-яке розгортання все одно спричинить створення нового ReplicaSet до того, як Kubernetes видалить старий.

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

Канаркові розгортання

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

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

Написання специфікації Deployment

Як і з усіма іншими конфігураціями Kubernetes, у Deployment потрібні поля .apiVersion, .kind і .metadata. Для загальної інформації щодо роботи з файлами конфігурацій дивіться документи розгортання застосунків, налаштування контейнерів та використання kubectl для керування ресурсами.

Коли панель управління створює нові Podʼи для Deployment, .metadata.name Deployment є частиною основи для назв цих Podʼів. Назва Deployment повинна бути дійсним значенням DNS-піддомену, але це може призводити до неочікуваних результатів для імен хостів Podʼів. Для найкращої сумісності назва повинна відповідати суворішим правилам DNS-мітки.

Deployment також потребує розділу .spec.

Шаблон Podʼа

.spec.template та .spec.selector — єдині обовʼязкові поля .spec.

.spec.template — це шаблон Podʼа. Він має точно таку саму схему, як і Pod, за винятком того, що він вкладений і не має apiVersion або kind.

Крім обовʼязкових полів для Podʼа, шаблон Podʼа в Deployment повинен вказувати відповідні мітки та відповідну політику перезапуску. Щодо міток, переконайтеся, що вони не перекриваються з іншими контролерами. Дивіться селектор.

Дозволяється лише .spec.template.spec.restartPolicy зі значенням Always, яке є стандартним, якщо нічого не вказано.

Репліки

.spec.replicas — це необовʼязкове поле, яке вказує кількість бажаних Podʼів. Стандартно встановлено значення 1.

Якщо ви вручну масштабуєте Deployment, наприклад, через kubectl scale deployment deployment --replicas=X, а потім ви оновлюєте цей Deployment на основі маніфесту (наприклад: виконуючи kubectl apply -f deployment.yaml), то застосування цього маніфесту перезаписує ручне масштабування, виконане раніше.

Якщо HorizontalPodAutoscaler (або будь-який аналогічний API для горизонтального масштабування) відповідає за масштабування Deployment, не встановлюйте значення в .spec.replicas.

Замість цього дозвольте панелі управління Kubernetes автоматично керувати полем .spec.replicas.

Селектор

.spec.selector — обовʼязкове поле, яке вказує селектор міток для Podʼів, на які спрямований цей Deployment.

.spec.selector повинен відповідати .spec.template.metadata.labels, інакше його відхилить API.

В API-версії apps/v1 .spec.selector та .metadata.labels не набувають стандартно значення .spec.template.metadata.labels, якщо їх не задано. Тому їх слід задавати явно. Також слід зазначити, що .spec.selector неможливо змінити після створення Deployment в apps/v1.

Deployment може примусово завершити Podʼи, мітки яких відповідають селектору, якщо їх шаблон відмінний від .spec.template або якщо загальна кількість таких Podʼів перевищує .spec.replicas. Він запускає нові Podʼи з .spec.template, якщо кількість Podʼів менше, ніж бажана кількість.

Примітка:

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

Якщо у вас є кілька контролерів із селекторами, що перетинаються, контролери будуть конфліктувати між собою і не поводитимуться коректно.

Стратегія

.spec.strategy визначає стратегію, якою замінюються старі Podʼи новими. .spec.strategy.type може бути "Recreate" або "RollingUpdate". Типовим значенням є "RollingUpdate".

Перестворення Deployment

Всі поточні Podʼи примусово зупиняються та вилучаються, перш ніж створюються нові, коли .spec.strategy.type==Recreate.

Примітка:

Це гарантує завершення Podʼів перед створенням лише для оновлень. Якщо ви оновлюєте Deployment, всі Podʼи старої ревізії будуть негайно завершені. Успішне видалення очікується перед створенням будь-якого Podʼа нової ревізії. Якщо ви вручну видаляєте Pod, життєвим циклом керує ReplicaSet, і заміну буде створено негайно (навіть якщо старий Pod все ще перебуває в стані Terminating). Якщо вам потрібна гарантія "at most" для ваших Podʼів, вам слід розглянути використання StatefulSet.

Поетапне оновлення Deployment

Deployment оновлює Podʼи в режимі поетапного оновлення (поступово зменшуючи масштаб старих ReplicaSets і збільшуючи масштаб нового), коли .spec.strategy.type==RollingUpdate. Ви можете вказати maxUnavailable та maxSurge, щоб контролювати процес поетапного оновлення.

Максимум недоступних

.spec.strategy.rollingUpdate.maxUnavailable — це необовʼязкове поле, яке вказує максимальну кількість Podʼів, які можуть бути недоступні під час процесу оновлення. Значення може бути абсолютним числом (наприклад, 5) або відсотком від бажаної кількості Podʼів (наприклад, 10%). Абсолютне число обчислюється з відсотків округленням униз. Значення не може бути 0, якщо .spec.strategy.rollingUpdate.maxSurge дорівнює 0. Стандартне значення — 25%.

Наприклад, якщо це значення встановлено на 30%, старий ReplicaSet може бути зменшений до 70% від бажаної кількості Podʼів негайно, коли починається поетапне оновлення. Як тільки нові Podʼи готові, старий ReplicaSet може бути ще більше масштабований вниз, а після цього може бути збільшено масштаб нового ReplicaSet, забезпечуючи, що загальна кількість Podʼів, доступних у будь-який час під час оновлення, становить принаймні 70% від бажаної кількості Podʼів.

Максимальне перевищення

.spec.strategy.rollingUpdate.maxSurge — це необовʼязкове поле, яке вказує максимальну кількість Podʼів, які можуть бути створені понад бажану кількість Podʼів. Значення може бути абсолютним числом (наприклад, 5) або відсотком від бажаної кількості Podʼів (наприклад, 10%). Значення не може бути 0, якщо maxUnavailable дорівнює 0. Абсолютне число обчислюється з відсотків округленням угору. Стандартне значення - 25%.

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

Ось кілька прикладів поетапного оновлення Deployment за допомогою maxUnavailable та maxSurge:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 labels:
   app: nginx
spec:
 replicas: 3
 selector:
   matchLabels:
     app: nginx
 template:
   metadata:
     labels:
       app: nginx
   spec:
     containers:
     - name: nginx
       image: nginx:1.14.2
       ports:
       - containerPort: 80
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxUnavailable: 1
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 labels:
   app: nginx
spec:
 replicas: 3
 selector:
   matchLabels:
     app: nginx
 template:
   metadata:
     labels:
       app: nginx
   spec:
     containers:
     - name: nginx
       image: nginx:1.14.2
       ports:
       - containerPort: 80
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxSurge: 1
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 labels:
   app: nginx
spec:
 replicas: 3
 selector:
   matchLabels:
     app: nginx
 template:
   metadata:
     labels:
       app: nginx
   spec:
     containers:
     - name: nginx
       image: nginx:1.14.2
       ports:
       - containerPort: 80
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxSurge: 1
     maxUnavailable: 1

Progress Deadline Seconds

.spec.progressDeadlineSeconds — це необовʼязкове поле, яке вказує кількість секунд, протягом яких ви хочете чекати на просування вашого Deployment, перш ніж система повідомить, що Deployment не зміг просуватися, — це відображається як стан з type: Progressing, status: "False" та reason: ProgressDeadlineExceeded в статусі ресурсу. Контролер Deployment буде продовжувати повторювати спроби Deployment. Стандартно це значення становить 600.

Якщо вказано це поле, воно повинно бути більше, ніж значення .spec.minReadySeconds.

Min Ready Seconds

.spec.minReadySeconds — це необовʼязкове поле, яке вказує мінімальну кількість секунд, протягом яких новостворений Pod повинен бути готовим без збоїв будь-якого з його контейнерів, щоб вважатися доступним. Стандартно це значення становить 0 (Pod буде вважатися доступним, як тільки він буде готовий). Щоб дізнатися більше про те, коли Pod вважається готовим, див. Проби контейнерів.

Podʼи, що завершують роботу

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

Ви можете бачити Podʼи, що завершують роботу, лише якщо функціональну можливість DeploymentReplicaSetTerminatingReplicas увімкнено на API-сервері та в kube-controller-manager

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

Обмеження історії ревізій

Історія ревізій Deployment зберігається в ReplicaSets, якими він керує.

.spec.revisionHistoryLimit — це необовʼязкове поле, яке вказує кількість старих ReplicaSets, які слід зберігати для можливості відкочування. Ці старі ReplicaSets витрачають ресурси в etcd та захаращують вивід kubectl get rs. Конфігурація кожної ревізії Deployment зберігається в його ReplicaSets; отже, після видалення старого ReplicaSet ви втрачаєте можливість відкотитися до цієї ревізії Deployment. Стандартно буде збережено 10 старих ReplicaSets, але ідеальне значення залежить від частоти та стабільності нових Deployment.

Зокрема, встановлення цього поля рівним нулю означає, що всі старі ReplicaSets з 0 репліками буде очищено. У цьому випадку нове розгортання Deployment не може бути скасовано, оскільки його історія ревізій очищена.

Пауза

.spec.paused — це необовʼязкове булеве поле для призупинення та відновлення Deployment. Єдина відмінність між призупиненим Deployment і тим, який не призупинений, полягає в тому, що будь-які зміни в PodTemplateSpec призупиненого Deployment не викличуть нових розгортань, поки він призупинений. Deployment типово не призупинений під час створення.

Що далі

4.2.2 - ReplicaSet

Призначення ReplicaSet полягає в забезпеченні стабільного набору реплік Podʼів, які працюють у будь-який момент часу. Зазвичай ви визначаєте Deployment та дозволяєте цьому Deployment автоматично керувати ReplicaSets.

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

Як працює ReplicaSet

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

ReplicaSet повʼязаний зі своїми Podʼами через поле metadata.ownerReferences Podʼа, яке вказує, якому ресурсу належить поточний обʼєкт. Усі Podʼи, які отримав ReplicaSet, мають у полі ownerReferences ідентифікаційну інформацію ReplicaSet, що ними володіє. Завдяки цим посиланням ReplicaSet знає про стан Podʼів, які він підтримує, і планує дії відповідно.

ReplicaSet визначає нові Podʼи для отримання за допомогою свого селектора. Якщо існує Pod, який не має OwnerReference або OwnerReference не є контролером, і він відповідає селектору ReplicaSet, цей ReplicaSet негайно його отримає.

Коли використовувати ReplicaSet

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

Це означає, що вам, можливо, ніколи не доведеться працювати з обʼєктами ReplicaSet: використовуйте Deployment та визначайте ваш застосунок у розділі spec.

Приклад

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5

Якщо зберегти цей маніфест у файл frontend.yaml та застосувати його до кластера Kubernetes, буде створено визначений ReplicaSet та Podʼи, якими він керує.

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

Ви можете переглянути розгорнуті ReplicaSets за допомогою команди:

kubectl get rs

І побачите, що створено frontend:

NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

Ви також можете перевірити стан ReplicaSet:

kubectl describe rs/frontend

Ви побачите вивід, подібний до цього:

Name:         frontend
Namespace:    default
Selector:     tier=frontend
Labels:       app=guestbook
              tier=frontend
Annotations:  <none>
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  tier=frontend
  Containers:
   php-redis:
    Image:        us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-gbgfx
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-rwz57
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-wkl7w

І нарешті, ви можете перевірити створені Podʼи:

kubectl get pods

І побачите щось подібне до цього:

NAME             READY   STATUS    RESTARTS   AGE
frontend-gbgfx   1/1     Running   0          10m
frontend-rwz57   1/1     Running   0          10m
frontend-wkl7w   1/1     Running   0          10m

Ви можете також перевірити, що посилання на власника цих Podʼів вказує на ReplicaSet frontend. Для цього отримайте YAML одного із запущених Podʼів:

kubectl get pods frontend-gbgfx -o yaml

Вивід буде схожий на цей, з інформацією про ReplicaSet frontend у полі ownerReferences метаданих:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2024-02-28T22:30:44Z"
  generateName: frontend-
  labels:
    tier: frontend
  name: frontend-gbgfx
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: e129deca-f864-481b-bb16-b27abfd92292
...

Володіння Podʼами без шаблону

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

Візьмемо приклад попереднього ReplicaSet для фронтенду та Podʼів, визначених у цьому маніфесті:

apiVersion: v1
kind: Pod
metadata:
  name: pod1
  labels:
    tier: frontend
spec:
  containers:
  - name: hello1
    image: gcr.io/google-samples/hello-app:2.0

---

apiVersion: v1
kind: Pod
metadata:
  name: pod2
  labels:
    tier: frontend
spec:
  containers:
  - name: hello2
    image: gcr.io/google-samples/hello-app:1.0

Оскільки ці Podʼи не мають контролера (або будь-якого обʼєкта) як власника та відповідають селектору ReplicaSet для фронтенду, вони одразу перейдуть у його володіння.

Припустимо, ви створюєте Podʼи після того, як ReplicaSet для фронтенду було розгорнуто і він налаштував свої початкові репліки Podʼів для виконання вимог до кількості реплік:

kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml

Нові Podʼи будуть отримані ReplicaSet та негайно завершені, оскільки ReplicaSet буде мати більше, ніж його бажана кількість.

Отримання Podʼів:

kubectl get pods

Вивід покаже, що нові Podʼи або вже завершено, або в процесі завершення:

NAME             READY   STATUS        RESTARTS   AGE
frontend-b2zdv   1/1     Running       0          10m
frontend-vcmts   1/1     Running       0          10m
frontend-wtsmm   1/1     Running       0          10m
pod1             0/1     Terminating   0          1s
pod2             0/1     Terminating   0          1s

Якщо ви створите Podʼи спочатку:

kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml

А потім створите ReplicaSet таким чином:

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

Ви побачите, що ReplicaSet отримав Podʼи та створив нові лише відповідно до свого опису, доки кількість його нових Podʼів та оригінальних не відповідала його бажаній кількості. Якщо отримати Podʼи:

kubectl get pods

Вивід покаже, що:

NAME             READY   STATUS    RESTARTS   AGE
frontend-hmmj2   1/1     Running   0          9s
pod1             1/1     Running   0          36s
pod2             1/1     Running   0          36s

Таким чином, ReplicaSet може володіти неоднорідним набором Podʼів.

Написання маніфесту ReplicaSet

Як і усі інші обʼєкти API Kubernetes, ReplicaSet потребує полів apiVersion, kind та metadata. Для ReplicaSet kind завжди є ReplicaSet.

Коли панель управління створює нові Podʼи для ReplicaSet, .metadata.name ReplicaSet є частиною основи для назв цих Podʼів. Назва ReplicaSet повинна бути дійсним значенням DNS-піддомену, але це може призвести до неочікуваних результатів для імен хостів Podʼів. Для найкращої сумісності назва має відповідати суворішим правилам для DNS-мітки.

ReplicaSet також потребує розділу .spec.

Шаблон Podʼа

.spec.template — це шаблон Podʼа, який також повинен мати встановлені мітки. У нашому прикладі frontend.yaml ми мали одну мітку: tier: frontend. Будьте обережні, щоб селектори не перекривалися з селекторами інших контролерів, інакше вони можуть спробувати перейняти цей Pod.

Для поля політики перезапуску шаблону, .spec.template.spec.restartPolicy, допустимим є тільки значення Always, яке є стандартним.

Селектор Podʼа

Поле .spec.selector є селектором міток. Як обговорювалося раніше, це мітки, які використовуються для ідентифікації потенційних Podʼів для володіння. У нашому прикладі frontend.yaml селектор був:

matchLabels:
  tier: frontend

У ReplicaSet .spec.template.metadata.labels має відповідати spec.selector, інакше він буде відхилений API.

Примітка:

Для 2 ReplicaSets, які вказують однаковий .spec.selector, але різні поля .spec.template.metadata.labels та .spec.template.spec, кожен ReplicaSet ігнорує Podʼи, створені іншим ReplicaSet.

Репліки

Ви можете вказати, скільки Podʼів мають виконуватись одночасно, встановивши значення .spec.replicas. ReplicaSet буде створювати/видаляти свої Podʼи, щоб кількість Podʼів відповідала цьому числу.

Якщо ви не вказали .spec.replicas, то типово значення дорівнює 1.

Робота з ReplicaSets

Видалення ReplicaSet та його Podʼів

Щоб видалити ReplicaSet і всі його Podʼи, використовуйте kubectl delete. Збирач сміття стандартно автоматично видаляє всі залежні Podʼи.

Під час використання REST API або бібліотеки client-go вам потрібно встановити propagationPolicy в Background або Foreground в опції -d. Наприклад:

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
  -H "Content-Type: application/json"

Видалення лише ReplicaSet

Ви можете видалити ReplicaSet, не впливаючи на його Podʼи, за допомогою kubectl delete з опцією --cascade=orphan. Під час використання REST API або бібліотеки client-go вам потрібно встановити propagationPolicy в Orphan. Наприклад:

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
  -H "Content-Type: application/json"

Після видалення оригіналу ви можете створити новий ReplicaSet для заміни. Поки старий та новий .spec.selector однакові, новий перейме старі Podʼи. Однак він не намагатиметься привести наявні Podʼи у відповідність до нового, іншого шаблону Podʼа. Для оновлення Podʼів до нової специфікації у контрольований спосіб використовуйте Deployment, оскільки ReplicaSets безпосередньо не підтримують поетапне оновлення.

Podʼи, що завершують роботу

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

Ви можете увімкнути цю функцію, встановивши функціональну можливість DeploymentReplicaSetTerminatingReplicas на API-сервері та в kube-controller-manager

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

Ізолювання Podʼів від ReplicaSet

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

Масштабування ReplicaSet

ReplicaSet можна легко масштабувати вгору або вниз, просто оновивши поле .spec.replicas. Контролер ReplicaSet гарантує, що бажана кількість Podʼів з відповідним селектором міток доступна та працює.

Під час зменшення масштабу контролер ReplicaSet вибирає Podʼи для видалення, сортуючи доступні Podʼи, щоб визначити, які Podʼи видаляти в першу чергу, за таким загальним алгоритмом:

  1. Перш за все прибираються Podʼи, що перебувають в очікуванні (і непридатні для планування).
  2. Якщо встановлено анотацію controller.kubernetes.io/pod-deletion-cost, то Pod із меншим значенням буде видалено першим.
  3. Podʼи на вузлах з більшою кількістю реплік йдуть перед Podʼами на вузлах з меншою кількістю реплік.
  4. Якщо часи створення Podʼів відрізняються, то Pod, створений пізніше, йде перед старішим Podʼом (часи створення групуються за цілочисельною логарифмічною шкалою).

Якщо все вище вказане збігається, вибір випадковий.

Вартість видалення Podʼа

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.22

За допомогою анотації controller.kubernetes.io/pod-deletion-cost користувачі можуть встановити уподобання щодо порядку видалення Podʼів під час зменшення масштабу ReplicaSet.

Анотація повинна бути встановлена на Podʼі, діапазон — [-2147483648, 2147483647]. Вона представляє вартість видалення Podʼа порівняно з іншими Podʼами, які належать тому ж ReplicaSet. Podʼи з меншою вартістю видалення видаляються раніше за Podʼи з більшою вартістю видалення.

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

Ця функція перебуває на стадії бета та типово увімкнена. Ви можете вимкнути її, використовуючи функціональну можливість PodDeletionCost як в kube-apiserver, так і в kube-controller-manager.

Примітка:

  • Це враховується за принципом best-effort, тому порядок видалення Podʼів не гарантується.
  • Користувачам слід уникати частого оновлення анотації, такого як оновлення її на основі значення метрики, оскільки це призведе до значного числа оновлень Podʼів на apiserver.

Приклад використання

Різні Podʼи застосунку можуть мати різний рівень використання. Під час зменшення масштабу застосунок може віддавати перевагу видаленню Podʼів з меншим використанням. Щоб уникнути частого оновлення Podʼів, застосунок повинен оновити controller.kubernetes.io/pod-deletion-cost один раз перед зменшенням масштабу (встановлення анотації в значення, пропорційне рівню використання Podʼа). Це працює, якщо сам застосунок контролює масштабування вниз; наприклад, Pod-драйвер розгортання Spark.

ReplicaSet як ціль горизонтального автомасштабування Podʼа

ReplicaSet також може бути ціллю для горизонтального автомасштабування Podʼа (HPA). Іншими словами, ReplicaSet може автоматично масштабуватися за допомогою HPA. Ось приклад HPA, який застосовується до ReplicaSet, створеного в попередньому прикладі.

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

Збереження цього маніфесту в hpa-rs.yaml та його застосування до кластера Kubernetes повинно створити визначене HPA, яке автоматично змінює масштаб цільового ReplicaSet залежно від використання CPU реплікованими Podʼами.

kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml

Або ви можете використовувати команду kubectl autoscale для досягнення того ж самого (і це простіше!)

kubectl autoscale rs frontend --max=10 --min=3 --cpu=50%

Альтернативи ReplicaSet

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

Окремі Podʼи

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

Job

Використовуйте Job замість ReplicaSet для Podʼів, які повинні завершитися самостійно (тобто пакетні завдання).

DaemonSet

Використовуйте DaemonSet замість ReplicaSet для Podʼів, які надають функції на рівні машини, такі як моніторинг машини або логування машини. Ці Podʼи мають термін служби, який повʼязаний з терміном служби машини: Pod повинен працювати на машині перед тим, як інші Podʼи почнуть роботу, і їх можна безпечно завершити, коли машина готова до перезавантаження/вимкнення.

ReplicationController

ReplicaSets є наступниками ReplicationControllers. Обидва мають те саме призначення та поводяться схоже, за винятком того, що ReplicationController не підтримує вимоги селектора на основі множини, як описано в посібнику про мітки. Тому ReplicaSets мають перевагу над ReplicationControllers.

Що далі

4.2.3 - StatefulSets

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

StatefulSet — це обʼєкт API робочого навантаження, який використовується для керування застосунками зі збереженням стану.

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

Як і Deployment, StatefulSet керує Podʼами, які базуються на ідентичній специфікації контейнерів. На відміну від Deployment, StatefulSet зберігає постійну ідентичність для кожного свого Podʼа. Ці Podʼи створюються за однаковою специфікацією, але не є взаємозамінними: у кожного є постійний ідентифікатор, який він зберігає під час будь-якого перепланування.

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

Використання StatefulSets

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

  • Стабільних, унікальних мережевих ідентифікаторів.
  • Стабільного, постійного сховища.
  • Упорядкованого, плавного розгортання та масштабування.
  • Упорядкованих, автоматизованих поетапних оновлень.

Тут «стабільний» означає збереження під час (пере)планування Podʼів. Якщо застосунок не потребує стабільних ідентифікаторів або упорядкованого розгортання, видалення чи масштабування, вам слід розгортати його за допомогою обʼєкта робочого навантаження, який забезпечує набір реплік без збереження стану (stateless). Deployment або ReplicaSet можуть краще підійти для ваших stateless-потреб.

Обмеження

  • Сховище для певного Podʼа має бути виділене PersistentVolume Provisioner на основі запитаного storage class або попередньо виділене адміністратором.
  • Видалення та/або масштабування StatefulSet вниз не призведе до видалення томів, повʼязаних з StatefulSet. Це зроблено для забезпечення безпеки даних, яка загалом є важливішою, ніж автоматичне очищення всіх повʼязаних ресурсів StatefulSet.
  • Наразі для StatefulSets обовʼязково потрібний Headless Service, щоб відповідати за мережеву ідентичність Podʼів. Ви відповідаєте за створення цього Service.
  • StatefulSets не надають жодних гарантій щодо завершення роботи Podʼів під час видалення StatefulSet. Для досягнення упорядкованого та плавного завершення роботи Podʼів у StatefulSet можна зменшити масштаб StatefulSet до 0 перед видаленням.
  • Під час використання поетапних оновлень зі стандартною політикою керування Podʼами (OrderedReady) можна потрапити в несправний стан, який вимагає ручного втручання для виправлення.

Компоненти

Наведений нижче приклад демонструє компоненти StatefulSet.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  minReadySeconds: 10 # by default is 0
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.24
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

Примітка:

Цей приклад використовує режим доступу ReadWriteOnce для простоти. Для промислового використання проєкт Kubernetes рекомендує натомість використовувати режим доступу ReadWriteOncePod.

У вищенаведеному прикладі:

  • Headless Service із назвою nginx використовується для керування мережевим доменом.
  • StatefulSet із назвою web має Spec, який вказує на те, що буде запущено 3 репліки контейнера nginx в унікальних Podʼах.
  • volumeClaimTemplates забезпечуватиме стабільне сховище за допомогою PersistentVolumes, виділених PersistentVolume Provisioner.

Назва обʼєкта StatefulSet повинна бути дійсною DNS-міткою.

Селектор Podʼів

Ви повинні встановити поле .spec.selector StatefulSet так, щоб воно збігалося з мітками його .spec.template.metadata.labels. Якщо не вказати відповідний селектор Podʼів, під час створення StatefulSet виникне помилка валідації.

Шаблони заявок на томи

Ви можете встановити поле .spec.volumeClaimTemplates, щоб створити PersistentVolumeClaim. Це забезпечить стабільне сховище для StatefulSet, якщо:

  • StorageClass, вказаний для заявки на том, налаштовано на використання динамічного виділення.
  • Кластер вже містить PersistentVolume з правильним StorageClass та достатньою кількістю доступного місця для зберігання.

Мінімальний час готовності

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.25

.spec.minReadySeconds є необовʼязковим полем, яке вказує мінімальну кількість секунд, протягом яких новостворений Pod повинен працювати та бути готовим без збоїв будь-якого з його контейнерів, щоб вважатися доступним. Це використовується для перевірки прогресу розгортання під час використання стратегії поетапного оновлення. Це поле стандартно дорівнює 0 (Pod вважатиметься доступним одразу після готовності). Докладніше про те, коли Pod вважається готовим, див. Проби контейнера.

Ідентичність Podʼа

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

Порядковий індекс

Для StatefulSet з N реплік кожному Podʼу в StatefulSet буде призначено цілочисельний порядковий номер, унікальний у наборі. Стандартно Podʼам буде призначено порядкові номери від 0 до N-1. Контролер StatefulSet також додасть мітку Podʼа з цим індексом: apps.kubernetes.io/pod-index.

Початковий порядковий номер

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.31; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.31. It was first available in the v1.26 release.

.spec.ordinals — це необовʼязкове поле, яке дозволяє налаштувати цілочисельні порядкові номери, призначені кожному Podʼу. Стандартно воно встановлено в nil. У межах цього поля ви можете налаштувати такі параметри:

  • .spec.ordinals.start: Якщо встановлено поле .spec.ordinals.start, Podʼам будуть призначені порядкові номери від .spec.ordinals.start до .spec.ordinals.start + .spec.replicas - 1.

Стабільний мережевий ідентифікатор

Кожен Pod у StatefulSet отримує імʼя хоста з назви StatefulSet та порядкового номера Podʼа. Шаблон для створеного імені хоста має вигляд $(назва statefulset)-$(порядковий номер). У наведеному вище прикладі буде створено три Podʼи з назвами web-0,web-1,web-2. StatefulSet може використовувати Headless Service для керування доменом своїх Podʼів. Домен, яким керує цей Service, має вигляд: $(назва сервісу).$(простір імен).svc.cluster.local, де "cluster.local" — це домен кластера. Під час створення кожен Pod отримує відповідний DNS-піддомен, який має вигляд: $(назва Podʼа).$(домен керівного сервісу), де керівний сервіс визначається полем serviceName у StatefulSet.

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

Якщо вам потрібно виявити Podʼи негайно після їх створення, у вас є кілька варіантів:

  • Запитуйте API Kubernetes безпосередньо (наприклад, використовуючи watch), а не покладаючись на DNS-запити.
  • Зменште час кешування у вашому постачальнику DNS Kubernetes (зазвичай це означає редагування ConfigMap для CoreDNS, який зараз кешує протягом 30 секунд).

Як зазначено в розділі Обмеження, ви відповідаєте за створення Headless Service, відповідального за мережеву ідентичність Podʼів.

Ось деякі приклади варіантів вибору домену кластера, назви Service, назви StatefulSet та того, як це впливає на DNS-імена Podʼів StatefulSet.

Домен кластераService (ns/назва)StatefulSet (ns/назва)Домен StatefulSetDNS PodʼаІмʼя хоста Podʼа
cluster.localdefault/nginxdefault/webnginx.default.svc.cluster.localweb-{0..N-1}.nginx.default.svc.cluster.localweb-{0..N-1}
cluster.localfoo/nginxfoo/webnginx.foo.svc.cluster.localweb-{0..N-1}.nginx.foo.svc.cluster.localweb-{0..N-1}
kube.localfoo/nginxfoo/webnginx.foo.svc.kube.localweb-{0..N-1}.nginx.foo.svc.kube.localweb-{0..N-1}

Примітка:

Домен кластера буде встановлено як cluster.local, якщо не буде інших налаштувань.

Стабільне сховище

Для кожного запису VolumeClaimTemplate, визначеного в StatefulSet, кожен Pod отримує один PersistentVolumeClaim. У прикладі з nginx кожен Pod отримує один PersistentVolume з StorageClass my-storage-class та 1 GiB виділеного сховища. Якщо StorageClass не вказано, використовуватиметься стандартний StorageClass. Коли Pod (пере)планується на вузол, його volumeMounts монтує PersistentVolumes, повʼязані із його PersistentVolume Claims. Зауважте, що PersistentVolumes, повʼязані із PersistentVolume Claims Podʼів, не видаляються під час видалення Podʼів чи StatefulSet. Це слід робити вручну.

Мітка назви Podʼа

Коли контролер StatefulSet створює Pod, він додає мітку statefulset.kubernetes.io/pod-name, яка дорівнює назві Podʼа. Ця мітка дозволяє прикріплювати Service до конкретного Podʼа в StatefulSet.

Мітка індексу Podʼа

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.32; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.32. It was first available in the v1.28 release.

Коли контролер StatefulSet створює Pod, новий Pod отримує мітку apps.kubernetes.io/pod-index. Значення цієї мітки — це порядковий індекс Podʼа. Ця мітка дозволяє спрямовувати трафік до певного індексу Podʼа, фільтрувати логи/метрики за допомогою мітки індексу Podʼа тощо. Зверніть увагу, що функціональна можливість PodIndexLabel для цієї функції стандартно увімкнена та заблокована; щоб вимкнути її, користувачам доведеться використовувати емуляцію сервера версії v1.31.

Гарантії розгортання та масштабування

  • Для StatefulSet із N реплік під час розгортання Podʼи створюються послідовно, у порядку від {0..N-1}.
  • Під час видалення Podʼів вони завершуються у зворотному порядку, від {N-1..0}.
  • Перед тим як застосувати масштабування до Podʼа, всі його попередники повинні бути в стані Running та Ready. Якщо встановлено .spec.minReadySeconds, попередники повинні бути доступними (готовими принаймні протягом minReadySeconds).
  • Перед тим як Pod буде завершено, всі його наступники повинні бути повністю зупинені.

StatefulSet не повинен вказувати pod.Spec.TerminationGracePeriodSeconds рівним 0. Така практика небезпечна і настійно не рекомендується. Докладніше дивіться в розділі примусове видалення Podʼів StatefulSet.

Коли створюється наведений вище приклад nginx, три Podʼи будуть розгорнуті в порядку web-0, web-1, web-2. web-1 не буде розгорнуто, поки web-0 не буде в стані Running та Ready, і web-2 не буде розгорнуто, поки web-1 не буде в стані Running та Ready. Якщо web-0 зазнає збою після того, як web-1 перейде в стан Running та Ready, але до запуску web-2, web-2 не буде запущено, поки web-0 не буде успішно перезапущено і він не перейде в стан Running та Ready.

Якщо користувач масштабує розгорнутий приклад, застосовуючи патч до StatefulSet так, щоб replicas=1, спочатку буде завершено web-2. web-1 не буде завершено, поки web-2 повністю не зупиниться і не буде видалений. Якщо web-0 зазнає збою після того, як web-2 вже завершено і повністю зупинено, але перед завершенням web-1, web-1 не буде завершено, поки web-0 не перейде в стан Running та Ready.

Політики керування Podʼами

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

Політика керування Podʼами "OrderedReady"

OrderedReady є типовим значенням політики керування Podʼами для StatefulSet. Вона реалізує поведінку, описану в розділі Гарантії розгортання та масштабування.

Політика керування Podʼами "Parallel"

Parallel вказує контролеру StatefulSet запускати або завершувати всі Podʼи паралельно і не чекати, поки Podʼи перейдуть у стан Running та Ready або повністю завершаться, перед запуском або завершенням іншого Podʼа.

Для операцій масштабування це означає, що всі Podʼи створюються або завершуються одночасно.

Для поетапних оновлень, коли .spec.updateStrategy.rollingUpdate.maxUnavailable більше 1, контролер StatefulSet завершує і створює до maxUnavailable Podʼів одночасно (також відоме як «bursting»). Це може прискорити оновлення, але може призвести до того, що Podʼи стануть готовими в неправильному порядку, що може бути неприйнятним для застосунків, які вимагають суворого порядку.

Стратегії оновлення

Поле .spec.updateStrategy обʼєкта StatefulSet дозволяє налаштовувати та вимикати автоматизовані поетапні оновлення для контейнерів, міток, ресурсів (запити/обмеження) та анотацій для Podʼів у StatefulSet. Є три можливих значення:

OnDelete
Коли поле .spec.updateStrategy.type StatefulSet встановлено в OnDelete, контролер StatefulSet не буде автоматично оновлювати Podʼи в StatefulSet. Користувачам необхідно вручну видаляти Podʼи, щоб спричинити створення контролером нових Podʼів, які відображають зміни, внесені в .spec.template StatefulSet.
RollingUpdate
Стратегія оновлення RollingUpdate реалізує автоматизовані поетапні оновлення для Podʼів у StatefulSet. Це значення є стандартною стратегією оновлення.
Recreate
СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the StatefulSetRecreateStrategy feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Стратегія оновлення Recreate видаляє всі Podʼи StatefulSet перед створенням нових Podʼів, які відображають зміни, внесені в .spec.template StatefulSet. Використання цієї стратегії вимагає увімкнення функціональної можливості StatefulSetRecreateStrategy. Див. Recreate для деталей.

Поетапні оновлення

Коли поле .spec.updateStrategy.type StatefulSet встановлено на RollingUpdate, контролер StatefulSet буде видаляти та знову створювати кожен Pod у StatefulSet. Він буде продовжувати в тому ж порядку, як Podʼи завершують роботу (від найбільшого індексу до найменшого), оновлюючи кожен Pod по одному.

Панель управління Kubernetes чекає, доки оновлений Pod буде переведений в стан Running та Ready, перш ніж оновити його попередника. Якщо ви встановили .spec.minReadySeconds (див. Мінімальний час готовності), панель управління також чекає вказану кількість секунд після того, як Pod стане готовим, перш ніж перейти далі.

Поетапні оновлення частинами

Стратегію оновлення RollingUpdate можна поділити на частини, вказавши .spec.updateStrategy.rollingUpdate.partition. Якщо вказано розділ, всі Podʼи з індексом, який більший або рівний розділу, будуть оновлені при оновленні .spec.template StatefulSet. Усі Podʼи з індексом, який менший за розділ, не будуть оновлені та, навіть якщо вони будуть видалені, вони будуть створені заново з попередньою версією. Якщо .spec.updateStrategy.rollingUpdate.partition StatefulSet більше, ніж .spec.replicas, оновлення .spec.template не буде розповсюджуватися на його Podʼи. У більшості випадків вам не знадобиться використовувати розділ, але він корисний, якщо ви хочете підготувати оновлення, розгорнути канарку або виконати розгортання за фазами.

Максимальна кількість недоступних Podʼів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35

Ви можете контролювати максимальну кількість Podʼів, які можуть бути недоступні під час оновлення, вказавши поле .spec.updateStrategy.rollingUpdate.maxUnavailable. Значення може бути абсолютним числом (наприклад, 5) або відсотком від бажаних Podʼів (наприклад, 10%). Абсолютне число обчислюється з відсоткового значення заокругленням вгору. Це поле не може бути 0. Стандартне значення — 1.

Це поле застосовується до всіх Podʼів у діапазоні від 0 до replicas - 1. Якщо є будь-який недоступний Pod у діапазоні від 0 до replicas - 1, він буде враховуватися в maxUnavailable.

Примітка:

Поле maxUnavailable перебуває на стадії бета і є стандартно увімкненим.

Примусове відкочування

Під час використання поетапних оновлень зі стандартною політикою керування Podʼами (OrderedReady) можна потрапити в несправний стан, який вимагає ручного втручання для виправлення.

Якщо ви оновлюєте шаблон Podʼа до конфігурації, яка ніколи не досягає стану Running та Ready (наприклад, через поганий бінарний файл або помилку конфігурації на рівні застосунку), StatefulSet припинить розгортання і залишиться у стані очікування.

У цьому стані недостатньо повернути шаблон Podʼа до справної конфігурації. Через відомий дефект, StatefulSet продовжуватиме очікувати, доки неробочий Pod стане готовим (що ніколи не відбувається), перед тим як спробувати повернути його до робочої конфігурації.

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

Recreate

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the StatefulSetRecreateStrategy feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли поле .spec.updateStrategy.type StatefulSet встановлено на Recreate, контролер StatefulSet видаляє всі Podʼи StatefulSet одночасно та чекає, доки вони повністю завершать роботу, перш ніж створювати будь-які нові Podʼи з оновленого .spec.template. На відміну від RollingUpdate, стара та нова ревізії Podʼа ніколи не працюють одночасно, тому ця стратегія призводить до простою протягом усього оновлення. Це дзеркально відображає стратегію Recreate для Deployment і є корисною для застосунків, які не можуть запускати дві версії одночасно, наприклад, для робочих навантажень, які потребують ексклюзивного доступу до спільного ресурсу або використовують формат на диску, несумісний між версіями.

Видалення завжди прибирає всі Podʼи разом, незалежно від політики керування Podʼами. Після того, як всі Podʼи завершать роботу, нові Podʼи створюються відповідно до цієї політики: з OrderedReady (стандартно) Podʼи перестворюються по одному, у порядку зростання індексів, чекаючи, доки кожен стане Running та Ready, перш ніж створювати наступний; з Parallel всі Podʼи перестворюються одночасно.

Оскільки це альфа-функція, ви повинні увімкнути функціональну можливість StatefulSetRecreateStrategy на kube-controller-manager та kube-apiserver, щоб використовувати цю стратегію.

Історія ревізій

ControllerRevision — це ресурс API Kubernetes, який використовується контролерами, такими як контролер StatefulSet, для відстеження історичних змін конфігурації.

StatefulSets використовують ControllerRevisions для ведення історії ревізій, що дозволяє виконувати відкочування і відстежувати версії.

Як StatefulSets відстежують зміни за допомогою ControllerRevisions

Коли ви оновлюєте шаблон Podʼа StatefulSet (spec.template), контролер StatefulSet:

  1. Готує новий обʼєкт ControllerRevision
  2. Зберігає знімок шаблону Podʼа і метаданих
  3. Присвоює інкрементний номер ревізії

Ключові властивості

Дивіться ControllerRevision, щоб дізнатися більше про ключові властивості та інші деталі.


Керування історією ревізій

Керуйте збереженими ревізіями за допомогою .spec.revisionHistoryLimit:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: webapp
spec:
  revisionHistoryLimit: 5  # Зберігати останні 5 ревізій
  # ... інші поля spec ...
  • Стандартно: 10 ревізій зберігаються, якщо не вказано інше
  • Очищення: найстаріші ревізії прибирає збір сміття, коли перевищено обмеження

Виконання відкочування

Ви можете повернутися до попередньої конфігурації за допомогою:

# Переглянути історію ревізій
kubectl rollout history statefulset/webapp

# Відкотитися до певної ревізії
kubectl rollout undo statefulset/webapp --to-revision=3

Це:

  • Застосує шаблон Podʼа з ревізії 3
  • Створить новий ControllerRevision з оновленим номером ревізії

Перевірка ControllerRevisions

Щоб переглянути повʼязані ControllerRevisions:

# Перелічити всі ревізії для StatefulSet
kubectl get controllerrevisions -l app.kubernetes.io/name=webapp

# Переглянути детальну конфігурацію конкретної ревізії
kubectl get controllerrevision/webapp-3 -o yaml

Найкращі практики

Політика зберігання
  • Встановіть revisionHistoryLimit в діапазоні 5–10 для більшості робочих навантажень.
  • Збільшуйте значення тільки в разі потреби в глибокій історії відкочування.
Моніторинг
  • Регулярно перевіряйте ревізії за допомогою:

    kubectl get controllerrevisions
    
  • Налаштуйте сповіщення про швидке зростання кількості ревізій.

Уникайте
  • Ручного редагування обʼєктів ControllerRevision.
  • Використання ревізій як механізму резервного копіювання (використовуйте справжні інструменти резервного копіювання).
  • Встановлення revisionHistoryLimit: 0 (вимикає можливість відкочування).

Збереження PersistentVolumeClaim

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.32; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.32. It was first available in the v1.23 release.

Необовʼязкове поле .spec.persistentVolumeClaimRetentionPolicy контролює, чи і як видаляються PVC під час життєвого циклу StatefulSet. Вам потрібно увімкнути функціональну можливість StatefulSetAutoDeletePVC на API-сервері та controller manager, щоб використовувати це поле. Після увімкнення ви можете налаштувати дві політики для кожного StatefulSet:

whenDeleted
Налаштовує поведінку збереження томів, яка застосовується під час видалення StatefulSet.
whenScaled
Налаштовує поведінку збереження томів, яка застосовується під час зменшення кількості реплік у StatefulSet, наприклад, під час зменшення масштабу набору.

Для кожної політики, яку ви можете налаштувати, ви можете встановити значення Delete або Retain.

Delete
PVC, створені за допомогою volumeClaimTemplate StatefulSet, видаляються для кожного Podʼа, на який впливає політика. З політикою whenDeleted всі PVC від volumeClaimTemplate видаляються після того, як їхні Podʼи були видалені. З політикою whenScaled видаляються лише PVC, що відповідають реплікам Podʼа, які прибираються під час зменшення масштабу, після того, як їхні Podʼи були видалені.
Retain (стандартно)
PVC від volumeClaimTemplate не змінюються, коли їх Pod видаляється. Це поведінка до цієї нової функції.

Майте на увазі, що ці політики застосовуються лише тоді, коли Podʼи прибираються через видалення StatefulSet або зменшення його масштабу. Наприклад, якщо Pod, повʼязаний із StatefulSet, зазнає збою через відмову вузла, і панель управління створює Pod-замінник, StatefulSet зберігає наявний PVC. Наявний том не зазнає впливу, і кластер приєднає його до вузла, де має запуститися новий Pod.

Стандартно для політик встановлено Retain, відповідно до поведінки StatefulSet до цієї нової функції.

Ось приклад політики:

apiVersion: apps/v1
kind: StatefulSet
...
spec:
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain
    whenScaled: Delete
...

Контролер StatefulSet додає посилання на власника до своїх PVC, які потім видаляються збирачем сміття після завершення Podʼа. Це дозволяє Podʼу чисто демонтувати всі томи перед видаленням PVC (і перед видаленням PV і тому, що лежать в їх основі, залежно від політики збереження). Коли ви встановлюєте політику whenDeleted у Delete, посилання на власника на екземпляр StatefulSet додається до всіх PVC, повʼязані із цим StatefulSet.

Політика whenScaled повинна видаляти PVC тільки під час зменшення масштабу Podʼа, а не під час видалення Podʼа з іншої причини. Під час узгодження контролер StatefulSet порівнює очікувану кількість реплік із фактичними Podʼами, що присутні на кластері. Будь-який Pod StatefulSet, ідентифікатор якого більший за кількість реплік, засуджується та позначається для видалення. Якщо політика whenScaled є Delete, засуджені Podʼи спочатку встановлюються власниками відповідних PVC шаблону StatefulSet, перед видаленням Podʼа. Це призводить до того, що PVC видаляються збирачем сміття лише після завершення засуджених Podʼів.

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

Репліки

.spec.replicas — це необовʼязкове поле, яке вказує кількість бажаних Podʼів. Стандартно воно дорівнює 1.

Якщо ви вручну масштабуєте StatefulSet через kubectl scale statefulset statefulset --replicas=X, а потім оновлюєте цей StatefulSet на основі маніфесту (наприклад, за допомогою kubectl apply -f statefulset.yaml), то застосування цього маніфесту перезапише попереднє ручне масштабування.

Якщо HorizontalPodAutoscaler (або будь-який подібний API для горизонтального масштабування) керує масштабуванням StatefulSet, не встановлюйте .spec.replicas. Замість цього дозвольте панелі управління Kubernetes автоматично керувати полем .spec.replicas.

Що далі

4.2.4 - DaemonSet

DaemonSet визначає Podʼи, які надають локальні для вузлів можливості. Вони можуть бути фундаментальними для роботи вашого кластера, наприклад, допоміжним інструментом для роботи з мережею, або бути частиною надбудови.

DaemonSet гарантує, що всі (або деякі) вузли запускають копію Podʼа. Коли до кластера додаються вузли, на них додаються Podʼи. Коли вузли видаляються з кластера, ці Podʼи прибирає збір сміття. Видалення DaemonSet призведе до очищення створених ним Podʼів.

Типові варіанти використання DaemonSet:

  • запуск демона кластерного сховища на кожному вузлі
  • запуск демона збору логів на кожному вузлі
  • запуск демона моніторингу вузла на кожному вузлі

У простому випадку один DaemonSet, який охоплює всі вузли, може використовуватися для кожного типу демона. Складніше налаштування може використовувати кілька DaemonSets для одного типу демона, але з різними прапорцями та/або різними запитами памʼяті та CPU для різних типів обладнання.

Створення специфікації DaemonSet

Створення DaemonSet

Ви можете описати DaemonSet у файлі YAML. Наприклад, файл daemonset.yaml нижче описує DaemonSet, який запускає Docker-образ fluentd-elasticsearch:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # these tolerations are to have the daemonset runnable on control plane nodes
      # remove them if your control plane nodes should not run pods
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v5.0.1
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      # it may be desirable to set a high priority class to ensure that a DaemonSet Pod
      # preempts running Pods
      # priorityClassName: important
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log

Створіть DaemonSet на основі файлу YAML:

kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml

Обовʼязкові поля

Як і з будь-якою іншою конфігурацією Kubernetes, DaemonSet потребує полів apiVersion, kind та metadata. Загальну інформацію щодо роботи з файлами конфігурації див. у розділах запуск stateless-застосунків та управління обʼєктами за допомогою kubectl.

Назва обʼєкта DaemonSet повинна бути дійсним імʼям DNS-піддомену.

DaemonSet також потребує розділу .spec.

Шаблон Podʼа

.spec.template — одне з обовʼязкових полів в .spec.

.spec.template — це шаблон Podʼа. Він має ту саму схему, що і Pod, за винятком того, що він вкладений і не має apiVersion або kind.

Окрім обовʼязкових полів для Podʼа, шаблон Podʼа в DaemonSet повинен вказувати відповідні мітки (див. селектор Podʼа).

Шаблон Podʼа в DaemonSet повинен мати RestartPolicy зі значенням Always або не вказувати її, тоді стандартним значенням буде Always.

Селектор Podʼа

Поле .spec.selector — це селектор Podʼа. Воно працює так само, як і .spec.selector в Job.

Ви повинні вказати селектор Podʼа, який відповідає міткам .spec.template. Крім того, після створення DaemonSet його .spec.selector не можна змінити. Зміна селектора Podʼа може призвести до ненавмисного залишення Podʼів сиротами, і це виявилося незрозумілим для користувачів.

.spec.selector — це обʼєкт, що складається з двох полів:

  • matchLabels — працює так само, як і .spec.selector у ReplicationController.
  • matchExpressions — дозволяє будувати складніші селектори, вказуючи ключ, список значень та оператор, який повʼязує ключ і значення.

Коли вказано обидва, результат обʼєднується логічною операцією AND.

.spec.selector повинен відповідати .spec.template.metadata.labels. Конфігурація, у якій ці два значення не збігаються, буде відхилена API.

Запуск Podʼів на вибраних вузлах

Якщо ви вказуєте .spec.template.spec.nodeSelector, тоді контролер DaemonSet буде створювати Podʼи на вузлах, які відповідають селектору вузла. Так само, якщо ви вказуєте .spec.template.spec.affinity, тоді контролер DaemonSet буде створювати Podʼи на вузлах, які відповідають цій спорідненості вузла. Якщо ви не вказали жодного з них, контролер DaemonSet буде створювати Podʼи на всіх вузлах.

Як плануються Daemon Podʼи

DaemonSet можна використовувати для того, щоб усі придатні вузли запускали копію Podʼа. Контролер DaemonSet створює Pod для кожного придатного вузла та додає поле spec.affinity.nodeAffinity Podʼа для відповідності цільовому хосту. Після створення Podʼа зазвичай у дію вступає стандартний планувальник і привʼязує Pod до цільового хоста, встановлюючи поле .spec.nodeName. Якщо новий Pod не може поміститися на вузлі, стандартний планувальник може витіснити (виселити) деякі наявні Podʼи на основі пріоритету нового Podʼа.

Примітка:

Якщо важливо, щоб Pod DaemonSet запускався на кожному вузлі, часто бажано встановити .spec.template.spec.priorityClassName DaemonSet на PriorityClass з вищим пріоритетом, щоб гарантувати таке виселення.

Користувач може вказати інший планувальник для Podʼів DaemonSet, встановивши поле .spec.template.spec.schedulerName DaemonSet.

Початкова спорідненість вузла, вказана в полі .spec.template.spec.affinity.nodeAffinity (якщо вказано), береться до уваги контролером DaemonSet під час оцінювання придатних вузлів, але замінюється на спорідненість вузла, що відповідає імені придатного вузла, на створеному Podʼі.

nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchFields:
      - key: metadata.name
        operator: In
        values:
        - target-host-name

Taints та tolerations

Контролер DaemonSet автоматично додає набір tolerations до Podʼів DaemonSet:

Tolerations для Podʼів DaemonSet
Ключ tolerationЕфектДеталі
node.kubernetes.io/not-readyNoExecutePodʼи DaemonSet можуть бути заплановані на вузли, які не є справними або готовими приймати Podʼи. Будь-які Podʼи DaemonSet, які працюють на таких вузлах, не будуть виселені.
node.kubernetes.io/unreachableNoExecutePodʼи DaemonSet можуть бути заплановані на вузли, які недоступні з контролера вузла. Будь-які Podʼи DaemonSet, які працюють на таких вузлах, не будуть виселені.
node.kubernetes.io/disk-pressureNoSchedulePodʼи DaemonSet можуть бути заплановані на вузли з тиском на диск.
node.kubernetes.io/memory-pressureNoSchedulePodʼи DaemonSet можуть бути заплановані на вузли з тиском на памʼять.
node.kubernetes.io/pid-pressureNoSchedulePodʼи DaemonSet можуть бути заплановані на вузли з тиском на процеси.
node.kubernetes.io/unschedulableNoSchedulePodʼи DaemonSet можуть бути заплановані на вузли, непридатні для планування.
node.kubernetes.io/network-unavailableNoScheduleДодається лише для Podʼів DaemonSet, які запитують мережу вузла, тобто для Podʼів із spec.hostNetwork: true. Такі Podʼи DaemonSet можуть бути заплановані на вузли із недоступною мережею.

Ви можете додавати власні tolerations до Podʼів DaemonSet, визначивши їх в шаблоні Podʼа DaemonSet.

Оскільки контролер DaemonSet автоматично встановлює toleration node.kubernetes.io/unschedulable:NoSchedule, Kubernetes може запускати Podʼи DaemonSet на вузлах, позначених як unschedulable.

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

Взаємодія з Daemon Podʼами

Деякі можливі шаблони для взаємодії з Podʼами у DaemonSet:

  • Push: Podʼи в DaemonSet налаштовані надсилати оновлення до іншого сервісу, такого як база даних статистики. У них немає клієнтів.
  • NodeIP та відомий порт: Podʼи в DaemonSet можуть використовувати hostPort, щоб до них можна було звертатися за IP-адресами вузлів. Клієнти якимось чином знають список IP-адрес вузлів, а порт знають за домовленістю.
  • DNS: Створіть headless сервіс із тим самим селектором Podʼа, а потім виявіть DaemonSets, використовуючи ресурс endpoints або отримайте кілька записів A з DNS.
  • Service: Створіть Service із тим самим селектором Podʼа та використовуйте його для звернення до демона на випадковому вузлі. Використовуйте Політику внутрішнього трафіку Service, щоб обмежитися Podʼами на тому самому вузлі.

Оновлення DaemonSet

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

Ви можете змінювати Podʼи, які створює DaemonSet. Проте Podʼи не дозволяють оновлювати всі поля. Крім того, контролер DaemonSet буде використовувати початковий шаблон під час наступного створення вузла (навіть з тим самим імʼям).

Ви можете видалити DaemonSet. Якщо ви вказали --cascade=orphan з kubectl, тоді Podʼи залишаться на вузлах. Якщо ви потім створите новий DaemonSet з тим самим селектором, новий DaemonSet перейме наявні Podʼи. Якщо потрібно замінити які-небудь Podʼи, DaemonSet їх замінює згідно з його updateStrategy.

Ви можете виконати поетапне оновлення DaemonSet.

Альтернативи DaemonSet

Скрипти ініціалізації

Звичайно, можна запускати процеси демонів, безпосередньо стартуючи їх на вузлі (наприклад, за допомогою init, upstartd або systemd). Це абсолютно прийнятно. Однак є кілька переваг запуску таких процесів через DaemonSet:

  • Можливість моніторингу та управління логами для демонів так само як і для застосунків.
  • Однакова мова конфігурації та інструменти (наприклад, шаблони Podʼа, kubectl) для демонів та застосунків.
  • Запуск демонів у контейнерах з обмеженням ресурсів підвищує ізоляцію між демонами та контейнерами застосунків. Однак цього також можна досягти, запускаючи демонів у контейнері, але не в Podʼі.

Окремі Podʼи

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

Статичні Podʼи

Можна створювати Podʼи, записавши файл у певну теку, за якою стежить kubelet. Їх називають статичними Podʼами. На відміну від DaemonSet, статичними Podʼами не можна управляти за допомогою kubectl або інших клієнтів API Kubernetes. Статичні Podʼи не залежать від apiserver, що робить їх корисними в разі початкового налаштування кластера. Також статичні Podʼи можуть бути визнані застарілими в майбутньому.

Deployments

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

Використовуйте Deployment для stateless-сервісів, таких як фронтенди, де збільшення та зменшення кількості реплік і розгортання оновлень важливіші за контроль над тим, на якому саме хості працює Pod. Використовуйте DaemonSet, коли важливо, щоб копія Podʼа завжди працювала на всіх або певних хостах, якщо DaemonSet надає функціональність рівня вузла, яка дозволяє іншим Podʼам правильно працювати на цьому конкретному вузлі.

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

Що далі

4.2.5 - Jobs

Jobs представляють одноразові завдання, які виконуються до завершення, а потім зупиняються.

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

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

Також можна використовувати Job для запуску кількох Podʼів паралельно.

Якщо ви хочете запускати Job (або одне завдання, або кілька паралельно) за розкладом, див. CronJob.

Запуск прикладу Job

Ось конфігурація прикладу Job. Тут обчислюється число π з точністю до 2000 знаків і виконується його вивід. Виконання зазвичай займає близько 10 секунд.

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

Ви можете запустити цей приклад за допомогою такої команди:

kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml

Вивід буде подібний до цього:

job.batch/pi created

Перевірте статус Job за допомогою kubectl:


Name:           pi
Namespace:      default
Selector:       batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels:         batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
                batch.kubernetes.io/job-name=pi
                ...
Annotations:    batch.kubernetes.io/job-tracking: ""
Parallelism:    1
Completions:    1
Start Time:     Mon, 02 Dec 2019 15:20:11 +0200
Completed At:   Mon, 02 Dec 2019 15:21:16 +0200
Duration:       65s
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
           batch.kubernetes.io/job-name=pi
  Containers:
   pi:
    Image:      perl:5.34.0
    Port:       <none>
    Host Port:  <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  21s   job-controller  Created pod: pi-xf9p4
  Normal  Completed         18s   job-controller  Job completed

apiVersion: batch/v1
kind: Job
metadata:
  annotations: batch.kubernetes.io/job-tracking: ""
             ...
  creationTimestamp: "2022-11-10T17:53:53Z"
  generation: 1
  labels:
    batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
    batch.kubernetes.io/job-name: pi
  name: pi
  namespace: default
  resourceVersion: "4751"
  uid: 204fb678-040b-497f-9266-35ffa8716d14
spec:
  backoffLimit: 4
  completionMode: NonIndexed
  completions: 1
  parallelism: 1
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
  suspend: false
  template:
    metadata:
      creationTimestamp: null
      labels:
        batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
        batch.kubernetes.io/job-name: pi
    spec:
      containers:
      - command:
        - perl
        - -Mbignum=bpi
        - -wle
        - print bpi(2000)
        image: perl:5.34.0
        imagePullPolicy: IfNotPresent
        name: pi
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Never
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
status:
  active: 1
  ready: 0
  startTime: "2022-11-10T17:53:57Z"
  uncountedTerminatedPods: {}

Щоб переглянути завершені Podʼи Job, використовуйте kubectl get pods.

Щоб вивести всі Podʼи, які належать Job, у машинночитаній формі, ви можете використовувати таку команду:

pods=$(kubectl get pods --selector=batch.kubernetes.io/job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods

Вивід буде подібний до цього:

pi-5rwd7

Тут селектор збігається з селектором Job. Параметр --output=jsonpath вказує вираз із назвою кожного Podʼа в отриманому списку.

Перегляньте стандартний вивід одного з Podʼів:

kubectl logs $pods

Інший спосіб переглянути логи Job:

kubectl logs jobs/pi

Вивід буде подібний до цього:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

Написання специфікації Job

Як і з будь-якою іншою конфігурацією Kubernetes, у Job мають бути вказані поля apiVersion, kind та metadata.

Коли панель управління створює нові Podʼи для Job, .metadata.name Job є частиною основи для назв цих Podʼів. Назва Job повинна бути дійсним значенням DNS-піддомену, але це може призводити до неочікуваних результатів для імен хостів Podʼів. Для найкращої сумісності назва повинна відповідати суворішим правилам для DNS-мітки. Навіть якщо назва є DNS-піддоменом, вона повинна бути не довшою за 63 символи.

Також у Job повинен бути розділ .spec.

Мітки для Job

Мітки Job матимуть префікс batch.kubernetes.io/ для job-name та controller-uid.

Шаблон Podʼа

.spec.template — єдине обовʼязкове поле .spec.

.spec.template — це шаблон Podʼа. Він має точно таку саму схему, як Pod, окрім того, що він вкладений і не має apiVersion чи kind.

Окрім обовʼязкових полів для Podʼа, шаблон Podʼа в Job повинен вказувати відповідні мітки (див. селектор Podʼа) та відповідну політику перезапуску.

Дозволяється лише RestartPolicy, що дорівнює Never або OnFailure.

Селектор Podʼа

Поле .spec.selector є необовʼязковим. Майже у всіх випадках вам не слід його вказувати. Дивіться розділ вказування вашого власного селектора Podʼа.

Паралельне виконання для Jobs

Існують три основних типи завдань, які підходять для виконання як Job:

  1. Непаралельні Jobs

    • зазвичай запускається лише один Pod, якщо тільки він не зазнає збою.
    • Job завершується, як тільки робота його Podʼа завершується успішно.
  2. Паралельні Jobs із фіксованою кількістю завершень:

    • вкажіть ненульове додатне значення для .spec.completions.
    • Job являє собою загальне завдання і є завершеним, коли є .spec.completions успішних Podʼів.
    • під час використання .spec.completionMode="Indexed", кожен Pod отримує різний індекс у діапазоні від 0 до .spec.completions-1.
  3. Паралельні Jobs із чергою роботи:

    • не вказуйте .spec.completions, типово встановлюється .spec.parallelism.
    • Podʼи повинні координувати свої дії між собою або із зовнішнім сервісом, щоб визначити, над чим кожен з них має працювати. Наприклад, Pod може отримати пакет із не більш ніж N елементів з черги роботи.
    • кожен Pod може незалежно визначити, чи завершилися всі рівноправні з ним Podʼи, а отже, чи завершено весь Job.
    • коли будь-який Pod з Job завершується успішно, нові Podʼи не створюються.
    • як тільки хоча б один Pod завершився успішно і всі Podʼи завершені, тоді Job завершується успішно.
    • як тільки будь-який Pod завершився успішно, жоден інший Pod не повинен виконувати роботу для цього завдання чи записувати будь-який вивід. Вони всі мають бути в процесі завершення.

Для непаралельного Job ви можете залишити як .spec.completions, так і .spec.parallelism не встановленими. Коли обидва не встановлені, обидва типово встановлюються на 1.

Для Job із фіксованою кількістю завершень вам слід встановити .spec.completions на кількість необхідних завершень. Ви можете встановити .spec.parallelism чи залишити його не встановленим, тоді він стандартно дорівнюватиме 1.

Для Job із чергою роботи ви повинні залишити .spec.completions не встановленим і встановити .spec.parallelism у невідʼємне ціле число.

За докладною інформацією про використання різних типів Job, див. розділ патерни Job.

Контроль паралелізму

Запитаний паралелізм (.spec.parallelism) може бути встановлений у будь-яке невідʼємне значення. Якщо значення не вказано, стандартно воно дорівнює 1. Якщо вказано як 0, то Job фактично призупинено, доки значення не буде збільшено.

Фактичний паралелізм (кількість Podʼів, які працюють в будь-який момент) може бути більше чи менше, ніж запитаний паралелізм, з різних причин:

  • Для Job із фіксованою кількістю завершень, фактична кількість Podʼів, які працюють паралельно, не перевищуватиме кількість решти завершень. Вищі значення .spec.parallelism фактично ігноруються.
  • Для Job із чергою роботи жоден новий Pod не запускається після успішного завершення будь-якого Podʼа, однак решті Podʼів дозволено завершити роботу.
  • Якщо у контролера Job не було часу відреагувати.
  • Якщо контролер Job не зміг створити Podʼи з будь-якої причини (нестача ResourceQuota, відсутність дозволу тощо), тоді може бути менше Podʼів, ніж запитано.
  • Контролер Job може обмежувати створення нових Podʼів через надмірну кількість збоїв попередніх Podʼів у тому самому Job.
  • Коли Pod коректно завершується, на його зупинку потрібен час.

Режим завершення

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

Для Jobs із фіксованою кількістю завершень, тобто Jobs зі значенням .spec.completions, відмінним від null, може бути встановлено режим завершення, який вказується в .spec.completionMode:

  • NonIndexed (стандартно): Job вважається завершеним, коли є .spec.completions успішно завершених Podʼів. Іншими словами, завершення всіх Podʼів рівнозначні. Зверніть увагу, що Jobs, у яких .spec.completions дорівнює null, неявно є NonIndexed.

  • Indexed: Podʼи Job отримують повʼязаний індекс завершення від 0 до .spec.completions-1. Індекс доступний через чотири механізми:

    • Анотація Podʼа batch.kubernetes.io/job-completion-index.
    • Мітка Podʼа batch.kubernetes.io/job-completion-index (для v1.28 і новіших). Зверніть увагу, що для використання цієї мітки функціональна можливість PodIndexLabel повинна бути увімкнена, і вона типово увімкнена.
    • Як частина імені хоста Podʼа, за шаблоном $(job-name)-$(index). Коли ви використовуєте Indexed Job у поєднанні з Service, Podʼи всередині Job можуть використовувати детерміністичні імена хостів для адресації одне одного через DNS. Докладні відомості щодо того, як налаштувати це, див. Job із комунікацією між Podʼами.
    • З контейнера завдання, в змінній середовища JOB_COMPLETION_INDEX.

    Job вважається завершеним, коли є успішно завершений Pod для кожного індексу. Докладні відомості щодо того, як використовувати цей режим, див. Indexed Job для паралельної обробки зі статичним призначенням роботи.

Примітка:

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

Інтеграція з Workload API

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.36; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the WorkloadWithJob feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість WorkloadWithJob увімкнено, контролер Job компілює конфігурацію .spec.scheduling Job в обʼєкти Workload та PodGroup (scheduling.k8s.io/v1beta1) перед створенням будь-яких Podʼів. Це дозволяє вам виражати явний намір планування для Job, такий як групове планування (всі Podʼи плануються разом або жодного), спільне розташування за топологією та поведінку під час порушень.

Коли ви опускаєте .spec.scheduling, Job типово використовує політику планування Basic, яка зберігає стандартний результат планування Pod за Podʼом для звичайного Job. Контролер все одно створює Workload та PodGroup для кожного відповідного Job (включно з Basic), тому спостережувані обʼєкти є узгодженими незалежно від політики.

Конфігурація планування

Поле .spec.scheduling приймає такі поля:

  • schedulingPolicy: потрібно вказати рівно одне з basic або gang. З gang всі Podʼи мають бути запланованими разом, перш ніж будь-який з них буде привʼязаний. Опущене gang.minCount стандартно дорівнює .spec.parallelism Job. Див. Політики планування PodGroup.
  • schedulingConstraints: обмеження спільного розташування за топологією для Podʼів Job.
  • disruptionMode: чи порушується робота Podʼів окремо (single), чи групою (all). Див. порушення та пріоритет.
  • resourceClaims: динамічні запити на ресурси, спільні для Podʼів Job.

Всі поля .spec.scheduling є незмінними після створення Job, окрім schedulingPolicy.gang.minCount, яке ви можете змінювати, щоб еластично масштабувати групу.

Групове планування

Цей Job запитує групове планування для своїх 8 Podʼів, розташованих в одній зоні, робота яких порушується разом:

apiVersion: batch/v1
kind: Job
metadata:
  name: distributed-training
  namespace: training
spec:
  parallelism: 8
  completions: 8
  completionMode: Indexed
  scheduling:
    schedulingPolicy:
      gang: {}                # minCount пропущено -> стандартно дорівнює parallelism (8)
    schedulingConstraints:
      key:
      - level: topology.kubernetes.io/zone
    disruptionMode:
      all: {}                 # робота всієї групи порушується разом
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: trainer
        image: training-image:latest
        resources:
          limits:
            nvidia.com/gpu: 1

Коли контролер Job обробляє цей Job, він:

  1. Створює обʼєкт Workload у тому самому просторі імен, що містить podGroupTemplate, скомпільований з .spec.scheduling (тут — політика групового планування з minCount: 8).
  2. Створює обʼєкт PodGroup на основі цього шаблону. PodGroup є одиницею планування під час виконання і містить вбудовану копію політики.
  3. Створює Podʼи з .spec.schedulingGroup.podGroupName, встановленим на імʼя PodGroup, звʼязуючи кожен Pod зі своєю групою планування.

Контролер виявляє ці обʼєкти через посилання у специфікації (Workload.spec.controllerRef та PodGroup.spec.workloadRef), а не за іменем. Обʼєкти, які створює контролер, містять запис ownerReferences, що вказує на Job, тому вони видаляються збирачем сміття, коли Job видаляється.

Типове (Basic) планування

Job, який опускає .spec.scheduling, типово використовує Basic, що діє як неявна відмова від групового планування. Його Podʼи плануються так само, як у звичайного Job, при цьому все одно створюються Workload та PodGroup типу Basic:

apiVersion: batch/v1
kind: Job
metadata:
  name: batch-processor
  namespace: batch
spec:
  parallelism: 10
  completions: 10
  # .spec.scheduling пропущено -> типово використовується Basic scheduling.
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: processor
        image: processor-image:v1

Контролери вищого рівня

Для автономного Job контролер Job володіє як Workload, так і PodGroup. Коли Job натомість містить ownerReference на батьківський контролер, який компілює Workload (наприклад, JobSet), контролер Job делегує володіння Workload цьому батьківському контролеру. Чи створює контролер Job все ще PodGroup під час виконання, залежить від того, що делегує батьківський контролер:

  • Якщо батьківський контролер встановлює анотацію scheduling.k8s.io/group-template-name на Job, контролер Job створює та володіє PodGroup, зіставленим з іменованим PodGroupTemplate батьківського контролера.
  • В іншому випадку батьківський контролер володіє обома обʼєктами, і контролер Job не створює жодного; він виявляє наявні обʼєкти та використовує їх під час створення Podʼів.

Якщо в шаблоні Podʼа Job вже задано .spec.template.spec.schedulingGroup, контролер Job не створює жодного обʼєкта, дозволяючи вам (або контролеру вищого рівня) самостійно керувати життєвим циклом Workload/PodGroup.

Поведінка CronJob

Jobs, створені за допомогою CronJob, є автономними; CronJob не створює та не керує обʼєктами Workload. Якщо jobTemplate CronJob встановлює .spec.scheduling, контролер Job створює окремі обʼєкти Workload та PodGroup для кожного екземпляра Job, скомпільовані з .spec.scheduling цього Job (типово Basic, якщо опущено). Ці обʼєкти видаляються збирачем сміття, коли кожен Job завершується або видаляється.

Обмеження для альфа-релізу

  • Кожен Job відповідає точно одному обʼєкту PodGroup; всі Podʼи в Job поділяють єдину політику планування.
  • Лише schedulingPolicy.gang.minCount є змінним; всі інші поля .spec.scheduling є незмінними після створення.
  • Призупинені Jobs зберігають свої обʼєкти Workload та PodGroup; вони не видаляються під час призупинення і не створюються заново під час відновлення.

Обробка збоїв Podʼа та контейнера

Контейнер у Podʼі може вийти з ладу з ряду причин, таких як завершення процесу з ненульовим кодом виходу або примусове припинення роботи контейнера через перевищення обмеження памʼяті тощо. Якщо це стається і .spec.template.spec.restartPolicy = "OnFailure", тоді Pod залишається на вузлі, але контейнер перезапускається. Отже, ваш застосунок повинен обробляти випадок, коли він перезапускається локально, або вказувати .spec.template.spec.restartPolicy = "Never". Докладні відомості про restartPolicy див. в розділі життєвий цикл Podʼа.

Весь Pod також може вийти з ладу з низки причин, таких як коли Pod виштовхується з вузла (вузол оновлюється, перезавантажується, видаляється тощо), або якщо контейнер Podʼа виходить з ладу і .spec.template.spec.restartPolicy = "Never". Коли Pod виходить з ладу, контролер Job запускає новий Pod. Це означає, що ваш застосунок повинен обробляти випадок, коли він перезапускається у новому Podʼі. Зокрема, він повинен обробляти тимчасові файли, блокування, неповний вивід та інше, викликане попередніми запусками.

Типово кожен збій Podʼа враховується в обмеженні .spec.backoffLimit, див. політика backoff для збоїв Podʼа. Однак ви можете налаштовувати обробку збоїв Podʼа, встановлюючи політику збоїв Podʼа для Job.

Додатково ви можете вирішити враховувати збої Podʼа незалежно для кожного індексу в Indexed Job, встановлюючи поле .spec.backoffLimitPerIndex (докладні відомості див. обмеження backoff на індекс).

Зверніть увагу, що навіть якщо ви вказали .spec.parallelism = 1 і .spec.completions = 1 і .spec.template.spec.restartPolicy = "Never", та сама програма може іноді бути запущена двічі.

Якщо ви вказали як .spec.parallelism, так і .spec.completions більше ніж 1, то може бути запущено кілька Podʼів одночасно. Таким чином, ваші Podʼи також повинні бути стійкими до паралелізму.

Якщо ви вказуєте поле .spec.podFailurePolicy, контролер Job не вважає збоєм Pod, що завершується (Pod зі встановленим полем .metadata.deletionTimestamp), доки цей Pod не стане термінальним (його .status.phase є Failed або Succeeded). Однак контролер Job створює Pod-замінник, щойно стає очевидним, що Pod перебуває в процесі завершення. Після того як Pod завершиться, контролер Job оцінює .backoffLimit і .podFailurePolicy для відповідного Job, враховуючи тепер уже завершений Pod.

Якщо хоча б одна з цих вимог не виконана, контролер Job вважає Pod, що завершується, негайним збоєм, навіть якщо цей Pod пізніше завершиться з phase: "Succeeded".

Політика backoff для збоїв Podʼа

Трапляються ситуації, коли ви хочете, щоб Job зазнав збою після певної кількості спроб через логічну помилку в конфігурації тощо. Для цього встановіть .spec.backoffLimit, щоб вказати кількість повторних спроб, після яких Job вважатиметься невдалим.

Стандартне значення .spec.backoffLimit дорівнює 6, якщо не вказано обмеження backoff на індекс (тільки Індексовані Job). Якщо вказано .spec.backoffLimitPerIndex, то .spec.backoffLimit стандартно дорівнює 2147483647 (MaxInt32).

Повʼязані з Job невдалі Podʼи створюються заново контролером Job з експоненціальною затримкою (10s, 20s, 40s …), яка обмежена десятьма хвилинами.

Кількість спроб обчислюється двома способами:

  • Кількість Podʼів з .status.phase = "Failed".
  • Під час використання restartPolicy = "OnFailure" кількість спроб у всіх контейнерах Podʼів з .status.phase, що дорівнює Pending або Running.

Якщо хоча б один із розрахунків досягне значення .spec.backoffLimit, Job вважається невдалим.

Примітка:

Якщо ваш Job має restartPolicy = "OnFailure", майте на увазі, що Pod, який виконує Job, буде завершено, щойно буде досягнуто обмеження backoff Job. Це може ускладнити налагодження виконуваного файлу Job. Ми радимо встановлювати restartPolicy = "Never" під час налагодження Job або використовувати систему логування, щоб вивід невдалих Jobs не було випадково втрачено.

обмеження backoff на індекс

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.33
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.33. It was first available in the v1.28 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate JobBackoffLimitPerIndex, Kubernetes ignores it but does not report any error.

Коли ви запускаєте Indexed Job, ви можете вибрати обробку спроб для збоїв Podʼа незалежно для кожного індексу. Для цього встановіть .spec.backoffLimitPerIndex, щоб вказати максимальну кількість невдалих спроб Podʼа для кожного індексу.

Коли обмеження backoff на індекс перевищується для певного індексу, Kubernetes вважає цей індекс невдалим і додає його до поля .status.failedIndexes. Індекси, які виконались успішно, реєструються в полі .status.completedIndexes, незалежно від того, чи ви встановили поле backoffLimitPerIndex.

Зауважте, що невдалий індекс не перериває виконання інших індексів. Щойно всі індекси завершаться для Job, в якому ви вказали обмеження backoff на індекс, якщо хоча б один з цих індексів виявився невдалим, контролер Job позначає весь Job як невдалий, встановлюючи стан Failed в статусі. Job отримує позначку "невдалий", навіть якщо деякі, можливо, майже всі індекси були оброблені успішно.

Ви також можете обмежити максимальну кількість позначених невдалих індексів, встановивши поле .spec.maxFailedIndexes. Коли кількість невдалих індексів перевищує значення поля maxFailedIndexes, контролер Job запускає завершення всіх Podʼів, що залишаються запущеними в цьому Job. Щойно всі Podʼи будуть завершені, контролер Job позначає весь Job як невдалий, встановлюючи стан Failed в статусі Job.

Ось приклад маніфесту для Job, який визначає backoffLimitPerIndex:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-backoff-limit-per-index-example
spec:
  completions: 10
  parallelism: 3
  completionMode: Indexed  # required for the feature
  backoffLimitPerIndex: 1  # maximal number of failures per index
  maxFailedIndexes: 5      # maximal number of failed indexes before terminating the Job execution
  template:
    spec:
      restartPolicy: Never # required for the feature
      containers:
      - name: example
        image: python
        command:           # The jobs fails as there is at least one failed index
                           # (all even indexes fail in here), yet all indexes
                           # are executed as maxFailedIndexes is not exceeded.
        - python3
        - -c
        - |
          import os, sys
          print("Hello world")
          if int(os.environ.get("JOB_COMPLETION_INDEX")) % 2 == 0:
            sys.exit(1)

У вищенаведеному прикладі контролер Job дозволяє один перезапуск для кожного з індексів. Коли загальна кількість невдалих індексів перевищує 5, тоді весь Job завершується.

Після завершення Job його стан виглядає так:

kubectl get -o yaml job job-backoff-limit-per-index-example
  status:
    completedIndexes: 1,3,5,7,9
    failedIndexes: 0,2,4,6,8
    succeeded: 5          # 1 succeeded pod for each of 5 succeeded indexes
    failed: 10            # 2 failed pods (1 retry) for each of 5 failed indexes
    conditions:
    - message: Job has failed indexes
      reason: FailedIndexes
      status: "True"
      type: FailureTarget
    - message: Job has failed indexes
      reason: FailedIndexes
      status: "True"
      type: Failed

Контролер Job додає стан FailureTarget до Job для запуску завершення та очищення Job. Коли всі Podʼи Job завершуються, контролер Job додає стан Failed з тими ж значеннями для reason і message, що й у стану FailureTarget. Для деталей дивіться Завершення Podʼів Job.

Додатково ви можете використовувати обмеження backoff на індекс разом з політикою збоїв Podʼа. Коли використовується обмеження backoff на індекс, доступна нова дія FailIndex, яка дозволяє вам уникати непотрібних повторів всередині індексу.

Політика збоїв Podʼа

This is a stable feature in Kubernetes, and has been since the 1.31 release. You can no longer toggle this feature (the associated feature gate has been removed).

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

У деяких ситуаціях вам може знадобитися кращий контроль обробки збоїв Podʼа, ніж контроль, який надає Політика backoff для збоїв Podʼа, яка базується на .spec.backoffLimit Job. Ось деякі приклади використання:

  • Для оптимізації витрат на виконання робочих навантажень, уникнення непотрібних перезапусків Podʼа, ви можете завершити Job, як тільки один із його Podʼів відмовить із кодом виходу, що вказує на помилку програмного забезпечення.
  • Щоб гарантувати, що ваш Job завершиться, навіть якщо є порушення, ви можете ігнорувати збої Podʼів, спричинені порушеннями (такими як витіснення, виселення, ініційоване API або виселення на основі taint), щоб вони не враховувалися в обмеженні спроб .spec.backoffLimit.

Ви можете налаштувати політику збоїв Podʼа в полі .spec.podFailurePolicy, щоб задовольнити наведені вище сценарії використання. Ця політика може обробляти збої Podʼа на основі кодів виходу контейнера та станів Podʼа.

Ось маніфест для Job, який визначає podFailurePolicy:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-pod-failure-policy-example
spec:
  completions: 12
  parallelism: 3
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: main
        image: docker.io/library/bash:5
        command: ["bash"]        # example command simulating a bug which triggers the FailJob action
        args:
        - -c
        - echo "Hello world!" && sleep 5 && exit 42
  backoffLimit: 6
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        containerName: main      # optional
        operator: In             # one of: In, NotIn
        values: [42]
    - action: Ignore             # one of: Ignore, FailJob, Count
      onPodConditions:
      - type: DisruptionTarget   # indicates Pod disruption

У вищенаведеному прикладі перше правило політики збоїв Podʼа вказує, що Job слід позначити як невдалий, якщо контейнер main завершиться кодом виходу 42. Наступні правила стосуються саме контейнера main:

  • код виходу 0 означає, що контейнер виконався успішно
  • код виходу 42 означає, що весь Job завершився невдало
  • будь-який інший код виходу вказує, що контейнер, а отже, і весь Pod завершилися невдало. Pod буде створено заново, якщо загальна кількість перезапусків менша за backoffLimit. Якщо backoffLimit досягнуто, весь Job завершився невдало.

Примітка:

Оскільки в шаблоні Podʼа вказано restartPolicy: Never, kubelet не перезапускає контейнер main в тому конкретному Podʼі.

Друге правило політики збоїв Podʼа, що вказує дію Ignore для невдалих Podʼів зі станом DisruptionTarget, виключає порушення роботи Podʼів із підрахунку обмеження спроб .spec.backoffLimit.

Примітка:

Якщо Job виявився невдалим через політику збоїв Podʼа чи через політику backoff для збоїв Podʼа, і Job виконується кількома Podʼами, Kubernetes завершує всі Podʼи в цьому Job, які все ще перебувають у статусах Pending або Running.

Ось деякі вимоги та семантика API:

  • якщо ви хочете використовувати поле .spec.podFailurePolicy для Job, вам також слід визначити шаблон Podʼа цього Job з .spec.restartPolicy, встановленим на Never.
  • правила політики збоїв Podʼа, які ви визначаєте у spec.podFailurePolicy.rules, оцінюються послідовно. Якщо одне з правил відповідає збою Podʼа, інші правила ігноруються. Якщо жодне правило не відповідає збою Podʼа, застосовується типова обробка.
  • ви можете обмежити правило певним контейнером, вказавши його імʼя у spec.podFailurePolicy.rules[*].onExitCodes.containerName. Якщо не вказано, правило застосовується до всіх контейнерів. Якщо вказано, воно повинно відповідати імені одного з контейнерів або initContainer у шаблоні Podʼа.
  • ви можете вказати дію, яка виконується, коли спрацьовує політика збоїв Podʼа, у spec.podFailurePolicy.rules[*].action. Можливі значення:
    • FailJob: використовуйте це, щоб вказати, що Job цього Podʼа має бути позначений як Failed, а всі запущені Podʼи мають бути завершені.
    • Ignore: використовуйте це, щоб вказати, що значення лічильника, що рахує .spec.backoffLimit, не повинне збільшуватися, і має бути створений Pod-замінник.
    • Count: використовуйте це, щоб вказати, що Pod повинен бути оброблений типовим способом. Лічильник .spec.backoffLimit повинен збільшитися.
    • FailIndex: використовуйте цю дію разом із обмеження backoff на індекс для уникнення непотрібних повторних спроб в межах індексу невдалого Podʼа.

Примітка:

Коли використовується podFailurePolicy, контролер Job враховує лише Podʼи у фазі Failed. Podʼи з відміткою про видалення, які не знаходяться в термінальній фазі (Failed чи Succeeded), вважаються такими, що ще завершуються. Це означає, що такі Podʼи зберігають завершувачі відстеження доки не досягнуть термінальної фази. З Kubernetes 1.27 Kubelet переводить видалені Podʼи в термінальну фазу (див.: Фаза Podʼа). Це забезпечує видалення завершувачів контролером Job.

Примітка:

Починаючи з Kubernetes v1.28, коли використовується політика збоїв Podʼа, контролер Job створює заново Podʼи, що завершуються, лише тоді, коли ці Podʼи досягли термінальної фази Failed. Ця поведінка подібна до podReplacementPolicy: Failed. Докладніше див. Політика заміни Podʼів.

Коли ви використовуєте podFailurePolicy, і Job зазнає невдачі через Pod, що відповідає правилу з дією FailJob, контролер Job запускає процес завершення Job, додаючи стан FailureTarget. Для деталей дивіться Завершення та очищення Job.

Політика успіху

Під час створення Indexed Job ви можете визначити, коли Job може бути визнаний успішним, за допомогою .spec.successPolicy, на основі успішних Podʼів.

Типово Job вважається успішним, коли кількість успішних Podʼів дорівнює .spec.completions. Існують ситуації, коли вам може знадобитися додатковий контроль для визнання Job успішним:

  • Під час виконання симуляцій з різними параметрами, вам можуть не знадобитися всі симуляції для успішного завершення загального Job.
  • Під час використання шаблону лідер-виконавець, лише успіх лідера визначає успіх чи невдачу Job. Прикладами цього є фреймворки, такі як MPI та PyTorch тощо.

Ви можете налаштувати політику успіху у полі .spec.successPolicy, щоб задовольнити вищезазначені випадки використання. Ця політика може керувати успіхом Job на основі успішних Podʼів. Після того, як Job відповідає політиці успіху, контролер Job завершує решту Podʼів. Політика успіху визначається правилами. Кожне правило може мати одну з таких форм:

  • Коли ви вказуєте лише succeededIndexes, одразу після успішного завершення всіх індексів, вказаних у succeededIndexes, контролер Job позначає Job як успішний. succeededIndexes повинен бути списком інтервалів між 0 і .spec.completions-1.
  • Коли ви вказуєте лише succeededCount, як тільки кількість успішних індексів досягне succeededCount, контролер Job позначає Job як успішний.
  • Коли ви вказуєте як succeededIndexes, так і succeededCount, як тільки кількість успішних індексів з підмножини індексів, вказаних у succeededIndexes, досягне succeededCount, контролер Job позначає Job як успішний.

Зверніть увагу, що коли ви вказуєте кілька правил у .spec.successPolicy.rules, контролер Job оцінює правила послідовно. Після того, як Job задовольняє правило, контролер Job ігнорує решту правил.

Ось приклад маніфеста для Job із successPolicy:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-success
spec:
  parallelism: 10
  completions: 10
  completionMode: Indexed # Required for the success policy
  successPolicy:
    rules:
      - succeededIndexes: 0,2-3
        succeededCount: 1
  template:
    spec:
      containers:
      - name: main
        image: python
        command:          # Provided that at least one of the Pods with 0, 2, and 3 indexes has succeeded,
                          # the overall Job is a success.
          - python3
          - -c
          - |
            import os, sys
            if os.environ.get("JOB_COMPLETION_INDEX") == "2":
              sys.exit(0)
            else:
              sys.exit(1)
      restartPolicy: Never

У вищенаведеному прикладі були вказані як succeededIndexes, так і succeededCount. Тому контролер Job позначить Job як успішний і завершить залишкові Podʼи після успіху будь-яких зазначених індексів, 0, 2 або 3. Job, який відповідає політиці успіху, отримує стан SuccessCriteriaMet з причиною SuccessPolicy. Після видалення залишкових Podʼів, Job отримує стан Complete.

Зверніть увагу, що succeededIndexes представлено як інтервали, розділені дефісом. Номери перераховані у вигляді першого та останнього елементів серії, розділених дефісом.

Примітка:

Коли ви вказуєте як політику успіху, так і деякі політики завершення, такі як .spec.backoffLimit і .spec.podFailurePolicy, щойно Job задовольняє будь-яку з політик, контролер Job дотримується політики завершення і ігнорує політику успіху.

Завершення Job та очищення

Коли Job завершується, Podʼи більше не створюються, але Podʼи зазвичай також не видаляються. Якщо їх не видаляти, можна переглядати логи завершених Podʼів для перевірки наявності помилок, попереджень чи іншого діагностичного виводу. Обʼєкт Job також залишається після завершення, щоб ви могли переглядати його статус. Користувач сам видаляє старі Jobs, переглянувши їхній статус. Видаліть Job за допомогою kubectl (наприклад, kubectl delete jobs/pi або kubectl delete -f ./job.yaml). Коли ви видаляєте Job за допомогою kubectl, усі створені ним Podʼи також видаляються.

Стандартно Job буде виконуватися безперервно, якщо тільки Pod не вийде з ладу (restartPolicy=Never) або контейнер не вийде з ладу з помилкою (restartPolicy=OnFailure), після чого Job дотримується .spec.backoffLimit, описаного вище. Як тільки досягнуто .spec.backoffLimit, Job буде позначено як невдалий, і будь-які запущені Podʼи будуть завершені.

Іншим способом завершити Job є встановлення активного крайнього терміну. Це робиться шляхом встановлення в поле .spec.activeDeadlineSeconds Job кількості секунд. activeDeadlineSeconds застосовується до тривалості Job, незалежно від кількості створених Podʼів. Як тільки Job досягає activeDeadlineSeconds, всі його запущені Podʼи завершуються, і статус Job стане type: Failed з reason: DeadlineExceeded.

Зауважте, що .spec.activeDeadlineSeconds Job має пріоритет перед його .spec.backoffLimit. Отже, Job, який повторно запускає один або кілька невдалих Podʼів, не розпочне розгортання додаткових Podʼів, якщо activeDeadlineSeconds вже досягнуто, навіть якщо backoffLimit ще не досягнуто.

Приклад:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

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

Памʼятайте, що restartPolicy застосовується до Podʼа, а не до самого Job: автоматичного перезапуску Job не відбудеться, якщо статус Job type: Failed. Тобто механізми завершення Job, активовані за допомогою .spec.activeDeadlineSeconds і .spec.backoffLimit, призводять до остаточного збою Job, який потребує ручного втручання.

Термінальні стани Job

Job має два можливих термінальних стани, кожному з яких відповідає стан Job (condition):

  • Успішне виконання: стан Job Complete
  • Невдача: стан Job Failed

Jobs зазнають невдачі з таких причин:

  • Кількість невдач Podʼа перевищила вказане .spec.backoffLimit у специфікації Job. Для деталей див. Політика backoff для збоїв Podʼа.
  • Час виконання Job перевищив вказане .spec.activeDeadlineSeconds.
  • Indexed Job, який використовує .spec.backoffLimitPerIndex, має збійні індекси. Для деталей див. обмеження backoff на індекс.
  • Кількість невдалих індексів у Job перевищила вказане значення spec.maxFailedIndexes. Для деталей див. обмеження backoff на індекс.
  • Невдалий Pod відповідає правилу у .spec.podFailurePolicy, яке має дію FailJob. Для деталей про те, як правила політики збоїв Podʼа можуть вплинути на оцінку невдачі, див. Політика збоїв Podʼа.

Jobs успішні з таких причин:

  • Кількість успішних Podʼів досягла вказаного .spec.completions.
  • Виконані критерії, зазначені у .spec.successPolicy. Для деталей див. Політика успіху.

У Kubernetes v1.31 та пізніших версіях контролер Job затримує додавання термінальних станів, Failed або Complete, поки всі Podʼи Job не будуть завершені.

У Kubernetes v1.30 та раніших версіях контролер Job додавав термінальні стани Complete або Failed відразу після того, як був запущений процес завершення Job та всі завершувачі Podʼа були видалені. Однак деякі Podʼи все ще могли працювати або завершуватися в момент додавання термінального стану.

У Kubernetes v1.31 та пізніших версіях контролер додає термінальні стани Job лише після завершення всіх Podʼів. Ви можете керувати цією поведінкою за допомогою функціональних можливостей JobManagedBy та JobPodReplacementPolicy (обидві стандартно увімкнені).

Завершення Podʼів Job

Контролер Job додає стан FailureTarget або стан SuccessCriteriaMet до Job, щоб запустити завершення Podʼів після того, як Job відповідає критеріям успіху або невдачі.

Фактори, такі як terminationGracePeriodSeconds, можуть збільшити час від моменту додавання контролером Job стану FailureTarget або стану SuccessCriteriaMet до моменту, коли всі Podʼи Job завершаться і контролер Job додасть термінальний стан (Failed або Complete).

Ви можете використовувати стан FailureTarget або стан SuccessCriteriaMet, щоб оцінити, чи Job зазнав невдачі чи досяг успіху, без необхідності чекати, поки контролер додасть термінальний стан.

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

Або, якщо ваш кластер має обмежену місткість ресурсів, ви можете дочекатися появи стану Failed на Job, що затримає ваш Job-замінник, але збереже ресурси, оскільки ви дочекаєтеся видалення всіх невдалих Podʼів.

Автоматичне очищення завершених Jobs

Зазвичай завершені Jobs вже не потрібні в системі. Їх зберігання створює тиск на API-сервер. Якщо Jobs керуються безпосередньо контролером вищого рівня, таким як CronJobs, їх можуть очищати CronJobs на основі заданої політики очищення за місткістю.

Механізм TTL для завершених Jobs

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.23

Ще один спосіб автоматично очищати завершені Jobs (Complete або Failed) — це використовувати механізм TTL, наданий контролером TTL для завершених ресурсів, вказуючи поле .spec.ttlSecondsAfterFinished у Job.

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

Наприклад:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job pi-with-ttl стане придатним для автоматичного видалення через 100 секунд після завершення.

Якщо поле встановлено в 0, Job стане придатним для автоматичного видалення одразу після завершення. Якщо поле не вказане, Job не буде очищено контролером TTL після завершення.

Примітка:

Рекомендується встановлювати поле ttlSecondsAfterFinished, оскільки некеровані Jobs (Jobs, які ви створили безпосередньо, а не опосередковано через інші API робочого навантаження, такі як CronJob) мають стандартну політику видалення orphanDependents, що призводить до того, що Podʼи, створені некерованим Job, залишаються після того, як Job повністю видалено. Навіть якщо панель управління зрештою прибирає збором сміття Podʼи видаленого Job після їх збою або завершення, іноді ці залишкові Podʼи можуть погіршувати продуктивність кластера або в найгіршому випадку спричинити вихід кластера з ладу через це погіршення.

Ви можете використовувати LimitRanges та ResourceQuotas, щоб обмежити кількість ресурсів, які певний простір імен може споживати.

Патерни використання Job

Обʼєкт Job може використовуватися для обробки набору незалежних, але повʼязаних робочих одиниць. Це можуть бути електронні листи для надсилання, кадри для рендерингу, файли для перекодування, діапазони ключів у базі даних NoSQL для сканування і так далі.

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

Існує кілька різних патернів для паралельних обчислень, кожен з власними перевагами та недоліками. Компроміси:

  • Один обʼєкт Job для кожної робочої одиниці або один обʼєкт Job для всіх робочих одиниць. Один Job на кожну робочу одиницю створює деяку накладну роботу для користувача та системи під час управління великою кількістю обʼєктів Job. Один Job для всіх робочих одиниць підходить краще для великої кількості одиниць.
  • Кількість створених Podʼів дорівнює кількості робочих одиниць або кожен Pod може обробляти кілька робочих одиниць. Коли кількість Podʼів дорівнює кількості робочих одиниць, Podʼи зазвичай вимагають менше змін у наявному коді та контейнерах. Кожен Pod, що обробляє кілька робочих одиниць, підходить краще для великої кількості одиниць.
  • Кілька підходів використовують чергу роботи. Це вимагає запуску сервісу черги та змін наявної програми чи контейнера, щоб вони використовували чергу роботи. Інші підходи легше адаптувати до наявного контейнеризованого застосунку.
  • Коли Job повʼязаний із headless Service, ви можете дозволити Podʼам у межах Job спілкуватися один з одним для спільних обчислень.

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

ПатернОдин обʼєкт JobКількість Podʼів менша за робочі одиниці?Використовувати застосунок без модифікацій?
Черга, Pod на одиницю роботи✓іноді
Черга змінної кількості Podʼів✓✓
Indexed Job зі статичним призначенням роботи✓✓
Job із комунікацією між Podʼами✓інодііноді
Розширення шаблону Job✓

Коли ви вказуєте завершення з .spec.completions, кожний Pod, створений контролером Job, має ідентичний spec. Це означає, що всі Podʼи для завдання матимуть однаковий командний рядок, однаковий образ, однакові томи та (майже) однакові змінні середовища. Ці патерни — це різні способи організації Podʼів для роботи над різними завданнями.

Ця таблиця показує обовʼязкові налаштування для .spec.parallelism та .spec.completions для кожного з патернів. Тут W — це кількість робочих одиниць.

Патерн.spec.completions.spec.parallelism
Черга, Pod на одиницю роботиWбудь-яка
Черга змінної кількості Podʼівnullбудь-яка
Indexed Job зі статичним призначенням роботиWбудь-яка
Job із комунікацією між PodʼамиWW
Розширення шаблону Job1повинно бути 1

Розширене використання

Призупинення Job

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

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

Щоб призупинити Job, ви можете оновити поле .spec.suspend Job на значення true; пізніше, коли ви захочете відновити його, оновіть його на значення false. Створення Job з .spec.suspend встановленим в true створить його в призупиненому стані.

У Kubernetes 1.35 або новіших версіях поле .status.startTime очищується під час призупинення Job, коли увімкнено функціональну можливість MutableSchedulingDirectivesForSuspendedJobs.

Під час відновлення Job після призупинення його поле .status.startTime буде скинуто на поточний час. Це означає, що таймер .spec.activeDeadlineSeconds буде зупинений і перезапущений, коли Job буде призупинено та відновлено.

Коли ви призупиняєте Job, будь-які запущені Podʼи, які не мають статусу Completed, будуть завершені з сигналом SIGTERM. Період коректного завершення Podʼа буде дотримано, і ваш Pod має обробити цей сигнал протягом цього періоду. Це може включати в себе збереження прогресу на майбутнє або скасування змін. Podʼи, завершені у такий спосіб, не враховуватимуться при підрахунку completions Job.

Приклад визначення Job в призупиненому стані може виглядати так:

kubectl get job myjob -o yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  suspend: true
  parallelism: 1
  completions: 5
  template:
    spec:
      ...

Ви також можете перемикати призупинення Job, використовуючи командний рядок.

Призупиніть активний Job:

kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":true}}'

Відновіть призупинений Job:

kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":false}}'

Статус Job може бути використаний для визначення того, чи Job призупинено чи його було призупинено раніше:

kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata та .spec опущено
status:
  conditions:
  - lastProbeTime: "2021-02-05T13:14:33Z"
    lastTransitionTime: "2021-02-05T13:14:33Z"
    status: "True"
    type: Suspended
  startTime: "2021-02-05T13:13:48Z"

Стан Job типу "Suspended" зі статусом "True" означає, що Job призупинено; поле lastTransitionTime може бути використане для визначення того, як довго Job було призупинено. Якщо статус цього стану є "False", то Job було раніше призупинено і зараз він працює. Якщо такого стану не існує в статусі Job, то Job ніколи не був зупинений.

Також створюються події, коли Job призупинено та відновлено:

kubectl describe jobs/myjob
Name:           myjob
...
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  12m   job-controller  Created pod: myjob-hlrpl
  Normal  SuccessfulDelete  11m   job-controller  Deleted pod: myjob-hlrpl
  Normal  Suspended         11m   job-controller  Job suspended
  Normal  SuccessfulCreate  3s    job-controller  Created pod: myjob-jvb44
  Normal  Resumed           3s    job-controller  Job resumed

Останні чотири події, зокрема події "Suspended" та "Resumed", є прямим наслідком перемикання поля .spec.suspend. Протягом цього часу ми бачимо, що жоден Pod не був створений, але створення Podʼів розпочалося знову, як тільки Job було відновлено.

Змінні директиви планування

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.27

У більшості випадків паралельний Job потребуватиме, щоб його Podʼи запускалися з певними обмеженнями, типу "всі в одній зоні" або "всі на GPU моделі x або y", але не комбінація обох.

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

Ця функція дозволяє оновлювати директиви планування Job до запуску, що дає власному контролеру черги змогу впливати на розташування Podʼів, водночас залишаючи саме призначення Podʼів вузлам за kube-scheduler.

Поля в шаблоні Podʼа Job, які можна оновити, це спорідненість вузла, селектор вузла, tolerations, мітки, анотації та scheduling gates.

Змінні директиви планування для призупинених Jobs

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.36; (стандартно увімкнено)

У Kubernetes 1.34 та раніших версіях змінювати директиви планування Podʼа дозволяється лише для призупинених Jobs, які ніколи не відновлювалися раніше. Починаючи з Kubernetes 1.35, це дозволяється для будь-яких призупинених Jobs, коли увімкнено функціональну можливість MutableSchedulingDirectivesForSuspendedJobs.

Крім того, ця функціональна можливість дозволяє очищати поле .status.startTime при призупиненні Job.

Змінні ресурси Podʼа для призупинених Jobs

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.36; (стандартно увімкнено)

Адміністратор кластера може визначити контроль допуску в Kubernetes, змінюючи запити на ресурси або обмеження для Job на основі правил політики.

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

Клієнт, який встановлює нові запити на ресурси або обмеження, може відрізнятися від клієнта, який спочатку створив Job, і не обовʼязково має бути адміністратором кластера.

Визначення власного селектора Podʼа

Зазвичай, коли ви створюєте обʼєкт Job, ви не вказуєте .spec.selector. Логіка системного визначення типових значень додає це поле під час створення Job. Вона обирає значення селектора, яке не перетинатиметься з жодним іншим Job.

Однак у деяких випадках вам може знадобитися перевизначити цей автоматично встановлений селектор. Для цього ви можете вказати .spec.selector Job.

Будьте дуже уважні при цьому. Якщо ви вказуєте селектор міток, який не є унікальним для Podʼів цього Job, і який відповідає неповʼязаним Podʼам, то Podʼи неповʼязаного Job можуть бути видалені, або цей Job може зарахувати інші Podʼи як свої завершення, або один чи обидва Jobs можуть відмовитися створювати Podʼи чи виконуватися до завершення. Якщо вибрано неунікальний селектор, інші контролери (наприклад, ReplicationController) та їхні Podʼи також можуть поводитися непередбачувано. Kubernetes не завадить вам помилитися під час вказування .spec.selector.

Ось приклад ситуації, коли вам може знадобитися використовувати цю функцію.

Скажімо, Job old вже запущений. Ви хочете, щоб наявні Podʼи продовжували працювати, але щоб решта Podʼів, які він створює, використовували інший шаблон Podʼа, а Job мав нову назву. Ви не можете оновити Job, оскільки ці поля не можна оновлювати. Отже, ви видаляєте Job old, але залишаєте його Podʼи запущеними, використовуючи kubectl delete jobs/old --cascade=orphan. Перед видаленням ви занотовуєте, який селектор він використовує:

kubectl get job old -o yaml

Вивід буде подібний до цього:

kind: Job
metadata:
  name: old
  ...
spec:
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

Потім ви створюєте новий Job із назвою new і явно вказуєте той самий селектор. Оскільки наявні Podʼи мають мітку batch.kubernetes.io/controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002, вони також контролюються Job new.

Вам потрібно вказати manualSelector: true у новому Job, оскільки ви не використовуєте селектор, який система зазвичай генерує автоматично.

kind: Job
metadata:
  name: new
  ...
spec:
  manualSelector: true
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

Сам новий Job матиме uid, відмінний від a8f3d00d-c6d2-11e5-9f87-42010af00002. Встановлення manualSelector: true говорить системі, що ви розумієте, що робите, і дозволяє цю невідповідність.

Відстеження Job за допомогою завершувачів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.26

Панель управління стежить за Podʼами, які належать до будь-якого Job, і виявляє, чи якийсь такий Pod видалено з API-сервера. Для цього контролер Job створює Podʼи з завершувачем batch.kubernetes.io/job-tracking. Контролер видаляє завершувач тільки після того, як Pod був врахований в стані Job, що дозволяє видалити Pod іншими контролерами або користувачами.

Примітка:

Див. Мій Pod залишається в стані завершення, якщо ви спостерігаєте, що Podʼи з Job залишаються з завершувачем відстеження.

Еластичні Indexed Jobs

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.31; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.31. It was first available in the v1.27 release.

Ви можете масштабувати Indexed Jobs вгору або вниз, змінюючи як .spec.parallelism, так і .spec.completions разом, так щоб .spec.parallelism == .spec.completions. Під час масштабування вниз Kubernetes видаляє Podʼи з вищими індексами.

Сценарії використання еластичних Indexed Jobs включають пакетні робочі навантаження, які вимагають масштабування Indexed Job, такі як завдання навчання MPI, Horovod, Ray та PyTorch.

Примітка:

Коли функціональна можливість WorkloadWithJob увімкнена і Job використовує групове планування, розмір групи слідує за змінним schedulingPolicy.gang.minCount (або .spec.parallelism, коли minCount не встановлено). Ви можете масштабувати групу одним із двох способів:

  • встановити .spec.scheduling.schedulingPolicy.gang.minCount безпосередньо, або
  • змінити .spec.parallelism, коли minCount не встановлено.

При будь-якій зміні контролер перекомпілює Workload та повторно синхронізує розмір PodGroup, масштабуючи групу на місці без перестворення Job. Оновлення застосовуються лише до Podʼів, які оцінюються в майбутніх циклах планування, і не впливають на вже заплановані Podʼи. gang.minCount, більше за .spec.parallelism, відхиляється, оскільки така група ніколи не може бути задоволена.

Відкладене створення Podʼів-замінників

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.34
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.34. It was first available in the v1.28 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate JobPodReplacementPolicy, Kubernetes ignores it but does not report any error.

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

Ви можете створювати Podʼи-замінники лише тоді, коли Podʼи, які знаходяться в стані завершення, повністю завершені (мають status.phase: Failed). Для цього встановіть .spec.podReplacementPolicy: Failed. Типова політика заміни залежить від того, чи Job має встановлену політику збоїв Podʼа. Якщо для Job не визначено політику збоїв Podʼа, відсутність поля podReplacementPolicy вибирає політику заміни TerminatingOrFailed: панель управління створює Podʼи-замінники негайно після видалення Podʼів (як тільки панель управління бачить, що Pod для цього Job має встановлене значення deletionTimestamp). Для Jobs із встановленою політикою збоїв Podʼа стандартне значення podReplacementPolicy — це Failed, інших значень не передбачено. Докладніше про політики збоїв Podʼа для Jobs можна дізнатися у розділі Політика збоїв Podʼа.

kind: Job
metadata:
  name: new
  ...
spec:
  podReplacementPolicy: Failed
  ...

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

kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata та .spec опущено
status:
  terminating: 3 # три Podʼи, які перебувають у стані завершення і ще не досягли стану Failed

Делегування керування обʼєктом Job зовнішньому контролеру

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.35; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.35. It was first available in the v1.30 release.

Ця функція дозволяє вам вимкнути вбудований контролер Job для конкретного Job і делегувати узгодження цього Job зовнішньому контролеру.

Ви вказуєте контролер, який узгоджує Job, встановлюючи власне значення для поля spec.managedBy — будь-яке значення окрім kubernetes.io/job-controller. Значення поля є незмінним.

Примітка:

Під час використання цієї функції переконайтеся, що контролер, вказаний у полі, встановлено, інакше Job може бути не узгоджений взагалі.

Примітка:

При розробці зовнішнього контролера Job, будьте уважні: ваш контролер повинен працювати відповідно до визначень специфікації API та полів статусу обʼєкта Job.

Докладно ознайомтесь з ними в API Job. Ми також рекомендуємо запустити наскрізні тести сумісності для обʼєкта Job, щоб перевірити вашу реалізацію.

Нарешті, при розробці зовнішнього контролера Job переконайтеся, що він не використовує завершувач batch.kubernetes.io/job-tracking, зарезервований для вбудованого контролера.

Альтернативи

Окремі Podʼи

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

Replication Controller

Jobs доповнюють Replication Controllers. Replication Controller керує Podʼами, завершення яких не очікується (наприклад, вебсервери), а Job керує Podʼами, завершення яких очікується (наприклад, пакетні завдання).

Як зазначено в розділі Життєвий цикл Podʼа, Job придатний лише для Podʼів з RestartPolicy, рівним OnFailure або Never.

Примітка:

Якщо RestartPolicy не встановлено, стандартне значення — Always.

Один Job запускає контролер Podʼів

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

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

Що далі

4.2.6 - Автоматичне очищення завершених Jobs

Механізм часу життя (TTL) для очищення старих Jobs, які завершили виконання.
СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.23

Коли ваш Job завершився, корисно залишити цей Job в API (і не видаляти його відразу), щоб ви могли визначити, успішно він виконався чи зазнав невдачі.

Контролер TTL-after-finished у Kubernetes забезпечує механізм TTL (time to live), щоб обмежити час життя обʼєктів Job, які завершили виконання.

Очищення завершених Jobs

Контролер TTL-after-finished підтримується лише для Jobs. Ви можете використовувати цей механізм для автоматичного очищення завершених Jobs (як Complete, так і Failed), вказавши поле .spec.ttlSecondsAfterFinished Job, як у цьому прикладі.

Контролер TTL-after-finished вважає, що Job можна очистити через TTL секунд після його завершення. Відлік починається, коли умова статусу Job змінюється, показуючи, що Job є або Complete, або Failed; щойно TTL закінчується, цей Job стає придатним для каскадного видалення. Коли контролер TTL-after-finished очищає Job, він видаляє його каскадно, тобто разом із залежними обʼєктами.

Kubernetes дотримується гарантій життєвого циклу обʼєкта для Job, таких як очікування завершувачів.

Ви можете встановити TTL секунд у будь-який момент. Ось деякі приклади встановлення поля .spec.ttlSecondsAfterFinished Job:

  • Вкажіть це поле в маніфесті Job, щоб Job можна було автоматично очистити через певний час після його завершення.
  • Вручну встановіть це поле для наявних, уже завершених Jobs, щоб вони стали придатними для очищення.
  • Використовуйте змінювальний вебхук допуску для динамічного встановлення цього поля під час створення Job. Адміністратори кластера можуть використовувати це, щоб застосовувати політику TTL для завершених Jobs.
  • Використовуйте змінювальний вебхук допуску для динамічного встановлення цього поля після завершення Job та вибору різних значень TTL на основі статусу Job і міток. У цьому випадку вебхук повинен виявляти зміни в полі .status Job і встановлювати TTL лише тоді, коли Job позначається як завершений.
  • Напишіть власний контролер для керування TTL очищення для Jobs, які відповідають певному селектору.

Застереження

Оновлення TTL для завершених Jobs

Ви можете змінювати період TTL, наприклад, поле .spec.ttlSecondsAfterFinished Jobs, після створення або завершення Job. Якщо ви збільшуєте період TTL після закінчення наявного періоду ttlSecondsAfterFinished, Kubernetes не гарантує збереження цього Job, навіть якщо оновлення для збільшення TTL повертає успішну відповідь API.

Зсув часу

Оскільки контролер TTL-after-finished використовує мітки часу, збережені в Jobs Kubernetes, для визначення того, чи минув TTL, ця функція чутлива до зсуву часу в кластері, через що панель управління може очистити обʼєкти Job у неправильний час.

Годинники не завжди точні, але різниця повинна бути дуже малою. Майте на увазі цей ризик під час встановлення ненульового TTL.

Що далі

4.2.7 - CronJob

CronJob запускає одноразові Jobs за повторюваним розкладом.
СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.21

CronJob створює Jobs за повторюваним розкладом.

CronJob призначений для виконання регулярних запланованих дій, таких як резервне копіювання, генерація звітів та інше. Один обʼєкт CronJob подібний до одного рядка файлу crontab (таблиця cron) у системі Unix. Він запускає Job періодично за заданим розкладом, записаним у форматі Cron.

У CronJob є обмеження та особливості. Наприклад, за певних обставин один CronJob може створювати кілька одночасних Jobs. Див. обмеження нижче.

Коли панель управління створює нові Jobs і (опосередковано) Podʼи для CronJob, .metadata.name CronJob є частиною основи для назв цих Podʼів. Назва CronJob повинна бути дійсним значенням DNS-піддомену, але це може призводити до неочікуваних результатів для імен хостів Podʼів. Для найкращої сумісності назва повинна відповідати суворішим правилам DNS-мітки. Навіть коли назва є DNS-піддоменом, вона не повинна бути довшою за 52 символи. Це тому, що контролер CronJob автоматично додає 11 символів до наданої вами назви, а довжина назви Job не повинна перевищувати 63 символи.

Приклад

Цей приклад маніфесту CronJob виводить поточний час та вітальне повідомлення щохвилини:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

(Виконання автоматизованих завдань за допомогою CronJob докладніше описує цей приклад).

Написання специфікації CronJob

Синтаксис розкладу

Поле .spec.schedule є обовʼязковим. Значення цього поля відповідає синтаксису Cron:

# ┌───────────── хвилина (0 - 59)
# │ ┌───────────── година (0 - 23)
# │ │ ┌───────────── день місяця (1 - 31)
# │ │ │ ┌───────────── місяць (1 - 12)
# │ │ │ │ ┌───────────── день тижня (0 - 6) (Неділя - Субота)
# │ │ │ │ │                                   АБО sun, mon, tue, wed, thu, fri, sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *

Наприклад, 0 3 * * 1 означає, що це завдання планується запускати щотижня в понеділок о 3 ранку.

Формат також включає розширені значення кроків "Vixie cron". Як пояснено в документації FreeBSD:

Значення кроків можна використовувати разом із діапазонами. Якщо після діапазону вказати /<number>, це задає крок із таким значенням у межах діапазону. Наприклад, 0-23/2 можна використовувати в полі годин для вказування виконання команди кожну другу годину (альтернативою в стандарті V7 є 0,2,4,6,8,10,12,14,16,18,20,22). Після зірочки також допускаються кроки, тому, якщо ви хочете сказати "кожні дві години", просто використовуйте */2.

Примітка:

Знак питання (?) у розкладі має той самий зміст, що й зірочка *, тобто позначає будь-яке доступне значення для даного поля.

Окрім стандартного синтаксису, також можна використовувати деякі макроси, такі як @monthly:

ЗаписОписЕквівалентно
@yearly (або @annually)Виконувати один раз на рік о півночі 1 січня0 0 1 1 *
@monthlyВиконувати один раз на місяць о півночі першого дня місяця0 0 1 * *
@weeklyВиконувати один раз на тиждень о півночі в неділю0 0 * * 0
@daily (або @midnight)Виконувати один раз на день о півночі0 0 * * *
@hourlyВиконувати один раз на годину на початку години0 * * * *

Для генерації виразів розкладу CronJob можна також використовувати вебінструменти, наприклад crontab.guru.

Шаблон Job

Поле .spec.jobTemplate визначає шаблон для Jobs, які створює CronJob, і воно обовʼязкове. Воно має точно таку саму схему, як Job, за винятком того, що воно вкладене і не має apiVersion або kind. Ви можете вказати спільні метадані для Jobs, створених за шаблоном, такі як мітки або анотації. Інформацію щодо написання .spec Job дивіться в розділі Написання специфікації Job.

Крайній термін для відкладеного запуску Job

Поле .spec.startingDeadlineSeconds є необовʼязковим. Це поле визначає крайній термін (у повних секундах) для запуску Job, якщо цей Job пропускає свій запланований час з будь-якої причини.

Після спливання крайнього терміну CronJob пропускає цей екземпляр Job (майбутні запуски все ще плануються). Наприклад, якщо у вас є Job резервного копіювання, який запускається двічі на день, ви можете дозволити йому запускатися із запізненням до 8 годин, але не пізніше, оскільки резервна копія, зроблена пізніше, не буде корисною: ви б натомість воліли дочекатися наступного запланованого запуску.

Jobs, які пропускають налаштований крайній термін, Kubernetes вважає невдалими. Якщо ви не вказали startingDeadlineSeconds для CronJob, екземпляри Job не мають крайнього терміну.

Якщо поле .spec.startingDeadlineSeconds встановлено (не є null), контролер CronJob вимірює час між тим, коли очікується створення Job, і поточним моментом. Якщо різниця перевищує цю межу, він пропускає це виконання.

Наприклад, якщо воно встановлене на 200, це дозволяє створити Job протягом 200 секунд після фактичного часу за розкладом.

Політика паралелізму

Поле .spec.concurrencyPolicy також є необовʼязковим. Воно визначає, як обробляти паралельні виконання Job, який створюється цим CronJob. Специфікація може вказувати лише одну з таких політик паралелізму:

  • Allow (стандартно): CronJob дозволяє паралельні запуски Jobs
  • Forbid: CronJob не дозволяє паралельні запуски; якщо настав час для нового запуску Job і попередній запуск Job ще не завершився, CronJob пропускає новий запуск Job. Також зауважте, що коли попередній запуск Job завершиться, .spec.startingDeadlineSeconds все одно враховується і може призвести до нового запуску Job.
  • Replace: Якщо настав час для нового запуску Job і попередній запуск Job ще не завершився, CronJob замінює поточний запуск Job новим запуском Job.

Зверніть увагу, що політика паралелізму застосовується лише до Jobs, створених тим самим CronJob. Якщо є кілька CronJobs, їхні відповідні Jobs завжди можуть виконуватися паралельно.

Призупинення розкладу

Ви можете призупинити виконання Jobs для CronJob, встановивши необовʼязкове поле .spec.suspend в значення true. Стандартно поле має значення false.

Це налаштування не впливає на Jobs, які CronJob вже розпочав.

Якщо ви встановите це поле в значення true, всі наступні виконання будуть призупинені (вони залишаються запланованими, але контролер CronJob не запускає Jobs для виконання задач), доки ви не скасуєте призупинення CronJob.

Увага:

Виконання, які призупинені під час запланованого часу, вважаються пропущеними Jobs. Коли .spec.suspend змінюється з true на false для наявного CronJob без крайнього терміну запуску, пропущені Jobs плануються негайно.

Обмеження історії Jobs

Поля .spec.successfulJobsHistoryLimit та .spec.failedJobsHistoryLimit вказують, скільки успішних та невдалих Jobs потрібно зберігати. Обидва поля є необовʼязковими.

  • .spec.successfulJobsHistoryLimit: Це поле вказує кількість успішно завершених Jobs, які слід зберігати. Стандартне значення — 3. Якщо встановити це поле в 0, успішні Jobs не зберігатимуться.

  • .spec.failedJobsHistoryLimit: Це поле вказує кількість невдало завершених Jobs, які слід зберігати. Стандартне значення — 1. Якщо встановити це поле в 0, невдалі Jobs не зберігатимуться.

Інший спосіб автоматичного очищення Jobs дивіться в розділі Автоматичне очищення завершених Jobs.

Часові пояси

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.27

Для CronJobs без вказаного часового поясу kube-controller-manager інтерпретує розклади відносно свого локального часового поясу.

Ви можете вказати часовий пояс для CronJob, встановивши .spec.timeZone на назву дійсного часового поясу. Наприклад, встановлення .spec.timeZone: "Etc/UTC" вказує Kubernetes інтерпретувати розклад відносно всесвітнього координованого часу.

База даних часових поясів зі стандартної бібліотеки Go включена до бінарних файлів і використовується як запасний варіант, якщо зовнішня база даних недоступна в системі.

Обмеження CronJob

Непідтримувані специфікації часових поясів

Зазначення часового поясу за допомогою змінних CRON_TZ або TZ всередині .spec.schedule офіційно не підтримується (і ніколи не підтримувалося). Якщо ви намагаєтеся встановити розклад, який включає вказування часового поясу TZ або CRON_TZ, Kubernetes не зможе створити чи оновити ресурс через помилку валідації. Натомість слід вказувати часові пояси, використовуючи поле часового поясу.

Зміна CronJob

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

Створення Job

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

Починаючи з Kubernetes v1.32, CronJobs застосовують анотацію batch.kubernetes.io/cronjob-scheduled-timestamp до створених ними Jobs. Ця анотація вказує початковий запланований час створення Job і має формат RFC3339.

Якщо startingDeadlineSeconds встановлено на велике значення або залишено не встановленим (типово), і якщо concurrencyPolicy встановлено на Allow, Jobs завжди запускатимуться принаймні один раз.

Увага:

Якщо startingDeadlineSeconds встановлено у значення менше ніж 10 секунд, CronJob може не бути заплановано. Це повʼязано з тим, що контролер CronJob виконує перевірки кожні 10 секунд.

Для кожного CronJob контролер CronJob перевіряє, скільки розкладів він пропустив протягом часу від його останнього запланованого часу до теперішнього часу. Якщо пропущено понад 100 розкладів, то він не запускає Job і записує помилку в лог.

too many missed start times. Set or decrease .spec.startingDeadlineSeconds or check clock skew

Ця поведінка застосовується для планування наздоганяння і не означає, що CronJob припинить роботу.

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

Важливо відзначити, що якщо поле startingDeadlineSeconds встановлено (не є nil), контролер підраховує, скільки пропущених Jobs сталося від значення startingDeadlineSeconds до теперішнього часу, а не від останнього запланованого часу до теперішнього часу. Наприклад, якщо startingDeadlineSeconds дорівнює 200, контролер підраховує, скільки Jobs було пропущено за останні 200 секунд.

CronJob вважається пропущеним, якщо не вдалося створити Job в запланований час. Наприклад, якщо concurrencyPolicy встановлено на Forbid і CronJob було спробовано запланувати, коли попередній Job все ще працював, то це враховуватиметься як пропуск.

Наприклад, припустимо, що CronJob встановлено для запуску нового Job кожну хвилину, починаючи з 08:30:00, і його поле startingDeadlineSeconds не встановлено. Якщо контролер CronJob виявиться недоступним з 08:29:00 по 10:21:00, Job не буде запущено, оскільки кількість пропущених Jobs, які пропустили свій розклад, понад 100.

Щоб краще проілюструвати цю концепцію, припустимо, що CronJob встановлено для запуску нового Job кожну хвилину, починаючи з 08:30:00, і його поле startingDeadlineSeconds встановлено на 200 секунд. Якщо контролер CronJob виявиться недоступним протягом того самого періоду, що й у попередньому прикладі (08:29:00 до 10:21:00), Job все одно запуститься о 10:22:00. Це трапляється тому, що тепер контролер перевіряє, скільки пропущених розкладів сталося за останні 200 секунд (тобто 3 пропущені розклади), а не від останнього запланованого часу до теперішнього часу.

CronJob відповідає лише за створення Job, які відповідають його розкладу, а Job, зі свого боку, відповідає за керування Podʼами, які він представляє.

Що далі

4.2.8 - ReplicationController

Застарілий API для керування робочими навантаженнями, які можуть горизонтально масштабуватися. Замінено API Deployment та ReplicaSet.

Примітка:

Deployment, який налаштовує ReplicaSet, тепер є рекомендованим способом налаштування реплікації.

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

Як працює ReplicationController

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

ReplicationController часто скорочується до "rc" в обговореннях та як скорочення в командах kubectl.

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

Запуск прикладу ReplicationController

Цей приклад конфігурації ReplicationController запускає три копії вебсервера nginx.

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

Запустіть цей приклад, завантаживши файл прикладу, а потім виконавши таку команду:

kubectl apply -f https://k8s.io/examples/controllers/replication.yaml

Вивід подібний до такого:

replicationcontroller/nginx created

Перевірте стан ReplicationController за допомогою цієї команди:

kubectl describe replicationcontrollers/nginx

Вивід подібний до такого:

Name:        nginx
Namespace:   default
Selector:    app=nginx
Labels:      app=nginx
Annotations:    <none>
Replicas:    3 current / 3 desired
Pods Status: 0 Running / 3 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       app=nginx
  Containers:
   nginx:
    Image:              nginx
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen       LastSeen     Count    From                        SubobjectPath    Type      Reason              Message
  ---------       --------     -----    ----                        -------------    ----      ------              -------
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-qrm3m
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-3ntk0
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-4ok8v

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

Pods Status:    3 Running / 0 Waiting / 0 Succeeded / 0 Failed

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

pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name})
echo $pods

Вивід подібний до такого:

nginx-3ntk0 nginx-4ok8v nginx-qrm3m

Тут селектор такий самий, як і селектор для ReplicationController (бачимо у виводі kubectl describe), і в іншій формі у replication.yaml. Опція --output=jsonpath вказує вираз із назвою кожного Podʼа в отриманому списку.

Створення маніфесту ReplicationController

Як і з усіма іншими конфігураціями Kubernetes, для ReplicationController потрібні поля apiVersion, kind і metadata.

Коли панель управління створює нові Podʼи для ReplicationController, .metadata.name ReplicationController є частиною основи для назв цих Podʼів. Назва ReplicationController повинна бути дійсним значенням DNS-піддомену, але це може призводити до неочікуваних результатів для імен хостів Podʼів. Для забезпечення найкращої сумісності назва повинна відповідати суворішим правилам для DNS-мітки.

Для загальної інформації про роботу з файлами конфігурації дивіться управління обʼєктами.

ReplicationController також потребує розділу .spec.

Шаблон Podʼа

.spec.template — єдине обовʼязкове поле в розділі .spec.

.spec.template — це шаблон Podʼа. Він має точно таку саму схему, як і Pod, за винятком того, що він вкладений і не має apiVersion або kind.

Окрім обовʼязкових полів для Podʼа, шаблон Podʼа в ReplicationController повинен вказувати відповідні мітки та відповідну політику перезапуску. Щодо міток, переконайтеся, що вони не перекриваються з іншими контролерами. Див. селектор Podʼа.

Дозволена лише .spec.template.spec.restartPolicy зі значенням Always, яке є стандартним, якщо нічого не вказано.

Для локальних перезапусків контейнерів ReplicationControllers делегують агентові на вузлі, наприклад, Kubelet.

Мітки на ReplicationController

ReplicationController може мати власні мітки (.metadata.labels). Зазвичай ви встановлюєте їх так само, як і .spec.template.metadata.labels; якщо .metadata.labels не вказано, то вони стандартно встановлюються як .spec.template.metadata.labels. Однак вони можуть бути різними, і .metadata.labels не впливають на поведінку ReplicationController.

Селектор Podʼа

Поле .spec.selector є селектором міток. ReplicationController керує всіма Podʼами з мітками, які відповідають селектору. Він не робить різниці між Podʼами, які він створив чи видалив, і Podʼами, які створив чи видалив інший процес чи особа. Це дозволяє замінити ReplicationController без впливу на запущені Podʼи.

Якщо вказано, то .spec.template.metadata.labels повинні дорівнювати .spec.selector, інакше їх відхилить API. Якщо .spec.selector не вказано, він буде встановлений типово як .spec.template.metadata.labels.

Також, як правило, ви не повинні створювати жодних Podʼів, мітки яких відповідають цьому селектору, безпосередньо, з іншим ReplicationController або з іншим контролером, таким як Job. Якщо ви це зробите, то ReplicationController вважатиме, що він створив інші Podʼи. Kubernetes не забороняє вам це робити.

Якщо у вас все-таки виникає кілька контролерів із селекторами, що перетинаються, вам доведеться керувати видаленням самостійно (див. нижче).

Кілька реплік

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

Якщо ви не вказали .spec.replicas, то типово встановлюється 1.

Робота з ReplicationControllers

Видалення ReplicationController та його Podʼів

Щоб видалити ReplicationController та всі його Podʼи, використовуйте команду kubectl delete. Kubectl масштабує ReplicationController до нуля і чекає, доки кожний Pod буде видалено, перед видаленням самого ReplicationController. Якщо цю команду kubectl перервати, її можна перезапустити.

Під час використання REST API або бібліотеки клієнта вам потрібно виконати кроки явно (масштабування реплік до 0, очікування видалення Podʼів, а потім видалення самого ReplicationController).

Видалення лише ReplicationController

Ви можете видалити ReplicationController, не впливаючи на його Podʼи.

Під час використання kubectl вкажіть опцію --cascade=orphan команді kubectl delete.

Під час використання REST API або бібліотеки клієнта ви можете видалити обʼєкт ReplicationController.

Після видалення оригіналу ви можете створити новий ReplicationController, щоб замінити його. Поки старий та новий .spec.selector однакові, новий перейме старі Podʼи. Однак він не буде вживати жодних зусиль, щоб наявні Podʼи відповідали новому, відмінному від оригінального, шаблону Podʼа. Щоб оновити Podʼи за новою специфікацією у керований спосіб, використовуйте поетапне оновлення.

Ізолювання Podʼів від ReplicationController

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

Поширені шаблони використання

Перепланування

Як було зазначено вище, чи у вас є 1 Pod, який вам потрібно тримати в роботі, чи 1000, ReplicationController гарантує, що зазначена кількість Podʼів існує, навіть у випадку відмови вузла або завершення Podʼа (наприклад, через дію іншого агента управління).

Масштабування

ReplicationController дозволяє масштабувати кількість реплік вгору або вниз, як вручну, так і за допомогою агента автомасштабування, оновлюючи поле replicas.

Поетапні оновлення

ReplicationController призначений для полегшення поетапного оновлення сервісу за допомогою заміни Podʼів один за одним.

Як пояснено в #1353, рекомендований підхід — створити новий ReplicationController з 1 реплікою, масштабувати нові (+1) та старі (-1) контролери по одному, а потім видалити старий контролер після досягнення 0 реплік. Це передбачувано оновлює набір Podʼів незалежно від неочікуваних відмов.

В ідеалі контролер поетапного оновлення мав би враховувати готовність застосунку і гарантувати, що достатня кількість Podʼів у будь-який момент продуктивно обслуговує запити.

Обом ReplicationControllers потрібно створювати Podʼи із принаймні однією відмінною міткою, такою як теґ образу основного контейнера Podʼа, оскільки саме оновлення образів зазвичай спонукають до поетапних оновлень.

Кілька каналів випуску

Окрім запуску кількох випусків застосунку під час поетапного оновлення, часто запускають кілька випусків протягом тривалого часу або навіть постійно, використовуючи кілька каналів випуску. Канали розрізняються за допомогою міток.

Наприклад, сервіс може охоплювати всі Podʼи з tier in (frontend), environment in (prod). Тепер скажімо, у вас є 10 реплікованих Podʼів, які складають цей рівень. Але ви хочете мати можливість випробувати нову версію цього компонента як канарку (canary). Ви можете налаштувати ReplicationController із replicas, встановленим на 9 для більшості реплік, з мітками tier=frontend, environment=prod, track=stable, та інший ReplicationController із replicas, встановленим на 1 для канарки, з мітками tier=frontend, environment=prod, track=canary. Тепер сервіс охоплює як канаркові, так і не канаркові Podʼи. Але ви можете окремо працювати з ReplicationControllers, щоб щось перевіряти, відстежувати результати тощо.

Використання ReplicationControllers із Services

Кілька ReplicationControllers можуть працювати за одним сервісом, так що, наприклад, частина трафіку іде до старої версії, а частина до нової.

ReplicationController ніколи не завершиться самостійно, але не очікується, що він житиме так довго, як сервіси. Сервіси можуть складатися з Podʼів, контрольованих кількома ReplicationControllers, і передбачається, що протягом життя сервісу (наприклад, для виконання оновлення Podʼів, які забезпечують роботу сервісу) буде створено і знищено багато ReplicationControllers. І самі сервіси, і їхні клієнти не повинні нічого знати про ReplicationControllers, які підтримують Podʼи цих сервісів.

Написання програм для реплікації

Podʼи, створені ReplicationController, призначені бути взаємозамінними та семантично ідентичними, хоча їхні конфігурації з часом можуть ставати неоднорідними. Це очевидно підходить для реплікованих stateless-серверів, але ReplicationControllers також можна використовувати для забезпечення доступності застосунків із вибором master, шардованих застосунків і застосунків із пулом робочих процесів. Такі застосунки повинні використовувати механізми динамічного призначення роботи, такі як черги роботи RabbitMQ, на відміну від статичного / одноразового налаштування конфігурації кожного Podʼа, що вважається антипатерном. Будь-яке налаштування Podʼа, таке як вертикальне автоматичне визначення розміру ресурсів (наприклад, CPU чи памʼяті), повинно виконуватися іншим онлайн-процесом контролера, подібним до самого ReplicationController.

Обовʼязки ReplicationController

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

ReplicationController назавжди обмежений цією вузькою відповідальністю. Він сам не виконуватиме проби готовності чи життєздатності. Замість автоматичного масштабування він призначений для управління зовнішнім автомасштабувальником (як обговорюється в #492), який буде змінювати його поле replicas. Ми не будемо додавати політик планування (наприклад, розподілення) до ReplicationController. Він також не повинен перевіряти, чи відповідають контрольовані Podʼи поточному зазначеному шаблону, оскільки це заважало б автоматичному визначенню розміру та іншим автоматизованим процесам. Так само крайні терміни завершення, залежності порядку, розширення конфігурації та інші функції мають бути в інших місцях. Навіть планується виділити механізм масового створення Podʼів (#170).

ReplicationController призначений бути компонованим базовим примітивом. Ми очікуємо, що на його основі та на основі інших доповнювальних примітивів у майбутньому буде побудовано високорівневі API та / або інструменти для зручності користувачів. "Макро" операції, які наразі підтримуються kubectl (run, scale), є прикладами перевірки цієї концепції. Наприклад, можемо уявити щось на зразок Asgard, що управляє ReplicationControllers, автомасштабувальниками, сервісами, політиками планування, канарковими розгортаннями та іншими аспектами.

Обʼєкт API

ReplicationController є ресурсом верхнього рівня в REST API Kubernetes. Докладніша інформація про обʼєкт API є за посиланням: Обʼєкт API ReplicationController.

Альтернативи ReplicationController

ReplicaSet

ReplicaSet — це ReplicationController нового покоління, який підтримує новий селектор міток на основі множини. Він використовується головним чином Deployment як механізм для оркестрування створення, видалення та оновлення Podʼів. Зауважте, що ми рекомендуємо використовувати Deployments замість безпосереднього використання ReplicaSets, хіба що вам потрібне власне оркестрування оновлень або оновлення взагалі не потрібні.

Deployment — це обʼєкт API вищого рівня, який оновлює свої базові ReplicaSets та їхні Podʼи. Deployments рекомендуються, якщо вам потрібна функціональність поетапного оновлення, оскільки вони є декларативними, виконуються на сервері та мають додаткові функції.

Окремі Podʼи

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

Job

Використовуйте Job замість ReplicationController для Podʼів, які мають завершуватись самі по собі (іншими словами, пакетні завдання).

DaemonSet

Використовуйте DaemonSet замість ReplicationController для Podʼів, які забезпечують функцію рівня машини, таку як моніторинг машини або логування машини. Ці Podʼи мають термін служби, який повʼязаний із терміном служби машини: Pod повинен працювати на машині перед тим, як інші Podʼи почнуть працювати, і може бути безпечно завершений, коли машина готова до перезавантаження або вимкнення.

Що далі

  • Дізнайтеся про Podʼи.
  • Дізнайтеся більше про Deployment, альтернативу ReplicationController.
  • ReplicationController — це частина REST API Kubernetes. Ознайомтесь з визначенням обʼєкта ReplicationController, щоб зрозуміти API для контролерів реплікації.

4.3 - Керування робочими навантаженнями

Ви розгорнули ваш застосунок та відкрили до нього доступ через Service. Що далі? Kubernetes надає низку інструментів, які допомагають керувати розгортанням вашого застосунку, зокрема масштабуванням та оновленням.

Організація конфігурацій ресурсів

Багато застосунків потребують створення кількох ресурсів, таких як Deployment разом із Service. Керування кількома ресурсами можна спростити, обʼєднавши їх в одному файлі (розділивши їх --- у YAML). Наприклад:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-svc
  labels:
    app: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Кілька ресурсів можна створити так само, як і один ресурс:

kubectl apply -f https://k8s.io/examples/application/nginx-app.yaml
service/my-nginx-svc created
deployment.apps/my-nginx created

Ресурси будуть створені в порядку, в якому вони зʼявляються в маніфесті. Тому краще спочатку вказати Service, оскільки це забезпечить, що планувальник зможе розподілити повʼязані з Service Podʼи в процесі їх створення контролером(ами), такими як Deployment.

kubectl apply також приймає кілька аргументів -f:

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-svc.yaml \
  -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml

Рекомендовано розміщувати ресурси, повʼязані з одним мікросервісом або рівнем застосунку, в одному файлі та групувати всі файли, повʼязані з вашим застосунком, в одній теці. Якщо рівні вашого застосунку звʼязуються один з одним за допомогою DNS, ви можете розгорнути всі компоненти вашого стека разом.

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

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx created

Якщо потрібно визначити більше маніфестів, наприклад, додати ConfigMap, це також можна зробити.

Зовнішні інструменти

У цьому розділі перераховані лише найпоширеніші інструменти, які використовуються для керування робочими навантаженнями в Kubernetes. Щоб переглянути більше інструментів, подивіться Application definition and image build в CNCF Landscape.

Helm

🛇 Цей елемент посилається на сторонній проєкт або продукт, який не є частиною Kubernetes. Докладніше

Helm — це інструмент для керування пакетами попередньо налаштованих ресурсів Kubernetes. Ці пакети відомі як Helm-чарти.

Kustomize

Kustomize проходить по маніфестах Kubernetes, щоб додати, видалити або оновити конфігураційні опції. Він доступний як окремий бінарний файл і як вбудована можливість kubectl.

Пакетні операції в kubectl

Створення ресурсів — не єдина операція, яку kubectl може виконувати в пакетному режимі. Він також може витягувати назви ресурсів з конфігураційних файлів для виконання інших операцій, зокрема для видалення тих же ресурсів, які ви створили:

kubectl delete -f https://k8s.io/examples/application/nginx-app.yaml
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

У випадку з двома ресурсами ви можете вказати обидва ресурси в командному рядку, використовуючи синтаксис resource/name:

kubectl delete deployments/my-nginx services/my-nginx-svc

Для більшої кількості ресурсів буде зручніше вказати селектор (запит за мітками), використовуючи -l або --selector, щоб відфільтрувати ресурси за їхніми мітками:

kubectl delete deployment,services -l app=nginx
deployment.apps "my-nginx" deleted
service "my-nginx-svc" deleted

Ланцюжки та фільтрація

Оскільки kubectl виводить назви ресурсів у тому ж синтаксисі, який він приймає, ви можете обʼєднувати операції в ланцюжки, використовуючи $() або xargs:

kubectl get $(kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service/ )
kubectl create -f docs/concepts/cluster-administration/nginx/ -o name | grep service/ | xargs -i kubectl get '{}'

Вивід може бути подібний до:

NAME           TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)      AGE
my-nginx-svc   LoadBalancer   10.0.0.208   <pending>     80/TCP       0s

За допомогою наведених вище команд ви спочатку створюєте ресурси в docs/concepts/cluster-administration/nginx/ і виводите створені ресурси у форматі виводу -o name (виводите кожен ресурс як resource/name). Потім за допомогою grep ви вибираєте лише Service, а потім виводите його за допомогою kubectl get.

Рекурсивні операції з локальними файлами

Якщо ви організували ваші ресурси в кількох підтеках всередині певної теки, ви можете рекурсивно виконувати операції і з підтеками, вказавши --recursive або -R разом з аргументом --filename/-f.

Наприклад, припустимо, є тека project/k8s/development, яка містить усі маніфести, необхідні для середовища розробки, організовані за типом ресурсу:

project/k8s/development
├── configmap
│   └── my-configmap.yaml
├── deployment
│   └── my-deployment.yaml
└── pvc
    └── my-pvc.yaml

Стандартно виконання пакетної операції для project/k8s/development зупиниться на першому рівні теки, не обробляючи жодних вкладених тек. Якщо ви спробували б створити ресурси з цієї теки, використовуючи наступну команду, ви б отримали помилку:

kubectl apply -f project/k8s/development
error: you must provide one or more resources by argument or filename (.json|.yaml|.yml|stdin)

Натомість вкажіть аргумент командного рядка --recursive або -R разом з аргументом --filename/-f:

kubectl apply -f project/k8s/development --recursive
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

Аргумент --recursive працює з будь-якою операцією, яка приймає аргумент --filename/-f, такою як: kubectl create, kubectl get, kubectl delete, kubectl describe або навіть kubectl rollout.

Аргумент --recursive також працює, коли вказані кілька аргументів -f:

kubectl apply -f project/k8s/namespaces -f project/k8s/development --recursive
namespace/development created
namespace/staging created
configmap/my-config created
deployment.apps/my-deployment created
persistentvolumeclaim/my-pvc created

Якщо ви хочете дізнатися більше про kubectl, ознайомтеся зі статтею Інструмент командного рядка (kubectl).

Оновлення застосунку без перебоїв у роботі

В якийсь момент вам, ймовірно, доведеться оновити ваш розгорнутий застосунок, зазвичай шляхом зазначення нового образу чи теґу образу. kubectl підтримує кілька операцій оновлення, кожна з яких застосовується до різних сценаріїв.

Ви можете запускати кілька копій вашого застосунку і використовувати розгортання (rollout) для поступового перенаправлення трафіку до нових справних Podʼів. З часом всі запущені Podʼи отримають нове програмне забезпечення.

У цьому розділі сторінки показано, як створювати та оновлювати застосунки за допомогою Deployments.

Припустимо, ви запускали версію 1.14.2 nginx:

kubectl create deployment my-nginx --image=nginx:1.14.2
deployment.apps/my-nginx created

Переконайтеся, що є 1 репліка:

kubectl scale --replicas 1 deployments/my-nginx --subresource='scale' --type='merge' -p '{"spec":{"replicas": 1}}'
deployment.apps/my-nginx scaled

і дозвольте Kubernetes додавати більше тимчасових реплік під час розгортання, встановивши максимальне перевищення (surge maximum) на 100%:

kubectl patch --type='merge' -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge": "100%" }}}}'
deployment.apps/my-nginx patched

Щоб оновитись до версії 1.16.1, змініть .spec.template.spec.containers[0].image з nginx:1.14.2 на nginx:1.16.1, використовуючи kubectl edit:

kubectl edit deployment/my-nginx
# Змініть маніфест, щоб використовувати новіший образ контейнера, потім збережіть зміни

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

Ви можете використовувати розгортання з DaemonSets, Deployments або StatefulSets.

Керування розгортаннями

Ви можете використовувати kubectl rollout для керування поступовим оновленням наявного застосунку.

Наприклад:

kubectl apply -f my-deployment.yaml

# дочекайтеся завершення розгортання
kubectl rollout status deployment/my-deployment --timeout 10m # тайм-аут 10 хвилин

або

kubectl apply -f backing-stateful-component.yaml

# не чекайте завершення розгортання, просто перевірте статус
kubectl rollout status statefulsets/backing-stateful-component --watch=false

Ви також можете призупиняти, відновлювати або скасовувати розгортання. Перегляньте kubectl rollout для отримання додаткової інформації.

Канаркові розгортання

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

Наприклад, можна використовувати мітку track для розрізнення різних версій.

Основна, стабільна версія буде мати мітку track зі значенням stable:

name: frontend
replicas: 3
...
labels:
   app: guestbook
   tier: frontend
   track: stable
...
image: gb-frontend:v3

А потім ви можете створити нову версію фронтенду для guestbook, яка має мітку track з іншим значенням (тобто canary), так що два набори Podʼів не перетинатимуться:

name: frontend-canary
replicas: 1
...
labels:
   app: guestbook
   tier: frontend
   track: canary
...
image: gb-frontend:v4

Service фронтенду охоплюватиме обидва набори реплік, вибираючи спільну підмножину їх міток (тобто пропускаючи мітку track), так що трафік буде перенаправлено на обидва застосунки:

selector:
   app: guestbook
   tier: frontend

Ви можете налаштувати кількість реплік стабільної та канаркової версій, щоб визначити частку реального промислового трафіку, яку отримає кожна з них (у цьому випадку 3:1). Коли ви будете впевнені в новій версії, ви можете оновити стабільний канал випуску до нової версії застосунку і видалити канаркову.

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

Оновлення анотацій

Іноді ви захочете додати анотації до ресурсів. Анотації — це довільні неідентифікувальні метадані, які можуть отримувати API-клієнти, такі як інструменти або бібліотеки. Це можна зробити за допомогою kubectl annotate. Наприклад:

kubectl annotate pods my-nginx-v4-9gw19 description='my frontend running nginx'
kubectl get pods my-nginx-v4-9gw19 -o yaml
apiVersion: v1
kind: pod
metadata:
  annotations:
    description: my frontend running nginx
...

Для отримання додаткової інформації дивіться анотації та kubectl annotate.

Масштабування вашого застосунку

Коли навантаження на ваш застосунок зростає або зменшується, використовуйте kubectl, щоб масштабувати ваш застосунок. Наприклад, щоб зменшити кількість реплік nginx з 3 до 1, виконайте:

kubectl scale deployment/my-nginx --replicas=1
deployment.apps/my-nginx scaled

Тепер у вас буде тільки один Pod, яким керує Deployment.

kubectl get pods -l app=my-nginx
NAME                        READY     STATUS    RESTARTS   AGE
my-nginx-2035384211-j5fhi   1/1       Running   0          30m

Щоб система автоматично вибирала кількість реплік nginx в межах від 1 до 3, виконайте:

# Для цього потрібне наявне джерело метрик контейнерів і Podʼів
kubectl autoscale deployment/my-nginx --min=1 --max=3
horizontalpodautoscaler.autoscaling/my-nginx autoscaled

Тепер ваші репліки nginx будуть масштабуватися вгору і вниз за потреби автоматично.

Для отримання додаткової інформації перегляньте kubectl scale, kubectl autoscale і горизонтальне автомасштабування Podʼів.

Оновлення ресурсів на місці

Іноді потрібно вносити до створених вами ресурсів вузькі оновлення, що не порушують роботу.

kubectl apply

Рекомендується підтримувати набір конфігураційних файлів у системі контролю версій (див. конфігурація як код), щоб їх можна було підтримувати та версіонувати разом з кодом для ресурсів, які вони конфігурують. Тоді ви можете використовувати kubectl apply для того, щоб надіслати зміни конфігурації в кластер.

Ця команда порівнює версію конфігурації, яку ви надсилаєте, з попередньою версією та застосовує внесені вами зміни, не перезаписуючи автоматичні зміни властивостей, які ви не вказали.

kubectl apply -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml
deployment.apps/my-nginx configured

Щоб дізнатися більше про механізм, що лежить в основі, прочитайте server-side apply.

kubectl edit

Також ви можете оновлювати ресурси за допомогою kubectl edit:

kubectl edit deployment/my-nginx

Це еквівалентно тому, щоб спочатку виконати get для ресурсу, відредагувати його в текстовому редакторі, а потім виконати apply для ресурсу з оновленою версією:

kubectl get deployment my-nginx -o yaml > /tmp/nginx.yaml
vi /tmp/nginx.yaml
# зробіть деякі редагування, а потім збережіть файл

kubectl apply -f /tmp/nginx.yaml
deployment.apps/my-nginx configured

rm /tmp/nginx.yaml

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

Для отримання додаткової інформації дивіться kubectl edit.

kubectl patch

Ви можете використовувати kubectl patch для оновлення обʼєктів API на місці. Ця підкоманда підтримує JSON patch, JSON merge patch і strategic merge patch.

Дивіться Оновлення обʼєктів API на місці за допомогою kubectl patch для отримання додаткових деталей.

Оновлення з порушенням роботи

В деяких випадках може знадобитися оновити поля ресурсу, які не можна оновити після його ініціалізації, або може знадобитися зробити рекурсивну зміну негайно, наприклад, щоб виправити несправні Podʼи, створені Deployment. Для зміни таких полів використовуйте replace --force, що видаляє і повторно створює ресурс. У цьому випадку ви можете змінити ваш оригінальний конфігураційний файл:

kubectl replace -f https://k8s.io/examples/application/nginx/nginx-deployment.yaml --force
deployment.apps/my-nginx deleted
deployment.apps/my-nginx replaced

Що далі

4.4 - Автомасштабування робочих навантажень

З автомасштабуванням ви можете автоматично оновлювати ваші робочі навантаження різними способами. Це дозволяє вашому кластеру еластичніше та ефективніше реагувати на зміни попиту на ресурси.

У Kubernetes ви можете масштабувати робоче навантаження залежно від поточного попиту на ресурси. Це дозволяє вашому кластеру більш еластично та ефективно реагувати на зміни попиту на ресурси.

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

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

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

Ручне масштабування робочих навантажень

Kubernetes підтримує ручне масштабування робочих навантажень. Горизонтальне масштабування можна виконати за допомогою інтерфейсу командного рядка kubectl. Для вертикального масштабування вам потрібно застосувати патч до визначення ресурсів вашого робочого навантаження.

Дивіться нижче приклади обох стратегій.

Автоматичне масштабування робочих навантажень

Kubernetes також підтримує автоматичне масштабування робочих навантажень, що є основною темою цієї сторінки.

Поняття автомасштабування в Kubernetes означає можливість автоматично оновлювати обʼєкт, який керує набором Podʼів (наприклад, Deployment).

Горизонтальне масштабування робочих навантажень

У Kubernetes ви можете автоматично масштабувати робоче навантаження горизонтально за допомогою HorizontalPodAutoscaler (HPA).

Він реалізований як ресурс API Kubernetes та контролер і періодично коригує кількість реплік в робочому навантаженні відповідно до спостережуваного використання ресурсів, наприклад, використання CPU чи памʼяті.

Є покроковий навчальний матеріал з налаштування HorizontalPodAutoscaler для Deployment.

Вертикальне масштабування робочих навантажень

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.25

Ви можете автоматично масштабувати робоче навантаження вертикально за допомогою VerticalPodAutoscaler (VPA). На відміну від HPA, VPA стандартно не постачається з Kubernetes, але це надбудова, яку вам або адміністратору кластера може знадобитися розгорнути, перш ніж ви зможете нею користуватися.

Після встановлення він дозволяє створювати CustomResourceDefinitions (CRDs) для ваших робочих навантажень, які визначають як і коли масштабувати ресурси керованих реплік.

Примітка:

Для роботи VPA у вашому кластері має бути встановлений Metrics Server.

Вертикальне масштабування Podʼів на місці

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.35
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.35. It was first available in the v1.27 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate InPlacePodVerticalScaling, Kubernetes ignores it but does not report any error.

Станом на Kubernetes 1.37 VPA не підтримує зміну розміру Podʼів на місці, але над цією інтеграцією працюють. Щоб вручну змінити розмір Podʼів на місці, див. Зміна розміру ресурсів контейнера на місці.

Автомасштабування на основі розміру кластера

Для робочих навантажень, які потрібно масштабувати залежно від розміру кластера (наприклад, cluster-dns чи інші системні компоненти), ви можете використовувати Cluster Proportional Autoscaler. Так само як і VPA, він не є частиною ядра Kubernetes, а розміщений як окремий проєкт на GitHub.

Cluster Proportional Autoscaler відстежує кількість придатних для планування вузлів та ядер і відповідно масштабує кількість реплік цільового робочого навантаження.

Якщо кількість реплік має залишитися незмінною, ви можете масштабувати свої робочі навантаження вертикально залежно від розміру кластера, використовуючи Cluster Proportional Vertical Autoscaler. Проєкт наразі перебуває у бета-версії та доступний на GitHub.

Тоді як Cluster Proportional Autoscaler масштабує кількість реплік робочого навантаження, Cluster Proportional Vertical Autoscaler коригує запити на ресурси для робочого навантаження (наприклад, Deployment або DaemonSet) залежно від кількості вузлів та/або ядер у кластері.

Автомасштабування на основі подій

Також існує можливість масштабування робочих навантажень на основі подій, наприклад, використовуючи Kubernetes Event Driven Autoscaler (KEDA).

KEDA — це проєкт CNCF зі статусом graduated, що дозволяє масштабувати ваші робочі навантаження залежно від кількості подій, які потрібно обробити, наприклад, кількості повідомлень у черзі. Існує широкий спектр адаптерів для різних джерел подій на вибір.

Автомасштабування на основі розкладу

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

Як і у випадку автомасштабування на основі подій, такої поведінки можна досягти за допомогою KEDA разом з його Cron scaler. Cron scaler дозволяє вам визначати розклади (і часові пояси) для збільшення чи зменшення ваших робочих навантажень.

Масштабування інфраструктури кластера

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

Масштабування інфраструктури кластера зазвичай передбачає додавання або видалення вузлів. Дивіться Автомасштабування вузлів для отримання додаткової інформації.

Що далі

4.5 - Горизонтальне автомасштабування Podʼів

У Kubernetes HorizontalPodAutoscaler автоматично оновлює ресурс робочого навантаження (наприклад, Deployment або StatefulSet), щоб автоматично масштабувати потужності відповідно до попиту.

Горизонтальне масштабування означає, що реакція на збільшення навантаження полягає у розгортанні додаткових Podʼів. Це відрізняється від вертикального масштабування, що для Kubernetes означає призначення додаткових ресурсів (наприклад, памʼяті або CPU) для уже запущених Podʼів робочого навантаження.

Якщо навантаження зменшується, а кількість Podʼів перевищує налаштований мінімум, HorizontalPodAutoscaler дає вказівку ресурсу робочого навантаження (Deployment, StatefulSet або іншому схожому ресурсу) масштабуватися вниз.

Горизонтальне автомасштабування Podʼів не застосовується до обʼєктів, які не можна масштабувати (наприклад, DaemonSet).

HorizontalPodAutoscaler реалізований як ресурс API Kubernetes та контролер. Ресурс визначає поведінку контролера. Контролер горизонтального автомасштабування Podʼів, який працює в панелі управління Kubernetes, періодично коригує бажаний масштаб своєї цілі (наприклад, Deployment), щоб він відповідав спостережуваним метрикам, таким як середнє використання CPU, середнє використання памʼяті або будь-яка інша власна метрика, яку ви вказуєте.

Є покроковий приклад використання горизонтального автомасштабування Podʼів.

Як працює HorizontalPodAutoscaler?

graph BT

hpa[HorizontalPodAutoscaler] --> scale[Scale]

subgraph rc[Deployment]
    scale
end

scale -.-> pod1[Pod 1]
scale -.-> pod2[Pod 2]
scale -.-> pod3[Pod N]

classDef hpa fill:#D5A6BD,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D;
classDef rc fill:#F9CB9C,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D;
classDef scale fill:#B6D7A8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D;
classDef pod fill:#9FC5E8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D;
class hpa hpa;
class rc rc;
class scale scale;
class pod1,pod2,pod3 pod

Схема 1. HorizontalPodAutoscaler керує масштабуванням Deployment та його ReplicaSet

У Kubernetes горизонтальне автомасштабування Podʼів реалізовано як цикл керування, що працює періодично (це не безперервний процес). Інтервал встановлюється параметром --horizontal-pod-autoscaler-sync-period для kube-controller-manager (а стандартне значення — 15 секунд).

Один раз протягом кожного періоду менеджер контролерів запитує використання ресурсів відповідно до метрик, вказаних у визначенні кожного HorizontalPodAutoscaler. Менеджер контролерів знаходить цільовий ресурс, визначений за допомогою scaleTargetRef, потім вибирає Podʼи на основі міток .spec.selector цільового ресурсу та отримує метрики або з API метрик ресурсів (для метрик ресурсів на кожен Pod), або з API власних метрик (для всіх інших метрик).

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

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

  • Для власних метрик на кожен Pod контролер працює аналогічно метрикам ресурсів на кожен Pod, за винятком того, що він працює із сирими значеннями, а не значеннями використання.

  • Для метрик обʼєктів та зовнішніх метрик отримується одна метрика, яка описує обʼєкт. Цю метрику порівнюють з цільовим значенням, щоб отримати співвідношення, як вище. У версії API autoscaling/v2 це значення можна за необхідності розділити на кількість Podʼів до порівняння.

Звичайне використання HorizontalPodAutoscaler — налаштувати його на отримання метрик з агрегованих API (metrics.k8s.io, custom.metrics.k8s.io або external.metrics.k8s.io). API metrics.k8s.io зазвичай надається надбудовою Metrics Server, яку потрібно запускати окремо. Для отримання додаткової інформації про метрики ресурсів див. Metrics Server.

Підтримка API метрик пояснює гарантії стабільності та статус підтримки цих різних API.

Контролер HorizontalPodAutoscaler має доступ до відповідних ресурсів робочого навантаження, які підтримують масштабування (такі як Deployments та StatefulSet). Ці ресурси мають субресурс з назвою scale, інтерфейс, який дозволяє динамічно встановлювати кількість реплік і переглядати поточний стан кожного з них. Для загальної інформації про субресурси в API Kubernetes див. Поняття API Kubernetes.

Подробиці алгоритму

У найпростішому вигляді контролер HorizontalPodAutoscaler працює зі співвідношенням між бажаним значенням метрики та поточним значенням метрики:

$$\begin{equation*} \text{бажаніРепліки} = ceil\left\lceil \text{поточніРепліки} \times \frac {\text{поточнеЗначенняМетрики}}{\text{бажанеЗначенняМетрики}} \right\rceil \end{equation*}$$

Наприклад, якщо поточне значення метрики — 200m, а бажане значення — 100m, кількість реплік буде подвоєна, оскільки \( { 200.0 \div 100.0 } = 2.0 \). Якщо поточне значення натомість — 50m, кількість реплік буде зменшена вдвічі, оскільки \( { 50.0 \div 100.0 } = 0.5 \). Панель управління пропускає будь-яку дію масштабування, якщо співвідношення достатньо близьке до 1,0 (в межах налаштовуваного допуску, стандартно 0,1).

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

Перед перевіркою допуску та визначенням кінцевих значень панель управління також перевіряє, чи не бракує якихось метрик і скільки Podʼів перебувають у стані Ready. Для метрик ресурсів на кожен Pod ігноруються всі Podʼи із заданою позначкою часу видалення (обʼєкти з такою позначкою перебувають у процесі вимкнення/видалення), а всі Podʼи, що зазнали збою, відкидаються. Для зовнішніх метрик та метрик обʼєктів кількість реплік розраховується на основі кількості Podʼів у станах Running та Ready; Podʼи, що завершують роботу, але все ще перебувають у стані Ready, продовжують враховуватися в цій загальній кількості.

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

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

Через технічні обмеження контролер HorizontalPodAutoscaler не може точно визначити, коли Pod уперше став готовим, вирішуючи, чи відкласти певні метрики CPU. Натомість він вважає Pod «ще не готовим», якщо той не готовий і перейшов у стан готовності протягом короткого налаштовуваного проміжку часу від свого запуску. Це значення налаштовується за допомогою параметра командного рядка --horizontal-pod-autoscaler-initial-readiness-delay, і стандартно воно становить 30 секунд. Після того, як Pod став готовим, будь-який перехід у стан готовності вважається першим, якщо він відбувся протягом довшого налаштовуваного часу від запуску. Це значення налаштовується за допомогою параметра командного рядка --horizontal-pod-autoscaler-cpu-initialization-period, і його стандартне значення — 5 хвилин.

Базове співвідношення масштабу \( \text{поточнеЗначенняМетрики} \over \text{бажанеЗначенняМетрики} \) потім обчислюється за Podʼами, що залишилися, тобто не були відкладені або відкинуті вище.

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

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

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

Зверніть увагу, що початкове значення для середнього використання повертається через статус HorizontalPodAutoscaler, без врахування ще не готових Podʼів або відсутніх метрик, навіть коли використовується нове співвідношення використання.

Якщо в HorizontalPodAutoscaler вказано кілька метрик, цей розрахунок виконується для кожної метрики, а потім обирається найбільше з бажаних кількостей реплік. Якщо будь-яка з цих метрик не може бути переведена у бажану кількість реплік (наприклад, через помилку отримання метрик з API метрик) і запропоновано масштабування вниз за метриками, які можна отримати, масштабування пропускається. Це означає, що HPA все ще може масштабуватися вгору, якщо одна або кілька метрик дають значення desiredReplicas, більше, ніж поточне значення.

Нарешті, прямо перед тим, як HPA масштабує ціль, рекомендація масштабування записується. Контролер розглядає всі рекомендації в налаштованому вікні та обирає найвищу рекомендацію в межах цього вікна. Це значення можна налаштувати за допомогою параметра командного рядка --horizontal-pod-autoscaler-downscale-stabilization, стандартне значення якого — 5 хвилин. Це означає, що зменшення масштабу відбуватиметься поступово, згладжуючи вплив метрик, що швидко змінюються.

Готовність Podʼа та метрики автомасштабування

Контролер HorizontalPodAutoscaler (HPA) містить два параметри командного рядка, які впливають на те, як збираються метрики CPU з Podʼів під час запуску:

  1. --horizontal-pod-autoscaler-cpu-initialization-period (стандартно: 5 хвилин)

Цей параметр визначає часовий проміжок після запуску Podʼа, протягом якого використання CPU ігнорується, крім випадків, коли: - Pod перебуває у стані Ready та - Зразок метрики було взято повністю протягом періоду, коли він перебував у стані Ready.

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

  1. --horizontal-pod-autoscaler-initial-readiness-delay (стандартно: 30 секунд)

Цей параметр командного рядка визначає короткий період затримки після запуску Podʼа, протягом якого контролер HPA розглядає Podʼи, що перебувають у стані Unready, як такі, що все ще ініціалізуються, навіть якщо вони раніше ненадовго перейшли у стан Ready.

Він призначений для того, щоб: - Уникнути включення Podʼів, які швидко коливаються між Ready та Unready під час запуску. - Забезпечити стабільність початкового сигналу готовності до того, як HPA вважатиме їх метрики дійсними.

Ви можете встановити ці параметри командного рядка тільки для всього кластера.

Ключова поведінка для готовності Podʼа

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

Поради стосовно готовності Podʼа

  • Налаштуйте startupProbe, який не проходить, поки високе використання CPU не мине, або
  • Переконайтеся, що ваш readinessProbe повідомляє про Ready тільки після того, як пік використання CPU спаде, використовуючи initialDelaySeconds.

А в ідеалі також встановіть --horizontal-pod-autoscaler-cpu-initialization-period, щоб покрити тривалість запуску.

Обʼєкт API

HorizontalPodAutoscaler є типом API в групі API Kubernetes autoscaling. Поточну стабільну версію можна знайти у версії API autoscaling/v2, яка включає підтримку масштабування за памʼяттю та власними метриками. Нові поля, введені в autoscaling/v2, зберігаються як анотації під час роботи з autoscaling/v1.

Під час створення обʼєкта API HorizontalPodAutoscaler переконайтеся, що вказана назва є дійсною назвою піддомену DNS. Більше деталей про обʼєкт API можна знайти на сторінці Обʼєкт HorizontalPodAutoscaler.

Стабільність масштабування робочого навантаження

Під час керування масштабом групи реплік за допомогою HorizontalPodAutoscaler можливі часті коливання кількості реплік через динамічний характер оцінюваних метрик. Це іноді називають thrashing або flapping (коливання). Це схоже на концепцію гістерезису в кібернетиці.

Автомасштабування під час поетапного оновлення

Kubernetes дозволяє виконувати поетапне оновлення для Deployment. У цьому випадку Deployment керує підлеглими ReplicaSets за вас. Коли ви налаштовуєте автомасштабування для Deployment, ви звʼязуєте HorizontalPodAutoscaler з одним Deployment. HorizontalPodAutoscaler керує полем replicas Deployment. Контролер Deployment відповідає за встановлення replicas підлеглих ReplicaSets так, щоб вони складали відповідну кількість під час розгортання, а також після його завершення.

Якщо ви виконуєте поетапне оновлення StatefulSet, у якого є автомасштабована кількість реплік, StatefulSet безпосередньо керує своїм набором Podʼів (немає проміжного ресурсу, аналогічного ReplicaSet).

Підтримка метрик ресурсів

Будь-яку ціль HPA можна масштабувати на основі використання ресурсів Podʼами в цілі масштабування. Під час визначення специфікації Podʼа слід вказувати запити на ресурси, такі як cpu та memory. Вони використовуються для визначення використання ресурсів, а контролер HPA використовує це для масштабування цілі вгору або вниз. Щоб використовувати масштабування на основі використання ресурсів, вкажіть джерело метрики так:

type: Resource
resource:
  name: cpu
  target:
    type: Utilization
    averageUtilization: 60

З цією метрикою контролер HPA буде підтримувати середнє використання ресурсів Podʼами в цілі масштабування на рівні 60%. Використання — це співвідношення між поточним використанням ресурсу та запитаними ресурсами Podʼа. Дивіться Алгоритм для отримання додаткової інформації про те, як обчислюється та усереднюється використання.

Примітка:

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

Метрики ресурсів контейнера

This is a stable feature in Kubernetes, and has been since the 1.30 release. You can no longer toggle this feature (the associated feature gate has been removed).

API HorizontalPodAutoscaler також підтримує джерело метрик контейнера, де HPA може відстежувати використання ресурсів окремих контейнерів у наборі Podʼів, щоб масштабувати цільовий ресурс. Це дозволяє налаштовувати пороги масштабування для контейнерів, які мають найбільше значення в конкретному Podʼі. Наприклад, якщо у вас є вебзастосунок і sidecar-контейнер, який надає логування, ви можете масштабувати на основі використання ресурсів вебзастосунка, ігноруючи sidecar-контейнер та його використання ресурсів.

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

type: ContainerResource
containerResource:
  name: cpu
  container: application
  target:
    type: Utilization
    averageUtilization: 60

У наведеному вище прикладі контролер HPA масштабує ціль так, що середнє використання CPU у контейнері application в усіх Podʼах становить 60%.

Примітка:

Якщо ви зміните назву контейнера, який відстежує HorizontalPodAutoscaler, ви можете зробити цю зміну у певному порядку, щоб забезпечити доступність та ефективність масштабування під час застосування змін. Перед тим, як ви оновите ресурс, який визначає контейнер (наприклад, Deployment), вам слід оновити повʼязаний HPA, щоб він відстежував як нову, так і стару назву контейнера. Таким чином, HPA може розраховувати рекомендацію масштабування протягом усього процесу оновлення.

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

Масштабування на основі власних метрик

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.23

(версія API autoscaling/v2beta2 раніше надавала цю можливість як бета-можливість)

За умови використання версії API autoscaling/v2 ви можете налаштувати HorizontalPodAutoscaler на масштабування на основі власної метрики (яка не є вбудованою в Kubernetes або будь-який компонент Kubernetes). Потім контролер HorizontalPodAutoscaler запитує ці власні метрики з API Kubernetes.

Вимоги наведено в розділі Підтримка API метрик.

Масштабування на основі кількох метрик

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.23

(версія API autoscaling/v2beta2 раніше надавала цю можливість як бета-можливість)

За умови використання версії API autoscaling/v2 ви можете вказати кілька метрик, на основі яких масштабуватиме HorizontalPodAutoscaler. Потім контролер HorizontalPodAutoscaler оцінює кожну метрику і пропонує новий масштаб на основі цієї метрики. HorizontalPodAutoscaler бере максимальний масштаб, рекомендований для кожної метрики, і встановлює робоче навантаження на такий розмір (за умови, що це не більше загального максимуму, який ви налаштували).

Підтримка API метрик

Стандартно контролер HorizontalPodAutoscaler отримує метрики з низки API. Щоб він мав доступ до цих API, адміністратори кластера повинні переконатися, що:

  • Увімкнено шар агрегації API.

  • Відповідні API зареєстровано:

    • Для метрик ресурсів це metrics.k8s.io API, яке зазвичай надає metrics-server. Його можна запустити як надбудову кластера.

      Примітка:

      HorizontalPodAutoscaler наразі підтримує API `metrics.k8s.io/v1beta1` для метрик ресурсів. Він ще не підтримує `metrics.k8s.io/v1`.
      
    • Для власних метрик це custom.metrics.k8s.io API. Його надають API-сервери «адаптера», які постачають розробники рішень для метрик. Перевірте у своєму конвеєрі метрик, чи доступний адаптер метрик Kubernetes.

    • Для зовнішніх метрик це external.metrics.k8s.io API. Він може бути наданий адаптерами власних метрик, які наведено вище.

Для отримання додаткової інформації про ці різні шляхи метрик та їх відмінності дивіться відповідні проєктні пропозиції для HPA V2, custom.metrics.k8s.io та external.metrics.k8s.io.

Для прикладів використання дивіться посібник з використання власних метрик та посібник з використання зовнішніх метрик.

Налаштовувана поведінка масштабування

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.23

(версія API autoscaling/v2beta2 раніше надавала цю можливість як бета-можливість)

Якщо ви використовуєте API HorizontalPodAutoscaler v2, ви можете використовувати поле behavior (див. довідку API) для налаштування окремих поведінок масштабування вгору та вниз. Ви вказуєте ці поведінки, встановлюючи scaleUp та/або scaleDown у полі behavior.

Політики масштабування дозволяють контролювати швидкість зміни кількості реплік під час масштабування. Також можна використати два параметри для запобігання коливанням: ви можете вказати вікно стабілізації для згладжування кількості реплік та допуск для ігнорування незначних коливань метрик нижче вказаного порогового значення.

Політики масштабування

Одну або декілька політик масштабування можна вказати у розділі behavior специфікації. Коли вказано кілька політик, стандартно вибирається та політика, яка дозволяє найбільше змін. Наступний приклад показує цю поведінку під час зменшення масштабу:

behavior:
  scaleDown:
    policies:
    - type: Pods
      value: 4
      periodSeconds: 60
    - type: Percent
      value: 10
      periodSeconds: 60

periodSeconds вказує на проміжок часу в минулому, протягом якого політика має бути істинною. Максимальне значення, яке можна встановити для periodSeconds, — 1800 (пів години). Перша політика (Pods) дозволяє зменшити щонайбільше 4 репліки за одну хвилину. Друга політика (Percent) дозволяє зменшити щонайбільше 10% поточних реплік за одну хвилину.

Оскільки стандартно вибирається політика, яка дозволяє найбільше змін, друга політика буде використовуватися лише тоді, коли кількість реплік буде більше ніж 40. За 40 або менше реплік буде застосована перша політика. Наприклад, якщо є 80 реплік, і ціль потрібно зменшити до 10 реплік, то під час першого кроку буде скорочено 8 реплік. На наступній ітерації, коли кількість реплік становить 72, 10% від Podʼів — 7,2, але число заокруглюється вгору до 8. На кожному циклі контролера автомасштабування кількість Podʼів, які мають бути змінені, перераховується на основі поточної кількості реплік. Коли кількість реплік падає нижче 40, застосовується перша політика (Pods) і зменшується по 4 репліки за раз.

Вибір політики можна змінити, вказавши поле selectPolicy для напрямку масштабування. Встановлення значення Min вибере політику, яка дозволяє найменшу зміну кількості реплік. Встановлення значення Disabled повністю вимикає масштабування в цьому напрямку.

Вікно стабілізації

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

Наприклад, у наступному фрагменті коду вказано вікно стабілізації для scaleDown.

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300

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

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

Допуск

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.37
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.37. It was first available in the v1.33 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate HPAConfigurableTolerance, Kubernetes ignores it but does not report any error.

Поле tolerance налаштовує порогове значення для мінливості метрики, не даючи автомасштабувальнику масштабувати через зміни, менші за це значення.

Цей допуск визначається як величина відхилення навколо бажаного значення метрики, нижче якої масштабування не відбуватиметься. Наприклад, розглянемо HorizontalPodAutoscaler, налаштований з цільовим споживанням памʼяті 100MiB та допуском масштабування вгору 5%:

behavior:
  scaleUp:
    tolerance: 0.05 # допуск 5% для масштабування вгору

За такої конфігурації алгоритм HPA розглядатиме масштабування вгору лише в тому випадку, якщо споживання памʼяті перевищує 105MiB (тобто на 5% вище цільового значення).

Якщо це поле не встановлено, HPA застосовуватиме стандартний допуск кластера — 10%. Це стандартне значення може бути оновлено як для масштабування вгору, так і вниз, використовуючи kube-controller-manager з аргументом командного рядка --horizontal-pod-autoscaler-tolerance. (Ви не можете використовувати API Kubernetes для налаштування цього стандартного значення.)

Стандартна поведінка

Для використання власного масштабування не всі поля повинні бути вказані. Можна вказати лише значення, які потрібно налаштувати. Ці власні значення обʼєднуються зі стандартними значеннями. Стандартні значення відповідають наявній поведінці в алгоритмі HPA.

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
    - type: Pods
      value: 4
      periodSeconds: 15
    selectPolicy: Max

Для зменшення масштабу вікно стабілізації становить 300 секунд (або значення параметра командного рядка --horizontal-pod-autoscaler-downscale-stabilization, якщо воно вказане). Є лише одна політика для зменшення масштабу, яка дозволяє видалити 100% поточно запущених реплік, що означає, що ціль масштабування може бути зменшена до мінімально допустимих реплік. Для збільшення масштабу вікно стабілізації відсутнє. Коли метрики показують, що ціль повинна бути збільшена, ціль збільшується негайно. Є 2 політики, за якими кожні 15 секунд можна додати не більше 4 Podʼів або 100% поточно запущених реплік до тих пір, поки HPA не досягне стабільного стану.

Приклад: зміна вікна стабілізації для зменшення масштабу

Щоб вказати власне значення вікна стабілізації для зменшення масштабу тривалістю в 1 хвилину, до HPA слід додати таку поведінку:

behavior:
  scaleDown:
    stabilizationWindowSeconds: 60

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

Щоб обмежити швидкість, з якою HPA видаляє Podʼи, до 10% за хвилину, до HPA слід додати таку поведінку:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60

Щоб переконатися, що за хвилину видаляється не більше 5 Podʼів, можна додати другу політику зменшення масштабу з фіксованим розміром 5 та встановити selectPolicy на значення Min. Встановлення selectPolicy на Min означає, що автомасштабувальник вибирає політику, яка впливає на найменшу кількість Podʼів:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
    - type: Pods
      value: 5
      periodSeconds: 60
    selectPolicy: Min

Приклад: вимкнення зменшення масштабу

Значення selectPolicy Disabled вимикає масштабування у вказаному напрямку. Щоб запобігти зменшенню масштабу, буде використана наступна політика:

behavior:
  scaleDown:
    selectPolicy: Disabled

Підтримка HorizontalPodAutoscaler в kubectl

HorizontalPodAutoscaler, як і кожний ресурс API, підтримується стандартним чином у kubectl. Ви можете створити новий автомасштабувальник за допомогою команди kubectl create. Ви можете переглянути список автомасштабувальників за допомогою kubectl get hpa або отримати детальний опис за допомогою kubectl describe hpa. Нарешті, ви можете видалити автомасштабувальник за допомогою kubectl delete hpa.

Крім того, є спеціальна команда kubectl autoscale для створення обʼєкта HorizontalPodAutoscaler. Наприклад, виконання kubectl autoscale rs foo --min=2 --max=5 --cpu=80% створить автомасштабувальник для ReplicaSet foo, з цільовим використанням CPU, встановленим на 80% і кількістю реплік від 2 до 5.

Масштабування до нуля та з нуля

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно увімкнено)

Для HorizontalPodAutoscaler, які масштабуються на основі власних (обʼєктних) або зовнішніх метрик, ви можете встановити spec.minReplicas у 0. Тоді робоче навантаження масштабується повністю до нуля реплік, коли немає навантаження, і масштабується назад, коли метрика вказує, що потрібна принаймні одна репліка. Це корисно для робочих навантажень, які простоюють протягом тривалого часу і дорогі в утриманні, наприклад, споживачі черги, якою користуються час від часу, або завдання, що потребують спеціального обладнання, як-от GPU.

Масштабування до нуля підтримується лише для обʼєктних та зовнішніх метрик. Воно недоступне для метрик ресурсів (таких як використання CPU або памʼяті), оскільки їх можна виміряти лише на запущених Podʼах. Встановлення minReplicas: 0 вимагає наявності принаймні однієї обʼєктної або зовнішньої метрики; інакше API-сервер відхиляє HorizontalPodAutoscaler.

Ця поведінка контролюється функціональною можливістю HPAScaleToZero, яка стандартно увімкнена. Функціональна можливість має бути увімкнена як на kube-apiserver (який дозволяє minReplicas: 0), так і на kube-controller-manager (який виконує масштабування).

Поки HPA утримує робоче навантаження на нулі реплік, він записує стан ScaledToZero зі значенням True у статус HorizontalPodAutoscaler. HPA використовує цей стан, щоб відрізнити робоче навантаження, яке він масштабував до нуля (і масштабує назад, коли метрика повертається), від того, яке було деактивовано вручну шляхом встановлення кількості його реплік у 0. Після того, як робоче навантаження масштабовано назад, стан встановлюється в False з причиною NotScaledToZero.

Неявна деактивація в режимі обслуговування

Ви можете неявно деактивувати HPA для цільового обʼєкта без необхідності змінювати саму конфігурацію HPA. Якщо бажана кількість реплік цільового обʼєкта встановлена на 0, а мінімальна кількість реплік HPA більше 0, HPA припиняє коригування цільового обʼєкта (і встановлює для себе стан ScalingActive у false), доки ви знову не активуєте його вручну, змінивши бажану кількість реплік цільового обʼєкта або мінімальну кількість реплік HPA.

Перехід Deployments та StatefulSets на горизонтальне автомасштабування

Під час увімкнення HPA рекомендується видалити значення spec.replicas з Deployment та/або StatefulSet в їхніх маніфестах. Якщо цього не зробити, щоразу, коли до цього обʼєкта застосовується зміна, наприклад, за допомогою kubectl apply -f deployment.yaml, Kubernetes отримуватиме вказівку масштабувати поточну кількість Podʼів до значення ключа spec.replicas. Це може бути небажаним і спричиняти проблеми, коли HPA активний, призводячи до коливань (thrashing або flapping).

Майте на увазі, що видалення spec.replicas може спричинити одноразове зниження кількості Podʼів, оскільки стандартне значення цього ключа — 1 (див. Репліки Deployment). Після оновлення всі Podʼи, крім одного, розпочнуть процедуру завершення. Після цього будь-яке подальше застосування Deployment працюватиме як звичайно і дотримуватиметься конфігурації поетапного оновлення, як і очікується. Ви можете уникнути цього зниження, обравши один із двох наступних методів залежно від того, як ви змінюєте свої Deployments:

  1. kubectl apply edit-last-applied deployment/<deployment_name>
  2. У редакторі видаліть spec.replicas. Після збереження та виходу з редактора kubectl застосовує оновлення. На цьому етапі не відбувається змін кількості Podʼів.
  3. Тепер ви можете видалити spec.replicas з маніфеста. Якщо ви використовуєте систему керування вихідним кодом, також зафіксуйте ваші зміни або виконайте будь-які інші кроки для перегляду вихідного коду, які відповідають вашому способу відстеження оновлень.
  4. Відтепер ви можете запускати kubectl apply -f deployment.yaml

Під час використання Server-Side Apply ви можете дотримуватися вказівок щодо передачі права власності, які охоплюють саме цей випадок використання.

Що далі

Якщо ви налаштовуєте автомасштабування у вашому кластері, вам також може бути корисно розглянути використання автомасштабування вузлів щоб забезпечити запуск потрібної кількості вузлів. Ви також можете дізнатися більше про вертикальне автомасштабування Podʼів.

Для отримання додаткової інформації про HorizontalPodAutoscaler:

4.6 - Вертикальне автомасштабування Podʼів

У Kubernetes VerticalPodAutoscaler автоматично оновлює ресурс керування робочим навантаженням (такий як Deployment або StatefulSet), щоб автоматично коригувати запити й обмеження ресурсів інфраструктури відповідно до фактичного використання.

Вертикальне масштабування означає, що відповіддю на збільшення попиту на ресурси є виділення більшої кількості ресурсів (наприклад, памʼяті або CPU) для Podʼів, які вже працюють для цього робочого навантаження. Це також відомо як rightsizing, або іноді autopilot. Це відрізняється від горизонтального масштабування, яке для Kubernetes означало б розгортання більшої кількості Podʼів для розподілу навантаження.

Якщо використання ресурсів зменшується, а запити на ресурси від Podʼа перевищують оптимальний рівень, VerticalPodAutoscaler дає вказівку ресурсу робочого навантаження (Deployment, StatefulSet або іншому подібному ресурсу) скоригувати запити на ресурси назад у бік зменшення, запобігаючи марнуванню ресурсів.

VerticalPodAutoscaler реалізовано як ресурс API Kubernetes і контролер. Ресурс визначає поведінку контролера. Контролер вертикального автомасштабування Podʼів, що працює в площині даних Kubernetes, періодично коригує запити на ресурси та обмеження для свого обʼєкта (наприклад, Deployment) на основі аналізу історичного використання ресурсів, кількості доступних ресурсів у кластері та подій у реальному часі, таких як ситуації нестачі памʼяті (OOM).

Обʼєкт API

VerticalPodAutoscaler визначається як Custom Resource Definition (CRD) в Kubernetes. На відміну від HorizontalPodAutoscaler, який є частиною основного API Kubernetes, VPA необхідно встановлювати окремо у вашому кластері.

Поточна стабільна версія API — autoscaling.k8s.io/v1. Більш детальну інформацію про встановлення VPA та API можна знайти в репозиторії VPA на GitHub.

Як працює VerticalPodAutoscaler?

graph BT
    metrics[Metrics Server]
    api[API Server]
    admission[VPA Admission Controller]

    vpa_cr[VerticalPodAutoscaler CRD]
    recommender[VPA recommender]
    updater[VPA updater]

    metrics --> recommender
    recommender -->|Зберігає
рекомендації| vpa_cr subgraph Робоче навантаження застосунку controller[Deployment / RC / StatefulSet] pod[Pod / Container] end vpa_cr -->|Перевірка на
наявність змін| updater updater -->|Виселяє або оновлює
Pod на місці| controller controller -->|Запит на
новий Pod| api api -->|Створення
нового Podʼа| admission admission -->|Отримує останні
рекомендації| vpa_cr admission -->|Інʼєкція нових
значень ресурсів| api api -->|Створює
Pod| controller controller -->|Новий Pod з
оптимальними
ресурсами| pod classDef vpa fill:#9FC5E8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef crd fill:#D5A6BD,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef metrics fill:#FFD966,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef app fill:#B6D7A8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; class recommender,updater,admission vpa; class vpa_cr crd; class metrics metrics; class controller,pod app;

Схема 1. VerticalPodAutoscaler контролює запити на ресурси та обмеження Podʼів у Deployment.

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

  • recommender, який аналізує використання ресурсів і надає рекомендації.
  • updater, який оновлює запити Podʼів на ресурси, виселяючи Podʼи або змінюючи їх на місці.
  • І вебхук VPA admission controller, який застосовує рекомендації щодо ресурсів до нових або створених заново Podʼів.

Один раз протягом кожного періоду Recommender запитує використання ресурсів для Podʼів, на які спрямовано кожне визначення VerticalPodAutoscaler. Recommender знаходить цільовий ресурс, визначений targetRef, потім вибирає Podʼи на основі міток .spec.selector цільового ресурсу та отримує метрики з API метрик ресурсів для аналізу фактичного споживання CPU та памʼяті.

Recommender аналізує поточні та історичні дані про використання ресурсів (CPU та памʼять) для кожного Podʼа, на який націлений VerticalPodAutoscaler. Він перевіряє:

  • Історичні моделі споживання з часом для виявлення тенденцій
  • Пікове використання та відхилення для забезпечення достатнього запасу
  • Події вичерпання памʼяті (OOM) та інші інциденти, повʼязані з ресурсами

На основі цього аналізу Recommender обчислює три типи рекомендацій:

  • Рекомендація щодо цільового значення (оптимальні ресурси для типового використання)
  • Нижня межа (мінімально необхідні ресурси)
  • Верхня межа (максимально доцільні ресурси).

Ці рекомендації зберігаються в полі .status.recommendation ресурсу VerticalPodAutoscaler.

Компонент updater відстежує ресурси VerticalPodAutoscaler і порівнює поточні запити на ресурси Podʼів з рекомендаціями. Коли різниця перевищує налаштовані порогові значення і політика оновлення дозволяє це, updater може:

  • Виселити Podʼи, ініціюючи їх повторне створення з новими запитами на ресурси (традиційний підхід)
  • Оновити ресурси Podʼа на місці без виселення, якщо кластер підтримує оновлення ресурсів Podʼів на місці

Вибраний метод залежить від налаштованого режиму оновлення, можливостей кластера та типу необхідних змін ресурсів. Оновлення на місці, якщо вони доступні, дозволяють уникнути порушення роботи Podʼа, але можуть мати обмеження щодо ресурсів, які можна модифікувати. Updater дотримується PodDisruptionBudgets, щоб мінімізувати вплив на роботу сервісу.

Контролер допуску працює як змінювальний вебхук, який перехоплює запити на створення Podʼів. Він перевіряє, чи є Pod ціллю VerticalPodAutoscaler, і, якщо так, застосовує рекомендовані запити на ресурси та обмеження перед створенням Podʼа. Конкретніше, контролер допуску використовує рекомендацію Target у розділі .status.recommendation ресурсу VerticalPodAutoscaler як нові запити на ресурси. Контролер допуску гарантує, що нові Podʼи запускаються з належними виділеними ресурсами, незалежно від того, чи вони створені під час початкового розгортання, після виселення компонентом updater або внаслідок операцій масштабування.

VerticalPodAutoscaler вимагає, щоб у кластері було встановлено джерело метрик, таке як надбудова Metrics Server від Kubernetes. Компоненти VPA отримують метрики з API metrics.k8s.io. Metrics Server потрібно запускати окремо, оскільки в більшості кластерів він стандартно не розгортається. Докладнішу інформацію про метрики ресурсів дивіться у розділі Metrics Server.

Режими оновлення

VerticalPodAutoscaler підтримує різні режими оновлення, які контролюють, як і коли рекомендації щодо ресурсів застосовуються до ваших Podʼів. Ви налаштовуєте режим оновлення за допомогою поля updateMode у специфікації VPA в розділі updatePolicy:

---
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "Recreate"  # Off, Initial, Recreate, InPlaceOrRecreate, InPlace

Off

У режимі оновлення Off recommender VPA все одно аналізує використання ресурсів і генерує рекомендації, але ці рекомендації не застосовуються до Podʼів автоматично. Рекомендації зберігаються лише в полі .status обʼєкта VPA.

Ви можете використовувати такий інструмент, як kubectl, щоб переглянути .status і рекомендації, що містяться в ньому.

Initial

У режимі Initial VPA встановлює запити на ресурси тільки під час першого створення Podʼа. Він не оновлює ресурси для вже запущених Podʼів, навіть якщо рекомендації змінюються з часом. Рекомендації застосовуються тільки під час створення Podʼа.

Recreate

У режимі Recreate VPA активно керує ресурсами Podʼів, виселяючи Podʼи, коли їхні поточні запити на ресурси значно відрізняються від рекомендацій. Коли Pod виселяється, контролер робочого навантаження (який керує Deployment, StatefulSet тощо) створює Pod-замінник, а контролер допуску VPA застосовує оновлені запити на ресурси до нового Podʼа.

InPlaceOrRecreate

У режимі InPlaceOrRecreate VPA намагається оновити запити та обмеження ресурсів Podʼа без перезапуску Podʼа, якщо це можливо. Однак, якщо оновлення на місці не можуть бути виконані для певної зміни ресурсу, VPA повертається до виселення Podʼа (аналогічно режиму Recreate) і дозволяє контролеру робочого навантаження створити Pod-замінник з оновленими ресурсами.

У цьому режимі updater застосовує рекомендації на місці за допомогою можливості Зміна розміру ресурсів контейнера на місці.

InPlace

Цей режим доступний як альфа-можливість у VPA 1.7.0 і вимагає Kubernetes 1.33 або новішої версії з увімкненою функціональною можливістю кластера InPlacePodVerticalScaling, а також увімкненою функціональною можливістю InPlace для updater VPA та контролера допуску. Він використовує можливість зміни розміру Podʼа на місці, щоб застосовувати оновлення без порушення роботи Podʼа.

У режимі InPlace VPA намагається оновити запити та обмеження ресурсів Podʼа без перезапуску або виселення Podʼа. На відміну від InPlaceOrRecreate, цей режим ніколи не повертається до виселення. Якщо оновлення на місці не може бути застосоване (наприклад, через недостатню місткість вузла), VPA відкладає оновлення та повторює спробу в наступному циклі узгодження.

Щоб використовувати режим InPlace, увімкніть функціональну можливість InPlace як для updater VPA, так і для контролера допуску:

--feature-gates=InPlace=true

Потім встановіть updateMode в "InPlace" у вашій специфікації VPA:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "InPlace"

Ключова відмінність від InPlaceOrRecreate: Коли зміна розміру відкладається, виконується або є неможливою, режим InPlace завжди чекає та повторює спробу — він ніколи не виселяє Pod, незалежно від того, як довго оновлення очікує.

Auto (застаріло)

Примітка:

Режим оновлення Auto визнано застарілим починаючи з версії VPA 1.4.0. Використовуйте Recreate для оновлень на основі виселення або InPlaceOrRecreate для оновлень на місці з резервним варіантом виселення.

Режим Auto наразі є синонімом режиму Recreate і працює ідентично. Він був введений для забезпечення можливості майбутнього розширення стратегій автоматичного оновлення.

Політики ресурсів

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

Політики ресурсів визначаються у полі resourcePolicy специфікації VPA:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "Recreate"
  resourcePolicy:
    containerPolicies:
    - containerName: "application"
      minAllowed:
        cpu: 100m
        memory: 128Mi
      maxAllowed:
        cpu: 2
        memory: 2Gi
      controlledResources:
      - cpu
      - memory
      controlledValues: RequestsAndLimits

minAllowed та maxAllowed

Ці поля встановлюють межі для рекомендацій VPA. VPA ніколи не рекомендуватиме ресурси нижче minAllowed або вище maxAllowed, навіть якщо фактичні дані про використання вказують на інші значення.

controlledResources

Поле controlledResources визначає, якими типами ресурсів VPA повинен керувати для контейнера в Podʼі. Якщо не вказано, VPA стандартно керує як CPU, так і памʼяттю. Ви можете обмежити VPA, щоб він керував лише певними ресурсами. Допустимі назви ресурсів: cpu та memory.

controlledValues

Поле controlledValues визначає, чи контролює VPA запити на ресурси, обмеження або і те, і інше:

RequestsAndLimits
VPA встановлює як запити, так і обмеження. Обмеження масштабується пропорційно до запиту на основі співвідношення запиту до обмеження, визначеного в специфікації Podʼа. Це стандартний режим.
RequestsOnly
VPA встановлює лише запити, залишаючи обмеження без змін. Обмеження враховуються і можуть спричиняти тротлінг або завершення процесів за нестачі памʼяті (OOM kills), якщо використання їх перевищує.

Дивіться запити та обмеження, щоб дізнатися більше про ці два поняття.

Ресурси LimitRange

Компоненти VPA, контролер допуску та updater, додатково обробляють рекомендації, щоб вони відповідали обмеженням, визначеним у LimitRanges. Ресурси LimitRange з type Pod та Container перевіряються в кластері Kubernetes.

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

Що далі

Якщо ви налаштовуєте автомасштабування у своєму кластері, ви також можете розглянути можливість використання автомасштабування вузлів, щоб переконатися, що ви використовуєте потрібну кількість вузлів. Ви також можете дізнатися більше про горизонтальне автомасштабування Podʼів.

4.7 - Workload API

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Ресурс API Workload визначає вимоги до планування та структуру застосунку з кількох Podʼів. У той час як контролери робочого навантаження, такі як Job, керують станом виконання застосунку, Workload визначає, як слід планувати групи Pods. Контролер Job є єдиним вбудованим контролером, який створює обʼєкти PodGroup з PodGroupTemplates ресурсу Workload під час виконання.

Що таке Workload?

Ресурс API Workload є частиною групи API scheduling.k8s.io/v1beta1, і ваш кластер повинен мати цю групу API увімкненою, а також функціональну можливість GenericWorkload, перш ніж ви зможете скористатися цим API.

Workload є статичним, довгоживучим шаблоном політики. Він визначає, які політики планування слід застосовувати до груп Podʼів, але самостійно не відстежує стан виконання. Стан виконання планування підтримується обʼєктами PodGroup, які контролери створюють з PodGroupTemplates ресурсу Workload.

Структура API

Workload складається з двох полів: списку PodGroupTemplates та необовʼязкового посилання на контролер. Вся специфікація Workload є незмінною після створення: ви не можете змінювати наявні шаблони, додавати нові шаблони або видаляти шаблони з podGroupTemplates.

PodGroupTemplates

Список spec.podGroupTemplates визначає окремі компоненти вашого робочого навантаження. Наприклад, завдання машинного навчання може мати шаблон driver та шаблон worker.

Кожен елемент у podGroupTemplates повинен мати:

  1. Унікальне поле name, яке буде використовуватися для посилання на шаблон у spec.podGroupTemplateRef обʼєкта PodGroup.
  2. Політику планування (basic або gang).

Кожен елемент також може мати поля пріоритету та режиму порушення.

Примітка:

У v1.36 поля пріоритету та режиму порушення вмикалися функціональною можливістю WorkloadAwarePreemption. Цю функціональну можливість було обʼєднано з GenericWorkload у v1.37.

Максимальна кількість PodGroupTemplates в одному Workload становить 8.

apiVersion: scheduling.k8s.io/v1beta1
kind: Workload
metadata:
  name: training-job-workload
  namespace: some-ns
spec:
  controllerRef:
    apiGroup: batch
    kind: Job
    name: training-job
  podGroupTemplates:
  - name: workers
    schedulingPolicy:
      gang:
        # gang придатна для планування лише тоді, коли одночасно можуть працювати 4 Podʼи
        minCount: 4
    priorityClassName: high-priority
    disruptionMode:
      all: {}

Коли контролер робочого навантаження створює PodGroup з одного з цих шаблонів, він копіює schedulingPolicy у власну специфікацію PodGroup. Зміни в Workload впливають лише на новостворені PodGroups, а не на наявні.

Посилання на обʼєкт керування робочим навантаженням

Поле controllerRef звʼязує Workload з конкретним обʼєктом вищого рівня, що визначає застосунок, наприклад Job або власний CRD. Це корисно для спостереження та інструментів. Ці дані не використовуються для планування або керування Workload.

CompositePodGroupTemplates

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли увімкнено функціональну можливість CompositePodGroup та групу API scheduling.k8s.io/v1alpha3, ви можете використовувати CompositePodGroupTemplates для визначення багаторівневих ієрархічних вимог до планування у Workload. Ці вимоги можуть включати забезпечення вкладених топологічних обмежень на різних рівнях інфраструктури кластера (багаторівневе топологічно орієнтоване планування), планування «все або нічого» для дочірніх груп (багаторівневе групове планування) або політики порушення на рівні групи.

CompositePodGroupTemplates можна визначити за допомогою поля spec.compositePodGroupTemplates в API Workload. Під час виконання контролери робочого навантаження створюють обʼєкти CompositePodGroup та PodGroup з цих шаблонів для підтримки стану планування ієрархії під час виконання. У той час як обʼєкти PodGroup керують групами Podʼів на кінцевих рівнях, обʼєкти CompositePodGroup представляють проміжні групи, які забезпечують виконання політик планування для дочірніх груп.

Примітка:

У специфікації Workload поля spec.compositePodGroupTemplates та spec.podGroupTemplates утворюють обʼєднання: Workload повинен визначати або spec.podGroupTemplates (для пласких робочих навантажень), або spec.compositePodGroupTemplates (для ієрархічних робочих навантажень), але не може визначати обидва.

Структура та обмеження

Поле spec.compositePodGroupTemplates визначає проміжні шаблони в дереві ієрархії шаблонів груп. Кожен елемент представляє шаблон для CompositePodGroup і може містити:

  • Дочірні шаблони: вкладені CompositePodGroupTemplates (для проміжних груп) або PodGroupTemplates (для кінцевих груп, що містять Podʼи).
  • Політику планування: визначає, як плануються дочірні групи в межах цієї складеної групи:
    • basic: дочірні групи приймаються та плануються незалежно.
    • gang: забезпечує багаторівневе планування «все або нічого» для дочірніх груп. Вимагає minGroupCount, який визначає мінімальну кількість дочірніх груп, які мають бути придатними для планування одночасно, щоб складена група була здійсненною.
  • Обмеження планування: необовʼязкові топологічні обмеження для багаторівневого топологічно орієнтованого планування.
  • Пріоритет, політика витіснення та режим порушення: необовʼязкові priorityClassName, disruptionMode (Single або All) та preemptionPolicy для витіснення з урахуванням робочого навантаження.

Щоб забезпечити стабільність кластера та ефективність панелі управління, ієрархія шаблонів груп застосовує такі обмеження:

  • Максимальна глибина вкладеності: ієрархія шаблонів груп підтримує максимальну глибину 4 рівні.
  • Обмеження списку: кожен список compositePodGroupTemplates та podGroupTemplates суворо обмежений максимум 8 елементами.

Примітка:

Наразі ви не можете додавати нові або видаляти наявні CompositePodGroupTemplates. Ви можете лише змінювати значення minCount у політиці групового планування, визначеній у кінцевих PodGroupTemplates.

Приклад

Наступний приклад визначає ієрархічний Workload з шаблоном CompositePodGroup, який забезпечує групове планування для двох дочірніх шаблонів PodGroup (minGroupCount: 2), кожен з яких визначає власну політику групового планування:

apiVersion: scheduling.k8s.io/v1alpha3
kind: Workload
metadata:
  name: gang-of-gangs-workload
  namespace: default
spec:
  compositePodGroupTemplates:
  - name: root
    schedulingPolicy:
      gang:
        # Вимагає, щоб обидва дочірні PodGroups були придатними для планування разом
        minGroupCount: 2
    podGroupTemplates:
    - name: workers-a
      schedulingPolicy:
        gang:
          # Вимагає, щоб 4 Podʼи в цій групі були придатними для планування
          minCount: 4
    - name: workers-b
      schedulingPolicy:
        gang:
          # Вимагає, щоб 4 Podʼи в цій групі були придатними для планування
          minCount: 4

Групове планування з Job

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.36; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the WorkloadWithJob feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональна можливість WorkloadWithJob увімкнена, контролер Job компілює конфігурацію .spec.scheduling обʼєкта Job в обʼєкти Workload та PodGroup перед тим, як створити будь-які Podʼи. Ви вмикаєте групове планування, встановивши .spec.scheduling.schedulingPolicy.gang у Job; пропущений gang.minCount стандартно дорівнює .spec.parallelism обʼєкта Job, тому всі Podʼи мають бути придатними для планування одночасно, перш ніж будь-який з них буде привʼязаний до вузлів.

Коли .spec.scheduling пропущено, Job стандартно використовує політику basic, яка зберігає звичайне планування Pod за Podʼом. У будь-якому випадку контролер Job створює Workload та PodGroup за вас, тому вам не потрібно створювати їх самостійно. Інші контролери робочих навантажень (наприклад, JobSet) можуть самостійно керувати своїми обʼєктами Workload та PodGroup.

Повний набір полів планування та приклади дивіться у розділі Інтеграція з Workload API.

Що далі

4.7.1 - Політики планування PodGroup

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Кожен PodGroup повинен оголосити політику планування у своєму полі spec.schedulingPolicy. Ця політика визначає, як планувальник обробляє набір Podʼів у групі.

Типи політик

Поле schedulingPolicy підтримує два типи політик: basic та gang. Ви повинні вказати рівно одну.

Політика basic

Політика basic вказує планувальнику оцінювати всі Podʼи за принципом best-effort. На відміну від політики gang, PodGroup, що використовує політику basic, вважається здійсненним незалежно від того, скільки його Podʼів наразі придатні для планування.

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

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

schedulingPolicy:
  basic: {}

Політика gang

Політика gang забезпечує планування «все або нічого». Це необхідно для тісно повʼязаних робочих навантажень, де частковий запуск призводить до взаємних блокувань або марнування ресурсів.

Цю політику можна використовувати для Jobs або будь-якого іншого пакетного процесу, де всі робочі процеси повинні працювати одночасно, щоб просуватися в роботі.

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

schedulingPolicy:
  gang:
    # Кількість Podʼів, які повинні бути заплановані одночасно
    # для того, щоб група була прийнята.
    minCount: 4

Встановлення політик через PodGroupTemplates

Під час використання Workload API ви визначаєте політики планування всередині PodGroupTemplates. Контролер робочого навантаження копіює політику з шаблону в кожен створений PodGroup, роблячи PodGroup самодостатнім. Зміни в шаблонах Workload впливають лише на новостворені PodGroups, а не на наявні.

Для автономних PodGroups (створених без Workload) ви встановлюєте spec.schedulingPolicy безпосередньо на самому PodGroup.

Політики в CompositePodGroups

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли увімкнено функціональну можливість CompositePodGroup та групу API scheduling.k8s.io/v1alpha3, CompositePodGroupTemplates у Workload та обʼєкти CompositePodGroup також оголошують політику планування.

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

Типи політик для CompositePodGroups

Подібно до PodGroups, поле spec.schedulingPolicy обʼєкта CompositePodGroup підтримує два типи:

  • basic: дочірні групи в межах CompositePodGroup оцінюються та приймаються незалежно.
  • gang: забезпечує багаторівневе планування «все або нічого» для дочірніх груп. CompositePodGroup придатний для планування, лише якщо принаймні minGroupCount дочірніх груп можуть бути заплановані одночасно.
schedulingPolicy:
  gang:
    # Мінімальна кількість дочірніх груп, які повинні бути заплановані
    # одночасно, щоб ця складова група була прийнята.
    minGroupCount: 2

Встановлення складених політик через шаблони

Під час використання Workload API політики планування для CompositePodGroups визначаються всередині CompositePodGroupTemplates. Контролери робочого навантаження копіюють schedulingPolicy, вказану в шаблонах, у кожен CompositePodGroup, створений під час виконання. На відміну від кінцевих PodGroupTemplates, де minCount можна оновлювати, minGroupCount у CompositePodGroupTemplate є незмінним.

Що далі

4.7.2 - Порушення та пріоритет групи Podʼів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

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

Типи режимів порушення

Примітка:

У v1.36 поля priority або disruptionMode обʼєкта PodGroup враховуються лише під час витіснення з урахуванням робочого навантаження. Під час фази планування Podʼів планувальник не враховує поля priority або disruptionMode обʼєкта PodGroup. Це обмеження більше не застосовується у v1.37.

API підтримує два режими порушення: Single та All. Стандартний режим — Single.

Single

Режим Single вказує планувальнику розглядати всі Podʼи в групі як окремі сутності, дозволяючи незалежно порушувати роботу окремого Podʼа з PodGroup.

All

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

CompositePodGroup

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

CompositePodGroup також може оголосити disruptionMode у своїй специфікації, який визначає, як планувальник порушує роботу дочірніх груп у межах складеної групи під час подій витіснення.

API підтримує два режими порушення для CompositePodGroups:

  • Single: дозволяє незалежно порушувати роботу окремих дочірніх груп у межах CompositePodGroup під час витіснення.
  • All: забезпечує семантику порушення «все або нічого» для всієї ієрархії CompositePodGroup. Якщо будь-який Pod, що міститься в ієрархії нижче цього CompositePodGroup, має бути витіснений, усі Podʼи з усієї ієрархії мають бути витіснені.

Якщо не вказано, стандартний режим — Single.

Примітка:

У v1.37 група може встановити свій режим порушення в All і мати дочірні групи з режимом Single. У такому випадку режим All верхнього рівня перевизначає режими Single нащадків.

Така конфігурація не рекомендується через неоднозначну семантику.

Пріоритет групи Podʼів

PodGroup використовує ту ж концепцію PriorityClass, що й окремі Podʼи. Після створення одного або кількох PriorityClasses ви можете створити PodGroup, який вказує одну з цих назв PriorityClass у своїй специфікації. Контролер допуску пріоритету використовує поле priorityClassName і заповнює ціле значення пріоритету. Якщо клас пріоритету не знайдено, PodGroup відхиляється. Коли priorityClassName не встановлено для PodGroup, Kubernetes шукає стандартне значення (PriorityClass з globalDefault, встановленим у true). Якщо немає PriorityClass з globalDefault, встановленим у true, PodGroup без priorityClassName має пріоритет нуль.

Пріоритет PodGroup є авторитетним пріоритетом для всіх Podʼів у групі під час подій витіснення з урахуванням робочого навантаження. Це значення також використовується для впорядкування PodGroup у черзі планування. Якщо пріоритети окремих Podʼів, що формують цей PodGroup, відрізняються від пріоритету PodGroup, PodGroup не буде заплановано з помилкою all pods in a single pod group should have the same priority as the pod group (усі Podʼи в одній групі Podʼів повинні мати такий самий пріоритет, як і сама група Podʼів).

Коли увімкнено функціональну можливість PodGroupPreemptionPolicy, PodGroup також має поле preemptionPolicy. Це поле також береться з PriorityClass. Воно є авторитетним полем для всіх Podʼів у групі та визначає, чи може PodGroup виконувати витіснення Podʼів і груп Podʼів з нижчим пріоритетом, щоб звільнити місце для себе. Коли функціональну можливість увімкнено, усі Podʼи в PodGroup повинні мати той самий preemptionPolicy, що й PodGroup. Інакше PodGroup не буде заплановано з помилкою all pods in a single pod group should have the same preemption policy as the pod group's preemption policy (усі Podʼи в одній групі Podʼів повинні мати ту саму політику витіснення, що й політика витіснення цієї групи Podʼів). Коли PodGroup має preemptionPolicy: Never, він не виконуватиме витіснення з урахуванням робочого навантаження. Якщо функціональну можливість вимкнено, усі Podʼи, що формують PodGroup, повинні мати однаковий preemptionPolicy. Інакше PodGroup не буде заплановано з помилкою all pods in a single pod group should have the same preemption policy (усі Podʼи в одній групі Podʼів повинні мати однакову політику витіснення).

Наступний YAML є прикладом конфігурації PodGroup, який використовує PriorityClass high-priority, що відповідає цілому значенню пріоритету 1000000. Контролер допуску пріоритету перевіряє специфікацію та визначає пріоритет PodGroup як 1000000.

apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
  namespace: ns-1
  name: job-1
spec:
  priorityClassName: high-priority

Пріоритет CompositePodGroup

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

API CompositePodGroup також має поля priorityClassName та priority, і їх визначення виконується так само, як і для PodGroups, через контролер допуску пріоритету.

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

Значення пріоритету також використовується для впорядкування кореневих CompositePodGroups в активній черзі планування.

Примітка:

У v1.37 планувальник не перевіряє, чи мають некореневі групи значення пріоритету, що дорівнює пріоритету кореневого CompositePodGroup.

PreemptionPolicy у CompositePodGroup

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the PodGroupPreemptionPolicy feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

API CompositePodGroup також має поле preemptionPolicy, і його визначення виконується точно так само, як і для API PodGroup.

Значення preemptionPolicy кореневого CompositePodGroup визначає, чи може бути викликане витіснення з урахуванням робочого навантаження для розміщення його Podʼів під час планування, якщо це необхідно:

  • політика PreemptLowerPriority дозволяє витісняти жертв з нижчим пріоритетом;
  • політика Never вимикає витіснення з урахуванням робочого навантаження для цього кореневого CompositePodGroup.

Усі Podʼи в межах однієї ієрархії груп повинні мати точно таку саму політику витіснення, яка має дорівнювати політиці витіснення кореневого CompositePodGroup.

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

Примітка:

У v1.37, коли функціональну можливість увімкнено, планувальник не перевіряє, чи мають некореневі групи політику витіснення, що дорівнює політиці витіснення кореневого CompositePodGroup.

Що далі

4.7.3 - Топологічно орієнтоване планування робочих навантажень

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.36; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the TopologyAwareWorkloadScheduling feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Топологічно орієнтоване планування робочих навантажень (Topology-Aware Scheduling, TAS) є можливістю Workload API, яка оптимізує розміщення Podʼів у межах кластера.

TAS забезпечує, щоб усі Podʼи в межах PodGroup були розміщені в одному топологічному домені, наприклад, в одній серверній стійці або в одній зоні. Це мінімізує затримки між Podʼами та запобігає фрагментації робочого навантаження по інфраструктурі кластера.

Топологічно орієнтоване планування з політикою gang

Під час застосування до PodGroups з політикою планування gang TAS симулює потенційне призначення (placement, розміщення) всієї групи Podʼів одразу. Він гарантує, що принаймні вказана кількість minCount Podʼів може розміститися разом в одному топологічному домені, перш ніж виділяти ресурси. Якщо жодного здійсненного розміщення не знайдено, весь PodGroup стає непридатним для планування.

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

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

Примітка:

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

Топологічно орієнтоване планування з політикою basic

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

Якщо жодного здійсненного розміщення для всього PodGroup не знайдено, може бути запланована лише підмножина Podʼів, і вони гарантовано відповідатимуть обмеженням планування.

Якщо нові Podʼи додаються до PodGroup, у якому деякі Podʼи вже заплановані, планувальник діятиме так само, як у випадку політики gang — змушуючи нові Podʼи розміститися в тому ж домені, якщо є достатньо ресурсів (у протилежному випадку нові Podʼи залишаться в стані очікування).

Налаштування API: обмеження планування

Кожен PodGroup (або PodGroupTemplate) може за бажанням оголосити поле schedulingConstraints, яке інтерпретується алгоритмом планування PodGroup на основі розміщення. Якщо обмеження визначені в PodGroupTemplate, вони будуть скопійовані до PodGroups, що на нього посилаються.

Станом на Kubernetes v1.36 API підтримує топологічні обмеження.

Примітка:

Станом на Kubernetes v1.36 ви можете вказати лише одне топологічне обмеження для кожного PodGroup.

Топологічне обмеження

Щоб визначити топологічне обмеження для PodGroup, потрібно встановити key, який відповідає мітці вузла Kubernetes, що представляє цільовий топологічний домен (наприклад, стійку або зону). Планувальник суворо забезпечує, щоб усі Podʼи в межах PodGroup були розміщені на вузлах, які мають точно однакове значення для цієї мітки.

Ось приклад PodGroup, налаштованого з топологічним обмеженням:

apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
  name: example-podgroup
spec:
  schedulingPolicy:
    gang:
      minCount: 4
  schedulingConstraints:
    topology:
      - key: topology.example.com/rack

Багаторівневе топологічно орієнтоване планування

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

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

Такі багаторівневі вимоги до спільного розміщення можна виразити за допомогою API CompositePodGroup та вказуючи топологічні обмеження на різних рівнях ієрархії груп.

Використання API CompositePodGroup вимагає увімкнення функціональної можливості CompositePodGroup та групи API scheduling.k8s.io/v1alpha3.

Визначення багаторівневих топологічних обмежень

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

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

Kubernetes не накладає жодних суворих вимог на фізичну ієрархію топологічних міток — топологічні ключі є довільними мітками вузлів. Однак порядок, у якому ви вказуєте топологічні обмеження від батьківської до дочірньої групи, визначає порядок, у якому планувальник поділяє топологічні домени.

Примітка:

Станом на Kubernetes v1.37 ви можете вказати лише одне топологічне обмеження в кожному CompositePodGroup.

Приклад

Наступний приклад налаштовує Workload, у якому батьківський CompositePodGroupTemplate обмежує все робоче навантаження однією зоною доступності (topology.example.com/zone), тоді як два дочірні елементи PodGroupTemplate (workers та driver) обмежують свої відповідні Podʼи серверними стійками (topology.example.com/rack) у межах цієї зони:

apiVersion: scheduling.k8s.io/v1alpha3
kind: Workload
metadata:
  name: example-workload
spec:
  compositePodGroupTemplates:
  - name: root
    schedulingPolicy:
      gang:
        minGroupCount: 2
    schedulingConstraints:
      topology:
      - key: topology.example.com/zone
    podGroupTemplates:
    - name: workers
      schedulingPolicy:
        gang:
          minCount: 8
      schedulingConstraints:
        topology:
        - key: topology.example.com/rack
    - name: driver
      schedulingPolicy:
        gang:
          minCount: 1
      schedulingConstraints:
        topology:
        - key: topology.example.com/rack

Після створення обʼєкта Workload відповідні обʼєкти груп створюються таким чином:

  • Кореневий CompositePodGroup, що посилається на шаблон root.
  • Два дочірні обʼєкти PodGroup (workers та driver), кожен з яких посилається на кореневий CompositePodGroup як на свою батьківську групу.
apiVersion: scheduling.k8s.io/v1alpha3
kind: CompositePodGroup
metadata:
  name: workload-root
spec:
  workloadRef:
    workloadName: example-workload
    templateName: root
  schedulingPolicy:
    gang:
      minGroupCount: 2
  schedulingConstraints:
    topology:
    - key: topology.example.com/zone
---
apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
  name: workload-workers
spec:
  parentCompositePodGroupName: workload-root
  workloadRef:
    workloadName: example-workload
    templateName: workers
  schedulingPolicy:
    gang:
      minCount: 8
  schedulingConstraints:
    topology:
    - key: topology.example.com/rack
---
apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
  name: workload-driver
spec:
  parentCompositePodGroupName: workload-root
  workloadRef:
    workloadName: example-workload
    templateName: driver
  schedulingPolicy:
    gang:
      minCount: 1
  schedulingConstraints:
    topology:
    - key: topology.example.com/rack

Під час планування планувальник спочатку обирає зону доступності для workload-root. Потім він поділяє вузли в цій зоні за стійками, щоб знайти здійсненні розміщення на стійках для workload-workers та workload-driver у межах обраної зони.

Наприклад, розглянемо кластер із пʼятьма вузлами, позначеними таким чином:

Вузолtopology.example.com/zonetopology.example.com/rack
node-azone-1rack-1
node-bzone-1rack-1
node-czone-1rack-2
node-dzone-2rack-1
node-ezone-2rack-3

Під час обробки workload-root планувальник оцінює можливі розміщення на всіх вузлах кластера на основі топологічного ключа topology.example.com/zone:

Оцінене можливе розміщенняВузли в можливому розміщенні
zone-1node-a, node-b, node-c
zone-2node-d, node-e

Під час оцінки можливих розміщень для workload-workers планувальник поділяє лише вузли в межах розміщення, прийнятого workload-root, на основі топологічного ключа topology.example.com/rack:

Батьківське розміщенняОцінене можливе розміщенняВузли в можливому розміщенні
zone-1rack-1node-a, node-b
zone-1rack-2node-c
zone-2rack-1node-d
zone-2rack-3node-e

Можливі розміщення, згенеровані для сусіднього PodGroup workload-driver, ідентичні тим, що згенеровані для workload-workers, оскільки обидві групи вказують той самий топологічний ключ (topology.example.com/rack).

Що далі

4.7.4 - API будівельних блоків планування та бібліотека workloadbuilder

Примітиви API планування багаторазового використання, які контролери робочих навантажень, як вбудовані, так і сторонні, вбудовують у свої власні API, а також спільна бібліотека workloadbuilder, яка компілює їх в обʼєкти Workload, PodGroup та CompositePodGroup.
СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Планування з урахуванням робочого навантаження (Workload-aware Scheduling) визначає набір будівельних блоків API багаторазового використання у групі API scheduling.k8s.io. Автори контролерів вбудовують ці примітиви у свої власні API, щоб користувачі виражали намір щодо планування (групове планування, топологія, поведінка під час порушення) з узгодженою схемою в усій екосистемі, а спільна бібліотека workloadbuilder компілює цей намір в орієнтовані на планувальник обʼєкти Workload, PodGroup та CompositePodGroup.

Вбудованим споживачем цих будівельних блоків наразі є контролер Job, який вмикається функціональною можливістю WorkloadWithJob.

Будівельні блоки багаторазового використання

Будівельні блоки — це строго типізовані структури Go у групі API scheduling.k8s.io/v1alpha3. Вони призначені для авторів контролерів: контролер вбудовує ці структури у свій власний тип API як поля, а бібліотека workloadbuilder компілює їх. Назви типів дотримуються двох конвенцій: типи кінцевого рівня мають префікс WorkloadPodGroup... (наприклад, WorkloadPodGroupSchedulingPolicy), а багаторівневі варіанти — WorkloadCompositePodGroup....

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

Щоб перейняти будівельні блоки, контролер додає ті з них, які хоче підтримувати, як поля у своїх власних типах. Наприклад, API Job групує всі чотири з них в один тип, доступний у spec.scheduling:

type JobSchedulingConfiguration struct {
    SchedulingPolicy      *schedulingv1alpha3.WorkloadPodGroupSchedulingPolicy
    SchedulingConstraints *schedulingv1alpha3.WorkloadPodGroupSchedulingConstraints
    DisruptionMode        *schedulingv1alpha3.WorkloadPodGroupDisruptionMode
    ResourceClaims        []schedulingv1alpha3.WorkloadPodGroupResourceClaim
}

Інший контролер може вкладати ті самі блоки для кожного компонента багаточастинного робочого навантаження або підтримувати лише їх підмножину.

Політика планування

Блок політики планування містить ті самі політики basic та gang, що й spec.schedulingPolicy PodGroup. Див. політики планування PodGroup для пояснення, що означає кожна політика та як планувальник її застосовує.

Єдина відмінність полягає в тому, що мінімальна кількість для gang у блоці є необовʼязковою. Користувачі можуть залишити minCount невстановленим, і в цьому випадку контролер надає стандартне значення, яке має сенс для його предметної області; контролер Job, наприклад, використовує паралелізм Job.

Обмеження планування

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

Режим порушення

Блок режиму порушення визначає, чи можна порушувати роботу Podʼів групи окремо (single) або лише як єдиного цілого (all), що відповідає режимам порушення Pod та PodGroup, задокументованим у розділі Порушення та пріоритет групи Podʼів.

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

Заявки на ресурси

Блок заявок на ресурси виражає, які заявки динамічного виділення ресурсів є спільними для кожного Podʼа в групі, а не виділяються на кожен Pod окремо. Кожен запис називає заявку в межах групи та вказує або на наявний ResourceClaim, або на ResourceClaimTemplate, з якого вона генерується. Група може оголосити не більше чотирьох заявок.

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

Складені будівельні блоки

Багаторівневі контролери, які оркеструють інші контролери (наприклад, JobSet, що створює Jobs), координують групу груп. Для цього рівня API надає аналогічний набір примітивів з префіксом WorkloadCompositePodGroup... (наприклад, WorkloadCompositePodGroupSchedulingPolicy). Вони мають таку саму форму, що й блоки кінцевого рівня, за винятком того, що складена політика gang використовує minGroupCount (мінімальну кількість дочірніх груп, які мають бути придатними для планування разом) замість minCount кінцевого рівня. Збереження окремих типів кінцевого та складеного рівнів дозволяє кожному рівню ієрархії розвиватися незалежно.

Приклад: інтеграція з Job

Контролер Job є вбудованим прикладом використання цих блоків. Користувач заповнює spec.scheduling Job, а контролер компілює його в Workload та PodGroup. Див. інтеграцію з Workload API для повного маніфесту Job, стандартних значень, які застосовуються, коли spec.scheduling пропущено, та які поля ви можете змінювати після створення.

Бібліотека workloadbuilder

workloadbuilder — це спільна бібліотека Go, яка перетворює намір контролера щодо планування в орієнтовані на планувальник Workload та його обʼєкти PodGroup/CompositePodGroup під час виконання, тож контролерам не потрібно заново реалізовувати застосування стандартних значень, перевірку та компіляцію шаблонів. Вона розроблена як для вбудованих контролерів (таких як контролер Job), так і для сторонніх контролерів (таких як JobSet або Kubeflow TrainJob), які підключають її через vendor, як будь-яку іншу залежність Go. Вона постачається з k8s.io/component-helpers/scheduling/schedulingv1/workloadbuilder.

Бібліотека споживає будівельні блоки scheduling.k8s.io/v1alpha3 та компілює їх в обʼєкти Workload та PodGroup scheduling.k8s.io/v1beta1, тоді як обʼєкти CompositePodGroup залишаються scheduling.k8s.io/v1alpha3.

Як контролер її використовує

Контролер описує своє робоче навантаження як дерево вузлів WorkloadItem, по одному на кожен логічний компонент. Вузол без дочірніх елементів стає одним PodGroupTemplate, тоді як вузол з дочірніми елементами стає CompositePodGroupTemplate над ними — саме так багаторівневий контролер представляє групу груп. Кожен вузол містить:

  • стандартну конфігурацію — власні стандартні значення контролера для всього, що користувач залишає невстановленим. Саме тут контролер вирішує, наприклад, що неналаштований Job залишається на плануванні basic.
  • вхідні дані — намір користувача, взятий з API контролера. Контролер записує кожен будівельний блок разом із шляхом до поля, на якому він розташований, щоб помилки перевірки вказували на точне поле, яке встановив користувач.
  • необовʼязкові зворотні виклики, які коригують обʼєднану конфігурацію. Саме так контролер надає контекстно-залежне стандартне значення, наприклад, заповнює невстановлену мінімальну кількість gang з паралелізму Job.

Потім контролер передає це дерево до Builder і виконує чотири виклики:

  1. NewBuilder створює builder із дерева разом із назвою, простором імен та посиланням на власника для обʼєкта, який буде створено. Власник стає посиланням на контролер Workload, яке використовується для виявлення та збору сміття.
  2. Validate розвʼязує дерево та повідомляє про будь-які проблеми як список помилок полів, які контролер повертає з власної перевірки API.
  3. BuildWorkload компілює дерево в Workload. Результат кешується, тому з одного скомпільованого результату можна створити кілька PodGroups.
  4. NewPodGroup створює PodGroup під час виконання з одного зі скомпільованих шаблонів, називаючи шаблон, з якого він повинен бути створений.
builder := workloadbuilder.NewBuilder(item, opts)
if errs := builder.Validate(ctx, workloadbuilder.ValidationInput{}); len(errs) > 0 {
    // reject the request
}
workload, err := builder.BuildWorkload()
podGroup, err := builder.NewPodGroup("trainer-pg", item.Name)

Повні робочі версії цього процесу, включаючи те, як контролер Job його підключає, дивіться в прикладах у довіднику пакета.

Перевірка конфігурації планування

Validate перевіряє конфігурацію на двох рівнях:

  • Структурна перевірка самих будівельних блоків: обовʼязкові поля, діапазони значень, правило, що рівно один член обʼєднання встановлений, та незмінність. Ці перевірки походять з правил декларативної перевірки, згенерованих з типів API.
  • Перевірки політики контролера, які декларативна перевірка не може виразити: списки дозволених значень, описані нижче, та міжпольові правила, такі як відхилення режиму порушення all разом з політикою basic.

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

Відмова від декларативної перевірки

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

  • Сторонні контролери залишають декларативну перевірку увімкненою, що є стандартною поведінкою. Ніщо інше не застосовує ці структурні правила до власного ресурсу, тому один виклик Validate покриває обидва рівні.
  • Вбудовані контролери встановлюють DisableDeclarativeValidation, оскільки API-сервер вже виконує декларативну перевірку вбудованих блоків під час перевірки батьківського обʼєкта. Пропуск першого рівня дозволяє уникнути перевірки тих самих полів двічі, залишаючи Validate виконувати лише перевірки політики контролера.

Вибір опцій планування

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

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

builder := workloadbuilder.NewBuilder(item, workloadbuilder.BuildOptions{
    Owner:                  owner,
    AllowedPolicies:        []workloadbuilder.SchedulingPolicyOption{workloadbuilder.BasicPolicy, workloadbuilder.GangPolicy},
    AllowedDisruptionModes: []workloadbuilder.DisruptionModeOption{workloadbuilder.SingleMode, workloadbuilder.AllMode},
})
allErrs := builder.Validate(ctx, workloadbuilder.ValidationInput{})

Створення PodGroups з наявного Workload

Коли Workload вже існує, незалежно від того, чи він скомпільований батьківським контролером, чи створений вручну, дочірній контролер, який керує лише PodGroup під час виконання, використовує натомість NewBuilderFromExistingWorkload. Цей builder створює обʼєкти PodGroup з наданого Workload, використовуючи власне посилання на власника. Він нічого не перевіряє та не компілює, тому наявний Workload ніколи не перекомпілюється.

builder := workloadbuilder.NewBuilderFromExistingWorkload(parentWorkload, workloadbuilder.BuildOptions{Owner: owner})
podGroup, err := builder.NewPodGroup("trainer-pg", "trainer-pgt-0")

Що далі

4.8 - PodGroup API

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

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

Що таке PodGroup?

Ресурс API PodGroup є частиною групи API scheduling.k8s.io/v1beta1, і ваш кластер повинен мати цю групу API увімкненою, а також функціональну можливість GenericWorkload, перш ніж ви зможете використовувати цей API.

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

Структура API

PodGroup складається з розділів spec, який визначає бажану поведінку планування, та status, який відображає поточний стан планування.

Політика планування

Кожен PodGroup містить політику планування (basic або gang) у spec.schedulingPolicy. Коли контролер робочого навантаження створює PodGroup, ця політика копіюється з PodGroupTemplate обʼєкта Workload під час створення. Для автономних PodGroups ви встановлюєте політику безпосередньо.

spec:
  schedulingPolicy:
    gang:
      minCount: 4

Посилання на шаблон

Необовʼязкове поле spec.podGroupTemplateRef повʼязує PodGroup з PodGroupTemplate у Workload, з якого його було створено. Це корисно для спостереження та інструментів.

spec:
  podGroupTemplateRef:
    workload:
      workloadName: training-policy
      podGroupTemplateName: worker

Пріоритет і режим порушення

Кожен PodGroup також може визначати власні spec.priority, spec.preemptionPolicy та spec.disruptionMode. Пріоритет і політика витіснення встановлюються за допомогою поля spec.priorityClassName, яке вказує на ресурс PriorityClass. Див. Порушення та пріоритет групи Podʼів для детального опису цих полів.

Коли контролер робочого навантаження створює PodGroup, ці поля копіюються з PodGroupTemplate обʼєкта Workload під час створення. Для автономних PodGroups ви встановлюєте поля безпосередньо.

spec:
  disruptionMode:
    all: {}
  priorityClassName: high-priority

Запит пристроїв DRA для PodGroup

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the DRAWorkloadResourceClaims feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Пристрої, доступні через Динамічне виділення ресурсів (DRA), можуть бути запитані PodGroup через поле spec.resourceClaims:

apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
  name: training-group
  namespace: some-ns
spec:
  ...
  resourceClaims:
  - name: pg-claim
    resourceClaimName: my-pg-claim
  - name: pg-claim-template
    resourceClaimTemplateName: my-pg-template

ResourceClaims, повʼязані з PodGroups, можуть бути спільно використані всіма Podʼами, що належать до групи. Замість того, щоб мати посилання на кожен окремий Pod у status.reservedFor ResourceClaim, достатньо посилання на PodGroup, і будь-яка кількість Podʼів у тому самому PodGroup може спільно використовувати ResourceClaim. ResourceClaims також можуть бути створені з ResourceClaimTemplates для кожного PodGroup, що дозволяє пристроям, виділеним для кожного створеного ResourceClaim, бути спільно використаними Podʼами в кожному PodGroup.

За докладною інформацією та більш повним прикладом звертайтесь до документації DRA.

Статус

Планувальник оновлює status.conditions, щоб повідомити, чи група була успішно запланована. Основним станом є PodGroupInitiallyScheduled, який є True, коли всі необхідні Podʼи були розміщені, і False, коли планування не вдається.

Примітка:

Стан PodGroupInitiallyScheduled відображає лише початкове рішення планувальника. Планувальник не оновлює її, якщо Podʼи пізніше зазнають збою або будуть виселені. Детальніше див. у розділі Обмеження.

Див. сторінку Життєвий цикл PodGroup для повного списку станів та причин.

Створення PodGroup

Ресурс API PodGroup є частиною групи API scheduling.k8s.io/v1beta1 (і ваш кластер повинен мати цю групу API увімкненою, а також функціональну можливість GenericWorkload, перш ніж ви зможете використовувати цей API).

Наступний маніфест створює PodGroup з політикою планування gang, яка вимагає, щоб щонайменше 4 Podʼи були придатними для планування одночасно:

apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
  name: training-worker-0
  namespace: default
spec:
  schedulingPolicy:
    gang:
      minCount: 4

Ви можете переглянути PodGroups у вашому кластері:

kubectl get podgroups

Щоб побачити повний статус, включаючи стани планування:

kubectl describe podgroup training-worker-0

Як це все поєднується

Взаємозвʼязок між контролерами, Workloads, PodGroups та Podʼами побудовано за такою схемою:

  1. Контролер робочого навантаження створює Workload, який визначає PodGroupTemplates з політиками планування.
  2. Для кожного екземпляра часу виконання контролер створює PodGroup з одного з PodGroupTemplates обʼєкта Workload.
  3. Контролер створює Podʼи, які посилаються на PodGroup через поле spec.schedulingGroup.podGroupName.

Контролер Job є єдиним вбудованим контролером робочого навантаження, який наразі дотримується цієї схеми. Власні контролери можуть реалізувати той самий процес для своїх власних типів робочих навантажень.

apiVersion: scheduling.k8s.io/v1beta1
kind: Workload
metadata:
  name: training-policy
spec:
  podGroupTemplates:
  - name: worker
    schedulingPolicy:
      gang:
        minCount: 4
---
apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
  name: training-worker-0
spec:
  podGroupTemplateRef:
    workload:
      workloadName: training-policy
      podGroupTemplateName: worker
  schedulingPolicy:
    gang:
      minCount: 4
---
apiVersion: v1
kind: Pod
metadata:
  name: worker-0
spec:
  schedulingGroup:
    podGroupName: training-worker-0
  containers:
  - name: ml-worker
    image: training:v1

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

Батьківська група

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість CompositePodGroup увімкнено, PodGroup може діяти як кінцевий вузол у багаторівневій ієрархії груп, що складається з PodGroups та CompositePodGroups. PodGroup може вказати свою батьківську групу за допомогою поля spec.parentCompositePodGroupName. Організація робочого навантаження в ієрархію груп може використовуватися для вираження багаторівневих вимог групового планування, багаторівневих обмежень топології та спільної долі під час порушення для частин робочого навантаження.

Для отримання додаткових відомостей про ієрархічні структури груп та багаторівневе групове планування див. огляд CompositePodGroup API.

Примітка:

PodGroup, який вказує spec.parentCompositePodGroupName, також повинен вказувати spec.workloadRef, повʼязуючи PodGroup з його шаблоном у Workload. Автономним обʼєктам PodGroup (створеним без spec.workloadRef) не дозволяється вказувати батьківський CompositePodGroup.

Що далі

4.8.1 - Життєвий цикл PodGroup

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

PodGroup планується як єдине ціле і захищений від передчасного видалення, поки його Podʼи все ще працюють.

Володіння та життєвий цикл

PodGroups належать контролеру робочого навантаження, який їх створив (наприклад, Job), через стандартні ownerReferences. Коли обʼєкт-власник видаляється, PodGroups автоматично видаляються збирачем сміття.

Назви PodGroup повинні бути унікальними в межах простору імен і бути дійсними DNS-піддоменами.

Порядок створення

Контролери повинні створювати обʼєкти в такому порядку:

  1. Workload — шаблон політики планування.
  2. PodGroup — екземпляр під час виконання.
  3. Pods — з spec.schedulingGroup.podGroupName, що вказує на PodGroup.

Якщо PodGroup включає podGroupTemplateRef, який вказує на Workload, що не існує (або видаляється), API-сервер відхиляє запит на створення PodGroup. Вказаний Workload повинен існувати перед створенням PodGroup.

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

Захист від видалення

PodGroup не може бути повністю видалено, поки будь-які його Podʼи все ще працюють. Спеціальний завершувач забезпечує блокування видалення, поки всі Pods, що посилаються на PodGroup, не досягнуть термінальної фази (Succeeded або Failed).

PodGroups, керовані контролером та користувачем

У більшості випадків контролери робочих навантажень (наприклад, Job) створюють PodGroups автоматично (керовані контролером). Контролер визначає podGroupName для кожного Podʼа під час створення, аналогічно тому, як DaemonSet встановлює спорідненість вузлів для кожного Podʼа.

Якщо вам потрібно більше контролю над іменуванням та життєвим циклом, ви можете безпосередньо створювати обʼєкти PodGroup та самостійно встановлювати spec.schedulingGroup.podGroupName у своїх шаблонах Podʼів (керовані користувачем). Це надає вам повний контроль над створенням та іменуванням PodGroup.

Обмеження

  • Всі Podʼи в PodGroup повинні використовувати той самий .spec.schedulerName. Якщо виявлено невідповідність, планувальник відхиляє всі Podʼи в групі як такі, що не можуть бути заплановані.
  • Всі Podʼи в PodGroup повинні використовувати той самий .spec.priority. Якщо виявлено невідповідність, планувальник відхиляє всі Podʼи в групі як такі, що не можуть бути заплановані.
  • Всі Podʼи в PodGroup повинні використовувати той самий .spec.preemptionPolicy. Якщо виявлено невідповідність, планувальник відхиляє всі Podʼи в групі як такі, що не можуть бути заплановані.
  • Поле spec.schedulingGroup у Podʼа є незмінним. Після встановлення Pod не може перейти до іншого PodGroup.
  • Максимальна кількість PodGroupTemplates в одному Workload становить 8.
  • Планувальник не оновлює інформацію про статус щодо поточного робочого стану PodGroup або подальших спроб планування після початкового розміщення. Отже, статус PodGroup не оновлюється, коли наявні Podʼи зазнають збою, виселяються або завершуються, або коли нововиявлені Podʼи не можуть бути заплановані.

Що далі

4.9 - CompositePodGroup API

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

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

Що таке CompositePodGroup?

Ресурс API CompositePodGroup є частиною групи API scheduling.k8s.io/v1alpha3. Ваш кластер повинен мати цю групу API увімкненою, а також функціональну можливість CompositePodGroup, перш ніж ви зможете використовувати цей API.

CompositePodGroup представляє групування дочірніх груп (які можуть бути обʼєктами CompositePodGroup або PodGroup). Він містить політики планування, режими порушення, налаштування пріоритету та необовʼязкові топологічні обмеження, які застосовуються колективно до його дочірніх груп.

Структура API

CompositePodGroup складається з розділу spec, який визначає бажану поведінку планування для його дочірніх груп, та субресурсу status.

Політика планування

Кожен CompositePodGroup містить політику планування (basic або gang) у spec.schedulingPolicy. Коли контролер робочого навантаження створює CompositePodGroup, ця політика копіюється з CompositePodGroupTemplate обʼєкта Workload під час створення.

Для політики gang на CompositePodGroup поле minGroupCount визначає мінімальну кількість дочірніх груп, які мають бути придатними для планування одночасно:

spec:
  schedulingPolicy:
    gang:
      minGroupCount: 2

Посилання на батьківську групу

Некореневі ресурси CompositePodGroup вказують свою батьківську групу за допомогою spec.parentCompositePodGroupName. Кореневі обʼєкти CompositePodGroup залишають це поле невстановленим.

spec:
  parentCompositePodGroupName: root-group-0

Посилання на робоче навантаження

Поле spec.workloadRef повʼязує CompositePodGroup з CompositePodGroupTemplate в обʼєкті Workload, з якого він був створений.

spec:
  workloadRef:
    workloadName: hierarchical-workload
    templateName: replica-group

Статус

Схема API CompositePodGroup включає субресурс status. В альфа-версії поле status присутнє в типі API, але kube-scheduler не оновлює та не заповнює стани статусу для обʼєктів CompositePodGroup. Відстеження статусу для складених груп буде реалізовано в майбутніх випусках.

Створення CompositePodGroup

Контролери робочого навантаження створюють обʼєкти CompositePodGroup автоматично з шаблонів Workload під час виконання.

Наступний маніфест створює кореневий CompositePodGroup з політикою планування gang, яка вимагає, щоб щонайменше 2 дочірні групи були придатними для планування одночасно:

apiVersion: scheduling.k8s.io/v1alpha3
kind: CompositePodGroup
metadata:
  name: root-group-0
  namespace: default
spec:
  workloadRef:
    workloadName: hierarchical-workload
    templateName: root
  schedulingPolicy:
    gang:
      minGroupCount: 2

Ви можете переглянути ресурси CompositePodGroup у вашому кластері:

kubectl get compositepodgroups

Щоб переглянути деталі конкретної складеної групи:

kubectl describe compositepodgroup root-group-0

Як це все поєднується

Взаємозвʼязок між контролерами, Workloads, CompositePodGroups, PodGroups та Podʼами побудовано за такою схемою:

  1. Контролер робочого навантаження створює Workload, який визначає дерево CompositePodGroupTemplates та кінцевих PodGroupTemplates.
  2. Для кожного екземпляра часу виконання контролер створює кореневий CompositePodGroup, дочірні обʼєкти CompositePodGroup та кінцеві обʼєкти PodGroup у порядку зверху вниз.
  3. Контролер створює Pods, які посилаються на свій кінцевий PodGroup через spec.schedulingGroup.podGroupName.

Наступний приклад ілюструє повну ієрархію маніфестів для дворівневого робочого навантаження:

apiVersion: scheduling.k8s.io/v1alpha3
kind: Workload
metadata:
  name: hierarchical-workload
  namespace: default
spec:
  compositePodGroupTemplates:
  - name: root
    schedulingPolicy:
      gang:
        minGroupCount: 2
    podGroupTemplates:
    - name: workers-a
      schedulingPolicy:
        gang:
          minCount: 4
    - name: workers-b
      schedulingPolicy:
        gang:
          minCount: 4
---
apiVersion: scheduling.k8s.io/v1alpha3
kind: CompositePodGroup
metadata:
  name: root-group-0
  namespace: default
spec:
  workloadRef:
    workloadName: hierarchical-workload
    templateName: root
  schedulingPolicy:
    gang:
      minGroupCount: 2
---
apiVersion: scheduling.k8s.io/v1alpha3
kind: PodGroup
metadata:
  name: workers-a-0
  namespace: default
spec:
  parentCompositePodGroupName: root-group-0
  workloadRef:
    workloadName: hierarchical-workload
    templateName: workers-a
  schedulingPolicy:
    gang:
      minCount: 4
---
apiVersion: scheduling.k8s.io/v1alpha3
kind: PodGroup
metadata:
  name: workers-b-0
  namespace: default
spec:
  parentCompositePodGroupName: root-group-0
  workloadRef:
    workloadName: hierarchical-workload
    templateName: workers-b
  schedulingPolicy:
    gang:
      minCount: 4
---
apiVersion: v1
kind: Pod
metadata:
  name: worker-a-0
  namespace: default
spec:
  schedulingGroup:
    podGroupName: workers-a-0
  containers:
  - name: worker
    image: registry.k8s.io/pause:3.9

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

Що далі

4.9.1 - Життєвий цикл CompositePodGroup

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

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

Володіння та збір сміття

Обʼєкт CompositePodGroup разом зі своїми дочірніми ресурсами CompositePodGroup та PodGroup належить контролеру робочого навантаження, який його створив, через ownerReferences Kubernetes. Коли обʼєкт-власник робочого навантаження видаляється, каскадний збір сміття автоматично видаляє повʼязану ієрархію груп.

Назви CompositePodGroup повинні бути унікальними в межах простору імен і бути дійсними DNS-піддоменами.

Порядок створення

Щоб забезпечити правильне розвʼязання ієрархії та планування, контролери робочого навантаження створюють ресурси в порядку зверху вниз:

  1. Workload: визначає статичні шаблони (CompositePodGroupTemplates та PodGroupTemplates).
  2. Кореневий CompositePodGroup: створюється з spec.workloadRef, що вказує на кореневий шаблон у Workload.
  3. Дочірні CompositePodGroups та PodGroups: створюються зверху вниз. Кожна дочірня група вказує свого батька за допомогою spec.parentCompositePodGroupName, а свій шаблон — за допомогою spec.workloadRef.
  4. Pods: створюються з spec.schedulingGroup.podGroupName, що вказує на їхній кінцевий PodGroup.

Якщо група посилається на батьківський CompositePodGroup, який не існує, або якщо Pod посилається на PodGroup, який ще не створено, планувальник відкладає планування, доки всі батьківські ресурси в ієрархії не існуватимуть.

Обмеження та правила перевірки

  • Узгоджена назва планувальника: всі Podʼи в усій ієрархії CompositePodGroup повинні використовувати той самий spec.schedulerName. Якщо виявлено невідповідність, планувальник відхиляє ієрархію як непридатну для планування.
  • Узгоджений пріоритет: всі Podʼи в усій ієрархії CompositePodGroup повинні вказувати те саме значення spec.priority, яке має дорівнювати пріоритету, вказаному кореневою групою. Якщо виявлено невідповідність, планувальник відхиляє ієрархію як непридатну для планування.
  • Узгоджена політика витіснення: всі Podʼи в усій ієрархії CompositePodGroup повинні використовувати той самий spec.preemptionPolicy. Крім того, коли функціональну можливість PodGroupPreemptionPolicy увімкнено, політика витіснення кореневої групи повинна дорівнювати політиці, вказаній Podʼами. Якщо виявлено невідповідність, планувальник відхиляє ієрархію як непридатну для планування.
  • Максимальна глибина вкладеності: ієрархія шаблонів груп підтримує максимальну глибину 4 рівні.
  • Обмеження списку: максимальна кількість дочірніх CompositePodGroupTemplates та PodGroupTemplates на будь-якому рівні Workload становить 8.
  • Незмінна кількість груп gang: поле spec.schedulingPolicy.gang.minGroupCount на CompositePodGroup є незмінним після створення.
  • Незмінні посилання на ієрархію: spec.parentCompositePodGroupName на групах та spec.schedulingGroup на Podʼах є незмінними після встановлення.

Що далі

5 - Services, балансування навантаження та мережа

Концепції та ресурси, що лежать в основі мережі в Kubernetes.

Мережева модель Kubernetes

Мережева модель Kubernetes складається з кількох частин:

  • Кожен Pod у кластері отримує власну унікальну в межах кластера IP-адресу.

    • Pod має власний приватний мережевий простір імен, який спільно використовують усі контейнери в цьому Podʼі. Процеси, що працюють у різних контейнерах одного Podʼа, можуть взаємодіяти між собою через localhost.
  • Мережа Podʼів (також називається мережею кластера) забезпечує взаємодію між Podʼами. Вона гарантує, що (за відсутності навмисної сегментації мережі):

    • Усі Podʼи можуть взаємодіяти з усіма іншими Podʼами, незалежно від того, чи перебувають вони на одному вузлі, чи на різних. Podʼи можуть взаємодіяти безпосередньо, без використання проксі-серверів чи трансляції адрес (NAT).

      У Windows це правило не застосовується до Podʼів із мережею хоста.

    • Агенти на вузлі (такі як системні демони чи kubelet) можуть взаємодіяти з усіма Podʼами на цьому вузлі.

  • API Service дозволяє надати стабільну (довговічну) IP-адресу або імʼя хоста для сервісу, реалізованого одним або кількома Podʼами-бекендами, при цьому окремі Podʼи, що складають сервіс, можуть змінюватися з часом.

    • Kubernetes автоматично керує обʼєктами EndpointSlice, щоб надавати інформацію про Podʼи, які наразі обслуговують Service.

    • Реалізація проксі сервісів відстежує набір обʼєктів Service та EndpointSlice і програмує площину даних для маршрутизації трафіку сервісу до його бекендів, використовуючи API операційної системи або хмарного провайдера для перехоплення чи переписування пакетів.

  • API Gateway (або його попередник Ingress) дозволяє зробити Services доступними для клієнтів поза кластером.

    • Простіший, але менш гнучкий механізм вхідного трафіку кластера доступний через type: LoadBalancer в API Service, якщо ви використовуєте підтримуваного хмарного провайдера.
  • NetworkPolicy — це вбудований API Kubernetes, який дозволяє контролювати трафік між Podʼами або між Podʼами та зовнішнім світом.

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

Лише кілька частин цієї моделі реалізовані безпосередньо в Kubernetes. Для решти Kubernetes визначає API, але відповідну функціональність надають зовнішні компоненти, деякі з яких є необовʼязковими:

  • Налаштування мережевого простору імен Podʼа виконує системне програмне забезпечення, яке реалізує Container Runtime Interface.

  • Мережею Podʼів керує реалізація мережі Podʼів. У Linux більшість середовищ виконання контейнерів використовують Container Networking Interface (CNI), щоб взаємодіяти з реалізацією мережі Podʼів, тому ці реалізації часто називають втулками CNI.

  • Kubernetes надає стандартну реалізацію проксіювання сервісів, яка називається kube-proxy, але деякі реалізації мережі Podʼів використовують власний проксі сервісів, тісніше інтегрований з рештою реалізації.

  • NetworkPolicy зазвичай також реалізується реалізацією мережі Podʼів. (Деякі простіші реалізації мережі Podʼів не реалізують NetworkPolicy, або адміністратор може налаштувати мережу Podʼів без підтримки NetworkPolicy. У таких випадках API буде присутній, але не матиме жодного ефекту.)

  • Існує багато реалізацій Gateway API: деякі специфічні для певних хмарних середовищ, деякі більше орієнтовані на середовища bare metal, а інші є більш універсальними.

Що далі

Навчальний матеріал Підключення застосунків за допомогою Services дає змогу дізнатися про Services та мережу Kubernetes на практичному прикладі.

Стаття Мережа кластера пояснює, як налаштувати мережу для вашого кластера, а також надає огляд задіяних технологій.

Щоб дізнатися про конкретні концепції мережі, дивіться:

  • Service — надання доступу до застосунку через одну зовнішню точку доступу
  • Ingress — маршрутизація HTTP/HTTPS з урахуванням протоколу за допомогою URI, імен хостів та шляхів
  • Gateway API — динамічне надання інфраструктури та розширена маршрутизація трафіку
  • Network Policies — контроль потоку трафіку на рівні IP-адрес або портів (рівень OSI 3 або 4)
  • DNS для Services та Podʼів — виявлення сервісів у вашому кластері за допомогою DNS

5.1 - Service

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

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

Ключова мета Services у Kubernetes полягає в тому, що вам не потрібно змінювати наявний застосунок, щоб використовувати незнайомий механізм виявлення сервісів. Ви можете запускати код у Podʼах, незалежно від того, чи розроблений він для світу cloud native, чи це старіший застосунок, який ви контейнеризували. Ви використовуєте Service, щоб зробити цей набір Podʼів доступним у мережі, щоб клієнти могли з ним взаємодіяти.

Якщо ви використовуєте Deployment для запуску застосунку, цей Deployment може динамічно створювати та знищувати Podʼи. У будь-який момент ви не знаєте, скільки з цих Podʼів працюють і є справними; ви навіть можете не знати, як називаються ці справні Podʼи. Podʼи Kubernetes створюються та знищуються відповідно до бажаного стану вашого кластера. Podʼи є ефемерними ресурсами (не варто очікувати, що окремий Pod буде надійним і довговічним).

Кожен Pod отримує власну IP-адресу (Kubernetes очікує, що це забезпечать мережеві втулки). Для певного Deployment у вашому кластері набір Podʼів, що працює в один момент часу, може відрізнятися від набору Podʼів, що виконує цей застосунок мить потому.

Це призводить до проблеми: якщо певний набір Podʼів (назвемо їх «бекендами») надає функціональність іншим Podʼам (назвемо їх «фронтендами») всередині вашого кластера, як фронтенди дізнаються та відстежують, до якої IP-адреси підключатися, щоб фронтенд міг використовувати бекенд-частину робочого навантаження?

Тут на допомогу приходять Services.

Services у Kubernetes

API Service, що є частиною Kubernetes, — це абстракція, яка допомагає відкривати доступ до груп Podʼів через мережу. Кожен обʼєкт Service визначає логічний набір точок доступу (зазвичай ці точки доступу є Podʼами) разом із політикою того, як зробити ці Podʼи доступними.

Наприклад, розгляньмо stateless-бекенд для обробки зображень, який працює з 3 репліками. Ці репліки взаємозамінні: фронтендам не важливо, який бекенд вони використовують. Хоча конкретні Podʼи, що утворюють набір бекендів, можуть змінюватися, клієнтам-фронтендам не потрібно про це знати чи самостійно відстежувати набір бекендів.

Абстракція Service робить можливим таке відокремлення.

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

Набір Podʼів, на які спрямований Service, зазвичай визначається селектором, який ви задаєте. Щоб дізнатися про інші способи визначення точок доступу Service, дивіться Services без селекторів.

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

API Gateway для Kubernetes надає додаткові можливості порівняно з Ingress і Service. Ви можете додати Gateway до свого кластера — це сімейство API розширення, реалізованих за допомогою CustomResourceDefinitions, — а потім використовувати їх для налаштування доступу до мережевих сервісів, що працюють у вашому кластері.

Виявлення сервісів у стилі cloud native

Якщо ви можете використовувати API Kubernetes для виявлення сервісів у своєму застосунку, ви можете надсилати запити до API-сервера щодо відповідних EndpointSlices. Kubernetes оновлює EndpointSlices для Service щоразу, коли змінюється набір Podʼів у цьому Service.

Для ненативних застосунків Kubernetes пропонує способи розмістити мережевий порт або балансувальник навантаження між вашим застосунком і бекенд-Podʼами.

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

Визначення Service

Service — це обʼєкт (так само, як Pod чи ConfigMap). Ви можете створювати, переглядати або змінювати визначення Service за допомогою API Kubernetes. Зазвичай для цих викликів API ви використовуєте інструмент, такий як kubectl.

Наприклад, припустимо, що у вас є набір Podʼів, кожен з яких слухає TCP-порт 9376 і має мітку app.kubernetes.io/name=MyApp. Ви можете визначити Service, щоб опублікувати цього слухача TCP:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Застосування цього маніфесту створює новий Service з назвою "my-service" зі стандартним типом Service ClusterIP. Service спрямовує трафік на TCP-порт 9376 будь-якого Podʼа з міткою app.kubernetes.io/name: MyApp.

Kubernetes призначає цьому Service IP-адресу (кластерний IP), яка використовується механізмом віртуальних IP-адрес. Докладніше про цей механізм читайте у Віртуальні IP та проксі для Service.

Контролер цього Service постійно шукає Podʼи, що відповідають його селектору, а потім вносить необхідні оновлення до набору EndpointSlices цього Service.

Назва обʼєкта Service повинна бути дійсною назвою мітки RFC 1123.

Примітка:

Service може зіставляти будь-який вхідний port із targetPort. Стандартно та для зручності targetPort має те саме значення, що й поле port.

Визначення портів

Визначення портів у Podʼах мають назви, і ви можете посилатися на ці назви в атрибуті targetPort Service. Наприклад, ми можемо привʼязати targetPort Service до порту Podʼа таким чином:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

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

Стандартним протоколом для Services є TCP; ви також можете використовувати будь-який інший підтримуваний протокол.

Оскільки багатьом Services потрібно відкривати більше ніж один порт, Kubernetes підтримує кілька визначень портів для одного Service. Кожне визначення порту може мати той самий protocol або інший.

Services без селекторів

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

Наприклад:

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

У будь-якому з цих сценаріїв ви можете визначити Service без зазначення селектора для вибору Podʼів. Наприклад:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376

Оскільки цей Service не має селектора, відповідні обʼєкти EndpointSlice не створюються автоматично. Ви можете зіставити Service з мережевою адресою та портом, де він працює, додавши обʼєкт EndpointSlice вручну. Наприклад:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-1 # за домовленістю використовуйте назву Service
                     # як префікс для назви EndpointSlice
  labels:
    # Ви повинні встановити мітку "kubernetes.io/service-name".
    # Встановіть її значення відповідно до назви Service
    kubernetes.io/service-name: my-service
addressType: IPv4
ports:
  - name: http # має збігатися з назвою порту Service, визначеного вище
    appProtocol: http
    protocol: TCP
    port: 9376
endpoints:
  - addresses:
      - "10.4.5.6"
  - addresses:
      - "10.1.2.3"

Власні EndpointSlices

Коли ви створюєте обʼєкт EndpointSlice для Service, ви можете використовувати будь-яку назву для EndpointSlice. Кожен EndpointSlice у просторі імен повинен мати унікальну назву. Ви повʼязуєте EndpointSlice із Service, встановлюючи на цьому EndpointSlice мітку kubernetes.io/service-name.

Примітка:

IP-адреси точок доступу не повинні бути: loopback-адресами (127.0.0.0/8 для IPv4, ::1/128 для IPv6) або link-local-адресами (169.254.0.0/16 та 224.0.0.0/24 для IPv4, fe80::/64 для IPv6).

IP-адреси точок доступу не можуть бути кластерними IP-адресами інших Services Kubernetes, оскільки kube-proxy не підтримує віртуальні IP-адреси як призначення.

Для EndpointSlice, який ви створюєте самостійно або у власному коді, також слід вибрати значення для мітки endpointslice.kubernetes.io/managed-by. Якщо ви створюєте власний код контролера для керування EndpointSlices, розгляньте можливість використання значення на кшталт "my-domain.example/name-of-controller". Якщо ви використовуєте сторонній інструмент, використовуйте назву інструмента в нижньому регістрі, замінивши пробіли та інші знаки пунктуації дефісами (-). Якщо люди безпосередньо використовують інструмент, такий як kubectl, для керування EndpointSlices, використовуйте назву, яка описує це ручне керування, наприклад, "staff" або "cluster-admins". Уникайте використання зарезервованого значення "controller", яке позначає EndpointSlices, керовані власною панеллю управління Kubernetes.

Доступ до Service без селектора

Доступ до Service без селектора працює так само, як якби він мав селектор. У прикладі Service без селектора трафік спрямовується до однієї з двох точок доступу, визначених у маніфесті EndpointSlice: TCP-зʼєднання до 10.1.2.3 або 10.4.5.6 на порт 9376.

Примітка:

API-сервер Kubernetes не дозволяє проксіювання до точок доступу, не повʼязаних із Podʼами. Дії на кшталт kubectl port-forward service/<service-name> forwardedPort:servicePort, де Service не має селектора, завершаться збоєм через це обмеження. Це запобігає використанню API-сервера Kubernetes як проксі до точок доступу, до яких викликач може не мати дозволу на доступ.

Service ExternalName — це особливий випадок Service, який не має селекторів і натомість використовує DNS-імена. Докладнішу інформацію дивіться в розділі ExternalName.

EndpointSlices

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.21

EndpointSlices — це обʼєкти, які представляють підмножину (зріз) мережевих точок доступу, що стоять за Service.

Ваш кластер Kubernetes відстежує, скільки точок доступу представляє кожен EndpointSlice. Якщо для Service стільки точок доступу, що досягається поріг, Kubernetes додає ще один порожній EndpointSlice і зберігає там інформацію про нові точки доступу. Стандартно Kubernetes створює новий EndpointSlice, коли всі наявні EndpointSlices містять щонайменше 100 точок доступу. Kubernetes не створює новий EndpointSlice, доки не потрібно додати ще одну точку доступу.

Докладнішу інформацію про цей API дивіться в EndpointSlices.

Endpoints (застаріло)

СТАН ФУНКЦІОНАЛУ: Deprecated since Kubernetes v1.33

API EndpointSlice є розвитком старішого API Endpoints. Застарілий API Endpoints має кілька проблем порівняно з EndpointSlice:

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

Через це рекомендується, щоб усі клієнти використовували API EndpointSlice, а не Endpoints.

Точки доступу понад місткість

Kubernetes обмежує кількість точок доступу, які можуть уміститися в одному обʼєкті Endpoints. Коли в Service понад 1000 точок доступу, Kubernetes обрізає дані в обʼєкті Endpoints. Оскільки Service можна повʼязати з кількома EndpointSlices, обмеження в 1000 точок доступу стосується лише застарілого API Endpoints.

У цьому випадку Kubernetes вибирає не більше 1000 можливих точок доступу бекенда для збереження в обʼєкті Endpoints і встановлює на Endpoints анотацію endpoints.kubernetes.io/over-capacity: truncated. Панель управління також вилучає цю анотацію, якщо кількість бекенд-Podʼів падає нижче 1000.

Трафік і далі надсилається на бекенди, але будь-який механізм балансування навантаження, який покладається на застарілий API Endpoints, надсилає трафік щонайбільше до 1000 доступних точок доступу.

Те саме обмеження API означає, що ви не можете вручну оновити Endpoints так, щоб він мав понад 1000 точок доступу.

Протокол застосунку

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.20

Поле appProtocol дає змогу вказати протокол застосунку для кожного порту Service. Воно використовується як підказка для реалізацій, щоб вони могли запропонувати розширену поведінку для протоколів, які розуміють. Значення цього поля дзеркально відтворюється у відповідних обʼєктах Endpoints та EndpointSlice.

Це поле відповідає стандартному синтаксису міток Kubernetes. Дійсні значення — одне з:

ПротоколОпис
kubernetes.io/h2cHTTP/2 без шифрування, як описано в RFC 9113
kubernetes.io/wsWebSocket без шифрування, як описано в RFC 6455
kubernetes.io/wssWebSocket через TLS, як описано в RFC 6455

Services з кількома портами

Для деяких Services вам потрібно відкрити більше одного порту. Kubernetes дозволяє налаштувати кілька визначень портів в обʼєкті Service. Під час використання кількох портів для Service ви повинні надати назви всім портам, щоб вони були однозначними. Наприклад:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

Примітка:

Як і назви в Kubernetes загалом, назви портів мають містити лише буквено-цифрові символи в нижньому регістрі та -. Назви портів також мають починатися й закінчуватися буквено-цифровим символом.

Наприклад, назви 123-abc та web є допустимими, а 123_abc та -web — ні.

Тип Service

Для деяких частин вашого застосунку (наприклад, фронтендів) вам може знадобитися відкрити доступ до Service на зовнішній IP-адресі, яка доступна поза вашим кластером.

Типи Service у Kubernetes дозволяють вказати, який саме Service вам потрібен.

Доступні значення type та їхня поведінка:

ClusterIP
Відкриває доступ до Service на внутрішній IP-адресі кластера. Вибір цього значення робить Service доступним лише зсередини кластера. Це стандартне значення, яке використовується, якщо ви явно не вказали type для Service. Ви можете відкрити доступ до Service із загальнодоступного інтернету за допомогою Ingress або Gateway.
NodePort
Відкриває доступ до Service на IP-адресі кожного вузла на статичному порту (NodePort). Щоб зробити порт вузла доступним, Kubernetes налаштовує кластерну IP-адресу так само, як якби ви запросили Service з type: ClusterIP.
LoadBalancer
Відкриває зовнішній доступ до Service за допомогою зовнішнього балансувальника навантаження. Kubernetes не надає компонент балансування навантаження безпосередньо; ви повинні надати його самі або інтегрувати свій кластер Kubernetes із хмарним провайдером.
ExternalName
Зіставляє Service із вмістом поля externalName (наприклад, з іменем хоста api.foo.bar.example). Це зіставлення налаштовує DNS-сервер вашого кластера на повернення запису CNAME із цим зовнішнім іменем хоста. Жодне проксіювання не налаштовується.

Поле type в API Service розроблено як вкладену функціональність: кожен рівень доповнює попередній. Однак із цього вкладеного дизайну є виняток. Ви можете визначити Service LoadBalancer, вимкнувши виділення NodePort для балансувальника навантаження.

type: ClusterIP

Цей стандартний тип Service призначає IP-адресу з пулу IP-адрес, який ваш кластер зарезервував для цієї мети.

Кілька інших типів Service базуються на типі ClusterIP.

Якщо ви визначаєте Service, у якому .spec.clusterIP має значення "None", Kubernetes не призначає IP-адресу. Докладнішу інформацію дивіться в розділі headless Services.

Вибір власної IP-адреси

Ви можете вказати власну кластерну IP-адресу в запиті на створення Service. Для цього встановіть поле .spec.clusterIP. Наприклад, це корисно, якщо у вас уже є запис DNS, який ви хочете використовувати повторно, або застарілі системи, налаштовані на певну IP-адресу, які важко переналаштувати.

Вибрана вами IP-адреса має бути дійсною адресою IPv4 або IPv6 із діапазону CIDR service-cluster-ip-range, налаштованого для API-сервера. Якщо ви спробуєте створити Service з недійсним значенням clusterIP, API-сервер поверне код стану HTTP 422, щоб вказати на проблему.

Читайте уникнення конфліктів, щоб дізнатися, як Kubernetes допомагає зменшити ризик і наслідки спроб двох різних Services використовувати ту саму IP-адресу.

type: NodePort

Якщо ви встановлюєте поле type у NodePort, панель управління Kubernetes виділяє порт із діапазону, вказаного прапорцем --service-node-port-range (стандартно: 30000-32767). Кожен вузол проксіює цей порт (той самий номер порту на кожному вузлі) у ваш Service. Ваш Service повідомляє виділений порт у своєму полі .spec.ports[*].nodePort.

Використання NodePort дає вам свободу налаштувати власне рішення для балансування навантаження, конфігурувати середовища, які не повністю підтримуються Kubernetes, або навіть безпосередньо відкрити IP-адреси одного чи кількох вузлів.

Для Service з портом вузла Kubernetes додатково виділяє порт (TCP, UDP або SCTP відповідно до протоколу Service). Кожен вузол кластера налаштовується на прослуховування цього призначеного порту та пересилання трафіку до однієї з готових точок доступу, повʼязаних із цим Service. Ви зможете звернутися до Service type: NodePort ззовні кластера, підʼєднавшись до будь-якого вузла за відповідним протоколом (наприклад, TCP) та відповідним портом (призначеним цьому Service).

Вибір власного порту

Якщо вам потрібен певний номер порту, ви можете вказати значення в полі nodePort. Панель управління або виділить вам цей порт, або повідомить, що транзакція API не вдалася. Це означає, що вам потрібно самостійно подбати про можливі конфлікти портів. Ви також повинні використовувати дійсний номер порту, що входить до діапазону, налаштованого для використання NodePort.

Ось приклад маніфесту для Service з type: NodePort, який вказує значення NodePort (у цьому прикладі 30007):

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      # Стандартно та для зручності `targetPort` встановлено
      # у те саме значення, що й поле `port`.
      targetPort: 80
      # Необовʼязкове поле
      # Стандартно та для зручності панель управління Kubernetes
      # виділить порт із діапазону (стандартно: 30000-32767)
      nodePort: 30007

Резервування діапазонів NodePort для уникнення конфліктів

Політика призначення портів Services NodePort застосовується як до автоматичного, так і до ручного призначення. Коли користувач хоче створити Service NodePort, який використовує певний порт, цей цільовий порт може конфліктувати з іншим, уже призначеним портом.

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

Під час використання стандартного діапазону NodePort 30000-32767 смуги розподіляються так:

  • Статична смуга: 30000-30085
  • Динамічна смуга: 30086-32767

Докладніше про те, як обчислюються статична та динамічна смуги, читайте в статті Уникнення конфліктів під час призначення портів Services NodePort.

Налаштування IP-адрес для Services type: NodePort

Під час використання kube-proxy у режимі iptables Services NodePort стандартно доступні на всіх IP-адресах вузла. Під час використання режиму nftables вони стандартно доступні лише на основній IP-адресі вузла (або основних IP-адресах у разі подвійного стека).

Ви можете змінити набір IP-адрес вузла, на яких доступні Services NodePort, за допомогою прапорця kube-proxy --nodeport-addresses або еквівалентного поля nodePortAddresses у файлі конфігурації kube-proxy. Він приймає розділений комами список IP-блоків (наприклад, 10.0.0.0/8, 192.0.2.0/25) або одне чи кілька з таких ключових слів:

  • primary — основна адреса IPv4 та/або IPv6 вузла згідно з обʼєктом Node. (Це стандартне значення для режиму nftables.)
  • localhost — loopback-адреси вузла (127.0.0.0/8, ::1/128).
  • all — усі адреси. (Це стандартне значення для режимів iptables та ipvs.)

Наприклад, якщо ви запускаєте kube-proxy з прапорцем --nodeport-addresses=192.168.0.0/24, kube-proxy спробує знайти локальну IP-адресу в цій підмережі на кожному вузлі та обслуговуватиме Services NodePort лише через цю IP-адресу.

Services type: NodePort через localhost

Механізми, які проксі сервісів використовують для реалізації Services NodePort, не завжди підтримують надання Services NodePort на localhost. Для kube-proxy:

  • Під час використання режиму iptables зі значенням --nodeport-addresses, яке містить 127.0.0.1, Services NodePort будуть доступні на 127.0.0.1. Однак це вимагає ввімкнення sysctl ядра (route_localnet), який у деяких кластерах може мати небезпечні побічні ефекти. NodePort на localhost для IPTables можна вимкнути, передавши kube-proxy --iptables-localhost-nodeports false або встановивши для --nodeport-addresses діапазон, який не містить 127.0.0.1.

  • Під час використання режиму ipvs або режиму iptables в одностекових кластерах IPv6 Services NodePort недоступні на localhost.

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the KubeProxyNFTablesLocalhostNodePorts feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

  • Під час використання режиму nftables Services NodePort будуть доступні на localhost, коли ввімкнено функціональну можливість KubeProxyNFTablesLocalhostNodePorts, а --nodeport-addresses має значення, яке явно містить localhost. (Значення лише all не ввімкне Services NodePort на localhost.) Це реалізовано перенаправленням зʼєднань NodePort на localhost через проксі в просторі користувача, тому це не так ефективно, як звичайне проксіювання сервісів.

Сторонні мережеві втулки, які мають власні реалізації проксі сервісів, можуть підтримувати NodePort на localhost, а можуть і не підтримувати; зверніться до документації цих втулків.

type: LoadBalancer

У хмарних провайдерів, які підтримують зовнішні балансувальники навантаження, встановлення поля type у LoadBalancer створює балансувальник навантаження для вашого Service. Фактичне створення балансувальника навантаження відбувається асинхронно, а інформація про створений балансувальник публікується в полі .status.loadBalancer Service. Наприклад:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

Трафік від зовнішнього балансувальника навантаження спрямовується на бекенд-Podʼи. Хмарний провайдер вирішує, як він балансується.

Щоб реалізувати Service з type: LoadBalancer, Kubernetes зазвичай починає зі змін, еквівалентних вашому запиту на Service з type: NodePort. Потім компонент cloud-controller-manager налаштовує зовнішній балансувальник навантаження на пересилання трафіку на цей призначений порт вузла.

Ви можете налаштувати Service із балансуванням навантаження так, щоб не призначати порт вузла, якщо реалізація хмарного провайдера це підтримує.

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

Примітка:

Поле .spec.loadBalancerIP для Service визнано застарілим у Kubernetes v1.24.

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

Якщо ви інтегруєтеся з провайдером, який підтримує вказання IP-адрес балансувальника навантаження для Service через анотацію (специфічну для провайдера), вам слід перейти на цей спосіб.

Якщо ви пишете код для інтеграції балансувальника навантаження з Kubernetes, уникайте використання цього поля. Ви можете інтегруватися з Gateway замість Service або визначити власні анотації (специфічні для провайдера) на Service, які задають еквівалентні дані.

Вплив життєздатності вузла на трафік балансувальника навантаження

Перевірки справності балансувальника навантаження є критично важливими для сучасних застосунків. Вони використовуються для визначення того, на який сервер (віртуальну машину або IP-адресу) балансувальник навантаження має спрямовувати трафік. API Kubernetes не визначають, як мають бути реалізовані перевірки справності для балансувальників навантаження, керованих Kubernetes; поведінку визначають хмарні провайдери (і ті, хто пише код інтеграції). Перевірки справності балансувальника навантаження широко використовуються для підтримки поля externalTrafficPolicy у Services.

Балансувальники навантаження зі змішаними типами протоколів

This is a stable feature in Kubernetes, and has been since the 1.26 release. You can no longer toggle this feature (the associated feature gate has been removed).

Стандартно для Services типу LoadBalancer, коли визначено більше одного порту, усі порти повинні мати той самий протокол, і цей протокол має підтримуватися хмарним провайдером.

Функціональна можливість MixedProtocolLBService (стандартно ввімкнена для kube-apiserver починаючи з v1.24) дозволяє використовувати різні протоколи для Services типу LoadBalancer, коли визначено більше одного порту.

Примітка:

Набір протоколів, які можна використовувати для Services із балансуванням навантаження, визначає ваш хмарний провайдер; він може накладати обмеження понад ті, що застосовує API Kubernetes.

Вимкнення виділення NodePort для балансувальника навантаження

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

За бажанням ви можете вимкнути виділення портів вузла для Service з type: LoadBalancer, встановивши поле spec.allocateLoadBalancerNodePorts у false. Це слід використовувати лише для реалізацій балансувальників навантаження, які спрямовують трафік безпосередньо до Podʼів, а не через порти вузла. Стандартно spec.allocateLoadBalancerNodePorts має значення true, і Services типу LoadBalancer продовжуватимуть виділяти порти вузла. Якщо встановити spec.allocateLoadBalancerNodePorts у false для наявного Service з виділеними портами вузла, ці порти вузла не будуть звільнені автоматично. Щоб звільнити ці порти вузла, ви повинні явно вилучити запис nodePorts у кожному порту Service.

Вказання класу реалізації балансувальника навантаження

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

Для Service із type, встановленим у LoadBalancer, поле .spec.loadBalancerClass дозволяє використовувати реалізацію балансувальника навантаження, відмінну від стандартної реалізації хмарного провайдера.

Стандартно .spec.loadBalancerClass не встановлено, і Service типу LoadBalancer використовує стандартну реалізацію балансувальника навантаження хмарного провайдера, якщо кластер налаштовано з хмарним провайдером за допомогою прапорця компонента --cloud-provider.

Якщо ви вказуєте .spec.loadBalancerClass, вважається, що за Services стежить реалізація балансувальника навантаження, яка відповідає вказаному класу. Будь-яка стандартна реалізація балансувальника навантаження (наприклад, та, яку надає хмарний провайдер) ігноруватиме Services, у яких встановлено це поле. spec.loadBalancerClass можна встановити лише для Service типу LoadBalancer. Після встановлення його не можна змінити. Значення spec.loadBalancerClass має бути ідентифікатором у стилі мітки з необовʼязковим префіксом, наприклад, "internal-vip" або "example.com/internal-vip". Назви без префікса зарезервовані для кінцевих користувачів.

Режим IP-адреси балансувальника навантаження

Для Service з type: LoadBalancer контролер може встановити .status.loadBalancer.ingress.ipMode. .status.loadBalancer.ingress.ipMode вказує, як поводиться IP-адреса балансувальника навантаження. Його можна вказати лише тоді, коли вказано також поле .status.loadBalancer.ingress.ip.

Є два можливі значення для .status.loadBalancer.ingress.ipMode: "VIP" та "Proxy". Стандартне значення — "VIP", що означає, що трафік доставляється на вузол із призначенням, встановленим на IP-адресу та порт балансувальника навантаження. Є два випадки, коли встановлюється значення "Proxy", залежно від того, як балансувальник навантаження хмарного провайдера доставляє трафік:

  • Якщо трафік доставляється на вузол, а потім через DNAT до Podʼа, призначенням буде IP-адреса вузла та порт вузла;
  • Якщо трафік доставляється безпосередньо до Podʼа, призначенням буде IP-адреса та порт Podʼа.

Реалізації Service можуть використовувати цю інформацію для коригування маршрутизації трафіку.

Внутрішній балансувальник навантаження

У змішаному середовищі іноді необхідно маршрутизувати трафік від Services всередині того самого (віртуального) блоку мережевих адрес.

У середовищі DNS із розділеним горизонтом вам знадобляться два Services, щоб маршрутизувати і зовнішній, і внутрішній трафік до ваших точок доступу.

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

Виберіть одну з вкладок.

metadata:
  name: my-service
  annotations:
    networking.gke.io/load-balancer-type: "Internal"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internal"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"
metadata:
  name: my-service
  annotations:
    service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
metadata:
  annotations:
    service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx
metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/oci-load-balancer-internal: true

type: ExternalName

Services типу ExternalName зіставляють Service з DNS-іменем, а не зі звичайним селектором, таким як my-service або cassandra. Ви вказуєте такі Services за допомогою параметра spec.externalName.

Наприклад, це визначення Service зіставляє Service my-service у просторі імен prod з my.database.example.com:

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

Примітка:

Service з type: ExternalName приймає рядок з адресою IPv4, але трактує цей рядок як DNS-імʼя, що складається з цифр, а не як IP-адресу (однак інтернет не дозволяє таких імен у DNS). DNS-сервери не розвʼязують Services із зовнішніми іменами, схожими на адреси IPv4.

Якщо ви хочете зіставити Service безпосередньо з певною IP-адресою, розгляньте можливість використання headless Services.

Під час пошуку хоста my-service.prod.svc.cluster.local DNS Service кластера повертає запис CNAME зі значенням my.database.example.com. Доступ до my-service працює так само, як і для інших Services, але з важливою відмінністю: перенаправлення відбувається на рівні DNS, а не через проксіювання чи пересилання. Якщо пізніше ви вирішите перенести базу даних у кластер, ви зможете запустити її Podʼи, додати відповідні селектори чи точки доступу та змінити type цього Service.

Увага:

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

Для протоколів, які використовують імена хостів, ця різниця може призвести до помилок або неочікуваних відповідей. HTTP-запити матимуть заголовок Host:, який вихідний сервер не розпізнає; сервери TLS не зможуть надати сертифікат, що відповідає імені хоста, до якого підʼєднався клієнт.

Headless Services

Іноді вам не потрібні балансування навантаження та одна IP-адреса Service. У цьому випадку ви можете створити так звані headless Services, явно вказавши "None" для кластерної IP-адреси (.spec.clusterIP).

Ви можете використовувати headless Service для взаємодії з іншими механізмами виявлення сервісів, не привʼязуючись до реалізації Kubernetes.

Для headless Services кластерна IP-адреса не виділяється, kube-proxy не обробляє ці Services, а платформа не виконує для них балансування навантаження чи проксіювання.

Headless Service дозволяє клієнту підʼєднуватися безпосередньо до будь-якого Podʼа, якому він віддає перевагу. Services, що є headless, не налаштовують маршрути та пересилання пакетів за допомогою віртуальних IP-адрес і проксі; натомість headless Services повідомляють IP-адреси точок доступу окремих Podʼів через внутрішні записи DNS, які обслуговує DNS-сервіс кластера. Щоб визначити headless Service, ви створюєте Service із .spec.type, встановленим у ClusterIP (що також є стандартним значенням type), і додатково встановлюєте .spec.clusterIP у None.

Рядкове значення None є особливим випадком і не тотожне тому, що поле .spec.clusterIP не встановлено.

Те, як автоматично налаштовується DNS, залежить від того, чи визначено для Service селектори:

З селекторами

Для headless Services, які визначають селектори, контролер точок доступу створює EndpointSlices в API Kubernetes і змінює конфігурацію DNS так, щоб повертати записи A або AAAA (адреси IPv4 або IPv6), які вказують безпосередньо на Podʼи, що стоять за Service.

Без селекторів

Для headless Services, які не визначають селектори, панель управління не створює обʼєкти EndpointSlice. Однак система DNS шукає та налаштовує одне з такого:

  • DNS-записи CNAME для Services з type: ExternalName.
  • DNS-записи A / AAAA для всіх IP-адрес готових точок доступу Service, для всіх типів Service, крім ExternalName.
    • Для точок доступу IPv4 система DNS створює записи A.
    • Для точок доступу IPv6 система DNS створює записи AAAA.

Коли ви визначаєте headless Service без селектора, port має збігатися з targetPort.

Виявлення сервісів

Для клієнтів, що працюють всередині вашого кластера, Kubernetes підтримує два основні способи виявлення Service: змінні середовища та DNS.

Змінні середовища

Коли Pod запускається на вузлі, kubelet додає набір змінних середовища для кожного активного Service. Він додає змінні {SVCNAME}_SERVICE_HOST та {SVCNAME}_SERVICE_PORT, де назва Service записується у верхньому регістрі, а дефіси перетворюються на підкреслення.

Наприклад, Service redis-primary, який відкриває TCP-порт 6379 і якому виділено кластерну IP-адресу 10.0.0.11, створює такі змінні середовища:

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

Примітка:

Коли у вас є Pod, якому потрібен доступ до Service, і ви використовуєте метод змінних середовища для публікації порту та кластерного IP для клієнтських Podʼів, ви повинні створити Service до появи клієнтських Podʼів. Інакше змінні середовища цих клієнтських Podʼів не будуть заповнені.

Якщо ви використовуєте лише DNS для виявлення кластерного IP для Service, вам не потрібно турбуватися про цю проблему порядку.

Kubernetes також підтримує та надає змінні, сумісні з можливістю Docker Engine "legacy container links". Ви можете переглянути makeLinkVariables, щоб побачити, як це реалізовано в Kubernetes.

DNS

Ви можете (і майже завжди повинні) налаштувати DNS-сервіс для свого кластера Kubernetes за допомогою надбудови.

DNS-сервер, обізнаний із кластером, такий як CoreDNS, стежить за появою нових Services в API Kubernetes і створює набір записів DNS для кожного з них. Якщо DNS увімкнено в усьому кластері, всі Podʼи повинні автоматично мати змогу розвʼязувати Services за їхніми DNS-іменами.

Наприклад, якщо у вас є Service з назвою my-service у просторі імен Kubernetes my-ns, панель управління та DNS Service разом створюють запис DNS для my-service.my-ns. Podʼи у просторі імен my-ns повинні мати змогу знайти цей Service, виконавши пошук за іменем my-service (my-service.my-ns також працюватиме).

Podʼи в інших просторах імен повинні вказувати імʼя як my-service.my-ns. Ці імена розвʼязуватимуться в кластерний IP, призначений для Service.

Kubernetes також підтримує записи DNS SRV (Service) для іменованих портів. Якщо Service my-service.my-ns має порт з назвою http із протоколом TCP, ви можете виконати запит DNS SRV для _http._tcp.my-service.my-ns, щоб дізнатися номер порту для http, а також IP-адресу.

DNS-сервер Kubernetes — єдиний спосіб доступу до Services ExternalName. Докладніше про розвʼязання ExternalName читайте в DNS для Services та Podʼів.

Механізм віртуальних IP-адрес

Прочитайте Віртуальні IP та проксі для Service, де пояснено механізм, який Kubernetes надає, щоб відкрити доступ до Service на віртуальній IP-адресі.

Політики трафіку

Ви можете встановити поля .spec.internalTrafficPolicy та .spec.externalTrafficPolicy, щоб контролювати, як Kubernetes маршрутизує трафік до справних («готових») бекендів.

Докладнішу інформацію дивіться в розділі Політики трафіку.

Контроль розподілу трафіку

Поле .spec.trafficDistribution надає ще один спосіб впливати на маршрутизацію трафіку в межах Service Kubernetes. Якщо політики трафіку зосереджені на суворих семантичних гарантіях, то розподіл трафіку дозволяє виражати уподобання (наприклад, маршрутизацію до топологічно ближчих точок доступу). Це може допомогти оптимізувати продуктивність, вартість або надійність. У Kubernetes 1.37 підтримуються такі значення:

PreferSameZone
Вказує на перевагу маршрутизації трафіку до точок доступу, які перебувають у тій самій зоні, що й клієнт.
PreferSameNode
Вказує на перевагу маршрутизації трафіку до точок доступу, які перебувають на тому самому вузлі, що й клієнт.
PreferClose (застаріло)
Це старіший псевдонім для PreferSameZone, менш зрозумілий щодо семантики.

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

Докладнішу інформацію дивіться в розділі Розподіл трафіку.

Збереження сесії

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

Зовнішні IP-адреси

СТАН ФУНКЦІОНАЛУ: Deprecated since Kubernetes v1.36

Усі користувачі мають розпочати перехід від externalIPs. Натомість розгляньте можливість використання контролера зовнішнього балансувальника навантаження або реалізації Gateway API.

Якщо є зовнішні IP-адреси, які маршрутизуються до одного чи кількох вузлів кластера, до Services Kubernetes можна відкрити доступ на цих externalIPs. Коли мережевий трафік надходить до кластера із зовнішньою IP-адресою (як IP-адресою призначення) та портом, що відповідають цьому Service, правила й маршрути, налаштовані Kubernetes, забезпечують маршрутизацію трафіку до однієї з точок доступу цього Service.

Під час визначення Service ви можете вказати externalIPs для будь-якого типу Service. У наведеному нижче прикладі клієнти можуть отримати доступ до Service з назвою "my-service" через TCP за адресою "198.51.100.32:80" (обчисленою з .spec.externalIPs[] та .spec.ports[].port).

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 49152
  externalIPs:
    - 198.51.100.32

Примітка:

Kubernetes не керує виділенням externalIPs; за це відповідає адміністратор кластера.

Обʼєкт API

Service — це ресурс верхнього рівня в REST API Kubernetes. Ви можете дізнатися більше про обʼєкт API Service.

Що далі

Дізнайтеся більше про Services і те, як вони вписуються в Kubernetes:

Для ширшого контексту прочитайте таке:

5.2 - Контролери Ingress

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

Примітка:

Проєкт Kubernetes рекомендує використовувати Gateway замість Ingress. API Ingress заморожено.

Це означає, що:

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

Контролери Ingress

Як проєкт, Kubernetes підтримує та супроводжує контролери Ingress AWS та GCE.

Контролери Ingress сторонніх розробників

Примітка: Цей розділ містить посилання на проєкти сторонніх розробників, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Проєкти вказано в алфавітному порядку. Щоб додати проєкт до цього списку, ознайомтеся з посібником з контенту перед надсиланням змін. Докладніше.
  • AKS Application Gateway Ingress Controller — це контролер Ingress, який налаштовує Azure Application Gateway.
  • Alibaba Cloud API Gateway Ingress — це контролер Ingress, який налаштовує Alibaba Cloud Native API Gateway, який також є комерційною версією Higress.
  • Apache APISIX Ingress Controller — це контролер Ingress, заснований на Apache APISIX.
  • Avi Kubernetes Operator забезпечує балансування навантаження L4-L7, використовуючи VMware NSX Advanced Load Balancer.
  • BFE Ingress Controller — це контролер Ingress, заснований на BFE.
  • BunkerWeb Ingress Controller — це контролер Ingress для BunkerWeb, WAF (Web Application Firewall) на базі nginx.
  • Cilium Ingress Controller — це контролер Ingress, який працює на основі Cilium.
  • Контролер Ingress Citrix працює з Citrix Application Delivery Controller.
  • Contour — це контролер Ingress на основі Envoy.
  • Emissary-Ingress API Gateway — це контролер Ingress на основі Envoy.
  • EnRoute — це шлюз API на основі Envoy, який може працювати як контролер Ingress.
  • F5 BIG-IP Container Ingress Services for Kubernetes дозволяє використовувати Ingress для налаштування віртуальних серверів F5 BIG-IP.
  • FortiADC Ingress Controller підтримує ресурси Kubernetes Ingress та дозволяє керувати обʼєктами FortiADC з Kubernetes
  • Gloo — це контролер Ingress з відкритим кодом на основі Envoy, який пропонує функціональність шлюзу API.
  • Higress — це шлюз API на основі Envoy, який може працювати як контролер Ingress.
  • Контролер Ingress HAProxy для Kubernetes — це контролер Ingress для HAProxy.
  • Istio Ingress — це контролер Ingress на основі Istio.
  • Контролер Ingress Kong для Kubernetes — це контролер Ingress, який керує Kong Gateway.
  • Kusk Gateway — це контролер Ingress, орієнтований на OpenAPI, на основі Envoy.
  • N42 Gateway — це контролер Ingress та Gateway API для HAProxy.
  • Контролер Ingress NGINX для Kubernetes працює з вебсервером NGINX (як проксі).
  • ngrok-operator — це контролер для ngrok, який підтримує як Ingress, так і Gateway API для додавання безпечного публічного доступу до ваших Services K8s.
  • Контролер Ingress OCI Native — це контролер Ingress для Oracle Cloud Infrastructure, який дозволяє керувати OCI Load Balancer.
  • OpenNJet Ingress Controller — це контролер Ingress на основі OpenNJet.
  • Контролер Ingress Pomerium — це контролер Ingress на основі Pomerium, який пропонує політику доступу з урахуванням контексту.
  • Skipper — HTTP-маршрутизатор і зворотний проксі для композиції сервісів, зокрема для таких випадків, як Kubernetes Ingress, спроєктований як бібліотека для створення власного проксі.
  • Контролер Ingress Traefik Kubernetes provider — це контролер Ingress для проксі Traefik.
  • Tyk Operator розширює Ingress власними ресурсами, щоб додати до Ingress можливості керування API. Tyk Operator працює з Tyk Gateway з відкритим кодом і панеллю управління Tyk Cloud.
  • Voyager — це контролер Ingress для HAProxy.
  • Контролер Ingress Wallarm — це контролер Ingress, який надає можливості WAAP (WAF) та захисту API.

Використання кількох контролерів Ingress

Ви можете розгортати будь-яку кількість контролерів Ingress, використовуючи клас Ingress у межах кластера. Зверніть увагу на значення .metadata.name вашого ресурсу класу Ingress. Під час створення Ingress ця назва знадобиться вам, щоб вказати поле ingressClassName в обʼєкті Ingress (див. довідник IngressSpec v1). ingressClassName є заміною застарілого методу анотації.

Якщо ви не вказуєте IngressClass для Ingress, а у вашому кластері рівно один IngressClass позначено як стандартний, Kubernetes застосовує до Ingress стандартний IngressClass кластера. Щоб позначити IngressClass як стандартний, встановіть для нього анотацію ingressclass.kubernetes.io/is-default-class зі строковим значенням "true".

В ідеалі всі контролери Ingress мають відповідати цій специфікації, але різні контролери Ingress працюють дещо по-різному.

Примітка:

Обовʼязково ознайомтеся з документацією вашого контролера Ingress, щоб зрозуміти особливості його вибору.

Що далі

  • Дізнайтеся більше про Ingress.

5.3 - Gateway API

Gateway API — це сімейство видів API, які забезпечують динамічне надання інфраструктури та розширену маршрутизацію трафіку.

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

Принципи дизайну

Наведені нижче принципи визначили дизайн та архітектуру Gateway API:

  • Орієнтований на ролі: Види Gateway API побудовано за організаційними ролями, відповідальними за керування мережевою взаємодією сервісів Kubernetes:

    • Провайдер інфраструктури: Керує інфраструктурою, яка дозволяє кільком ізольованим кластерам обслуговувати кількох орендарів, наприклад, хмарний провайдер.
    • Оператор кластера: Керує кластерами та зазвичай займається політиками, мережевим доступом, дозволами застосунків тощо.
    • Розробник застосунків: Керує застосунком, що працює в кластері, і зазвичай займається конфігурацією на рівні застосунку та композицією Service.
  • Переносний: Специфікації Gateway API визначено як власні ресурси, і їх підтримують багато реалізацій.

  • Виразний: Види Gateway API підтримують функціональність для поширених сценаріїв маршрутизації трафіку, як-от зіставлення за заголовками, розподіл трафіку за вагою та інші, які в Ingress були можливі лише за допомогою власних анотацій.

  • Розширюваний: Gateway дозволяє повʼязувати власні ресурси на різних рівнях API. Це робить можливим детальне налаштування у відповідних місцях структури API.

Модель ресурсів

Gateway API має чотири стабільні види API:

  • GatewayClass: Визначає набір шлюзів зі спільною конфігурацією, якими керує контролер, що реалізує цей клас.

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

  • HTTPRoute: Визначає специфічні для HTTP правила зіставлення трафіку від слухача (listener) Gateway із поданням мережевих точок доступу бекенду. Ці точки доступу часто подаються як Service.

  • GRPCRoute: Визначає специфічні для gRPC правила зіставлення трафіку від слухача (listener) Gateway із поданням мережевих точок доступу бекенду. Ці точки доступу часто подаються як Service.

Gateway API організовано у різні види API, повʼязані взаємозалежними відносинами, що відображає рольову природу організацій. Обʼєкт Gateway повʼязаний рівно з одним GatewayClass; GatewayClass описує контролер шлюзу, відповідальний за керування Gateways цього класу. Потім із Gateways повʼязуються один або кілька видів маршрутів, наприклад, HTTPRoute. Gateway може фільтрувати маршрути, які можна прикріпити до його listeners, утворюючи з маршрутами модель двосторонньої довіри.

Наведена нижче схема ілюструє звʼязки між трьома стабільними видами Gateway API:

graph LR;
subgraph cluster["Кластер"]
direction LR
HTTPRoute --> Gateway;
Gateway --> GatewayClass;
end
classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class HTTPRoute,Gateway,GatewayClass k8s;
class cluster cluster;

GatewayClass

Gateways можуть бути реалізовані різними контролерами, часто з різними конфігураціями. Gateway має посилатися на GatewayClass, який містить назву контролера, що реалізує цей клас.

Мінімальний приклад GatewayClass:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: example-class
spec:
  controllerName: example.com/gateway-controller

У цьому прикладі контролер, який реалізує Gateway API, налаштовано на керування GatewayClasses із назвою контролера example.com/gateway-controller. Gateways цього класу керуватиме контролер реалізації.

Повне визначення цього виду API дивіться в довіднику GatewayClass.

Gateway

Gateway описує екземпляр інфраструктури обробки трафіку. Він визначає мережеву точку доступу, яку можна використовувати для обробки трафіку, тобто фільтрування, балансування, розділення тощо, для таких бекендів, як Service. Наприклад, Gateway може представляти хмарний балансувальник навантаження або проксі-сервер у кластері, налаштований на приймання HTTP-трафіку.

Типовий приклад ресурсу Gateway:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
  namespace: example-namespace
spec:
  gatewayClassName: example-class
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    hostname: "www.example.com"
    allowedRoutes:
      namespaces:
        from: Same

У цьому прикладі екземпляр інфраструктури обробки трафіку запрограмовано на прослуховування HTTP-трафіку на порту 80. Оскільки поле addresses не вказано, адресу або імʼя хоста для Gateway призначає контролер реалізації. Ця адреса використовується як мережева точка доступу для обробки трафіку мережевих точок доступу бекенду, визначених у маршрутах.

Повне визначення цього виду API дивіться в довіднику Gateway. Вказівки щодо налаштування слухачів HTTPS/TLS дивіться в посібнику Gateway API з TLS.

Примітка:

Стандартно Gateway приймає маршрути лише з того самого простору імен. Для маршрутів з інших просторів імен потрібно налаштувати allowedRoutes.

HTTPRoute

Вид HTTPRoute визначає поведінку маршрутизації HTTP-запитів від слухача Gateway до мережевих точок доступу бекенду. Для бекенду Service реалізація може подавати мережеву точку доступу бекенду як IP-адресу Service або як EndpointSlices, що стоять за цим Service. HTTPRoute подає конфігурацію, яка застосовується до базової реалізації Gateway. Наприклад, визначення нового HTTPRoute може призвести до налаштування додаткових маршрутів трафіку в хмарному балансувальнику навантаження або проксі-сервері в кластері.

Типовий приклад HTTPRoute:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-httproute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "www.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /login
    backendRefs:
    - name: example-svc
      port: 8080

У цьому прикладі HTTP-трафік від Gateway example-gateway із заголовком Host: зі значенням www.example.com і шляхом запиту /login буде спрямовано до Service example-svc на порт 8080.

Повне визначення цього виду API дивіться в довіднику HTTPRoute.

GRPCRoute

Вид GRPCRoute визначає поведінку маршрутизації запитів gRPC від слухача Gateway до мережевих точок доступу бекенду. Для бекенду Service реалізація може подавати мережеву точку доступу бекенду як IP-адресу Service або як EndpointSlices, що стоять за цим Service. GRPCRoute подає конфігурацію, яка застосовується до базової реалізації Gateway. Наприклад, визначення нового GRPCRoute може призвести до налаштування додаткових маршрутів трафіку в хмарному балансувальнику навантаження або проксі-сервері в кластері.

Gateways, що підтримують GRPCRoute, повинні підтримувати HTTP/2 без початкового оновлення з HTTP/1, тому трафік gRPC гарантовано проходить належним чином.

Типовий приклад GRPCRoute:

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: example-grpcroute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "svc.example.com"
  rules:
  - backendRefs:
    - name: example-svc
      port: 50051

У цьому прикладі трафік gRPC від Gateway example-gateway із хостом svc.example.com буде спрямовано до Service example-svc на порт 50051 з того самого простору імен.

GRPCRoute дозволяє зіставляти конкретні сервіси gRPC, як показано в наведеному нижче прикладі:

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: example-grpcroute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "svc.example.com"
  rules:
  - matches:
    - method:
        service: com.example
        method: Login
    backendRefs:
    - name: foo-svc
      port: 50051

У цьому випадку GRPCRoute зіставлятиме будь-який трафік для svc.example.com і застосовуватиме свої правила маршрутизації, щоб переспрямувати трафік до правильного бекенду. Оскільки вказано лише одне зіставлення, переспрямовуватимуться тільки запити до методу com.example.User.Login на svc.example.com. RPC будь-якого іншого методу цей маршрут не зіставлятиме.

Повне визначення цього виду API дивіться в довіднику GRPCRoute.

Потік запитів

Ось простий приклад маршрутизації HTTP-трафіку до Service за допомогою Gateway та HTTPRoute:

graph LR;
  client([клієнт])-. HTTP 
запит .->Gateway; Gateway-->HTTPRoute; HTTPRoute-->|Правило
маршрутизації|Service; Service-->pod1[Pod]; Service-->pod2[Pod]; classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; class Gateway,HTTPRoute,Service,pod1,pod2 k8s; class client plain;

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

  1. Клієнт починає готувати HTTP-запит для URL http://www.example.com
  2. DNS-резолвер клієнта запитує імʼя призначення та дізнається про його відповідність одній або кільком IP-адресам, повʼязаним із Gateway.
  3. Клієнт надсилає запит на IP-адресу Gateway; зворотний проксі отримує HTTP-запит і використовує заголовок Host:, щоб зіставити його з конфігурацією, отриманою з Gateway і прикріпленого HTTPRoute.
  4. За потреби зворотний проксі може виконати зіставлення заголовків запиту та/або шляху на основі правил зіставлення HTTPRoute.
  5. За потреби зворотний проксі може змінити запит, наприклад, додати або вилучити заголовки, на основі правил фільтрації HTTPRoute.
  6. Насамкінець зворотний проксі пересилає запит до одного або кількох бекендів.

Відповідність

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

Дивіться документацію щодо відповідності, щоб дізнатися про такі деталі, як канали випусків, рівні підтримки та запуск тестів на відповідність.

Міграція з Ingress

Gateway API є наступником API Ingress. Однак він не містить виду Ingress. Тому потрібне одноразове перетворення наявних ресурсів Ingress на ресурси Gateway API.

Докладніше про міграцію ресурсів Ingress на ресурси Gateway API дивіться в посібнику з міграції з Ingress.

Що далі

Ресурси Gateway API не реалізовані в Kubernetes нативно: специфікації визначено як власні ресурси, що підтримуються широким спектром реалізацій. Встановіть CRD Gateway API або виконайте інструкції зі встановлення обраної реалізації. Після встановлення реалізації скористайтеся посібником Початок роботи, щоб швидко почати роботу з Gateway API.

Примітка:

Обовʼязково ознайомтеся з документацією обраної вами реалізації, щоб розуміти можливі застереження.

Додаткові відомості про всі види Gateway API дивіться в специфікації API.

5.4 - Мережеві політики

Якщо ви хочете контролювати потік трафіку на рівні IP-адреси чи порту (рівень 3 або 4 моделі OSI), NetworkPolicies дозволяють визначати правила потоку трафіку всередині вашого кластера, а також між Podʼами та зовнішнім світом. Ваш кластер повинен використовувати мережевий втулок, який підтримує застосування NetworkPolicy.

Якщо ви хочете контролювати потік трафіку на рівні IP-адреси чи порту для протоколів TCP, UDP та SCTP, вам варто розглянути використання NetworkPolicies Kubernetes для окремих застосунків у вашому кластері. NetworkPolicies — це конструкція, орієнтована на застосунки, яка дозволяє визначити, як Pod може взаємодіяти через мережу з різними мережевими «сутностями» (ми вживаємо тут слово «сутність», щоб не перевантажувати поширеніші терміни, як-от «точки доступу» та «Services», які мають у Kubernetes особливі значення). NetworkPolicies застосовуються до зʼєднання, на одному або обох кінцях якого є Pod, і не стосуються інших зʼєднань.

Сутності, з якими може взаємодіяти Pod, визначаються за допомогою комбінації таких трьох ідентифікаторів:

  1. Інші дозволені Podʼи (виняток: Pod не може заблокувати доступ до самого себе)
  2. Дозволені простори імен
  3. IP-блоки (виняток: трафік до вузла і від вузла, де працює Pod, завжди дозволений, незалежно від IP-адреси Podʼа чи вузла)

Під час визначення NetworkPolicy на основі Podʼів або просторів імен ви використовуєте селектор, щоб вказати, який трафік дозволений до Podʼів і від Podʼів, що відповідають цьому селектору.

Натомість під час створення NetworkPolicies на основі IP ми визначаємо політики на основі IP-блоків (діапазонів CIDR).

Передумови

Мережеві політики реалізуються мережевим втулком. Щоб використовувати мережеві політики, вам потрібне мережеве рішення, яке підтримує NetworkPolicy. Створення ресурсу NetworkPolicy без контролера, який його реалізує, не матиме ефекту.

Два види ізоляції Podʼів

Існує два види ізоляції для Podʼа: ізоляція для вихідного трафіку (egress) та ізоляція для вхідного трафіку (ingress). Вони стосуються того, які зʼєднання можуть бути встановлені. «Ізоляція» тут не є абсолютною, а означає «застосовуються певні обмеження». Альтернатива, «неізольований для $direction», означає, що в зазначеному напрямку обмежень немає. Обидва види ізоляції (або її відсутності) оголошуються незалежно, і обидва мають значення для зʼєднання від одного Podʼа до іншого.

Стандартно Pod не є ізольованим для вихідного трафіку: усі вихідні зʼєднання дозволені. Pod є ізольованим для вихідного трафіку, якщо існує будь-яка NetworkPolicy, яка одночасно вибирає цей Pod і має "Egress" у своєму policyTypes; ми кажемо, що така політика застосовується до Podʼа для вихідного трафіку. Коли Pod ізольований для вихідного трафіку, єдині дозволені зʼєднання з Podʼа — це ті, що дозволені списком egress якоїсь NetworkPolicy, яка застосовується до Podʼа для вихідного трафіку. Трафік відповідей для цих дозволених зʼєднань також буде неявно дозволений. Дія цих списків egress обʼєднується адитивно.

Стандартно Pod не є ізольованим для вхідного трафіку: усі вхідні зʼєднання дозволені. Pod є ізольованим для вхідного трафіку, якщо існує будь-яка NetworkPolicy, яка одночасно вибирає цей Pod і має "Ingress" у своєму policyTypes; ми кажемо, що така політика застосовується до Podʼа для вхідного трафіку. Коли Pod ізольований для вхідного трафіку, єдині дозволені зʼєднання до Podʼа — це зʼєднання з вузла Podʼа та ті, що дозволені списком ingress якоїсь NetworkPolicy, яка застосовується до Podʼа для вхідного трафіку. Трафік відповідей для цих дозволених зʼєднань також буде неявно дозволений. Дія цих списків ingress обʼєднується адитивно.

Мережеві політики не конфліктують, вони адитивні. Якщо одна чи кілька політик застосовуються до певного Podʼа в певному напрямку, то дозволені в цьому напрямку зʼєднання цього Podʼа — це обʼєднання того, що дозволяють застосовні політики. Тому порядок обчислення не впливає на результат політики.

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

Ресурс NetworkPolicy

Повне визначення ресурсу дивіться в довіднику NetworkPolicy.

Приклад NetworkPolicy може виглядати так:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Примітка:

Надсилання цього маніфесту (POST) до API-сервера вашого кластера не матиме жодного ефекту, якщо обране вами мережеве рішення не підтримує мережеві політики.

Обовʼязкові поля: Як і всім іншим конфігураціям Kubernetes, NetworkPolicy потрібні поля apiVersion, kind та metadata. Загальну інформацію про роботу з файлами конфігурації дивіться в Налаштування Podʼів для використання ConfigMap та Управління обʼєктами.

spec: Специфікація NetworkPolicy містить усю інформацію, необхідну для визначення конкретної мережевої політики в заданому просторі імен.

podSelector: Кожна NetworkPolicy містить podSelector, який вибирає групу Podʼів, до яких застосовується політика. Політика з прикладу вибирає Podʼи з міткою "role=db". Порожній podSelector вибирає всі Podʼи в просторі імен.

policyTypes: Кожна NetworkPolicy містить список policyTypes, який може включати Ingress, Egress або обидва значення. Поле policyTypes вказує, чи застосовується ця політика до вхідного трафіку до вибраних Podʼів, до вихідного трафіку від вибраних Podʼів чи до обох. Якщо в NetworkPolicy не вказано жодного policyTypes, то стандартно завжди буде встановлено Ingress, а Egress буде встановлено, якщо NetworkPolicy має хоча б одне правило вихідного трафіку.

ingress: Кожна NetworkPolicy може містити список дозволених правил ingress. Кожне правило дозволяє трафік, який відповідає обом секціям: from і ports. Політика з прикладу містить одне правило, яке відповідає трафіку на одному порту від одного з трьох джерел: перше вказано через ipBlock, друге через namespaceSelector, а третє через podSelector.

egress: Кожна NetworkPolicy може містити список дозволених правил egress. Кожне правило дозволяє трафік, який відповідає обом секціям: to і ports. Політика з прикладу містить одне правило, яке відповідає трафіку на одному порту до будь-якого призначення в 10.0.0.0/24.

Отже, NetworkPolicy з прикладу:

  1. ізолює Podʼи role=db у просторі імен default як для вхідного, так і для вихідного трафіку (якщо вони ще не були ізольовані)

  2. (правила Ingress) дозволяє зʼєднання до всіх Podʼів у просторі імен default з міткою role=db на TCP-порт 6379 від:

    • будь-якого Podʼа в просторі імен default з міткою role=frontend
    • будь-якого Podʼа в просторі імен з міткою project=myproject
    • IP-адрес у діапазонах 172.17.0.0–172.17.0.255 та 172.17.2.0–172.17.255.255 (тобто всіх адрес 172.17.0.0/16, крім 172.17.1.0/24)
  3. (правила Egress) дозволяє зʼєднання від будь-якого Podʼа в просторі імен default з міткою role=db до CIDR 10.0.0.0/24 на TCP-порт 5978

Додаткові приклади дивіться в покроковому посібнику Оголошення мережевої політики.

Поведінка селекторів to та from

Існує чотири види селекторів, які можна вказати в секції from правила ingress або в секції to правила egress:

podSelector: Вибирає певні Podʼи в тому самому просторі імен, що й NetworkPolicy, які мають бути дозволені як джерела вхідного трафіку або призначення вихідного трафіку.

namespaceSelector: Вибирає певні простори імен, усі Podʼи яких мають бути дозволені як джерела вхідного трафіку або призначення вихідного трафіку.

namespaceSelector та podSelector: Один запис to/from, який вказує і namespaceSelector, і podSelector, вибирає певні Podʼи в певних просторах імен. Будьте уважні й використовуйте правильний синтаксис YAML. Наприклад:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

Ця політика містить один елемент from, який дозволяє зʼєднання від Podʼів з міткою role=client у просторах імен з міткою user=alice. Але наведена далі політика відрізняється:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

Вона містить два елементи в масиві from і дозволяє зʼєднання від Podʼів у локальному просторі імен з міткою role=client або від будь-якого Podʼа в будь-якому просторі імен з міткою user=alice.

Якщо є сумніви, скористайтеся kubectl describe, щоб побачити, як Kubernetes інтерпретував політику.

ipBlock: Вибирає певні діапазони IP CIDR, дозволені як джерела вхідного трафіку або призначення вихідного трафіку. Це мають бути IP-адреси поза кластером, оскільки IP-адреси Podʼів є ефемерними та непередбачуваними.

Механізми вхідного та вихідного трафіку кластера часто вимагають переписування IP-адреси джерела або призначення пакетів. У таких випадках не визначено, чи відбувається це до обробки NetworkPolicy, чи після неї, і поведінка може відрізнятися для різних комбінацій мережевого втулка, хмарного провайдера, реалізації Service тощо.

Для вхідного трафіку це означає, що в одних випадках ви можете фільтрувати вхідні пакети за фактичною початковою IP-адресою джерела, а в інших «IP-адреса джерела», за якою працює NetworkPolicy, може бути IP-адресою LoadBalancer, вузла Podʼа тощо.

Для вихідного трафіку це означає, що на зʼєднання від Podʼів до IP-адрес Service, які переписуються на зовнішні для кластера IP-адреси, політики на основі ipBlock можуть поширюватися, а можуть і не поширюватися.

Стандартні політики

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

Стандартна заборона всього вхідного трафіку

Ви можете створити «стандартну» політику ізоляції вхідного трафіку для простору імен, створивши NetworkPolicy, яка вибирає всі Podʼи, але не дозволяє жодного вхідного трафіку до цих Podʼів.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

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

Дозвіл на весь вхідний трафік

Якщо ви хочете дозволити всі вхідні зʼєднання до всіх Podʼів у просторі імен, ви можете створити політику, яка явно це дозволяє.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

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

Стандартна заборона всього вихідного трафіку

Ви можете створити «стандартну» політику ізоляції вихідного трафіку для простору імен, створивши NetworkPolicy, яка вибирає всі Podʼи, але не дозволяє жодного вихідного трафіку від цих Podʼів.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

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

Увага:

Стандартна політика заборони всього вихідного трафіку також блокує DNS-трафік. Якщо вашим робочим навантаженням потрібне розвʼязання DNS-імен, ви повинні додати окрему NetworkPolicy, яка дозволяє вихідний трафік до DNS-сервісу вашого кластера.

Дозвіл на весь вихідний трафік

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

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

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

Стандартна заборона всього вхідного та всього вихідного трафіку

Ви можете створити «стандартну» політику для простору імен, яка забороняє весь вхідний І вихідний трафік, створивши в цьому просторі імен наведену нижче NetworkPolicy.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Це гарантує, що навіть Podʼам, не вибраним жодною іншою NetworkPolicy, не буде дозволено вхідний або вихідний трафік.

Фільтрація мережевого трафіку

NetworkPolicy визначена для зʼєднань рівня 4 (TCP, UDP та, за потреби, SCTP). Для всіх інших протоколів поведінка може відрізнятися залежно від мережевого втулка.

Примітка:

Ви повинні використовувати втулок CNI, який підтримує NetworkPolicies для протоколу SCTP.

Коли визначено мережеву політику deny all, гарантовано забороняються лише зʼєднання TCP, UDP та SCTP. Для інших протоколів, таких як ARP чи ICMP, поведінка не визначена. Те саме стосується дозвільних правил: коли певний Pod дозволений як джерело вхідного трафіку або призначення вихідного трафіку, не визначено, що відбувається (наприклад) з пакетами ICMP. Такі протоколи, як ICMP, можуть бути дозволені одними мережевими втулками та заборонені іншими.

Націлювання на діапазон портів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.25

Під час написання NetworkPolicy ви можете націлитися на діапазон портів, а не на один порт.

Це можна зробити за допомогою поля endPort, як показано в наведеному нижче прикладі:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 32000
          endPort: 32768

Наведене вище правило дозволяє будь-якому Podʼу з міткою role=db у просторі імен default взаємодіяти з будь-якою IP-адресою в діапазоні 10.0.0.0/24 через TCP за умови, що цільовий порт перебуває в діапазоні від 32000 до 32768.

Під час використання цього поля застосовуються такі обмеження:

  • Поле endPort має бути рівним полю port або більшим за нього.
  • endPort можна визначити, лише якщо також визначено port.
  • Обидва порти мають бути числовими.

Примітка:

Ваш кластер повинен використовувати втулок CNI, який підтримує поле endPort у специфікаціях NetworkPolicy. Якщо ваш мережевий втулок не підтримує поле endPort, а ви вкажете NetworkPolicy із цим полем, політика буде застосована лише для одного поля port.

Націлювання на кілька просторів імен за міткою

У цьому сценарії ваша NetworkPolicy типу Egress націлена на кілька просторів імен за назвами їхніх міток. Щоб це працювало, потрібно додати мітки до цільових просторів імен. Наприклад:

kubectl label namespace frontend namespace=frontend
kubectl label namespace backend namespace=backend

Додайте мітки в namespaceSelector у документі NetworkPolicy. Наприклад:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-namespaces
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchExpressions:
        - key: namespace
          operator: In
          values: ["frontend", "backend"]

Примітка:

Неможливо безпосередньо вказати назви просторів імен у NetworkPolicy. Ви повинні використовувати namespaceSelector із matchLabels або matchExpressions, щоб вибрати простори імен за їхніми мітками.

Націлювання на простір імен за його назвою

Панель управління Kubernetes встановлює незмінну мітку kubernetes.io/metadata.name на всі простори імен, значенням мітки є назва простору імен.

Хоча NetworkPolicy не може націлитися на простір імен за його назвою через якесь поле обʼєкта, ви можете використати стандартизовану мітку, щоб націлитися на конкретний простір імен.

Життєвий цикл Podʼа

Примітка:

Наведене нижче стосується кластерів із сумісним мережевим втулком та сумісною реалізацією NetworkPolicy.

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

Щойно NetworkPolicy оброблено мережевим втулком,

  1. Усі новостворені Podʼи, на які впливає певна NetworkPolicy, будуть ізольовані до їхнього запуску. Реалізації NetworkPolicy повинні гарантувати, що фільтрація діє протягом усього життєвого циклу Podʼа, починаючи з першої миті запуску будь-якого контейнера в цьому Podʼі. Оскільки NetworkPolicies застосовуються на рівні Podʼа, вони однаково застосовуються до init-контейнерів, sidecar-контейнерів і звичайних контейнерів.

  2. Дозвільні правила будуть зрештою застосовані після правил ізоляції (або можуть бути застосовані одночасно з ними). У найгіршому випадку новостворений Pod може взагалі не мати мережевого звʼязку під час першого запуску, якщо правила ізоляції вже застосовані, а дозвільні правила ще ні.

Кожна створена NetworkPolicy рано чи пізно буде оброблена мережевим втулком, але через API Kubernetes неможливо дізнатися, коли саме це станеться.

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

Кожна NetworkPolicy рано чи пізно буде застосована до всіх вибраних Podʼів. Оскільки мережевий втулок може реалізовувати NetworkPolicy розподілено, Podʼи можуть бачити дещо неузгоджене подання мережевих політик під час першого створення Podʼа або коли змінюються Podʼи чи політики. Наприклад, новостворений Pod, який має мати доступ і до Podʼа A на вузлі 1, і до Podʼа B на вузлі 2, може виявити, що Pod A доступний йому одразу, а Pod B — лише через кілька секунд.

NetworkPolicy та Podʼи з hostNetwork

Поведінка NetworkPolicy для Podʼів із hostNetwork не визначена, але вона має обмежуватися двома можливостями:

  • Мережевий втулок може відрізняти трафік Podʼів із hostNetwork від усього іншого трафіку (зокрема, відрізняти трафік різних Podʼів із hostNetwork на тому самому вузлі) і застосовує NetworkPolicy до Podʼів із hostNetwork так само, як і до Podʼів у мережі Podʼів.
  • Мережевий втулок не може належним чином відрізнити трафік Podʼів із hostNetwork, тому він ігнорує такі Podʼи під час зіставлення podSelector та namespaceSelector. Трафік до Podʼів і від Podʼів із hostNetwork обробляється так само, як і весь інший трафік до IP-адреси вузла та від неї. (Це найпоширеніша реалізація.)

Це стосується випадків, коли

  1. Pod із hostNetwork вибирається за допомогою spec.podSelector.

      ...
      spec:
        podSelector:
          matchLabels:
            role: client
      ...
    
  2. Pod із hostNetwork вибирається за допомогою podSelector чи namespaceSelector у правилі ingress чи egress.

      ...
      ingress:
        - from:
          - podSelector:
              matchLabels:
                role: client
      ...
    

Водночас, оскільки Podʼи з hostNetwork мають ті самі IP-адреси, що й вузли, на яких вони розміщені, їхні зʼєднання вважатимуться зʼєднаннями вузла. Наприклад, ви можете дозволити трафік від Podʼа з hostNetwork за допомогою правила ipBlock.

Що не можна зробити за допомогою мережевих політик (принаймні поки що)

У Kubernetes 1.37 наведена нижче функціональність відсутня в API NetworkPolicy, але ви можете реалізувати обхідні рішення за допомогою компонентів операційної системи (таких як SELinux, OpenVSwitch, IPTables тощо), технологій рівня 7 (контролери Ingress, реалізації Service Mesh) або контролерів допуску. Якщо ви лише починаєте знайомитися з мережевою безпекою в Kubernetes, варто зазначити, що наведені нижче сценарії користувачів поки що не можна реалізувати за допомогою API NetworkPolicy.

  • Примусове проходження внутрішньокластерного трафіку через спільний шлюз (для цього, ймовірно, найкраще підійде service mesh або інший проксі).
  • Будь-що, повʼязане з TLS (для цього використовуйте service mesh або контролер Ingress).
  • Політики для окремих вузлів (для цього можна використовувати нотацію CIDR, але не можна націлитися на вузли саме за їхніми ідентифікаторами в Kubernetes).
  • Націлювання на Services за назвою (проте ви можете націлюватися на Podʼи чи простори імен за їхніми мітками, що часто є прийнятним обхідним рішенням).
  • Створення запитів на політики («Policy requests»), які виконує третя сторона, або керування ними.
  • Стандартні політики, які застосовуються до всіх просторів імен чи Podʼів (деякі сторонні дистрибутиви Kubernetes і проєкти можуть це робити).
  • Розширені запити до політик та інструменти перевірки досяжності.
  • Можливість логування подій мережевої безпеки (наприклад, заблокованих чи прийнятих зʼєднань).
  • Можливість явно забороняти політиками (наразі модель NetworkPolicies — стандартна заборона з можливістю додавати лише дозвільні правила).
  • Можливість заборонити loopback-трафік або вхідний трафік від хоста (Podʼи наразі не можуть блокувати доступ через localhost, а також не можуть блокувати доступ від вузла, на якому вони розміщені).

Вплив NetworkPolicy на наявні зʼєднання

Коли змінюється набір NetworkPolicies, що застосовуються до наявного зʼєднання, — через зміну самих NetworkPolicies або через зміну відповідних міток просторів імен чи Podʼів, вибраних політикою (і субʼєкта, і його співрозмовників (peers)), посеред наявного зʼєднання, — те, чи вплине ця зміна на це наявне зʼєднання, визначається реалізацією. Приклад: створено політику, яка призводить до заборони раніше дозволеного зʼєднання; реалізація базового мережевого втулка відповідає за визначення того, чи закриє ця нова політика наявні зʼєднання. Рекомендується не змінювати політики, Podʼи чи простори імен у спосіб, який може вплинути на наявні зʼєднання.

Що далі

5.5 - DNS для Services та Podʼів

Ваше робоче навантаження може виявляти Services у вашому кластері за допомогою DNS; на цій сторінці пояснено, як це працює.

Kubernetes створює DNS-записи для Services та Podʼів. Ви можете звертатися до Services за стабільними DNS-іменами замість IP-адрес.

Kubernetes публікує інформацію про Podʼи та Services, яка використовується для програмування DNS. kubelet налаштовує DNS Podʼів так, щоб запущені контейнери могли знаходити Services за іменем, а не за IP.

Services, визначеним у кластері, призначаються DNS-імена. Стандартно список пошуку DNS клієнтського Podʼа містить власний простір імен Podʼа та стандартний домен кластера.

Простори імен Services

DNS-запит може повертати різні результати залежно від простору імен Podʼа, який його робить. DNS-запити, які не вказують простір імен, обмежуються простором імен Podʼа. Щоб отримати доступ до Services в інших просторах імен, вказуйте простір імен у DNS-запиті.

Наприклад, розгляньмо Pod у просторі імен test. Service data міститься в просторі імен prod.

Запит data не повертає результатів, оскільки використовує простір імен Podʼа test.

Запит data.prod повертає очікуваний результат, оскільки вказує простір імен.

DNS-запити можуть бути розширені за допомогою /etc/resolv.conf Podʼа. kubelet налаштовує цей файл для кожного Podʼа. Наприклад, запит лише data може бути розширено до data.test.svc.cluster.local. Для розширення запитів використовуються значення параметра search. Щоб дізнатися більше про DNS-запити, див. сторінку довідки resolv.conf.

nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

Отже, Pod у просторі імен test може успішно розвʼязувати як data.prod, так і data.prod.svc.cluster.local.

DNS-записи

Для яких обʼєктів створюються DNS-записи?

  1. Services
  2. Podʼи

У розділах нижче докладно описано підтримувані типи DNS-записів та їхню структуру. Будь-яка інша структура, імена чи запити, які випадково працюють, вважаються деталями реалізації й можуть змінитися без попередження. Актуальнішу специфікацію див. у Kubernetes DNS-Based Service Discovery.

Services

Записи A/AAAA

«Звичайні» (не headless) Services отримують DNS-записи A та/або AAAA залежно від сімейства або сімейств IP цього Service, з іменем у вигляді my-svc.my-namespace.svc.cluster-domain.example. Це імʼя розвʼязується в кластерний IP Service.

Headless Services (без кластерного IP) теж отримують DNS-записи A та/або AAAA, з іменем у вигляді my-svc.my-namespace.svc.cluster-domain.example. На відміну від звичайних Services, це імʼя розвʼязується в набір IP-адрес усіх Podʼів, вибраних Service. Очікується, що клієнти використовуватимуть увесь набір або стандартний вибір round-robin із нього.

Записи SRV

Записи SRV створюються для іменованих портів, які є частиною звичайних або headless Services.

  • Для кожного іменованого порту SRV-запис має вигляд _port-name._port-protocol.my-svc.my-namespace.svc.cluster-domain.example.
  • Для звичайного Service він розвʼязується в номер порту та доменне імʼя: my-svc.my-namespace.svc.cluster-domain.example.
  • Для headless Service він розвʼязується в кілька відповідей, по одній для кожного Podʼа, що обслуговує Service, і містить номер порту та доменне імʼя Podʼа у вигляді hostname.my-svc.my-namespace.svc.cluster-domain.example.

Podʼи

Записи A/AAAA

Версії Kube-DNS до впровадження специфікації DNS мали таке розвʼязання DNS:

<pod-IPv4-address>.<namespace>.pod.<cluster-domain>

Наприклад, якщо Pod в просторі імен default має IP-адресу 172.17.0.3, а доменне імʼя вашого кластера — cluster.local, то у Podʼа буде DNS-імʼя:

172-17-0-3.default.pod.cluster.local

Деякі кластерні механізми DNS, такі як CoreDNS, також надають записи A для:

<pod-ipv4-address>.<service-name>.<my-namespace>.svc.<cluster-domain.example>

Наприклад, якщо Pod у просторі імен cafe має IP-адресу 172.17.0.3, є точкою доступу Service з назвою barista, а доменне імʼя вашого кластера — cluster.local, то Pod матиме такий запис DNS A в межах Service.

172-17-0-3.barista.cafe.svc.cluster.local

Поля hostname та subdomain Podʼа

Наразі під час створення Podʼа його імʼя хоста (як його видно зсередини Podʼа) — це значення metadata.name Podʼа.

У специфікації Podʼа є необовʼязкове поле hostname, яке можна використовувати, щоб вказати інше імʼя хоста. Якщо його вказано, воно має пріоритет над назвою Podʼа й стає іменем хоста Podʼа (знову ж таки, як його видно зсередини Podʼа). Наприклад, Pod зі spec.hostname, встановленим у "my-host", матиме імʼя хоста "my-host".

У специфікації Podʼа також є необовʼязкове поле subdomain, яким можна вказати, що Pod є частиною підгрупи простору імен. Наприклад, Pod зі spec.hostname, встановленим у "foo", і spec.subdomain, встановленим у "bar", у просторі імен "my-namespace" матиме імʼя хоста "foo" та повне доменне імʼя (FQDN) "foo.bar.my-namespace.svc.cluster.local" (знову ж таки, як його видно зсередини Podʼа).

Якщо в тому самому просторі імен, що й Pod, існує headless Service із такою самою назвою, як піддомен, DNS-сервер кластера також повертає записи A та/або AAAA для повного імені хоста Podʼа.

Приклад:

apiVersion: v1
kind: Service
metadata:
  name: busybox-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # name не є обовʼязковим для Services з одним портом
    port: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

Для наведеного вище Service "busybox-subdomain" і Podʼів, які встановлюють spec.subdomain у "busybox-subdomain", перший Pod бачитиме своє FQDN як "busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example". DNS обслуговує за цим іменем записи A та/або AAAA, що вказують на IP-адресу Podʼа. Обидва Podʼи, "busybox1" та "busybox2", матимуть власні адресні записи.

EndpointSlice може вказувати DNS-імʼя хоста для будь-яких адрес точок доступу разом з їхнім IP.

Примітка:

Записи A та AAAA не створюються для назв Podʼів, оскільки для Podʼа відсутній hostname. Pod без hostname, але з subdomain створить запис A або AAAA лише для headless Service (busybox-subdomain.my-namespace.svc.cluster-domain.example), що вказує на IP-адреси Podʼів. Крім того, Pod має бути готовим, щоб мати запис, якщо для Service не встановлено publishNotReadyAddresses=True.

Поле setHostnameAsFQDN Podʼа

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.22

Коли Pod налаштовано так, що він має повне доменне імʼя (FQDN), його імʼя хоста — це коротке імʼя хоста. Наприклад, якщо у вас є Pod із повним доменним імʼям busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example, то стандартно команда hostname в цьому Podʼі повертає busybox-1, а команда hostname --fqdn повертає FQDN.

Коли ви встановлюєте setHostnameAsFQDN: true у специфікації Podʼа, kubelet записує FQDN Podʼа в імʼя хоста для простору імен цього Podʼа. У цьому випадку як hostname, так і hostname --fqdn повертають FQDN Podʼа.

Примітка:

У Linux поле hostname ядра (поле nodename структури struct utsname) обмежено 64 символами.

Якщо Pod вмикає цю можливість, а його FQDN довше за 64 символи, він не запуститься. Pod залишиться у статусі Pending (ContainerCreating, як показує kubectl), генеруючи події помилок, такі як Failed to construct FQDN from Pod hostname and cluster domain, FQDN long-FQDN is too long (64 characters is the max, 70 characters requested). Один зі способів покращити досвід користувача в цьому сценарії — створити контролер вебхука допуску, щоб контролювати розмір FQDN під час створення користувачами обʼєктів верхнього рівня, наприклад Deployment.

Політика DNS Podʼа

Політику DNS можна встановлювати для кожного Podʼа окремо. Наразі Kubernetes підтримує такі політики DNS для Podʼів. Ці політики вказуються в полі dnsPolicy специфікації Podʼа.

  • "Default": Pod успадковує конфігурацію розвʼязання імен від вузла, на якому він працює. Докладніше дивіться в повʼязаному обговоренні.
  • "ClusterFirst": Будь-який DNS-запит, який не відповідає налаштованому суфіксу домену кластера, наприклад "www.kubernetes.io", DNS-сервер пересилає до вищестоящого сервера імен. Адміністратори кластера можуть налаштувати додаткові stub-домени та вищестоящі DNS-сервери. Як у цих випадках обробляються DNS-запити, дивіться в повʼязаному обговоренні.
  • "ClusterFirstWithHostNet": Для Podʼів, що працюють із hostNetwork, слід явно встановити політику DNS "ClusterFirstWithHostNet". Інакше Podʼи, що працюють із hostNetwork і "ClusterFirst", повернуться до поведінки політики "Default".

Примітка:

Це не підтримується в Windows. Деталі дивіться нижче.
  • "None": Дозволяє Podʼу ігнорувати налаштування DNS із середовища Kubernetes. Усі налаштування DNS мають надаватися за допомогою поля dnsConfig у специфікації Podʼа. Див. підрозділ Конфігурація DNS Podʼа нижче.

Примітка:

"Default" — це не стандартна політика DNS. Якщо dnsPolicy не вказано явно, використовується "ClusterFirst".

У прикладі нижче показано Pod із політикою DNS "ClusterFirstWithHostNet", оскільки hostNetwork встановлено в true.

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

Конфігурація DNS Podʼа

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.14

Конфігурація DNS Podʼа дає користувачам більше контролю над налаштуваннями DNS Podʼа.

Поле dnsConfig є необовʼязковим і може працювати з будь-якими налаштуваннями dnsPolicy. Однак коли dnsPolicy Podʼа встановлено в "None", поле dnsConfig потрібно вказати обовʼязково.

Нижче наведено властивості, які користувач може вказати в полі dnsConfig:

  • nameservers: список IP-адрес, які використовуватимуться як DNS-сервери для Podʼа. Можна вказати не більше 3 IP-адрес. Коли dnsPolicy Podʼа встановлено в "None", список повинен містити принаймні одну IP-адресу, інакше ця властивість є необовʼязковою. Указані сервери буде обʼєднано з базовими серверами імен, згенерованими із зазначеної політики DNS, а дубльовані адреси вилучено.
  • searches: список доменів пошуку DNS для пошуку імен хостів у Podʼі. Ця властивість є необовʼязковою. Якщо її вказано, цей список буде обʼєднано з базовими доменами пошуку, згенерованими з вибраної політики DNS. Дубльовані доменні імена вилучаються. Kubernetes дозволяє до 32 доменів пошуку.
  • options: необовʼязковий список обʼєктів, де кожний обʼєкт може мати властивість name (обовʼязкова) і властивість value (необовʼязкова). Вміст цієї властивості буде обʼєднано з параметрами, згенерованими із зазначеної політики DNS. Дубльовані записи вилучаються.

Ось приклад Podʼа з власними налаштуваннями DNS:

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 192.0.2.1 # this is an example
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

Коли наведений вище Pod створено, контейнер test отримує такий вміст у своєму файлі /etc/resolv.conf:

nameserver 192.0.2.1
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0

Для налаштування IPv6 шлях пошуку та сервер імен слід налаштувати так:

kubectl exec -it dns-example -- cat /etc/resolv.conf

Вивід буде подібний до такого:

nameserver 2001:db8:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.example
options ndots:5

Обмеження списку доменів пошуку DNS

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.28

Kubernetes сам не обмежує конфігурацію DNS, доки довжина списку доменів пошуку не перевищить 32 або загальна довжина всіх доменів пошуку не перевищить 2048. Це обмеження стосується файлу конфігурації резолвера вузла, конфігурації DNS Podʼа та обʼєднаної конфігурації DNS відповідно.

Примітка:

Деякі середовища виконання контейнерів раніших версій можуть мати власні обмеження щодо кількості доменів пошуку DNS. Залежно від середовища виконання контейнерів, Podʼи з великою кількістю доменів пошуку DNS можуть залишатися в стані очікування.

Відомо, що цю проблему мають containerd v1.5.5 або раніші та CRI-O v1.21 або раніші.

Розвʼязання DNS на вузлах Windows

  • ClusterFirstWithHostNet не підтримується для Podʼів, що працюють на вузлах Windows. Windows вважає всі імена з крапкою . повними доменними іменами (FQDN) і пропускає розвʼязання FQDN.
  • У Windows можна використовувати кілька резолверів DNS. Оскільки їхня поведінка дещо відрізняється, для розвʼязання імен рекомендовано використовувати командлет PowerShell Resolve-DNSName.
  • У Linux є список суфіксів DNS, який використовується після невдалого розвʼязання імені як повного. У Windows можна мати лише 1 суфікс DNS — суфікс, повʼязаний із простором імен цього Podʼа (наприклад, mydns.svc.cluster.local). Windows може розвʼязувати FQDN, Services або мережеві імена, які можна розвʼязати за допомогою цього єдиного суфікса. Наприклад, Pod, запущений у просторі імен default, матиме суфікс DNS default.svc.cluster.local. Усередині Podʼа Windows можна розвʼязувати і kubernetes.default.svc.cluster.local, і kubernetes, але не частково кваліфіковані імена (kubernetes.default або kubernetes.default.svc).

Що далі

Настанови щодо адміністрування конфігурацій DNS дивіться на сторінці Налаштування DNS Service.

5.6 - Подвійний стек IPv4/IPv6

Kubernetes дозволяє налаштувати мережу з одним стеком IPv4, мережу з одним стеком IPv6 або мережу з подвійним стеком, де активні обидва мережеві сімейства. На цій сторінці пояснено, як це зробити.
СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.23

Двостекова мережа IPv4/IPv6 дозволяє виділяти адреси IPv4 та IPv6 для Podʼів та Services.

Двостекова мережа IPv4/IPv6 стандартно увімкнена у вашому кластері Kubernetes починаючи з версії 1.21, що дозволяє одночасно призначати адреси IPv4 та IPv6.

Підтримувані можливості

Двостекова мережа IPv4/IPv6 у вашому кластері Kubernetes надає такі можливості:

  • Двостекова мережа Podʼів (одна адреса IPv4 та одна адреса IPv6 на Pod)
  • Services з підтримкою IPv4 та IPv6
  • Маршрутизація вихідного трафіку Podʼів за межі кластера (наприклад, в Інтернет) через інтерфейси IPv4 та IPv6

Передумови

Щоб використовувати двостекові кластери Kubernetes IPv4/IPv6, потрібно виконати такі передумови:

  • Kubernetes 1.20 або новіше

    Інформацію про використання двостекових Services із попередніми версіями Kubernetes дивіться в документації відповідної версії Kubernetes.

  • Підтримка двостекової мережі провайдером (хмарний чи інший провайдер має надати вузлам Kubernetes маршрутизовані мережеві інтерфейси IPv4/IPv6)

  • Мережевий втулок, який підтримує двостекову мережу.

Налаштування двостекової мережі IPv4/IPv6

Щоб налаштувати подвійний стек IPv4/IPv6, задайте двостекові мережеві призначення кластера:

  • kube-apiserver:
    • --service-cluster-ip-range=<CIDR IPv4>,<CIDR IPv6>
  • kube-controller-manager:
    • --cluster-cidr=<CIDR IPv4>,<CIDR IPv6>
    • --service-cluster-ip-range=<CIDR IPv4>,<CIDR IPv6>
    • --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 стандартно /24 для IPv4 та /64 для IPv6
  • kube-proxy:
    • --cluster-cidr=<CIDR IPv4>,<CIDR IPv6>
  • kubelet:
    • --node-ip=<IP IPv4>,<IP IPv6>
      • Цей параметр є обовʼязковим для двостекових вузлів bare metal (вузлів, які не визначають хмарного провайдера прапорцем --cloud-provider). Якщо ви використовуєте хмарного провайдера й вирішили перевизначити IP-адреси вузлів, вибрані ним, встановіть параметр --node-ip.
      • (Застарілі вбудовані хмарні провайдери не підтримують двостековий --node-ip.)

Примітка:

Приклад CIDR IPv4: 10.244.0.0/16 (хоча ви вказуватимете власний діапазон адрес)

Приклад CIDR IPv6: fdXY:IJKL:MNOP:15::/64 (це показує формат, але не є дійсною адресою — дивіться RFC 4193)

Services

Ви можете створювати Services, які можуть використовувати IPv4, IPv6 або обидва.

Сімейство адрес Service стандартно відповідає сімейству адрес першого діапазону кластерних IP Services (налаштованого через прапорець --service-cluster-ip-range для kube-apiserver).

Під час визначення Service ви можете за бажанням налаштувати його як двостековий. Щоб вказати потрібну поведінку, встановіть у полі .spec.ipFamilyPolicy одне з таких значень:

  • SingleStack: одностековий Service. Панель управління виділяє кластерний IP для Service, використовуючи перший налаштований діапазон кластерних IP Services.
  • PreferDualStack: виділяє кластерні IP IPv4 та IPv6 для Service, коли подвійний стек увімкнено. Якщо подвійний стек не увімкнено або він не підтримується, застосовується одностекова поведінка.
  • RequireDualStack: виділяє .spec.clusterIPs Service з діапазонів адрес IPv4 та IPv6, коли подвійний стек увімкнено. Якщо подвійний стек не увімкнено або він не підтримується, створення обʼєкта API Service завершиться невдачею.
    • Вибирає .spec.clusterIP зі списку .spec.clusterIPs на основі сімейства адрес першого елемента у масиві .spec.ipFamilies.

Якщо ви хочете визначити, яке сімейство IP використовувати для одного стека, або задати порядок сімейств IP для подвійного стека, ви можете вибрати сімейства адрес, встановивши необовʼязкове поле .spec.ipFamilies у Service.

Примітка:

Поле .spec.ipFamilies умовно змінюване: ви можете додавати або видаляти вторинне сімейство IP-адрес, але не можете змінювати основне сімейство IP-адрес наявного Service.

Ви можете встановити .spec.ipFamilies в будь-яке з таких значень масиву:

  • ["IPv4"]
  • ["IPv6"]
  • ["IPv4","IPv6"] (подвійний стек)
  • ["IPv6","IPv4"] (подвійний стек)

Перше вказане вами сімейство використовується для застарілого поля .spec.clusterIP.

Сценарії конфігурації двостекового Service

Ці приклади демонструють поведінку різних сценаріїв конфігурації двостекового Service.

Параметри подвійного стека в нових Services

  1. Специфікація цього Service явно не визначає .spec.ipFamilyPolicy. Коли ви створюєте цей Service, Kubernetes призначає кластерний IP для Service з першого налаштованого service-cluster-ip-range і встановлює .spec.ipFamilyPolicy у SingleStack. (Services без селекторів та headless Services із селекторами поводитимуться так само.)

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
    spec:
      selector:
        app: MyApp
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9376
  2. Специфікація цього Service явно визначає PreferDualStack в .spec.ipFamilyPolicy. Коли ви створюєте цей Service у двостековому кластері, Kubernetes призначає адреси IPv4 та IPv6 для Service. Панель управління оновлює .spec Service, щоб зафіксувати призначені IP-адреси. Поле .spec.clusterIPs є основним і містить обидві призначені IP-адреси; .spec.clusterIP є вторинним полем, значення якого обчислюється з .spec.clusterIPs.

    • Для поля .spec.clusterIP панель управління записує IP-адресу з того самого сімейства адрес, що й перший діапазон кластерних IP Services.
    • В одностековому кластері обидва поля .spec.clusterIPs та .spec.clusterIP містять лише одну адресу.
    • У кластері з увімкненим подвійним стеком вказання RequireDualStack в .spec.ipFamilyPolicy працює так само, як і PreferDualStack.
    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      ipFamilyPolicy: PreferDualStack
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    
  3. Специфікація цього Service явно визначає IPv6 та IPv4 в .spec.ipFamilies, а також PreferDualStack в .spec.ipFamilyPolicy. Коли Kubernetes призначає адреси IPv6 та IPv4 у .spec.clusterIPs, .spec.clusterIP встановлюється на адресу IPv6, оскільки це перший елемент масиву .spec.clusterIPs, що перевизначає стандартну поведінку.

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
      labels:
        app.kubernetes.io/name: MyApp
    spec:
      ipFamilyPolicy: PreferDualStack
      ipFamilies:
      - IPv6
      - IPv4
      selector:
        app.kubernetes.io/name: MyApp
      ports:
        - protocol: TCP
          port: 80
    

Стандартні значення подвійного стека в наявних Services

Ці приклади демонструють стандартну поведінку, коли подвійний стек щойно увімкнено в кластері, де вже існують Services. (Оновлення наявного кластера до версії 1.21 або новішої вмикає подвійний стек.)

  1. Коли в кластері увімкнено подвійний стек, панель управління налаштовує наявні Services (чи то IPv4, чи IPv6), встановлюючи .spec.ipFamilyPolicy у SingleStack, а .spec.ipFamilies — у сімейство адрес наявного Service. Кластерний IP наявного Service зберігається в .spec.clusterIPs.

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
    spec:
      selector:
        app: MyApp
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9376

    Цю поведінку можна перевірити, переглянувши наявний Service за допомогою kubectl.

    kubectl get svc my-service -o yaml
    
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app.kubernetes.io/name: MyApp
      name: my-service
    spec:
      clusterIP: 10.0.197.123
      clusterIPs:
      - 10.0.197.123
      ipFamilies:
      - IPv4
      ipFamilyPolicy: SingleStack
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        app.kubernetes.io/name: MyApp
      type: ClusterIP
    status:
      loadBalancer: {}
    
  2. Коли в кластері увімкнено подвійний стек, панель управління налаштовує наявні headless Services із селекторами, встановлюючи .spec.ipFamilyPolicy у SingleStack, а .spec.ipFamilies — у сімейство адрес першого діапазону кластерних IP Services (налаштованого через прапорець --service-cluster-ip-range для kube-apiserver), навіть якщо .spec.clusterIP встановлено в None.

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
    spec:
      selector:
        app: MyApp
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9376

    Цю поведінку можна перевірити, переглянувши наявний headless Service із селекторами за допомогою kubectl.

    kubectl get svc my-service -o yaml
    
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app.kubernetes.io/name: MyApp
      name: my-service
    spec:
      clusterIP: None
      clusterIPs:
      - None
      ipFamilies:
      - IPv4
      ipFamilyPolicy: SingleStack
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        app.kubernetes.io/name: MyApp
    

Перемикання Services між одностековим та двостековим режимами

Services можна перемикати з одностекового режиму на двостековий і навпаки.

  1. Щоб перемкнути Service з одностекового режиму на двостековий, змініть .spec.ipFamilyPolicy з SingleStack на PreferDualStack або RequireDualStack, залежно від потреби. Коли ви переводите цей Service з одностекового режиму на двостековий, Kubernetes призначає відсутнє сімейство адрес, тож Service тепер має адреси IPv4 та IPv6.

    Відредагуйте специфікацію Service, оновивши .spec.ipFamilyPolicy з SingleStack на PreferDualStack.

    До:

    spec:
      ipFamilyPolicy: SingleStack
    

    Після:

    spec:
      ipFamilyPolicy: PreferDualStack
    
  2. Щоб перемкнути Service з двостекового режиму на одностековий, змініть .spec.ipFamilyPolicy з PreferDualStack або RequireDualStack на SingleStack. Коли ви переводите цей Service з двостекового режиму на одностековий, Kubernetes залишає лише перший елемент масиву .spec.clusterIPs, встановлює .spec.clusterIP на цю IP-адресу, а .spec.ipFamilies — на сімейство адрес .spec.clusterIPs.

Headless Services без селектора

Для Headless Services без селекторів і без явно вказаного .spec.ipFamilyPolicy стандартним значенням поля .spec.ipFamilyPolicy є RequireDualStack.

Service типу LoadBalancer

Щоб створити двостековий балансувальник навантаження для вашого Service:

  • Встановіть значення поля .spec.type в LoadBalancer
  • Встановіть значення поля .spec.ipFamilyPolicy в PreferDualStack або RequireDualStack

Примітка:

Щоб використовувати двостековий Service типу LoadBalancer, ваш хмарний провайдер має підтримувати балансувальники навантаження IPv4 та IPv6.

Вихідний трафік

Якщо ви хочете увімкнути вихідний трафік, щоб досягати призначень за межами кластера (наприклад, публічного Інтернету) з Podʼа, який використовує адреси IPv6, що не маршрутизуються публічно, вам потрібно дати Podʼу змогу використовувати публічно маршрутизовану адресу IPv6 за допомогою такого механізму, як прозоре проксіювання або IP-маскування. Проєкт ip-masq-agent підтримує IP-маскування у двостекових кластерах.

Примітка:

Переконайтеся, що ваш провайдер CNI підтримує IPv6.

Підтримка Windows

Kubernetes у Windows не підтримує одностекову мережу «лише IPv6». Однак підтримується двостекова мережа IPv4/IPv6 для Podʼів і вузлів з одностековими Services.

Ви можете використовувати двостекову мережу IPv4/IPv6 з мережами l2bridge.

Примітка:

Оверлейні мережі (VXLAN) у Windows не підтримують двостекову мережу.

Докладніше про різні режими мережі у Windows читайте в розділі Мережа у Windows.

Що далі

5.7 - Маршрутизація з урахуванням топології

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

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

Мотивація

Кластери Kubernetes дедалі частіше розгортаються в багатозональних середовищах. Маршрутизація з урахуванням топології надає механізм, який допомагає утримувати трафік у межах зони, з якої він надійшов. Під час обчислення точок доступу для Service контролер EndpointSlice враховує топологію (регіон і зону) кожної точки доступу та заповнює поле hints, щоб призначити її зоні. Компоненти кластера, такі як kube-proxy, можуть використовувати ці підказки, щоб впливати на маршрутизацію трафіку (надаючи перевагу топологічно ближчим точкам доступу).

Увімкнення маршрутизації з урахуванням топології

Ви можете увімкнути маршрутизацію з урахуванням топології для Service, встановивши анотацію service.kubernetes.io/topology-mode у значення Auto. Коли в кожній зоні є достатньо точок доступу, в EndpointSlices заповнюються підказки топології (Topology Hints), що призначають окремі точки доступу конкретним зонам, і трафік маршрутизується ближче до місця, звідки він надійшов.

Коли це працює найкраще

Ця можливість працює найкраще, коли:

1. Вхідний трафік рівномірно розподілений

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

2. У Service є 3 чи більше точок доступу на зону

У кластері з трьома зонами це означає 9 чи більше точок доступу. Якщо точок доступу менше ніж 3 на зону, існує висока (≈50%) ймовірність, що контролер EndpointSlice не зможе рівномірно розподілити точки доступу й натомість повернеться до стандартного підходу до маршрутизації в межах кластера.

Як це працює

Евристика "Auto" намагається пропорційно розподілити кількість точок доступу між зонами. Зверніть увагу, що ця евристика працює найкраще для Services зі значною кількістю точок доступу.

Контролер EndpointSlice

Контролер EndpointSlice відповідає за встановлення підказок в EndpointSlices, коли цю евристику увімкнено. Контролер призначає кожній зоні пропорційну кількість точок доступу. Ця пропорція ґрунтується на доступних для розподілу ядрах CPU вузлів, що працюють у цій зоні. Наприклад, якщо в одній зоні є 2 ядра CPU, а в іншій лише 1 ядро CPU, контролер призначить удвічі більше точок доступу зоні з 2 ядрами CPU.

У цьому прикладі показано, як виглядає EndpointSlice із заповненими підказками:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-hints
  labels:
    kubernetes.io/service-name: example-svc
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    zone: zone-a
    hints:
      forZones:
        - name: "zone-a"

kube-proxy

Компонент kube-proxy фільтрує точки доступу, до яких він маршрутизує трафік, на основі підказок, встановлених контролером EndpointSlice. У більшості випадків це означає, що kube-proxy може маршрутизувати трафік до точок доступу в тій самій зоні. Іноді контролер призначає точки доступу з іншої зони, щоб забезпечити рівномірніший розподіл точок доступу між зонами. Через це частина трафіку маршрутизуватиметься до інших зон.

Запобіжники

Панель управління Kubernetes та kube-proxy на кожному вузлі застосовують деякі запобіжні правила перед використанням Topology Aware Hints. Якщо ці перевірки не проходять, kube-proxy вибирає точки доступу з будь-якої частини вашого кластера, незалежно від зони.

  1. Недостатня кількість точок доступу: Якщо точок доступу менше, ніж зон у кластері, контролер не призначатиме жодних підказок.

  2. Неможливо досягти збалансованого розподілу: У деяких випадках досягти збалансованого розподілу точок доступу між зонами неможливо. Наприклад, якщо zone-a удвічі більша за zone-b, але є лише 2 точки доступу, точка доступу, призначена zone-a, може отримувати вдвічі більше трафіку, ніж точка доступу zone-b. Контролер не призначає підказок, якщо не може знизити це значення «очікуваного перевантаження» нижче прийнятного порогу для кожної зони. Важливо, що це не ґрунтується на зворотному звʼязку в реальному часі. Окремі точки доступу все ще можуть бути перевантажені.

  3. Один чи кілька вузлів мають недостатньо інформації: Якщо будь-який вузол не має мітки topology.kubernetes.io/zone або не повідомляє значення CPU, доступного для розподілу, панель управління не встановлює жодних топологічних підказок для точок доступу, тож kube-proxy не фільтрує точки доступу за зоною.

  4. Одна чи кілька точок доступу не мають підказки щодо зони: У такому разі kube-proxy вважає, що відбувається перехід до Topology Aware Hints або від них. Фільтрувати точки доступу для Service в такому стані було б небезпечно, тому kube-proxy повертається до використання всіх точок доступу.

  5. Зону не представлено в підказках: Якщо kube-proxy не може знайти принаймні одну точку доступу з підказкою для зони, в якій він працює, він повертається до використання точок доступу з усіх зон. Найімовірніше це трапиться, коли ви додаєте нову зону до наявного кластера.

Обмеження

  • Topology Aware Hints не використовуються, коли для Service internalTrafficPolicy встановлено в Local. Обидві можливості можна використовувати в одному кластері для різних Services, але не для одного Service.

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

  • Контролер EndpointSlice ігнорує неготові вузли під час обчислення пропорцій для кожної зони. Це може мати небажані наслідки, якщо значна частина вузлів неготова.

  • Контролер EndpointSlice ігнорує вузли з мітками node-role.kubernetes.io/control-plane або node-role.kubernetes.io/master. Це може бути проблемою, якщо на цих вузлах також працюють робочі навантаження.

  • Контролер EndpointSlice не враховує tolerations під час розгортання або обчислення пропорцій для кожної зони. Якщо Podʼи, що обслуговують Service, обмежені підмножиною вузлів кластера, це не враховуватиметься.

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

Власні евристики

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

Що далі

5.8 - Мережа у Windows

Kubernetes підтримує роботу вузлів як на Linux, так і на Windows. Ви можете поєднувати обидва види вузлів в одному кластері. На цій сторінці наведено огляд мережевих аспектів, специфічних для операційної системи Windows.

Мережа контейнерів у Windows

Мережа для контейнерів Windows надається через втулки CNI. Контейнери Windows працюють подібно до віртуальних машин з погляду мережі. Кожен контейнер має віртуальний мережевий адаптер (vNIC), підключений до віртуального комутатора Hyper-V (vSwitch). Host Networking Service (HNS) та Host Compute Service (HCS) спільно створюють контейнери й підключають vNIC контейнерів до мереж. HCS відповідає за керування контейнерами, а HNS — за керування мережевими ресурсами, такими як:

  • Віртуальні мережі (зокрема створення vSwitches)
  • Точки доступу / vNIC
  • Namespaces
  • Політики, зокрема інкапсуляція пакетів, правила балансування навантаження, ACL та правила NAT.

HNS та vSwitch у Windows реалізують простори імен і можуть за потреби створювати віртуальні NIC для Podʼа чи контейнера. Однак багато конфігурацій, таких як DNS, маршрути та метрики, зберігаються в базі даних реєстру Windows, а не у файлах в /etc, як у Linux. Реєстр Windows контейнера відокремлений від реєстру хоста, тому такі прийоми, як зіставлення /etc/resolv.conf з хоста в контейнер, не дають того ефекту, що в Linux. Це потрібно налаштовувати за допомогою API Windows, що виконуються в контексті цього контейнера. Тому реалізації CNI повинні викликати HNS, а не покладатися на зіставлення файлів, щоб передавати мережеві дані в Pod чи контейнер.

Режими мережі

Windows підтримує пʼять різних мережевих драйверів/режимів: L2bridge, L2tunnel, Overlay (Beta), Transparent та NAT. У гетерогенному кластері з робочими вузлами Windows і Linux потрібно вибрати мережеве рішення, сумісне і з Windows, і з Linux. У таблиці нижче наведено зовнішні (out-of-tree) втулки, які підтримуються у Windows, з рекомендаціями щодо використання кожного CNI:

Мережевий драйверОписЗміни пакетів контейнерівМережеві втулкиХарактеристики мережевих втулків
L2bridgeКонтейнери підключені до зовнішнього vSwitch. Контейнери підключені до базової мережі (underlay), хоча фізичній мережі не потрібно знати MAC-адреси контейнерів, оскільки вони переписуються на вході та виході.MAC переписується на MAC хоста, IP може бути переписаний на IP хоста за допомогою політики HNS OutboundNAT.win-bridge, Azure-CNI, Flannel host-gateway використовує win-bridgewin-bridge використовує режим мережі L2bridge, підключає контейнери до базової мережі хостів і забезпечує найкращу продуктивність. Потребує маршрутів, визначених користувачем (UDR), для звʼязку між вузлами.
L2TunnelЦе особливий випадок l2bridge, який використовується лише в Azure. Усі пакети надсилаються на хост віртуалізації, де застосовується політика SDN.MAC переписується, IP видно в базовій мережіAzure-CNIAzure-CNI дозволяє інтегрувати контейнери з Azure vNET і дає їм змогу використовувати набір можливостей, які надає Azure Virtual Network. Наприклад, безпечно підключатися до сервісів Azure або використовувати NSG Azure. Див. azure-cni, де є кілька прикладів
OverlayКонтейнерам надається vNIC, підключений до зовнішнього vSwitch. Кожна оверлейна мережа отримує власну IP-підмережу, визначену власним IP-префіксом. Драйвер оверлейної мережі використовує інкапсуляцію VXLAN.Інкапсулюється із зовнішнім заголовком.win-overlay, Flannel VXLAN (використовує win-overlay)win-overlay слід використовувати, коли віртуальні мережі контейнерів потрібно ізолювати від базової мережі хостів (наприклад, з міркувань безпеки). Дозволяє повторно використовувати IP-адреси для різних оверлейних мереж (які мають різні теґи VNID), якщо у вашому центрі обробки даних обмаль IP-адрес. Цей варіант потребує KB4489899 у Windows Server 2019.
Transparent (особливий випадок для ovn-kubernetes)Потребує зовнішнього vSwitch. Контейнери підключені до зовнішнього vSwitch, що забезпечує взаємодію всередині Podʼа через логічні мережі (логічні комутатори та маршрутизатори).Пакет інкапсулюється через тунелювання GENEVE або STT, щоб досягти Podʼів, які не перебувають на тому самому хості.
Пакети пересилаються або відкидаються на основі метаданих тунелю, які надає мережевий контролер ovn.
NAT виконується для трафіку північ-південь.
ovn-kubernetesРозгортається за допомогою ansible. Розподілені ACL можна застосовувати за допомогою політик Kubernetes. Підтримка IPAM. Балансування навантаження можливе без kube-proxy. NAT виконується без використання iptables/netsh.
NAT (не використовується в Kubernetes)Контейнерам надається vNIC, підключений до внутрішнього vSwitch. DNS/DHCP надається внутрішнім компонентом, який називається WinNATMAC та IP переписуються на MAC/IP хоста.natНаведено тут для повноти

Як зазначено вище, втулок CNI Flannel також підтримується у Windows через мережевий бекенд VXLAN (бета-підтримка; делегує до win-overlay) та мережевий бекенд host-gateway (стабільна підтримка; делегує до win-bridge).

Цей втулок підтримує делегування одному з еталонних втулків CNI (win-overlay, win-bridge) для спільної роботи з демоном Flannel у Windows (Flanneld), щоб автоматично призначати оренду підмережі вузла та створювати мережу HNS. Цей втулок читає власний файл конфігурації (cni.conf) і обʼєднує його зі змінними середовища з файлу subnet.env, згенерованого FlannelD. Потім він делегує налаштування мережі одному з еталонних втулків CNI й надсилає правильну конфігурацію з призначеною вузлу підмережею до втулка IPAM (наприклад, host-local).

Для обʼєктів Node, Pod та Service підтримуються такі мережеві потоки для трафіку TCP/UDP:

  • Pod → Pod (IP)
  • Pod → Pod (імʼя)
  • Pod → Service (кластерний IP)
  • Pod → Service (PQDN, але лише якщо немає ".")
  • Pod → Service (FQDN)
  • Pod → зовнішній (IP)
  • Pod → зовнішній (DNS)
  • Node → Pod
  • Pod → Node

Керування IP-адресами (IPAM)

У Windows підтримуються такі варіанти IPAM:

Пряме повернення до сервера (DSR)

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.34
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.34. It was first available in the v1.14 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate WinDSR, Kubernetes ignores it but does not report any error.

Режим балансування навантаження, у якому виправлення IP-адрес і LBNAT відбуваються безпосередньо на порту vSwitch контейнера; трафік сервісу надходить з IP-адресою джерела, що дорівнює IP-адресі вихідного Podʼа. Це оптимізує продуктивність, дозволяючи зворотному трафіку, що маршрутизується через балансувальники навантаження, оминати балансувальник і відповідати безпосередньо клієнту; це зменшує навантаження на балансувальник і загальну затримку. Докладніше читайте в Direct Server Return (DSR) in a nutshell.

Балансування навантаження та Services

Service Kubernetes — це абстракція, яка визначає логічний набір Podʼів і спосіб доступу до них мережею. У кластері з вузлами Windows можна використовувати такі типи Service:

  • NodePort
  • ClusterIP
  • LoadBalancer
  • ExternalName

Мережа контейнерів Windows у деяких важливих аспектах відрізняється від мережі Linux. Документація Microsoft про мережу контейнерів Windows містить додаткові подробиці та контекст.

У Windows можна використовувати такі налаштування для конфігурації Services та поведінки балансування навантаження:

Налаштування Services у Windows
МожливістьОписМінімальна підтримувана збірка ОС WindowsЯк увімкнути
Спорідненість сесії (session affinity)Гарантує, що підключення від певного клієнта щоразу передаються тому самому Podʼу.Windows Server 2022Встановіть service.spec.sessionAffinity у "ClientIP"
Direct Server Return (DSR)Див. примітки про DSR вище.Windows Server 2019Встановіть такий аргумент командного рядка (для версії 1.37): --enable-dsr=true
Preserve-DestinationПропускає DNAT трафіку сервісу, зберігаючи віртуальну IP-адресу цільового сервісу в пакетах, що досягають Podʼа-бекенда. Також вимикає пересилання між вузлами.Windows Server, версія 1903Встановіть "preserve-destination": "true" в анотаціях сервісу та увімкніть DSR у kube-proxy.
Двостекова мережа IPv4/IPv6Нативний звʼязок IPv4-IPv4 паралельно зі звʼязком IPv6-IPv6 до кластера, з нього та всередині ньогоWindows Server 2019Див. Подвійний стек IPv4/IPv6
Збереження IP клієнтаГарантує збереження IP-адреси джерела вхідного трафіку. Також вимикає пересилання між вузлами.Windows Server 2019Встановіть service.spec.externalTrafficPolicy у "Local" та увімкніть DSR у kube-proxy

Обмеження

Така мережева функціональність не підтримується на вузлах Windows:

  • Режим мережі хоста
  • Локальний доступ до NodePort із самого вузла (працює для інших вузлів або зовнішніх клієнтів)
  • Понад 64 Podʼи-бекенди (або унікальні адреси призначення) для одного Service
  • Звʼязок IPv6 між Podʼами Windows, підключеними до оверлейних мереж
  • Local Traffic Policy в режимі без DSR
  • Вихідний звʼязок за протоколом ICMP через win-overlay, win-bridge або за допомогою втулка Azure-CNI. Зокрема, площина даних Windows (VFP) не підтримує транспозицію пакетів ICMP, і це означає:
    • Пакети ICMP, спрямовані до призначень у тій самій мережі (наприклад, звʼязок між Podʼами через ping), працюють як очікується;
    • TCP/UDP-пакети працюють, як очікується;
    • Пакети ICMP, спрямовані через віддалену мережу (наприклад, звʼязок Podʼа із зовнішнім інтернетом через ping), не можуть бути транспоновані, тому не маршрутизуються назад до свого джерела;
    • Оскільки TCP/UDP-пакети все ще можуть бути транспоновані, під час налагодження звʼязку із зовнішнім світом ви можете замінити ping <destination> на curl <destination>.

Інші обмеження:

  • Еталонні мережеві втулки Windows win-bridge та win-overlay не реалізують специфікацію CNI v0.4.0 через відсутність реалізації CHECK.
  • Втулок CNI Flannel VXLAN має такі обмеження у Windows:
    • Звʼязок між вузлом і Podʼом можливий лише для локальних Podʼів із Flannel v0.12.0 (або новішим).
    • Flannel обмежений використанням VNI 4096 та UDP-порту 4789. Докладніше про ці параметри див. в офіційній документації бекенда Flannel VXLAN.

5.9 - Виділення ClusterIP для Service

У Kubernetes Services — це абстрактний спосіб надати доступ до застосунку, що працює на наборі Podʼів. Services можуть мати віртуальну IP-адресу в межах кластера (за допомогою Service з type: ClusterIP). Клієнти можуть підключатися за цією віртуальною IP-адресою, а Kubernetes балансує трафік до цього Service між різними Podʼами, що його обслуговують.

Як виділяються ClusterIP для Services?

Коли Kubernetes потрібно призначити віртуальну IP-адресу для Service, це відбувається одним із двох способів:

динамічно
панель управління кластера автоматично вибирає вільну IP-адресу з налаштованого діапазону IP для Services із type: ClusterIP.
статично
ви вказуєте IP-адресу на свій вибір із налаштованого діапазону IP для Services.

У всьому вашому кластері ClusterIP кожного Service має бути унікальним. Спроба створити Service із конкретним ClusterIP, який уже виділено, поверне помилку.

Навіщо резервувати Cluster IP для Services?

Іноді вам може знадобитися, щоб Services працювали на добре відомих IP-адресах, аби інші компоненти та користувачі в кластері могли ними користуватися.

Найкращий приклад — DNS Service для кластера. За неформальною домовленістю деякі інсталятори Kubernetes призначають DNS Service 10-ту IP-адресу з діапазону IP для Services. Припустимо, ви налаштували кластер із діапазоном IP для Services 10.96.0.0/16 і хочете, щоб IP-адресою вашого DNS Service була 10.96.0.10; тоді вам доведеться створити такий Service:

apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: CoreDNS
  name: kube-dns
  namespace: kube-system
spec:
  clusterIP: 10.96.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  selector:
    k8s-app: kube-dns
  type: ClusterIP

Але, як уже пояснювалося, IP-адресу 10.96.0.10 не зарезервовано. Якщо інші Services буде створено раніше або паралельно з динамічним виділенням, є ймовірність, що їм буде виділено цю IP-адресу. Тоді ви не зможете створити DNS Service, оскільки це завершиться помилкою конфлікту.

Як уникнути конфліктів ClusterIP для Services?

Стратегія виділення ClusterIP для Services, реалізована в Kubernetes, зменшує ризик колізій.

Діапазон ClusterIP поділено на основі формули min(max(16, cidrSize / 16), 256), яку можна описати так: ніколи не менше ніж 16 і не більше ніж 256, з поступовим кроком між ними.

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

Приклади

Приклад 1

У цьому прикладі використовується діапазон IP-адрес: 10.96.0.0/24 (нотація CIDR) для IP-адрес Services.

Розмір діапазону: 28 - 2 = 254 Зсув смуги: min(max(16, 256/16), 256) = min(16, 256) = 16 Початок статичної смуги: 10.96.0.1 Кінець статичної смуги: 10.96.0.16 Кінець діапазону: 10.96.0.254

pie showData
    title 10.96.0.0/24
    "Статичний" : 16
    "Динамічний" : 238

Приклад 2

У цьому прикладі використовується діапазон IP-адрес: 10.96.0.0/20 (нотація CIDR) для IP-адрес Services.

Розмір діапазону: 212 - 2 = 4094 Зсув смуги: min(max(16, 4096/16), 256) = min(256, 256) = 256 Початок статичної смуги: 10.96.0.1 Кінець статичної смуги: 10.96.1.0 Кінець діапазону: 10.96.15.254

pie showData
    title 10.96.0.0/20
    "Статичний" : 256
    "Динамічний" : 3838

Приклад 3

У цьому прикладі використовується діапазон IP-адрес: 10.96.0.0/16 (нотація CIDR) для IP-адрес Services.

Розмір діапазону: 216 - 2 = 65534 Зсув смуги: min(max(16, 65536/16), 256) = min(4096, 256) = 256 Початок статичної смуги: 10.96.0.1 Кінець статичної смуги: 10.96.1.0 Кінець діапазону: 10.96.255.254

pie showData
    title 10.96.0.0/16
    "Статичний" : 256
    "Динамічний" : 65278

Що далі

5.10 - Політика внутрішнього трафіку Service

Якщо два Podʼи у вашому кластері хочуть взаємодіяти, і обидва Podʼи фактично працюють на одному вузлі, використовуйте політику внутрішнього трафіку Service, щоб утримувати мережевий трафік у межах цього вузла. Якщо уникнути проходження трафіку туди й назад через мережу кластера, це може допомогти з надійністю, продуктивністю (затримкою мережі та пропускною здатністю) або вартістю.
СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.26

Політика внутрішнього трафіку Service дозволяє обмежити внутрішній трафік, маршрутизуючи його лише до точок доступу в межах вузла, з якого надійшов трафік. Тут «внутрішній» трафік означає трафік, який надходить від Podʼів у поточному кластері. Це може допомогти зменшити витрати та покращити продуктивність.

Використання політики внутрішнього трафіку Service

Ви можете увімкнути політику лише для внутрішнього трафіку для Service, встановивши його .spec.internalTrafficPolicy у Local. Це вказує kube-proxy використовувати для внутрішнього трафіку кластера лише локальні для вузла точки доступу.

Примітка:

Для Podʼів на вузлах без точок доступу для певного Service цей Service поводиться так, ніби в нього немає точок доступу (для Podʼів на цьому вузлі), навіть якщо він має точки доступу на інших вузлах.

У цьому прикладі показано, як виглядає Service, коли ви встановлюєте .spec.internalTrafficPolicy у Local:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  internalTrafficPolicy: Local

Як це працює

kube-proxy фільтрує точки доступу, до яких він маршрутизує трафік, на основі параметра spec.internalTrafficPolicy. Коли встановлено Local, враховуються лише локальні для вузла точки доступу. Коли встановлено Cluster (стандартно) або параметр не встановлено, Kubernetes враховує всі точки доступу.

Що далі

6 - Сховища інформації

6.1 - Ефемерні томи

У цьому документі описано ефемерні томи в Kubernetes. Рекомендується ознайомитися з томами, зокрема з PersistentVolumeClaim та PersistentVolume.

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

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

Ефемерні томи призначені для таких сценаріїв використання. Оскільки томи живуть стільки ж, скільки Pod, і створюються та видаляються разом із ним, Podʼи можна зупиняти й перезапускати, не обмежуючись місцем, де доступний певний постійний том.

Ефемерні томи вказуються inline у специфікації Podʼа, що спрощує розгортання застосунків і керування ними.

Типи ефемерних томів

Kubernetes підтримує кілька різновидів ефемерних томів для різних цілей:

  • emptyDir: порожній під час запуску Podʼа, сховище надається локально з базової теки kubelet (зазвичай кореневий диск) або з оперативної памʼяті
  • configMap, downwardAPI, secret: вставляють різні види даних Kubernetes у Pod
  • image: дозволяє монтувати файли образів контейнерів або артефакти безпосередньо в Pod.
  • ефемерні томи CSI: схожі на попередні види томів, але надаються спеціальними драйверами CSI, які спеціально підтримують цю можливість
  • загальні ефемерні томи, які можуть надавати всі драйвери сховища, що також підтримують постійні томи

emptyDir, configMap, downwardAPI, secret надаються як локальне тимчасове сховище. Ними керує kubelet на кожному вузлі.

Ефемерні томи CSI повинні надаватися сторонніми драйверами сховища CSI.

Загальні ефемерні томи можуть надаватися сторонніми драйверами сховища CSI, а також будь-яким іншим драйвером сховища, який підтримує динамічне надання. Деякі драйвери CSI написані спеціально для ефемерних томів CSI і не підтримують динамічне надання: їх не можна використовувати для загальних ефемерних томів.

Перевага сторонніх драйверів полягає в тому, що вони можуть пропонувати функціональність, яку сам Kubernetes не підтримує, наприклад, сховище з іншими характеристиками продуктивності, ніж диск, яким керує kubelet, або вставлення інших даних.

Ефемерні томи CSI

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.25

Примітка:

Ефемерні томи CSI підтримує лише частина драйверів CSI. Список драйверів Kubernetes CSI показує, які драйвери підтримують ефемерні томи.

Концептуально ефемерні томи CSI схожі на типи томів configMap, downwardAPI та secret: сховище керується локально на кожному вузлі й створюється разом з іншими локальними ресурсами після того, як Pod заплановано на вузол. На цьому етапі Kubernetes уже не має поняття про перепланування Podʼів. Створення тому має бути таким, щоб збій був малоймовірним, інакше запуск Podʼа застрягне. Зокрема, планування Podʼів з урахуванням місткості сховища для цих томів не підтримується. Наразі на них також не поширюються обмеження використання ресурсів сховища Podʼа, оскільки kubelet може застосовувати їх лише до сховища, яким керує сам.

Ось приклад маніфесту для Podʼа, який використовує ефемерне сховище CSI:

kind: Pod
apiVersion: v1
metadata:
  name: my-csi-app
spec:
  containers:
    - name: my-frontend
      image: busybox:1.28
      volumeMounts:
      - mountPath: "/data"
        name: my-csi-inline-vol
      command: [ "sleep", "1000000" ]
  volumes:
    - name: my-csi-inline-vol
      csi:
        driver: inline.storage.kubernetes.io
        volumeAttributes:
          foo: bar

volumeAttributes визначають, який том готує драйвер. Ці атрибути специфічні для кожного драйвера й не стандартизовані. Додаткові інструкції дивіться в документації кожного драйвера CSI.

Обмеження драйверів CSI

Ефемерні томи CSI дозволяють користувачам передавати volumeAttributes безпосередньо драйверу CSI як частину специфікації Podʼа. Драйвер CSI, який дозволяє volumeAttributes, що зазвичай доступні лише адміністраторам, НЕ підходить для використання в inline-ефемерному томі. Наприклад, параметри, які зазвичай визначаються в StorageClass, не слід відкривати користувачам через inline-ефемерні томи.

Адміністратори кластера, яким потрібно обмежити драйвери CSI, дозволені для використання як inline-томи у специфікації Podʼа, можуть зробити це так:

  • Вилучити Ephemeral із volumeLifecycleModes у специфікації CSIDriver, що не дасть використовувати драйвер як inline-ефемерний том.
  • Використати вебхук допуску, щоб обмежити використання цього драйвера.

Загальні ефемерні томи

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.23

Загальні ефемерні томи схожі на томи emptyDir у тому сенсі, що надають кожному Podʼу теку для робочих даних, яка зазвичай порожня після надання. Але вони також можуть мати додаткові можливості:

Приклад:

kind: Pod
apiVersion: v1
metadata:
  name: my-app
spec:
  containers:
    - name: my-frontend
      image: busybox:1.28
      volumeMounts:
      - mountPath: "/scratch"
        name: scratch-volume
      command: [ "sleep", "1000000" ]
  volumes:
    - name: scratch-volume
      ephemeral:
        volumeClaimTemplate:
          metadata:
            labels:
              type: my-frontend-volume
          spec:
            accessModes: [ "ReadWriteOnce" ]
            storageClassName: "scratch-storage-class"
            resources:
              requests:
                storage: 1Gi

Життєвий цикл та PersistentVolumeClaim

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

Це запускає привʼязку та/або надання тому: або негайно, якщо StorageClass використовує негайну привʼязку томів, або коли Pod попередньо заплановано на вузол (режим привʼязки томів WaitForFirstConsumer). Другий варіант рекомендовано для загальних ефемерних томів, оскільки тоді планувальник може вільно вибрати придатний вузол для Podʼа. За негайної привʼязки планувальник змушений вибрати вузол, який має доступ до тому, щойно том стає доступним.

З погляду власності на ресурси Pod, який має загальне ефемерне сховище, є власником PersistentVolumeClaim(ів), що надають це ефемерне сховище. Коли Pod видаляють, збирач сміття Kubernetes видаляє PVC, що зазвичай спричиняє видалення тому, оскільки стандартна політика повторного використання класів сховища — видаляти томи. Ви можете створити квазіефемерне локальне сховище за допомогою StorageClass із політикою повторного використання retain: сховище переживає Pod, і в цьому випадку потрібно подбати про окреме очищення тому.

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

Іменування PersistentVolumeClaim

Назви автоматично створених PVC є детермінованими: назва є комбінацією назви Podʼа та назви тому з дефісом (-) посередині. У наведеному вище прикладі назва PVC буде my-app-scratch-volume. Таке детерміноване іменування полегшує взаємодію з PVC, оскільки його не потрібно шукати, якщо відомі назва Podʼа та назва тому.

Детерміноване іменування також створює потенційний конфлікт між різними Podʼами (Pod "pod-a" з томом "scratch" та інший Pod з назвою "pod" і томом "a-scratch" отримають однакову назву PVC "pod-a-scratch"), а також між Podʼами та PVC, створеними вручну.

Такі конфлікти виявляються: PVC використовується для ефемерного тому, лише якщо його створено для цього Podʼа. Ця перевірка базується на відносинах власності. Наявний PVC не перезаписується і не змінюється. Але це не розвʼязує конфлікт, оскільки без правильного PVC Pod не може запуститися.

Увага:

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

Безпека

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

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

Що далі

Ефемерні томи, керовані kubelet

Дивіться локальне тимчасове сховище.

Ефемерні томи CSI

Загальні ефемерні томи

6.2 - Класи атрибутів томів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.36
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.36. It was first available in the v1.29 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate VolumeAttributesClass, Kubernetes ignores it but does not report any error.

На цій сторінці передбачається, що ви знайомі зі StorageClasses, томами та PersistentVolumes у Kubernetes.

VolumeAttributesClass надає адміністраторам можливість описати змінні «класи» сховища, які вони пропонують. Різні класи можуть відповідати різним рівням якості обслуговування. Сам Kubernetes не має власної думки щодо того, що представляють ці класи.

Ця можливість загальнодоступна (GA) з версії 1.34, і користувачі можуть вимкнути її.

Крім того, VolumeAttributesClasses можна використовувати лише зі сховищем на основі Container Storage Interface і лише там, де відповідний драйвер CSI реалізує API ModifyVolume.

API VolumeAttributesClass

Кожен VolumeAttributesClass містить driverName та parameters, які використовуються, коли PersistentVolume (PV), що належить до цього класу, потрібно динамічно надати або змінити.

Назва обʼєкта VolumeAttributesClass є важливою: саме за нею користувачі можуть запитати певний клас. Адміністратори встановлюють назву та інші параметри класу під час першого створення обʼєктів VolumeAttributesClass. Хоча назву обʼєкта VolumeAttributesClass у PersistentVolumeClaim можна змінювати, параметри наявного класу є незмінними.

apiVersion: storage.k8s.io/v1
kind: VolumeAttributesClass
metadata:
  name: silver
driverName: csi.storage.example.com
parameters:
  iops: "3000"
  throughput: "50Mi"

Provisioner

Кожен VolumeAttributesClass має provisioner, який визначає, який втулок томів використовується для надання PV. Поле driverName є обовʼязковим.

Підтримку цієї можливості для VolumeAttributesClass реалізовано в kubernetes-csi/external-provisioner.

Ви не обмежені вказанням kubernetes-csi/external-provisioner. Ви також можете запускати та вказувати зовнішні provisioners — незалежні програми, які відповідають специфікації, визначеній Kubernetes. Автори зовнішніх provisioners мають повну свободу щодо того, де розміщено їхній код, як постачається provisioner, як його потрібно запускати, який втулок томів він використовує тощо.

Щоб зрозуміти, як provisioner працює з VolumeAttributesClass, зверніться до документації CSI external-provisioner.

Resizer

Кожен VolumeAttributesClass має resizer, який визначає, який втулок томів використовується для зміни PV. Поле driverName є обовʼязковим.

Підтримку зміни томів для VolumeAttributesClass реалізовано в kubernetes-csi/external-resizer.

Наприклад, наявний PersistentVolumeClaim використовує VolumeAttributesClass з назвою silver:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pv-claim
spec:
  …
  volumeAttributesClassName: silver
  …

У кластері доступний новий VolumeAttributesClass gold:

apiVersion: storage.k8s.io/v1
kind: VolumeAttributesClass
metadata:
  name: gold
driverName: csi.storage.example.com
parameters:
  iops: "4000"
  throughput: "60Mi"

Кінцевий користувач може оновити PVC, вказавши новий VolumeAttributesClass gold, і застосувати зміни:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pv-claim
spec:
  …
  volumeAttributesClassName: gold
  …

Щоб зрозуміти, як resizer працює з VolumeAttributesClass, зверніться до документації CSI external-resizer.

Параметри

VolumeAttributeClasses мають параметри, які описують томи, що до них належать. Залежно від provisioner або resizer можуть прийматися різні параметри. Наприклад, значення 4000 для параметра iops і параметр throughput є специфічними для GCE PD. Якщо параметр пропущено, під час надання тому використовується стандартне значення. Якщо користувач застосовує PVC з іншим VolumeAttributesClass із пропущеними параметрами, стандартне значення параметрів може використовуватися залежно від реалізації драйвера CSI. Докладніше дивіться в документації відповідного драйвера CSI.

Для VolumeAttributesClass можна визначити не більше 512 параметрів. Загальна довжина обʼєкта параметрів, включно з ключами та значеннями, не може перевищувати 256 KiB.

6.3 - Класи сховищ

У цьому документі описано концепцію StorageClass у Kubernetes. Рекомендується ознайомитися з томами та постійними томами.

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

Концепція класу сховища в Kubernetes схожа на «профілі» в дизайні деяких інших систем зберігання.

Обʼєкти StorageClass

Кожен StorageClass містить поля provisioner, parameters та reclaimPolicy, які використовуються, коли PersistentVolume, що належить до цього класу, потрібно надати динамічно, щоб задовольнити PersistentVolumeClaim (PVC).

Назва обʼєкта StorageClass є важливою: саме за нею користувачі можуть запитати певний клас. Адміністратори встановлюють назву та інші параметри класу під час першого створення обʼєктів StorageClass.

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

Ось приклад StorageClass:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: low-latency
  annotations:
    storageclass.kubernetes.io/is-default-class: "false"
provisioner: csi-driver.example-vendor.example
reclaimPolicy: Retain # default value is Delete
allowVolumeExpansion: true
mountOptions:
  - discard # this might enable UNMAP / TRIM at the block storage layer
volumeBindingMode: WaitForFirstConsumer
parameters:
  guaranteedReadWriteLatency: "true" # provider-specific

Стандартний StorageClass

Ви можете позначити StorageClass як стандартний для свого кластера. Інструкції щодо встановлення стандартного StorageClass дивіться в Зміна стандартного StorageClass.

Якщо PVC не вказує storageClassName, використовується стандартний StorageClass.

Якщо ви встановите анотацію storageclass.kubernetes.io/is-default-class у true для кількох StorageClass у своєму кластері, а потім створите PersistentVolumeClaim без встановленого storageClassName, Kubernetes використає найновіший стандартний StorageClass.

Примітка:

Намагайтеся мати у своєму кластері лише один StorageClass, позначений як стандартний. Kubernetes дозволяє мати кілька стандартних StorageClasses, щоб забезпечити безшовну міграцію.

Ви можете створити PersistentVolumeClaim, не вказуючи storageClassName для нового PVC, навіть якщо у вашому кластері немає стандартного StorageClass. У цьому випадку новий PVC створюється так, як ви його визначили, а storageClassName цього PVC залишається невстановленим, доки не стане доступним стандартний клас.

Ви можете мати кластер без стандартного StorageClass. Якщо ви не позначите жоден StorageClass як стандартний (і його не встановлено для вас, наприклад, хмарним провайдером), Kubernetes не зможе застосувати це стандартне значення до PersistentVolumeClaims, яким воно потрібне.

Якщо або коли стандартний StorageClass стає доступним, панель управління визначає всі наявні PVC без storageClassName. Для PVC, які мають порожнє значення storageClassName або не мають цього ключа, панель управління оновлює storageClassName відповідно до нового стандартного StorageClass. Якщо у вас є наявний PVC, де storageClassName має значення "", і ви налаштовуєте стандартний StorageClass, цей PVC не буде оновлено.

Щоб і надалі привʼязуватися до PV із storageClassName, встановленим у "" (за наявності стандартного StorageClass), вам потрібно встановити storageClassName повʼязаного PVC у "".

Provisioner

Кожен StorageClass має provisioner, який визначає, який втулок томів використовується для надання PV. Це поле є обовʼязковим.

Втулок томівВнутрішній provisionerПриклад конфігурації
AzureFile✓Azure File
CephFS--
FC--
FlexVolume--
iSCSI--
Local-Local
NFS-NFS
PortworxVolume✓Portworx Volume
RBD-Ceph RBD
VsphereVolume✓vSphere

Ви не обмежені вказанням перелічених тут «внутрішніх» provisioners (назви яких мають префікс "kubernetes.io" і які постачаються разом із Kubernetes). Ви також можете запускати та вказувати зовнішні provisioners — незалежні програми, які відповідають специфікації, визначеній Kubernetes. Автори зовнішніх provisioners мають повну свободу щодо того, де розміщено їхній код, як постачається provisioner, як його потрібно запускати, який втулок томів він використовує (зокрема Flex) тощо. У репозиторії kubernetes-sigs/sig-storage-lib-external-provisioner розміщено бібліотеку для написання зовнішніх provisioners, яка реалізує більшу частину специфікації. Деякі зовнішні provisioners перелічено в репозиторії kubernetes-sigs/sig-storage-lib-external-provisioner.

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

Політика повторного використання

PersistentVolumes, які динамічно створюються за допомогою StorageClass, матимуть політику повторного використання, вказану в полі reclaimPolicy класу, яка може бути Delete або Retain. Якщо reclaimPolicy не вказано під час створення обʼєкта StorageClass, стандартним значенням буде Delete.

PersistentVolumes, створені вручну та керовані за допомогою StorageClass, матимуть ту політику повторного використання, яку їм було призначено під час створення.

Розширення томів

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

Такі типи томів підтримують розширення, коли базовий StorageClass має поле allowVolumeExpansion зі значенням true.

Таблиця типів томів і версій Kubernetes, яких вони потребують
Тип томуПотрібна версія Kubernetes для розширення тому
Azure File1.11
CSI1.24
FlexVolume1.13
Portworx1.11
rbd1.11

Примітка:

Можливість розширення томів можна використовувати лише для збільшення тому, але не для його зменшення.

Параметри монтування

PersistentVolumes, які динамічно створюються за допомогою StorageClass, матимуть параметри монтування, вказані в полі mountOptions класу.

Якщо втулок томів не підтримує параметри монтування, але їх вказано, надання завершиться невдачею. Параметри монтування не перевіряються ні на рівні класу, ні на рівні PV. Якщо параметр монтування недійсний, монтування PV завершиться невдачею.

Режим привʼязки тому

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

Режим Immediate означає, що привʼязка томів і динамічне надання відбуваються одразу після створення PersistentVolumeClaim. Для бекендів сховища, які обмежені топологією й не доступні глобально з усіх вузлів кластера, PersistentVolumes будуть привʼязані або надані без урахування вимог планування Podʼа. Це може призвести до Podʼів, не придатних для планування.

Адміністратор кластера може розвʼязати цю проблему, вказавши режим WaitForFirstConsumer, який відкладає привʼязку та надання PersistentVolume до створення Podʼа, що використовує PersistentVolumeClaim. PersistentVolumes будуть вибрані або надані відповідно до топології, заданої обмеженнями планування Podʼа. Це, зокрема, вимоги до ресурсів, селектори вузлів, спорідненість та антиспорідненість Podʼів, а також taints і tolerations.

Такі втулки підтримують WaitForFirstConsumer із динамічним наданням:

  • Томи CSI, за умови, що конкретний драйвер CSI це підтримує

Такі втулки підтримують WaitForFirstConsumer із привʼязкою попередньо створених PersistentVolume:

  • Томи CSI, за умови, що конкретний драйвер CSI це підтримує
  • local

Примітка:

Якщо ви вирішили використовувати WaitForFirstConsumer, не використовуйте nodeName у специфікації Podʼа для задання спорідненості вузла. Якщо в цьому випадку використати nodeName, планувальник буде оминуто, а PVC залишиться в стані pending.

Натомість ви можете використати селектор вузла для kubernetes.io/hostname:

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  nodeSelector:
    kubernetes.io/hostname: kube-01
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage

Дозволені топології

Коли оператор кластера вказує режим привʼязки томів WaitForFirstConsumer, у більшості випадків більше не потрібно обмежувати надання конкретними топологіями. Однак, якщо це все ще потрібно, можна вказати allowedTopologies.

У цьому прикладі показано, як обмежити топологію наданих томів конкретними зонами; це слід використовувати як заміну параметрам zone та zones для підтримуваних втулків.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner:  example.com/example
parameters:
  type: pd-standard
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
  - key: topology.kubernetes.io/zone
    values:
    - us-central-1a
    - us-central-1b

Параметри

StorageClasses мають параметри, які описують томи, що належать до класу сховища. Залежно від provisioner можуть прийматися різні параметри. Якщо параметр пропущено, використовується певне стандартне значення.

Для StorageClass можна визначити не більше 512 параметрів. Загальна довжина обʼєкта параметрів, включно з ключами та значеннями, не може перевищувати 256 KiB.

AWS EBS

Kubernetes 1.37 не містить типу тому awsElasticBlockStore.

Вбудований драйвер сховища AWSElasticBlockStore визнано застарілим у випуску Kubernetes v1.19, а потім повністю вилучено у випуску v1.27.

Проєкт Kubernetes пропонує натомість використовувати зовнішній драйвер сховища AWS EBS.

Ось приклад StorageClass для драйвера CSI AWS EBS:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  csi.storage.k8s.io/fstype: xfs
  type: io1
  iopsPerGB: "50"
  encrypted: "true"
  tagSpecification_1: "key1=value1"
  tagSpecification_2: "key2=value2"
allowedTopologies:
- matchLabelExpressions:
  - key: topology.ebs.csi.aws.com/zone
    values:
    - us-east-2c

tagSpecification: Теґи з цим префіксом застосовуються до динамічно наданих томів EBS.

AWS EFS

Щоб налаштувати сховище AWS EFS, можна використовувати зовнішній драйвер AWS_EFS_CSI_DRIVER.

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
parameters:
  provisioningMode: efs-ap
  fileSystemId: fs-92107410
  directoryPerms: "700"
  • provisioningMode: Тип тому, який надає Amazon EFS. Наразі підтримується лише надання на основі точки доступу (efs-ap).
  • fileSystemId: Файлова система, під якою створюється точка доступу.
  • directoryPerms: Дозволи кореневої теки, створеної точкою доступу.

Докладніше дивіться в документації AWS_EFS_CSI_Driver Dynamic Provisioning.

NFS

Щоб налаштувати сховище NFS, можна використовувати вбудований драйвер або драйвер CSI для NFS у Kubernetes (рекомендовано).

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: example-nfs
provisioner: example.com/external-nfs
parameters:
  server: nfs-server.example.com
  path: /share
  readOnly: "false"
  • server: Server — це імʼя хоста або IP-адреса сервера NFS.
  • path: Шлях, який експортується сервером NFS.
  • readOnly: Прапорець, який вказує, чи буде сховище змонтовано лише для читання (стандартно false).

Kubernetes не містить внутрішнього provisioner для NFS. Щоб створити StorageClass для NFS, потрібно використовувати зовнішній provisioner. Ось кілька прикладів:

vSphere

Існує два типи provisioners для класів сховища vSphere:

Вбудовані provisioners застаріли. Докладніше про provisioner CSI дивіться в Kubernetes vSphere CSI Driver та міграції vSphereVolume на CSI.

Provisioner CSI

Provisioner StorageClass для vSphere CSI працює з кластерами Tanzu Kubernetes. Приклад дивіться в репозиторії vSphere CSI.

Provisioner vCP

У наведених нижче прикладах використовується provisioner StorageClass VMware Cloud Provider (vCP).

  1. Створіть StorageClass із форматом диска, вказаним користувачем.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast
    provisioner: kubernetes.io/vsphere-volume
    parameters:
      diskformat: zeroedthick
    

    diskformat: thin, zeroedthick та eagerzeroedthick. Стандартно: "thin".

  2. Створіть StorageClass із форматом диска на вказаному користувачем сховищі даних.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast
    provisioner: kubernetes.io/vsphere-volume
    parameters:
      diskformat: zeroedthick
      datastore: VSANDatastore
    

    datastore: Користувач також може вказати datastore у StorageClass. Том буде створено в datastore, вказаному в StorageClass, у цьому випадку — VSANDatastore. Це поле є необовʼязковим. Якщо datastore не вказано, том буде створено в datastore, вказаному у файлі конфігурації vSphere, який використовується для ініціалізації хмарного провайдера vSphere.

  3. Керування політикою сховища в Kubernetes

    • Використання наявної політики SPBM vCenter

      Однією з найважливіших можливостей vSphere для керування сховищем є керування на основі політик. Керування сховищем на основі політик (Storage Policy Based Management, SPBM) — це фреймворк політик сховища, який надає єдину уніфіковану панель управління для широкого спектра сервісів даних і рішень зберігання. SPBM дозволяє адміністраторам vSphere долати початкові проблеми надання сховища, такі як планування місткості, диференційовані рівні обслуговування та керування запасом місткості.

      Політики SPBM можна вказати в StorageClass за допомогою параметра storagePolicyName.

    • Підтримка політики Virtual SAN всередині Kubernetes

      Адміністратори Vsphere Infrastructure (VI) матимуть змогу вказувати власні можливості сховища Virtual SAN під час динамічного надання томів. Тепер ви можете визначати вимоги до сховища, такі як продуктивність і доступність, у вигляді можливостей сховища під час динамічного надання томів. Вимоги до можливостей сховища перетворюються на політику Virtual SAN, яка потім передається на рівень Virtual SAN під час створення постійного тому (віртуального диска). Віртуальний диск розподіляється по сховищу даних Virtual SAN відповідно до вимог.

      Докладніше про використання політик сховища для керування постійними томами дивіться в Storage Policy Based Management for dynamic provisioning of volumes.

Ceph RBD (застаріло)

Примітка:

СТАН ФУНКЦІОНАЛУ: Deprecated since Kubernetes v1.28

Цей внутрішній provisioner Ceph RBD застарів. Використовуйте CephFS RBD CSI driver.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/rbd # This provisioner is deprecated
parameters:
  monitors: 198.19.254.105:6789
  adminId: kube
  adminSecretName: ceph-secret
  adminSecretNamespace: kube-system
  pool: kube
  userId: kube
  userSecretName: ceph-secret-user
  userSecretNamespace: default
  fsType: ext4
  imageFormat: "2"
  imageFeatures: "layering"
  • monitors: Монітори Ceph, розділені комою. Цей параметр є обовʼязковим.

  • adminId: Ідентифікатор клієнта Ceph, який може створювати образи в пулі. Стандартно "admin".

  • adminSecretName: Назва Secret для adminId. Цей параметр є обовʼязковим. Наданий Secret повинен мати тип "kubernetes.io/rbd".

  • adminSecretNamespace: Простір імен для adminSecretName. Стандартно "default".

  • pool: Пул Ceph RBD. Стандартно "rbd".

  • userId: Ідентифікатор клієнта Ceph, який використовується для зіставлення образу RBD. Стандартно такий самий, як adminId.

  • userSecretName: Назва Ceph Secret для userId для зіставлення образу RBD. Він має існувати в тому самому просторі імен, що й PVC. Цей параметр є обовʼязковим. Наданий Secret повинен мати тип "kubernetes.io/rbd", наприклад, створений таким чином:

    kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" \
      --from-literal=key='QVFEQ1pMdFhPUnQrSmhBQUFYaERWNHJsZ3BsMmNjcDR6RFZST0E9PQ==' \
      --namespace=kube-system
    
  • userSecretNamespace: Простір імен для userSecretName.

  • fsType: fsType, який підтримує Kubernetes. Стандартно: "ext4".

  • imageFormat: Формат образу Ceph RBD, "1" або "2". Стандартно "2".

  • imageFeatures: Цей параметр є необовʼязковим, і його слід використовувати лише тоді, коли ви встановили imageFormat у "2". Наразі підтримується лише можливість layering. Стандартно "", і жодну можливість не ввімкнено.

Azure Disk

Kubernetes 1.37 не містить типу тому azureDisk.

Вбудований драйвер сховища azureDisk визнано застарілим у випуску Kubernetes v1.19, а потім повністю вилучено у випуску v1.27.

Проєкт Kubernetes пропонує натомість використовувати сторонній драйвер сховища Azure Disk.

Azure File (застаріло)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azurefile
provisioner: kubernetes.io/azure-file
parameters:
  skuName: Standard_LRS
  location: eastus
  storageAccount: azure_storage_account_name # example value
  • skuName: Рівень SKU облікового запису Azure Storage. Стандартно порожній.
  • location: Розташування облікового запису Azure Storage. Стандартно порожнє.
  • storageAccount: Назва облікового запису Azure Storage. Стандартно порожня. Якщо обліковий запис сховища не вказано, серед усіх облікових записів сховища, повʼязаних із групою ресурсів, шукається той, що відповідає skuName та location. Якщо обліковий запис сховища вказано, він має бути в тій самій групі ресурсів, що й кластер, а skuName та location ігноруються.
  • secretNamespace: Простір імен Secret, що містить назву та ключ облікового запису Azure Storage. Стандартно той самий, що й у Podʼа.
  • secretName: Назва Secret, що містить назву та ключ облікового запису Azure Storage. Стандартно azure-storage-account-<accountName>-secret.
  • readOnly: Прапорець, який вказує, чи буде сховище змонтовано лише для читання. Стандартно false, що означає монтування для читання й запису. Це налаштування також впливає на налаштування ReadOnly у VolumeMounts.

Під час надання сховища для облікових даних монтування створюється Secret із назвою secretName. Якщо в кластері ввімкнено і RBAC, і ролі контролерів, додайте дозвіл create на ресурс secret для clusterrole system:controller:persistent-volume-binder.

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

Portworx volume (застаріло)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: portworx-io-priority-high
provisioner: kubernetes.io/portworx-volume # This provisioner is deprecated
parameters:
  repl: "1"
  snap_interval: "70"
  priority_io: "high"
  • fs: файлова система для створення: none/xfs/ext4 (стандартно: ext4).
  • block_size: розмір блоку в кілобайтах (стандартно: 32).
  • repl: кількість синхронних реплік у формі коефіцієнта реплікації 1..3 (стандартно: 1). Тут очікується рядок, тобто "1", а не 1.
  • priority_io: визначає, чи буде том створено зі сховища вищої продуктивності чи нижчого пріоритету: high/medium/low (стандартно: low).
  • snap_interval: інтервал у хвилинах для запуску знімків. Знімки є інкрементними на основі різниці з попереднім знімком, 0 вимикає знімки (стандартно: 0). Тут очікується рядок, тобто "70", а не 70.
  • aggregation_level: вказує кількість частин, на які буде розподілено том, 0 означає неагрегований том (стандартно: 0). Тут очікується рядок, тобто "0", а не 0.
  • ephemeral: вказує, чи слід очищати том після відмонтування, чи він має бути постійним. Для випадку використання emptyDir це значення можна встановити в true, а для випадку persistent volumes, наприклад, для баз даних на кшталт Cassandra, слід установити false: true/false (стандартно false). Тут очікується рядок, тобто "true", а не true.

Локальне сховище

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner # indicates that this StorageClass does not support automatic provisioning
volumeBindingMode: WaitForFirstConsumer

Локальні томи не підтримують динамічне надання в Kubernetes 1.37; однак StorageClass однаково слід створити, щоб відкласти привʼязку тому до фактичного планування Podʼа на відповідний вузол. Це задається режимом привʼязки томів WaitForFirstConsumer.

Відкладення привʼязки тому дозволяє планувальнику враховувати всі обмеження планування Podʼа під час вибору відповідного PersistentVolume для PersistentVolumeClaim.

6.4 - Динамічне надання томів

Динамічне надання томів дозволяє створювати томи сховища на вимогу. Без динамічного надання адміністратори кластера змушені вручну звертатися до свого хмарного провайдера або постачальника сховища, щоб створити нові томи сховища, а потім створювати обʼєкти PersistentVolume, які представляють їх у Kubernetes. Можливість динамічного надання усуває потребу адміністраторів кластера заздалегідь надавати сховище. Натомість сховище надається автоматично, коли користувачі створюють обʼєкти PersistentVolumeClaim.

Загальні відомості

Реалізація динамічного надання томів базується на обʼєкті API StorageClass із групи API storage.k8s.io. Адміністратор кластера може визначити стільки обʼєктів StorageClass, скільки потрібно, і кожен із них вказує втулок томів (також відомий як provisioner), який надає том, і набір параметрів, які слід передати цьому provisioner під час надання. Адміністратор кластера може визначити та зробити доступними кілька різновидів сховища (з однієї або різних систем зберігання) у межах кластера, кожен із власним набором параметрів. Така архітектура також гарантує, що кінцевим користувачам не потрібно перейматися складністю та нюансами надання сховища, але вони все одно можуть вибирати з кількох варіантів сховища.

Докладніше дивіться в концепції Класи сховищ.

Увімкнення динамічного надання

Щоб увімкнути динамічне надання, адміністратор кластера має заздалегідь створити для користувачів один або кілька обʼєктів StorageClass. Обʼєкти StorageClass визначають, який provisioner слід використовувати та які параметри потрібно передати цьому provisioner під час виклику динамічного надання. Назва обʼєкта StorageClass має бути дійсним іменем DNS-піддомену.

Наведений нижче маніфест створює клас сховища "slow", який надає стандартні постійні диски, подібні до звичайних дисків.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard

Наведений нижче маніфест створює клас сховища "fast", який надає постійні диски, подібні до SSD.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

Використання динамічного надання

Користувачі запитують динамічно надане сховище, вказуючи клас сховища у своєму PersistentVolumeClaim. До Kubernetes v1.6 це робилося за допомогою анотації volume.beta.kubernetes.io/storage-class. Однак ця анотація застаріла з версії v1.9. Тепер користувачі можуть і повинні використовувати натомість поле storageClassName обʼєкта PersistentVolumeClaim. Значення цього поля має збігатися з назвою StorageClass, налаштованого адміністратором (дивіться Увімкнення динамічного надання).

Наприклад, щоб вибрати клас сховища "fast", користувач створює такий PersistentVolumeClaim:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast
  resources:
    requests:
      storage: 30Gi

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

Стандартна поведінка

Динамічне надання можна ввімкнути в кластері так, щоб усі PersistentVolumeClaims надавалися динамічно, якщо клас сховища не вказано. Адміністратор кластера може ввімкнути таку поведінку таким чином:

Адміністратор може позначити певний StorageClass як стандартний, додавши до нього анотацію storageclass.kubernetes.io/is-default-class. Коли в кластері є стандартний StorageClass і користувач створює PersistentVolumeClaim без вказаного storageClassName, контролер допуску DefaultStorageClass автоматично додає поле storageClassName, що вказує на стандартний клас сховища.

Зверніть увагу, що якщо ви встановите анотацію storageclass.kubernetes.io/is-default-class у true для кількох StorageClass у своєму кластері, а потім створите PersistentVolumeClaim без встановленого storageClassName, Kubernetes використає найновіший стандартний StorageClass.

Урахування топології

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

6.5 - Класи знімків томів

У цьому документі описано концепцію VolumeSnapshotClass у Kubernetes. Рекомендується ознайомитися зі знімками томів та класами сховищ.

Вступ

Так само як StorageClass надає адміністраторам можливість описувати «класи» сховища, які вони пропонують під час надання тому, VolumeSnapshotClass надає можливість описувати «класи» сховища під час надання знімка тому.

Ресурс VolumeSnapshotClass

Кожен VolumeSnapshotClass містить поля driver, deletionPolicy та parameters, які використовуються, коли VolumeSnapshot, що належить до цього класу, потрібно надати динамічно.

Назва обʼєкта VolumeSnapshotClass є важливою: саме за нею користувачі можуть запитати певний клас. Адміністратори встановлюють назву та інші параметри класу під час першого створення обʼєктів VolumeSnapshotClass, і ці обʼєкти не можна оновлювати після створення.

Примітка:

За встановлення CRD відповідає дистрибутив Kubernetes. Якщо потрібних CRD немає, створення VolumeSnapshotClass завершиться невдачею.
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:

Адміністратори можуть вказати стандартний VolumeSnapshotClass для VolumeSnapshots, які не запитують привʼязки до певного класу, додавши анотацію snapshot.storage.kubernetes.io/is-default-class: "true":

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
  annotations:
    snapshot.storage.kubernetes.io/is-default-class: "true"
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:

Якщо існує кілька драйверів CSI, для кожного з них можна вказати стандартний VolumeSnapshotClass.

Залежності VolumeSnapshotClass

Коли ви створюєте VolumeSnapshot без зазначення VolumeSnapshotClass, Kubernetes автоматично вибирає стандартний VolumeSnapshotClass, драйвер CSI якого збігається з драйвером CSI StorageClass цього PVC.

Така поведінка дозволяє кільком стандартним обʼєктам VolumeSnapshotClass співіснувати в кластері, якщо кожен із них повʼязаний з унікальним драйвером CSI.

Завжди переконуйтеся, що для кожного драйвера CSI існує лише один стандартний VolumeSnapshotClass. Якщо з одним драйвером CSI створено кілька стандартних обʼєктів VolumeSnapshotClass, створення VolumeSnapshot завершиться невдачею, оскільки Kubernetes не зможе визначити, який із них використати.

Driver

Класи знімків томів мають драйвер, який визначає, який втулок томів CSI використовується для надання VolumeSnapshots. Це поле є обовʼязковим.

DeletionPolicy

Класи знімків томів мають deletionPolicy. Вона дозволяє налаштувати, що станеться з VolumeSnapshotContent, коли обʼєкт VolumeSnapshot, до якого він привʼязаний, буде видалено. Значення deletionPolicy класу знімків томів може бути Retain або Delete. Це поле є обовʼязковим.

Якщо deletionPolicy має значення Delete, базовий знімок сховища буде видалено разом з обʼєктом VolumeSnapshotContent. Якщо deletionPolicy має значення Retain, то і базовий знімок, і VolumeSnapshotContent залишаються.

Параметри

Класи знімків томів мають параметри, які описують знімки томів, що належать до цього класу. Залежно від driver можуть прийматися різні параметри.

6.6 - Клонування томів CSI

У цьому документі описано концепцію клонування наявних томів CSI у Kubernetes. Рекомендується ознайомитися з томами.

Вступ

Можливість клонування томів CSI додає підтримку вказання наявних PVC у полі dataSource, щоб позначити, що користувач хоче клонувати том.

Клон — це дублікат наявного тому Kubernetes, який можна використовувати так само, як будь-який стандартний том. Єдина відмінність полягає в тому, що під час надання, замість створення «нового» порожнього тому, бекенд-пристрій створює точний дублікат вказаного тому.

З погляду API Kubernetes реалізація клонування додає можливість вказати наявний PVC як dataSource під час створення нового PVC. Вихідний PVC має бути привʼязаним і доступним (не використовуватися).

Під час використання цієї можливості користувачі повинні знати таке:

  • Підтримка клонування (VolumePVCDataSource) доступна лише для драйверів CSI.
  • Підтримка клонування доступна лише для динамічних provisioners.
  • Драйвери CSI можуть реалізовувати функціональність клонування томів, а можуть і не реалізовувати.
  • Ви можете клонувати PVC лише тоді, коли він існує в тому самому просторі імен, що й цільовий PVC (джерело та призначення мають бути в одному просторі імен).
  • Клонування підтримується з іншим Storage Class.
    • Цільовий том може мати той самий або інший клас сховища, ніж джерело.
    • Можна використовувати стандартний клас сховища, пропустивши storageClassName у специфікації.
  • Клонування можна виконати лише між двома томами, які використовують однакове значення VolumeMode (якщо ви запитуєте том у блоковому режимі, джерело ТАКОЖ ПОВИННО бути в блоковому режимі)

Надання

Клони надаються так само, як і будь-який інший PVC, за винятком додавання dataSource, який посилається на наявний PVC у тому самому просторі імен.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: clone-of-pvc-1
    namespace: myns
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: cloning
  resources:
    requests:
      storage: 5Gi
  dataSource:
    kind: PersistentVolumeClaim
    name: pvc-1

Примітка:

Ви повинні вказати значення місткості для spec.resources.requests.storage, і воно має бути таким самим або більшим за місткість вихідного тому.

Результатом є новий PVC із назвою clone-of-pvc-1, який має точно такий самий вміст, як і вказане джерело pvc-1.

Використання

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

6.7 - Наповнювачі томів та джерела даних

У цьому документі описано наповнювачі томів (volume populators) та джерела даних (data sources) у Kubernetes. Рекомендується ознайомитися з постійними томами.

Коли ви створюєте PersistentVolumeClaim, том, який Kubernetes надає для нього, зазвичай спочатку порожній. Джерело даних дозволяє натомість запитати, щоб новий том було попередньо заповнено наявними даними. Наповнювачі томів — це контролери, які виконують це заповнення на основі джерела даних, на яке посилається PersistentVolumeClaim.

Kubernetes має вбудовану підтримку джерел даних, які клонують наявний том або відновлюють знімок тому. Власні наповнювачі томів розширюють цей механізм. Джерело даних — це власний ресурс, тобто обʼєкт, тип якого визначено за допомогою CustomResourceDefinition. Контролер-наповнювач стежить за PersistentVolumeClaims, які посилаються на такий ресурс, і заповнює з нього новий том.

Наповнювачі томів та джерела даних

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.24

Kubernetes підтримує власні наповнювачі томів. Щоб використовувати власні наповнювачі томів, ви повинні ввімкнути функціональну можливість AnyVolumeDataSource для kube-apiserver та kube-controller-manager.

Наповнювачі томів використовують поле специфікації PVC з назвою dataSourceRef. На відміну від поля dataSource, яке може містити лише посилання на інший PersistentVolumeClaim або на VolumeSnapshot, поле dataSourceRef може містити посилання на будь-який обʼєкт у тому самому просторі імен, за винятком основних обʼєктів, відмінних від PVC. Для кластерів, у яких увімкнено цю функціональну можливість, слід надавати перевагу dataSourceRef, а не dataSource.

Посилання на джерела даних

Поле dataSourceRef поводиться майже так само, як поле dataSource. Якщо вказано одне з них, а інше ні, API-сервер надасть обом полям однакове значення. Жодне з полів не можна змінити після створення, а спроба вказати різні значення для цих двох полів призведе до помилки валідації. Тому обидва поля завжди матимуть однаковий вміст.

Існують дві відмінності між полями dataSourceRef та dataSource, про які користувачі повинні знати:

  • Поле dataSource ігнорує недійсні значення (ніби поле було порожнім), тоді як dataSourceRef ніколи не ігнорує значення і спричинить помилку, якщо використано недійсне значення. Недійсними значеннями є будь-які основні обʼєкти (обʼєкти без apiGroup), крім PVC.
  • Поле dataSourceRef може містити різні типи обʼєктів, тоді як поле dataSource дозволяє лише PVC та VolumeSnapshots.

Коли ввімкнено можливість CrossNamespaceVolumeDataSource, зʼявляються додаткові відмінності:

  • Поле dataSource дозволяє лише локальні обʼєкти, тоді як поле dataSourceRef дозволяє обʼєкти в будь-яких просторах імен.
  • Коли вказано простір імен, dataSource та dataSourceRef не синхронізуються.

Користувачі завжди повинні використовувати dataSourceRef у кластерах, де ввімкнено цю функціональну можливість, і повертатися до dataSource у кластерах, де її не ввімкнено. За жодних обставин не потрібно переглядати обидва поля. Дубльовані значення з дещо різною семантикою існують лише для зворотної сумісності. Зокрема, суміш старіших і новіших контролерів може взаємодіяти, оскільки поля однакові.

Використання наповнювачів томів

Наповнювачі томів — це контролери, які можуть створювати непорожні томи, вміст яких визначається власним ресурсом. Користувачі створюють заповнений том, посилаючись на власний ресурс за допомогою поля dataSourceRef:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: populated-pvc
spec:
  dataSourceRef:
    name: example-name
    kind: ExampleDataSource
    apiGroup: example.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Оскільки наповнювачі томів є зовнішніми компонентами, спроби створити PVC, який використовує один із них, можуть завершитися невдачею, якщо встановлено не всі потрібні компоненти. Зовнішні контролери повинні генерувати Events на PVC, щоб надавати зворотний звʼязок про статус створення, зокрема попередження, якщо PVC неможливо створити через відсутність певного компонента.

Ви можете встановити у свій кластер alpha-контролер volume data source validator. Цей контролер генерує попереджувальні Events на PVC, якщо жоден наповнювач не зареєстровано для обробки такого виду джерела даних. Коли для PVC встановлено відповідний наповнювач, саме цей контролер-наповнювач відповідає за створення Events, повʼязаних зі створенням тому та проблемами під час цього процесу.

Джерела даних між просторами імен

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.26

Kubernetes підтримує джерела даних томів між просторами імен. Щоб використовувати їх, ви повинні ввімкнути функціональні можливості AnyVolumeDataSource та CrossNamespaceVolumeDataSource для kube-apiserver та kube-controller-manager. Крім того, ви повинні ввімкнути функціональну можливість CrossNamespaceVolumeDataSource для csi-provisioner.

Увімкнення функціональної можливості CrossNamespaceVolumeDataSource дозволяє вказати простір імен у полі dataSourceRef.

Примітка:

Коли ви вказуєте простір імен для джерела даних тому, Kubernetes перевіряє наявність ReferenceGrant в іншому просторі імен, перш ніж прийняти посилання. ReferenceGrant є частиною API розширення gateway.networking.k8s.io. Докладніше дивіться в розділі ReferenceGrant документації Gateway API. Це означає, що перед використанням цього механізму ви повинні розширити свій кластер Kubernetes принаймні ресурсом ReferenceGrant із Gateway API.

Використання джерела даних тому між просторами імен

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.26

Створіть ReferenceGrant, щоб дозволити власнику простору імен прийняти посилання. Ви визначаєте заповнений том, вказуючи джерело даних тому між просторами імен за допомогою поля dataSourceRef. У вихідному просторі імен уже повинен існувати дійсний ReferenceGrant:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-ns1-pvc
  namespace: default
spec:
  from:
  - group: ""
    kind: PersistentVolumeClaim
    namespace: ns1
  to:
  - group: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: new-snapshot-demo
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: foo-pvc
  namespace: ns1
spec:
  storageClassName: example
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  dataSourceRef:
    apiGroup: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: new-snapshot-demo
    namespace: default
  volumeMode: Filesystem

Що далі

6.8 - Місткість сховища

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

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

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

Перш ніж ви розпочнете

Kubernetes v1.37 містить підтримку API на рівні кластера для відстеження місткості сховища. Щоб скористатися нею, ви також повинні використовувати драйвер CSI, який підтримує відстеження місткості. Зверніться до документації драйверів CSI, які ви використовуєте, щоб дізнатися, чи доступна ця підтримка і, якщо так, як її використовувати. Якщо ви не використовуєте Kubernetes v1.37, перегляньте документацію для своєї версії Kubernetes.

API

Для цієї можливості існує два розширення API:

  • Обʼєкти CSIStorageCapacity: їх створює драйвер CSI у просторі імен, де його встановлено. Кожен обʼєкт містить інформацію про місткість для одного класу сховища і визначає, які вузли мають доступ до цього сховища.
  • Поле CSIDriverSpec.StorageCapacity: якщо встановлено значення true, планувальник Kubernetes враховуватиме місткість сховища для томів, які використовують драйвер CSI.

Планування

Планувальник Kubernetes використовує інформацію про місткість сховища, якщо:

  • Pod використовує том, який ще не створено,
  • цей том використовує StorageClass, який посилається на драйвер CSI та використовує режим привʼязки тому WaitForFirstConsumer, і
  • обʼєкт CSIDriver для цього драйвера має StorageCapacity зі значенням true.

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

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

Для ефемерних томів CSI планування завжди відбувається без урахування місткості сховища. Це ґрунтується на припущенні, що цей тип тому використовують лише спеціальні драйвери CSI, які є локальними для вузла й не потребують там значних ресурсів.

Перепланування

Коли вузол вибрано для Podʼа з томами WaitForFirstConsumer, це рішення все ще є попереднім. Наступним кроком драйверу сховища CSI надсилається запит створити том із підказкою, що том має бути доступний на вибраному вузлі.

Оскільки Kubernetes міг вибрати вузол на підставі застарілої інформації про місткість, може статися, що том не вдасться створити. Тоді вибір вузла скидається, і планувальник Kubernetes знову намагається знайти вузол для Podʼа.

Обмеження

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

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

Що далі

6.9 - Обмеження томів на вузлі

На цій сторінці описано максимальну кількість томів, які можна підʼєднати до вузла, для різних хмарних провайдерів.

Хмарні провайдери, такі як Google, Amazon і Microsoft, зазвичай обмежують кількість томів, які можна підʼєднати до вузла. Важливо, щоб Kubernetes дотримувався цих обмежень. Інакше Podʼи, заплановані на вузол, можуть застрягти в очікуванні підʼєднання томів.

Стандартні обмеження Kubernetes

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

Хмарний сервісМаксимальна кількість томів на вузол
Amazon Elastic Block Store (EBS)39
Google Persistent Disk16
Microsoft Azure Disk Storage16

Обмеження динамічних томів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.17

Обмеження динамічних томів підтримуються для таких типів томів.

  • Amazon EBS
  • Google Persistent Disk
  • Azure Disk
  • CSI

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

  • У Google Compute Engine до вузла можна підʼєднати до 127 томів, залежно від типу вузла.

  • Для дисків Amazon EBS на типах екземплярів M5,C5,R5,T3 та Z1D Kubernetes дозволяє підʼєднувати до вузла лише 25 томів. Для інших типів екземплярів в Amazon Elastic Compute Cloud (EC2) Kubernetes дозволяє підʼєднувати до вузла 39 томів.

  • В Azure до вузла можна підʼєднати до 64 дисків, залежно від типу вузла. Докладніше дивіться Sizes for virtual machines in Azure.

  • Якщо драйвер сховища CSI оголошує максимальну кількість томів для вузла (за допомогою NodeGetInfo), kube-scheduler дотримується цього обмеження. Докладніше дивіться в специфікаціях CSI.

  • Для томів, керованих вбудованими втулками, які було перенесено на драйвер CSI, максимальною кількістю томів буде та, про яку повідомляє драйвер CSI.

Mutable CSI Node Allocatable Count

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.36; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.36. It was first available in the v1.33 release.

Драйвери CSI можуть динамічно змінювати максимальну кількість томів, які можна підʼєднати до вузла, під час роботи. Це підвищує точність планування та зменшує кількість збоїв планування Podʼів через зміни в доступності ресурсів.

Щоб скористатися цією можливістю, ви повинні ввімкнути функціональну можливість MutableCSINodeAllocatableCount у таких компонентах:

  • kube-apiserver
  • kubelet

Періодичні оновлення

Якщо цю можливість увімкнено, драйвери CSI можуть запитувати періодичні оновлення своїх обмежень томів, встановивши поле nodeAllocatableUpdatePeriodSeconds у специфікації CSIDriver. Наприклад:

apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
  name: hostpath.csi.k8s.io
spec:
  nodeAllocatableUpdatePeriodSeconds: 60

kubelet періодично викликатиме точку доступу NodeGetInfo відповідного драйвера CSI, щоб оновити максимальну кількість томів, які можна підʼєднати, використовуючи інтервал, вказаний у полі nodeAllocatableUpdatePeriodSeconds. Мінімально допустиме значення цього поля — 10 секунд.

Якщо операція підʼєднання тому завершується помилкою ResourceExhausted (код gRPC 8), Kubernetes негайно оновлює кількість доступних для розподілу томів цього вузла. Крім того, kubelet позначає уражені Podʼи як Failed, що дозволяє їхнім контролерам обробити повторне створення. Це запобігає безстроковому застряганню Podʼів у стані ContainerCreating.

Запобігання розміщенню Podʼа без драйвера CSI

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно увімкнено)

Функціональна можливість VolumeLimitScaling стандартно ввімкнена в Kubernetes v1.37.

Однак запобігання розміщенню Podʼів на вузлах без драйвера CSI вимагає явної згоди через поле spec.preventPodSchedulingIfMissing обʼєкта CSIDriver.

Поле preventPodSchedulingIfMissing стандартно має значення false, і його потрібно встановити в true, якщо ви не хочете, щоб Podʼи планувалися на вузлах без драйвера CSI. Стандартне значення false вибрано з міркувань зворотної сумісності та сумісності з Cluster AutoScaler, який може не враховувати обмеження томів CSI під час автомасштабування (дивіться розділ нижче).

apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
  name: hostpath.csi.k8s.io
spec:
  preventPodSchedulingIfMissing: true

Обмеження підʼєднання томів CSI та cluster autoscaler

Cluster autoscaler може враховувати обмеження томів CSI, коли встановлено --enable-csi-node-aware-scheduling=true. Цей параметр не залежить від функціональної можливості VolumeLimitScaling.

Якщо ви використовуєте cluster autoscaler, встановлюйте spec.preventPodSchedulingIfMissing у true лише тоді, коли cluster autoscaler налаштовано з --enable-csi-node-aware-scheduling=true. Інакше його симуляції планування не включатимуть потрібну інформацію CSINode для нових вузлів, і cluster autoscaler може не масштабуватися вгору для Podʼів в очікуванні, які використовують томи CSI.

6.10 - Локальне тимчасове сховище

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

Podʼи використовують тимчасове локальне сховище як робочий простір, для кешування та для логів. kubelet може надавати Podʼам робочий простір, використовуючи локальне тимчасове сховище для монтування томів emptyDir у контейнери.

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

Увага:

Якщо вузол виходить з ладу, дані в його тимчасовому сховищі можуть бути втрачені. Ваші застосунки не можуть очікувати жодних SLA щодо продуктивності (наприклад, дискових IOPS) від локального тимчасового сховища.

Примітка:

Щоб квота ресурсів працювала для ephemeral-storage, потрібно виконати дві дії:

  • Адміністратор встановлює квоту ресурсів для ephemeral-storage в просторі імен.
  • Користувач повинен вказати обмеження для ресурсу ephemeral-storage у специфікації Podʼа.

Якщо користувач не вказав обмеження ресурсу ephemeral-storage у специфікації Podʼа, квота ресурсів не застосовується до ephemeral-storage.

Kubernetes дозволяє відстежувати, резервувати та обмежувати обсяг тимчасового локального сховища, яке може використовувати Pod.

Конфігурації для локального тимчасового сховища

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

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

kubelet також записує логи контейнерів на рівні вузла і обробляє їх аналогічно тимчасовому локальному сховищу.

kubelet записує логи у файли всередині налаштованої теки логів (стандартно /var/log) і має базову теку для інших локально збережених даних (стандартно /var/lib/kubelet).

Зазвичай і /var/lib/kubelet, і /var/log розташовані в кореневій файловій системі, і kubelet розроблено з урахуванням такого розташування.

Ваш вузол може мати скільки завгодно інших файлових систем, які не використовуються для Kubernetes.

Ви використовуєте на вузлі одну файлову систему для тимчасових даних запущених Podʼів, таких як логи та томи emptyDir. Цю файлову систему також можна використовувати для інших даних, наприклад, для системних логів, не повʼязаних із Kubernetes; вона може бути навіть кореневою файловою системою.

kubelet також записує логи контейнерів на рівні вузла у першу файлову систему і обробляє їх аналогічно тимчасовому локальному сховищу.

Ви також використовуєте окрему файлову систему на іншому логічному пристрої зберігання. У цій конфігурації середовище виконання контейнерів зберігає і шари образів контейнерів, і шари, доступні для запису, у цій другій файловій системі. Налаштуйте це місце зберігання в середовищі виконання контейнерів, а не в kubelet.

Перша файлова система не містить жодних шарів образів або шарів, доступних для запису.

Ваш вузол може мати скільки завгодно інших файлових систем, які не використовуються для Kubernetes.

У цій конфігурації шари образів контейнерів розташовані в окремій файловій системі, а шари контейнерів, доступні для запису, — у тій самій файловій системі, що й тимчасові дані kubelet, такі як логи та томи emptyDir.

Ця конфігурація вимагає підтримки сигналів виселення containerfs. Докладніше про функціональну можливість і середовища виконання контейнерів, які підтримують цю конфігурацію, дивіться у виселенні внаслідок тиску на вузол.

На сторінці Виселення внаслідок тиску на вузол ці файлові системи, за якими ведеться спостереження, позначаються як nodefs, imagefs та containerfs. Ці назви не завжди означають окремі точки монтування.

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

Якщо у вас інша конфігурація, kubelet не застосовує обмеження ресурсів для тимчасового локального сховища.

Примітка:

kubelet відстежує томи emptyDir типу tmpfs як використання памʼяті контейнера, а не як локальне тимчасове сховище.

Примітка:

kubelet може відстежувати тимчасове сховище лише у файлових системах, які він спостерігає через підтримувані схеми розташування. Якщо ви монтуєте додаткові файлові системи за такими шляхами, як /var/lib/kubelet, /var/log або тека сховища середовища виконання контейнерів, поза цими схемами, kubelet може неправильно повідомляти про тимчасове сховище.

Встановлення запитів та обмежень для локального тимчасового сховища

Ви можете вказати ephemeral-storage для керування локальним тимчасовим сховищем. Кожен контейнер Podʼа може вказати одне або обидва з такого:

  • spec.containers[].resources.limits.ephemeral-storage
  • spec.containers[].resources.requests.ephemeral-storage

Обмеження та запити для ephemeral-storage вимірюються в байтах. Ви можете виразити обсяг сховища як просте ціле число або як число з фіксованою комою, використовуючи один із таких суфіксів: E, P, T, G, M, k. Ви також можете використовувати еквіваленти степеня двійки: Ei, Pi, Ti, Gi, Mi, Ki. Наприклад, усі наведені нижче величини мають приблизно однакове значення:

  • 128974848
  • 129e6
  • 129M
  • 123Mi

Зверніть увагу на регістр суфіксів. Якщо ви запитуєте 400m тимчасового сховища, це запит на 0,4 байта. Той, хто це ввів, мабуть, мав на увазі 400 мебібайтів (400Mi) або 400 мегабайтів (400M).

У наведеному нижче прикладі Pod має два контейнери. Кожен контейнер має запит на 2GiB локального тимчасового сховища. Кожен контейнер має обмеження в 4GiB локального тимчасового сховища. Отже, Pod має запит на 4GiB локального тимчасового сховища та обмеження у 8GiB локального тимчасового сховища. 500Mi цього обмеження може використати том emptyDir.

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
    volumeMounts:
    - name: ephemeral
      mountPath: "/tmp"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
    volumeMounts:
    - name: ephemeral
      mountPath: "/tmp"
  volumes:
    - name: ephemeral
      emptyDir:
        sizeLimit: 500Mi

Як плануються Podʼи із запитами ephemeral-storage

Коли ви створюєте Pod, планувальник Kubernetes вибирає вузол, на якому працюватиме Pod. Кожен вузол має максимальний обсяг локального тимчасового сховища, який він може надати для Podʼів. Докладнішу інформацію дивіться в розділі Node Allocatable.

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

Керування споживанням тимчасового сховища

Якщо kubelet керує локальним тимчасовим сховищем як ресурсом, то kubelet вимірює використання сховища в:

  • томах emptyDir, за винятком томів tmpfs emptyDir
  • теках, що містять логи на рівні вузла
  • шарах контейнерів, доступних для запису

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

Для ізоляції на рівні контейнера, якщо використання шару контейнера, доступного для запису, та логів перевищує його обмеження сховища, kubelet позначає Pod для виселення.

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

Увага:

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

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

Дивіться підтримувані конфігурації для тимчасового локального сховища.

kubelet підтримує різні способи вимірювання використання сховища Podʼа:

kubelet виконує регулярні заплановані перевірки, які сканують кожен том emptyDir, теку логів контейнера та шар контейнера, доступний для запису.

Сканування вимірює, скільки місця використовується.

Примітка:

У цьому режимі kubelet не відстежує відкриті дескриптори видалених файлів.

Якщо ви (або контейнер) створюєте файл у томі emptyDir, щось потім відкриває цей файл, а ви видаляєте файл, поки він ще відкритий, то inode видаленого файлу залишається, доки ви не закриєте цей файл, але kubelet не вважає це місце використаним.

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.31; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the LocalStorageCapacityIsolationFSQuotaMonitoring feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Квоти проєктів — це можливість на рівні операційної системи для керування використанням сховища у файлових системах. У Kubernetes ви можете ввімкнути квоти проєктів для моніторингу використання сховища. Переконайтеся, що файлова система, на якій розміщено томи emptyDir на вузлі, підтримує квоти проєктів. Наприклад, XFS та ext4fs пропонують квоти проєктів.

Примітка:

Квоти проєктів дозволяють моніторити використання сховища; вони не застосовують обмеження.

Kubernetes використовує ідентифікатори проєктів, починаючи з 1048576. Використовувані ідентифікатори зареєстровані в /etc/projects та /etc/projid. Якщо ідентифікатори проєктів у цьому діапазоні використовуються в системі для інших цілей, їх необхідно зареєструвати в /etc/projects та /etc/projid, щоб Kubernetes не використовував їх.

Квоти є швидшими та точнішими, ніж сканування тек. Коли тека призначається проєкту, всі файли, створені в цій теці, створюються в цьому проєкті, і ядро має лише відстежувати, скільки блоків використовується файлами в цьому проєкті. Якщо файл створено та видалено, але він має відкритий дескриптор файлу, він продовжує споживати простір. Відстеження квот точно фіксує цей простір, тоді як сканування тек пропускає простір, який використовують видалені файли.

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

Якщо ви хочете використовувати квоти проєктів, вам слід:

  • Увімкнути функціональну можливість LocalStorageCapacityIsolationFSQuotaMonitoring=true за допомогою поля featureGates у конфігурації kubelet.

  • Переконатися, що функціональну можливість UserNamespacesSupport увімкнено, а ядро, реалізація CRI та середовище виконання OCI підтримують простори імен користувачів.

  • Переконатися, що в кореневій файловій системі (або необовʼязковій файловій системі середовища виконання) увімкнено квоти проєктів. Усі файлові системи XFS підтримують квоти проєктів. Для файлових систем ext4 потрібно ввімкнути можливість відстеження квот проєктів, поки файлова система не змонтована.

    # For ext4, with /dev/block-device not mounted
    sudo tune2fs -O project -Q prjquota /dev/block-device
    
  • Переконатися, що коренева файлова система (або необовʼязкова файлова система середовища виконання) змонтована з увімкненими квотами проєктів. Для XFS і ext4fs параметр монтування називається prjquota.

Якщо ви не хочете використовувати квоти проєктів, вам слід:

Що далі

6.11 - Моніторинг справності томів

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.21; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CSIVolumeHealth feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Моніторинг справності томів CSI дозволяє драйверу CSI повідомляти Kubernetes про проблеми зі справністю тому або бекенду його сховища безпосередньо. Драйвер звітує через RPC CSI, а Kubernetes показує ці звіти у трьох полях статусу: PersistentVolumeClaim .status.healthStatus, Pod .status.volumeHealth та CSINode .status.storageHealth. Автоматизація може стежити за цими стійкими полями статусу, а не відтворювати справність томів з ефемерних Events чи зі специфічних для постачальника dashboards.

Примітка:

Функціональна можливість CSIVolumeHealth існує з Kubernetes v1.21, але механізм, описаний на цій сторінці, є переробкою, яка замінює початкову альфа-реалізацію. Що змінилося, дивіться в розділі Обмеження.

Як це працює

Специфікація CSI визначає чотири RPC для звітування про справність:

  • На втулку контролера CSI: ControllerListVolumeHealth та ControllerGetVolumeHealth, для справності кожного тому, яку спостерігає контролер.
  • На втулку вузла CSI: NodeGetVolumeHealth, для справності кожного тому, яку спостерігає вузол, та NodeGetStorageHealth, для справності бекенду сховища, як її видно з цього вузла.

Драйверу потрібно реалізувати лише ті RPC, які він хоче підтримувати, і оголосити їхню підтримку через можливості втулка CSI. Драйвер, який не реалізує жодного з цих RPC, ніколи не опитується, і звітування для нього залишається неактивним. Драйвер, який оголошує можливість контролера LIST_VOLUME_HEALTH, також повинен оголосити GET_VOLUME_HEALTH; цю вимогу забезпечує sidecar-контейнер csi-external-health-monitor-controller.

Кожен звіт про справність містить status із невеликого набору значень, придатних для машинного аналізу, разом із визначеним драйвером reason та необовʼязковим зрозумілим людині message:

  • Значення статусу на рівні тому (використовуються для PersistentVolumeClaim.status.healthStatus та Pod.status.volumeHealth): Inaccessible, DataLoss, Degraded.
  • Значення статусу бекенду сховища (використовуються для CSINode.status.storageHealth): StorageUnreachable, StorageDegraded.

Звіти зі сторони вузла та зі сторони контролера незалежні: том може бути Inaccessible з одного вузла, який втратив шлях до даних бекенду, тоді як втулок контролера все ще повідомляє, що том справний, і навпаки.

Справність, про яку повідомляється в Podʼах

Для томів, які використовують драйвер CSI з підтримкою RPC NodeGetVolumeHealth зі сторони вузла, kubelet періодично викликає цей RPC для кожного тому CSI, який він змонтував для Podʼа, і записує результат у pod.status.volumeHealth, використовуючи як ключ назву тому з pod.spec.volumes. Інтервал опитування — це налаштування kubelet volumeStatsAggPeriod (прапорець командного рядка --volume-stats-agg-period).

apiVersion: v1
kind: Pod
# ...
status:
  volumeHealth:
  - name: my-volume
    healthConditions:
    - status: Inaccessible
      reason: VolumeNotFound
      message: "volume not found on the storage backend"
    lastTransitionTime: "2026-07-20T12:00:00Z"

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

Справність, про яку повідомляється в CSINode

Для кожного зареєстрованого на вузлі драйвера CSI, який підтримує NodeGetStorageHealth, kubelet періодично викликає цей RPC і записує результат у csinode.status.storageHealth, використовуючи як ключ назву драйвера.

apiVersion: storage.k8s.io/v1
kind: CSINode
# ...
status:
  storageHealth:
  - name: csi.example.com
    healthConditions:
    - status: StorageUnreachable
      reason: NetworkPartition
      message: "data path to the storage backend is unreachable from this node"

Запис StorageHealthCondition може за потреби обмежувати свою дію конкретним accessMode або volumeMode для бекендів, які деградують асиметрично (наприклад, мережева проблема, яка впливає на доступ ReadWriteMany, але не на ReadWriteOnce).

Запис у csinodes/status — це нова можливість, додана цією функціональністю: режим авторизації Node та втулок допуску NodeRestriction дозволяють kubelet змінювати лише обʼєкт CSINode, який відповідає його власному вузлу, і лише поки ввімкнено функціональну можливість CSIVolumeHealth.

Справність, про яку повідомляється в PersistentVolumeClaims

Справність томів, яку спостерігає контролер, записує в persistentvolumeclaim.status.healthStatus sidecar-контейнер csi-external-health-monitor-controller, що працює поряд із втулком контролера драйвера CSI. Sidecar-контейнер викликає ControllerListVolumeHealth (або переходить на ControllerGetVolumeHealth для кожного тому) і записує результат:

apiVersion: v1
kind: PersistentVolumeClaim
# ...
status:
  healthStatus:
    healthConditions:
    - status: Inaccessible
      reason: VolumeNotFound
      message: "volume not found on the storage backend"
    lastTransitionTime: "2026-07-20T12:00:00Z"

Жоден вузол ніколи не записує це поле; це робить лише sidecar-контейнер, що працює в панелі управління. Це не дає скомпрометованому або несправному вузлу впливати на те, що інші користувачі кластера бачать у PVC.

Чи доступний цей шлях для конкретного драйвера, залежить від того, чи цей драйвер і його розгортання sidecar-контейнера csi-external-health-monitor-controller перейшли на нові RPC контролера.

Увімкнення моніторингу справності томів

Моніторингом справності томів керує одна функціональна можливість CSIVolumeHealth на kube-apiserver та kubelet:

  • На kube-apiserver увімкнення функціональної можливості дозволяє записувати та читати нові поля статусу; вимкнення вилучає ці поля під час наступного запису в обʼєкт, але зберігає вже збережені значення.
  • На kubelet увімкнення функціональної можливості запускає періодичне опитування зі сторони вузла, описане вище.

Моніторинг зі сторони контролера, який заповнює persistentvolumeclaim.status.healthStatus, не має власної функціональної можливості. Розгортання sidecar-контейнера csi-external-health-monitor-controller поряд із втулком контролера вашого драйвера CSI саме по собі є ввімкненням цього моніторингу.

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

Моніторинг

kubelet надає метрику типу gauge csi_node_storage_health_status з мітками driver_name, status та reason і значенням 1 для кожного стану бекенду сховища, про який наразі повідомляється для драйвера на цьому вузлі.

Обмеження

  • Kubernetes лише показує ці звіти про справність, але не діє на їх основі. Ніщо в Kubernetes не переплановує Podʼи, не перемикає томи на резерв і не реагує якось інакше на повідомлений стан самостійно. Створення контролера виправлення на основі цих полів статусу залишається за операторами кластерів і постачальниками.
  • Старіша альфа-реалізація тієї самої функціональної можливості CSIVolumeHealth (доступна з Kubernetes v1.21) повідомляла про ненормальні стани томів за допомогою Events Kubernetes та метрики kubelet_volume_stats_health_status_abnormal. Цей механізм замінено полями статусу та RPC, описаними на цій сторінці, і його більше не існує.

Що далі

  • Прочитайте KEP-1432, щоб ознайомитися з повним дизайном.
  • Дивіться документацію драйверів CSI, щоб дізнатися, які драйвери CSI реалізують моніторинг справності томів.

6.12 - Сховище у Windows

На цій сторінці наведено огляд сховища, специфічного для операційної системи Windows.

Постійне сховище

У Windows є багатошаровий драйвер файлової системи для монтування шарів контейнера та створення копії файлової системи на основі NTFS. Усі шляхи файлів у контейнері розвʼязуються лише в контексті цього контейнера.

  • У Docker монтування томів може бути спрямоване лише на теку в контейнері, а не на окремий файл. Це обмеження не стосується containerd.
  • Монтування томів не може проєктувати файли або теки назад на файлову систему хоста.
  • Файлові системи лише для читання не підтримуються, оскільки завжди потрібен доступ для запису до реєстру Windows та бази даних SAM. Однак томи лише для читання підтримуються.
  • Маски користувача та дозволи для томів недоступні. Оскільки SAM не є спільним для хоста й контейнера, між ними немає зіставлення. Усі дозволи розвʼязуються в контексті контейнера.

Як наслідок, на вузлах Windows не підтримуються такі можливості сховища:

  • Монтування підшляхів тому: у контейнері Windows можна змонтувати лише весь том
  • Монтування підшляхів тому для Secrets
  • Проєкція монтування хоста
  • Коренева файлова система лише для читання (зіставлені томи все ще підтримують readOnly)
  • Зіставлення блокових пристроїв
  • Памʼять як носій сховища (наприклад, emptyDir.medium зі значенням Memory)
  • Можливості файлової системи, такі як uid/gid; дозволи файлової системи Linux для кожного користувача
  • Встановлення дозволів Secret за допомогою DefaultMode (через залежність від UID/GID)
  • Підтримка сховища/томів на основі NFS
  • Розширення змонтованого тому (resizefs)

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

Компоненти керування томами постачаються як втулки томів Kubernetes. У Windows підтримуються такі широкі класи втулків томів Kubernetes:

Вбудовані втулки томів

Наведені нижче вбудовані втулки підтримують постійне сховище на вузлах Windows:

7 - Конфігурація

Механізми конфігурації в Kubernetes.

Kubernetes надає способи відокремлення конфігурації від коду. Це поширена практика, яка узгоджується з принципами DevOps та з архітектурою cloud native.

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

Щоб дізнатися про налаштування інструмента командного рядка kubectl, прочитайте налаштування доступу до кількох кластерів.

API Kubernetes, повʼязані з конфігурацією

Kubernetes надає два основні види API, які ви можете використовувати для зберігання конфігурації, готової для завантаження та використання Podʼом (або іншим компонентом): ConfigMap та Secret.

Види API спеціального призначення CertificateSigningRequest та ClusterTrustBundle також містять дані конфігурації, що стосуються деяких конкретних випадків, таких як конфігурація TLS.

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

Щоб дізнатися про використання конфігурації у ваших застосунках, прочитайте про інʼєкцію даних у застосунки.

ConfigMaps

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

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

Secrets

Secret — це обʼєкт, який містить певний обсяг конфіденційних даних, таких як пароль, токен або ключ.

Secrets схожі на ConfigMaps, але спеціально призначені для зберігання конфіденційних даних. Прочитайте сторінку про Secrets, щоб дізнатися про переваги (та обмеження) щодо інформаційної безпеки та Secrets.

Конфігурація через sidecar-контейнери або init-контейнери

Ви також можете використовувати init-контейнер або sidecar-контейнер, щоб надати конфігурацію вашому робочому навантаженню.

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

Sidecar-контейнер як помічник

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

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

Init-контейнер як помічник

Podʼи можуть мати init-контейнери, які запускаються перед головним застосунком. На відміну від sidecar-контейнерів, init-контейнери завершуються до того, як запустяться головні контейнери Podʼа (з основним застосунком), тому конфігурація встановлюється тільки один раз.

У вас є два основні варіанти конфігурації за допомогою init-контейнерів:

Конфігурація через файлову систему

За цього варіанту init-контейнер отримує конфігурацію та записує її у файл або кілька файлів, зазвичай у локальний для Podʼа том.

Конфігурація через змінні середовища

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

Це робиться за допомогою локального тому, але лише init-контейнер монтує цей том. Потім kubelet читає з цього тому перед запуском контейнера застосунку.

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

Конфігурація кластера

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

Що далі

Усі наведені нижче посилання стосуються загальної концепції конфігурації та Kubernetes:

а також ви можете переглянути список форматів конфігураційних файлів у розділі довідки.

У межах цього розділу документації ви можете прочитати про:

7.1 - ConfigMaps

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

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

Увага:

ConfigMap не забезпечує конфіденційності або шифрування. Якщо дані, які ви хочете зберегти, конфіденційні, використовуйте Secret замість ConfigMap або використовуйте додаткові (сторонні) інструменти для збереження ваших даних в таємниці.

Мотивація

Використовуйте ConfigMap для визначення конфігураційних даних окремо від коду застосунку.

Наприклад, уявіть, що ви розробляєте застосунок, який ви можете запускати на власному компʼютері (для розробки) і в хмарі (для обробки реального трафіку). Ви пишете код, який читає змінну середовища з назвою DATABASE_HOST. Локально ви встановлюєте цю змінну в значення localhost. У хмарі ви встановлюєте її так, щоб вона посилалася на Service, який надає доступ до компонента бази даних вашому кластеру. Це дозволяє вам отримати образ контейнера, який працює в хмарі, і у разі потреби налагоджувати той самий код локально.

Примітка:

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

Обʼєкт ConfigMap

ConfigMap — це обʼєкт API, який дозволяє зберігати конфігураційні дані для використання іншими обʼєктами. На відміну від більшості обʼєктів Kubernetes, у яких є spec, ConfigMap має поля data та binaryData. Ці поля приймають пари ключ-значення як свої значення. Обидва поля data і binaryData є необовʼязковими. Поле data призначене для зберігання рядків UTF-8, тоді як поле binaryData призначене для зберігання бінарних даних у вигляді рядків, закодованих у base64.

Назва ConfigMap повинна бути дійсним піддоменом DNS.

Кожний ключ у полі data або binaryData повинен складатися з буквено-цифрових символів, -, _ або .. Ключі, збережені в data, не повинні перетинатися з ключами у полі binaryData.

Починаючи з версії v1.19, ви можете додати поле immutable до визначення ConfigMap, щоб створити незмінний ConfigMap.

ConfigMaps та Podʼи

Ви можете написати spec Podʼа, який посилається на ConfigMap і конфігурує контейнер(и) в цьому Podʼі на основі даних з ConfigMap. Pod і ConfigMap повинні бути в тому самому просторі імен.

Примітка:

spec статичного Podʼа не може посилатися на ConfigMap або на інші обʼєкти API.

Ось приклад ConfigMap, який має деякі ключі з окремими значеннями, а також інші ключі, де значення виглядає як фрагмент формату конфігурації.

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # ключі у вигляді властивостей; кожен ключ зіставляється з простим значенням
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"

  # ключі у формі файлів
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true

Є чотири різних способи використання ConfigMap для конфігурації контейнера всередині Podʼа:

  1. В команді та аргументах контейнера
  2. У змінних середовища для контейнера
  3. Додайте файл до тому тільки для читання, щоб застосунок міг його читати
  4. Напишіть код для виконання всередині Podʼа, який використовує API Kubernetes для читання ConfigMap

Ці різні методи підходять для різних способів моделювання даних, які використовуються. Для перших трьох методів kubelet використовує дані з ConfigMap під час запуску контейнера(ів) для Podʼа.

Четвертий метод означає, що вам потрібно написати код для читання ConfigMap та його даних. Однак, оскільки ви використовуєте прямий доступ до API Kubernetes, ваш застосунок може підписатися на отримання оновлень, коли змінюється ConfigMap, і реагувати на це. Завдяки прямому доступу до API Kubernetes цей підхід також дозволяє вам отримувати доступ до ConfigMap в іншому просторі імен.

Ось приклад Podʼа, який використовує значення з game-demo для конфігурації Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: demo
      image: alpine
      command: ["sleep", "3600"]
      env:
        # Define the environment variable
        - name: PLAYER_INITIAL_LIVES # Notice that the case is different here
                                     # from the key name in the ConfigMap.
          valueFrom:
            configMapKeyRef:
              name: game-demo           # The ConfigMap this value comes from.
              key: player_initial_lives # The key to fetch.
        - name: UI_PROPERTIES_FILE_NAME
          valueFrom:
            configMapKeyRef:
              name: game-demo
              key: ui_properties_file_name
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
  # You set volumes at the Pod level, then mount them into containers inside that Pod
  - name: config
    configMap:
      # Provide the name of the ConfigMap you want to mount.
      name: game-demo
      # An array of keys from the ConfigMap to create as files
      items:
      - key: "game.properties"
        path: "game.properties"
      - key: "user-interface.properties"
        path: "user-interface.properties"
        

ConfigMap не розрізняє однорядкові значення властивостей та багаторядкові файлоподібні значення. Важливо, як Podʼи та інші обʼєкти використовують ці значення.

У цьому прикладі визначення тому та монтування його всередину контейнера demo як /config створює два файли, /config/game.properties та /config/user-interface.properties, хоча в ConfigMap є чотири ключі. Це тому, що визначення Podʼа містить масив items в розділі volumes. Якщо ви взагалі опустите масив items, кожен ключ у ConfigMap стане файлом із такою самою назвою, що й ключ, і ви отримаєте 4 файли.

Використання ConfigMap

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

Найпоширеніший спосіб використання ConfigMap — це налаштування контейнерів, які працюють у Podʼі в тому самому просторі імен. Ви також можете використовувати ConfigMap окремо.

Наприклад, ви можете зустріти надбудови або оператори, які налаштовують свою поведінку на основі ConfigMap.

Використання ConfigMaps як файлів у Podʼі

Щоб використовувати ConfigMap в томі в Podʼі:

  1. Створіть ConfigMap або використовуйте наявний. Декілька Podʼів можуть посилатися на один ConfigMap.
  2. Змініть ваше визначення Podʼа, щоб додати том в .spec.volumes[]. Дайте тому будь-яку назву та встановіть поле .spec.volumes[].configMap.name для посилання на ваш обʼєкт ConfigMap.
  3. Додайте .spec.containers[].volumeMounts[] до кожного контейнера, який потребує ConfigMap. Вкажіть .spec.containers[].volumeMounts[].readOnly = true та задайте в .spec.containers[].volumeMounts[].mountPath назву невикористаної теки, де ви хочете, щоб зʼявився ConfigMap.
  4. Змініть ваш образ або командний рядок так, щоб програма шукала файли у цій теці. Кожен ключ у мапі data ConfigMap стає імʼям файлу в mountPath.

Ось приклад Podʼа, який монтує ConfigMap в том:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    configMap:
      name: myconfigmap

Кожен ConfigMap, який ви хочете використовувати, слід зазначити в .spec.volumes.

Якщо в Podʼі є кілька контейнерів, то кожен контейнер потребує свого власного блоку volumeMounts, але потрібно лише одне поле .spec.volumes на ConfigMap.

Змонтовані ConfigMaps оновлюються автоматично

Коли ConfigMap, що наразі використовується в томі, оновлюється, спроєцьовані ключі згодом також оновлюються. Kubelet перевіряє, чи змонтований ConfigMap є актуальним під час кожної періодичної синхронізації. Однак kubelet використовує свій локальний кеш для отримання поточного значення ConfigMap. Тип кешу налаштовується за допомогою поля configMapAndSecretChangeDetectionStrategy в структурі KubeletConfiguration. ConfigMap можна поширити за допомогою watch (типово), на основі ttl або шляхом перенаправлення всіх запитів безпосередньо до API-сервера. Отже, загальна затримка від моменту оновлення ConfigMap до моменту, коли нові ключі буде спроєцьовано в Pod, може дорівнювати періоду синхронізації kubelet + затримці поширення кешу, де затримка поширення кешу залежить від обраного типу кешу (вона дорівнює затримці поширення watch, ttl кешу або нулю відповідно).

ConfigMap, що використовуються як змінні середовища, не оновлюються автоматично і потребують перезапуску Podʼа.

Примітка:

Контейнер, який використовує ConfigMap як том з монтуванням subPath, не отримує оновлення ConfigMap.

Використання ConfigMaps як змінних середовища

Щоб використовувати ConfigMap у змінних середовища в Podʼі:

  1. Для кожного контейнера у вашій специфікації Podʼа додайте змінну середовища для кожного ключа ConfigMap, який ви хочете використовувати, до поля env[].valueFrom.configMapKeyRef.
  2. Змініть ваш образ та/або командний рядок так, щоб програма шукала значення у вказаних змінних середовища.

Ось приклад визначення ConfigMap як змінної середовища Podʼа:

Цей ConfigMap (myconfigmap.yaml) містить дві властивості: username та access_level:

apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap
data:
  username: k8s-admin
  access_level: "1"

Ця команда створить обʼєкт ConfigMap:

kubectl apply -f myconfigmap.yaml

Цей Pod використовує вміст ConfigMap як змінні середовища:

apiVersion: v1
kind: Pod
metadata:
  name: env-configmap
spec:
  containers:
    - name: app
      command: ["/bin/sh", "-c", "printenv"]
      image: busybox:latest
      envFrom:
        - configMapRef:
            name: myconfigmap

Поле envFrom вказує Kubernetes створити змінні середовища з джерел, вкладених у нього. Внутрішній configMapRef посилається на ConfigMap за його назвою і вибирає всі його пари ключ-значення. Додайте Pod до свого кластера, а потім перегляньте його логи, щоб побачити виведення команди printenv. Це має підтвердити, що дві пари ключ-значення з ConfigMap були встановлені як змінні середовища:

kubectl apply -f env-configmap.yaml
kubectl logs pod/env-configmap

Вивід буде подібний до цього:

...
username: "k8s-admin"
access_level: "1"
...

Іноді Pod не потребуватиме доступу до всіх значень у ConfigMap. Наприклад, можна мати інший Pod, який використовує тільки значення username з ConfigMap. Для цього випадку можна використовувати синтаксис env.valueFrom, який дозволяє вибирати окремі ключі в ConfigMap. Імʼя змінної середовища також може відрізнятися від ключа в ConfigMap. Наприклад:

apiVersion: v1
kind: Pod
metadata:
  name: env-configmap
spec:
  containers:
  - name: envars-test-container
    image: nginx
    env:
    - name: CONFIGMAP_USERNAME
      valueFrom:
        configMapKeyRef:
          name: myconfigmap
          key: username

У Podʼі, створеному з цього маніфесту, ви побачите, що змінна середовища CONFIGMAP_USERNAME встановлена на значення username з ConfigMap. Інші ключі з даних ConfigMap не будуть скопійовані у середовище.

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

Незмінні ConfigMap

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.21

Функція Kubernetes Immutable Secrets та ConfigMaps надає можливість зробити окремі Secrets та ConfigMaps незмінними. Для кластерів, що інтенсивно використовують ConfigMap (принаймні десятки тисяч унікальних монтувань ConfigMap до Podʼів), запобігання змінам їх даних має такі переваги:

  • захищає від випадкових (або небажаних) оновлень, які можуть призвести до відмов застосунків
  • покращує продуктивність кластера шляхом значного зниження навантаження на kube-apiserver, завдяки закриттю спостережень за ConfigMap, позначеними як незмінні.

Ви можете створити незмінний ConfigMap, встановивши поле immutable в true. Наприклад:

apiVersion: v1
kind: ConfigMap
metadata:
  ...
data:
  ...
immutable: true

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

Що далі

7.2 - Secrets

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

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

Secrets схожі на ConfigMaps, але спеціально призначені для зберігання конфіденційних даних.

Увага:

Kubernetes Secrets стандартно зберігаються незашифрованими в базовому сховищі даних API-сервера (etcd). Будь-хто з доступом до API може отримати або змінити Secret, так само як будь-хто з доступом до etcd. Крім того, будь-хто, хто має дозвіл на створення Podʼа у просторі імен, може використовувати цей доступ для читання будь-якого Secret у цьому просторі імен; це включає і непрямий доступ, такий як можливість створення Deployment.

Щоб безпечно використовувати Secrets, виконайте принаймні такі кроки:

  1. Увімкніть шифрування у стані спокою для Secrets.
  2. Увімкніть або налаштуйте правила RBAC з доступом до Secrets за принципом найменших привілеїв.
  3. Обмежте доступ до Secrets конкретними контейнерами.
  4. Розгляньте використання зовнішніх постачальників сховища для Secrets.

Для отримання додаткових рекомендацій щодо керування та покращення безпеки ваших Secrets ознайомтесь із Належними практиками для Kubernetes Secrets.

Докладніше див. у розділі Інформаційна безпека для Secrets.

Застосування Secrets

Ви можете використовувати Secrets для таких цілей:

Панель управління Kubernetes також використовує Secrets; наприклад, Secrets bootstrap-токенів — це механізм, що допомагає автоматизувати реєстрацію вузлів.

Сценарій використання: dotfiles у томі Secret

Ви можете зробити ваші дані "прихованими", визначивши ключ, який починається з крапки. Цей ключ являє собою dotfile або "прихований" файл. Наприклад, коли цей Secret монтується в том secret-volume, том міститиме один файл із назвою .secret-file, а контейнер dotfile-test-container матиме цей файл за шляхом /etc/secret-volume/.secret-file.

Примітка:

Файли, які починаються з крапок, приховані від виводу ls -l; щоб побачити їх під час перегляду вмісту теки, використовуйте ls -la.
apiVersion: v1
kind: Secret
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
    - name: secret-volume
      secret:
        secretName: dotfile-secret
  containers:
    - name: dotfile-test-container
      image: registry.k8s.io/busybox
      command:
        - ls
        - "-l"
        - "/etc/secret-volume"
      volumeMounts:
        - name: secret-volume
          readOnly: true
          mountPath: "/etc/secret-volume"

Сценарій використання: Secret, видимий для одного контейнера в Podʼі

Припустимо, що у вас є програма, яка потребує обробки HTTP-запитів, виконання складної бізнес-логіки та підписування деяких повідомлень за допомогою HMAC. Оскільки у неї складна логіка застосунку, на сервері може бути непомічена вразливість віддаленого читання файлів, яка може розкрити приватний ключ зловмиснику.

Це можна розділити на два процеси у двох контейнерах: фронтенд-контейнер, який обробляє взаємодію з користувачем та бізнес-логіку, але не може бачити приватний ключ; і контейнер-підписувач, який може бачити приватний ключ та відповідає на прості запити на підпис від фронтенду (наприклад, через мережу localhost).

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

Альтернативи Secrets

Замість використання Secret для захисту конфіденційних даних ви можете скористатися альтернативами.

Ось деякі з варіантів:

  • Якщо ваш компонент cloud native має автентифікуватися в іншому застосунку, який, як ви знаєте, працює в тому самому кластері Kubernetes, ви можете використовувати ServiceAccount та його токени, щоб ідентифікувати вашого клієнта.
  • Існують сторонні інструменти, які ви можете запускати, як в межах, так і поза вашим кластером, які керують чутливими даними. Наприклад, сервіс, до якого Podʼи звертаються через HTTPS і який розкриває Secret, якщо клієнт правильно автентифікується (наприклад, з токеном ServiceAccount).
  • Для автентифікації ви можете реалізувати власний підписувач для сертифікатів X.509 і використовувати CertificateSigningRequests, щоб дозволити цьому власному підписувачу видавати сертифікати Podʼам, які їх потребують.
  • Ви можете використовувати втулок пристрою, щоб надати певному Podʼу доступ до локального для вузла апаратного забезпечення шифрування. Наприклад, ви можете планувати довірені Podʼи на вузли, які надають Trusted Platform Module, налаштований окремо (out-of-band).

Ви також можете комбінувати два або більше з цих варіантів, включаючи використання самих обʼєктів Secret.

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

Типи Secret

Під час створення Secret ви можете вказати його тип, використовуючи поле type ресурсу Secret, або певні еквівалентні прапорці командного рядка kubectl (якщо вони доступні). Тип Secret використовується для полегшення програмної обробки даних Secret.

Kubernetes надає кілька вбудованих типів для деяких типових сценаріїв використання. Ці типи відрізняються за умовами перевірки та обмеженнями, які Kubernetes накладає на них.

Вбудований типВикористання
Opaqueдовільні користувацькі дані
kubernetes.io/service-account-tokenтокен ServiceAccount
kubernetes.io/dockercfgсеріалізований файл ~/.dockercfg
kubernetes.io/dockerconfigjsonсеріалізований файл ~/.docker/config.json
kubernetes.io/basic-authоблікові дані для базової автентифікації
kubernetes.io/ssh-authоблікові дані для SSH-автентифікації
kubernetes.io/tlsдані для клієнта або сервера TLS
bootstrap.kubernetes.io/tokenдані bootstrap-токена

Ви можете визначити та використовувати власний тип Secret, присвоївши непорожній рядок як значення type обʼєкта Secret (порожній рядок розглядається як тип Opaque).

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

Якщо ви визначаєте тип Secret, призначений для загального використання, дотримуйтесь домовленостей та структуруйте тип Secret так, щоб він мав назву вашого домену перед назвою типу, відокремлену символом /. Наприклад: cloud-hosting.example.net/cloud-api-credentials.

Opaque Secrets

Opaque — це стандартний тип Secret, якщо ви не вказуєте явно тип у маніфесті Secret. Під час створення Secret за допомогою kubectl вам потрібно використовувати підкоманду generic, щоб вказати тип Secret Opaque. Наприклад, ця команда створює порожній Secret типу Opaque:

kubectl create secret generic empty-secret
kubectl get secret empty-secret

Вивід виглядає так:

NAME           TYPE     DATA   AGE
empty-secret   Opaque   0      2m6s

У стовпчику DATA показується кількість елементів даних, збережених у Secret. У цьому випадку 0 означає, що ви створили порожній Secret.

Secrets токенів ServiceAccount

Тип Secret kubernetes.io/service-account-token використовується для зберігання токена, який ідентифікує ServiceAccount. Це застарілий механізм, який забезпечує довгострокові облікові дані ServiceAccount для Podʼів.

У Kubernetes v1.22 та пізніших рекомендований підхід полягає в тому, щоб натомість отримувати короткостроковий токен ServiceAccount з автоматичною ротацією за допомогою API TokenRequest. Ви можете отримати ці короткострокові токени такими методами:

  • Викликайте API TokenRequest безпосередньо або через клієнт API, такий як kubectl. Наприклад, ви можете використовувати команду kubectl create token.
  • Запитуйте змонтований токен у спроєцьованому томі у вашому маніфесті Podʼа. Kubernetes створює токен і монтує його в Pod. Токен автоматично анулюється, коли Pod, в якому він монтується, видаляється. Докладні відомості див. в розділі Запуск Podʼа з проєкцією токена ServiceAccount.

Примітка:

Ви повинні створювати Secret токена ServiceAccount лише в тому випадку, якщо ви не можете використовувати API TokenRequest для отримання токена, і вам прийнятно з погляду безпеки зберігання безстрокового токена доступу в доступному для читання обʼєкті API. Для інструкцій див. Створення довгострокового API-токена для ServiceAccount вручну.

Під час використання цього типу Secret вам потрібно переконатися, що анотація kubernetes.io/service-account.name містить назву наявного ServiceAccount. Якщо ви створюєте як Secret, так і обʼєкти ServiceAccount, ви повинні спочатку створити обʼєкт ServiceAccount.

Після створення Secret контролер Kubernetes заповнює деякі інші поля, такі як анотація kubernetes.io/service-account.uid, та ключ token в полі data, який заповнюється токеном автентифікації.

У цьому прикладі конфігурації оголошується Secret токена ServiceAccount:

apiVersion: v1
kind: Secret
metadata:
  name: secret-sa-sample
  annotations:
    kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
  extra: YmFyCg==

Після створення Secret дочекайтеся, коли Kubernetes заповнить ключ token в полі data.

Для отримання додаткової інформації про роботу ServiceAccounts перегляньте документацію про ServiceAccount. Ви також можете перевірити поле automountServiceAccountToken та поле serviceAccountName у Pod для отримання інформації про посилання на облікові дані ServiceAccount з Podʼів.

Secrets конфігурації Docker

Якщо ви створюєте Secret для зберігання облікових даних для доступу до реєстру образів контейнерів, ви повинні використовувати одне з таких значень type для цього Secret:

  • kubernetes.io/dockercfg: зберігає серіалізований ~/.dockercfg, який є застарілим форматом налаштування командного рядка Docker. У полі data Secret міститься ключ .dockercfg, значення якого — це вміст файлу ~/.dockercfg, закодованого у форматі base64.
  • kubernetes.io/dockerconfigjson: зберігає серіалізований JSON, який відповідає тим самим правилам формату, що й файл ~/.docker/config.json, який є новим форматом для ~/.dockercfg. У полі data Secret має міститися ключ .dockerconfigjson, значення якого — це вміст файлу ~/.docker/config.json, закодованого у форматі base64.

Нижче наведено приклад для Secret типу kubernetes.io/dockercfg:

apiVersion: v1
kind: Secret
metadata:
  name: secret-dockercfg
type: kubernetes.io/dockercfg
data:
  .dockercfg: |
    eyJhdXRocyI6eyJodHRwczovL2V4YW1wbGUvdjEvIjp7ImF1dGgiOiJvcGVuc2VzYW1lIn19fQo=

Примітка:

Якщо ви не хочете виконувати кодування у формат base64, ви можете натомість використати поле stringData.

Під час створення Secrets конфігурації Docker за допомогою маніфесту API-сервер перевіряє, чи існує очікуваний ключ у полі data, і чи надане значення можна розібрати як дійсний JSON. API-сервер не перевіряє, чи є цей JSON фактично файлом конфігурації Docker.

Ви також можете використовувати kubectl для створення Secret для доступу до реєстру контейнерів, наприклад, коли у вас немає файлу конфігурації Docker:

kubectl create secret docker-registry secret-tiger-docker \
  --docker-email=tiger@acme.example \
  --docker-username=tiger \
  --docker-password=pass1234 \
  --docker-server=my-registry.example:5000

Ця команда створює Secret типу kubernetes.io/dockerconfigjson.

Отримайте поле .data.dockerconfigjson з цього нового Secret та розкодуйте дані:

kubectl get secret secret-tiger-docker -o jsonpath='{.data.*}' | base64 -d

Вивід еквівалентний такому JSON-документу (який також є дійсним файлом конфігурації Docker):

{
  "auths": {
    "my-registry.example:5000": {
      "username": "tiger",
      "password": "pass1234",
      "email": "tiger@acme.example",
      "auth": "dGlnZXI6cGFzczEyMzQ="
    }
  }
}

Увага:

Значення auth закодовано у формат base64; воно приховане, але не є секретним. Будь-хто, хто може прочитати цей Secret, може дізнатися токен доступу (bearer) до реєстру.

Рекомендується використовувати постачальників облікових даних для динамічного й безпечного надання Secrets для завантаження образів на запит.

Secret базової автентифікації

Тип kubernetes.io/basic-auth наданий для зберігання облікових даних, необхідних для базової автентифікації. Під час використання цього типу Secret поле data Secret повинно містити один із двох таких ключів:

  • username: імʼя користувача для автентифікації
  • password: пароль або токен для автентифікації

Обидва значення для цих ключів є рядками, закодованими в base64. Ви також можете надати відкритий текст, використовуючи поле stringData у маніфесті Secret.

Цей маніфест є прикладом Secret для базової автентифікації:

apiVersion: v1
kind: Secret
metadata:
  name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
  username: admin # required field for kubernetes.io/basic-auth
  password: t0p-Secret # required field for kubernetes.io/basic-auth

Примітка:

Поле stringData для Secret погано працює із застосуванням на боці сервера (server-side apply).

Тип Secret для базової автентифікації наданий лише для зручності. Ви можете створити тип Opaque для облікових даних, які використовуються для базової автентифікації. Однак використання визначеного та публічного типу Secret (kubernetes.io/basic-auth) допомагає іншим людям зрозуміти призначення вашого Secret та встановлює домовленості для очікуваних назв ключів.

Secret для автентифікації SSH

Вбудований тип kubernetes.io/ssh-auth наданий для зберігання даних, що використовуються в автентифікації SSH. Під час використання цього типу Secret вам потрібно вказати пару ключ-значення ssh-privatekey в полі data (або stringData) як SSH-автентифікаційні дані для використання.

Цей маніфест є прикладом Secret, який використовується для автентифікації SSH з використанням пари публічного/приватного ключів:

apiVersion: v1
kind: Secret
metadata:
  name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
  # the data is abbreviated in this example
  ssh-privatekey: |
    UG91cmluZzYlRW1vdGljb24lU2N1YmE=

Тип Secret для автентифікації SSH наданий лише для зручності. Ви можете створити тип Opaque для облікових даних, які використовуються для автентифікації SSH. Однак використання визначеного та публічного типу Secret (kubernetes.io/ssh-auth) допомагає іншим людям зрозуміти призначення вашого Secret та встановлює домовленості для очікуваних назв ключів. API Kubernetes перевіряє, чи встановлені необхідні ключі для Secret цього типу.

Увага:

Приватні ключі SSH не встановлюють довіру між клієнтом SSH та хост-сервером самі по собі. Для зменшення ризику атак "man-in-the-middle" потрібен додатковий спосіб встановлення довіри, наприклад, файл known_hosts, доданий до ConfigMap.

TLS Secrets

Тип Secret kubernetes.io/tls призначений для зберігання сертифіката та повʼязаного з ним ключа, які зазвичай використовуються для TLS.

Одним з поширених використань TLS Secret є налаштування шифрування під час передачі для Ingress, але ви також можете використовувати його з іншими ресурсами або безпосередньо у вашому навантаженні. Під час використання цього типу Secret ключі tls.key та tls.crt повинні бути надані в полі data (або stringData) конфігурації Secret, хоча API-сервер фактично не перевіряє значення кожного ключа.

Як альтернативу використанню stringData, ви можете використовувати поле data для надання сертифіката та приватного ключа у вигляді base64-кодованого тексту. Докладніше див. Обмеження щодо назв і даних Secret.

Цей YAML містить приклад конфігурації TLS Secret:

apiVersion: v1
kind: Secret
metadata:
  name: secret-tls
type: kubernetes.io/tls
data:
  # values are base64 encoded, which obscures them but does NOT provide
  # any useful level of confidentiality
  # Replace the following values with your own base64-encoded certificate and key.
  tls.crt: "REPLACE_WITH_BASE64_CERT" 
  tls.key: "REPLACE_WITH_BASE64_KEY"

Тип TLS Secret наданий лише для зручності. Ви можете створити тип Opaque для облікових даних, які використовуються для TLS-автентифікації. Однак використання визначеного та публічного типу Secret (kubernetes.io/tls) допомагає забезпечити однорідність формату Secret у вашому проєкті. API-сервер перевіряє, чи встановлені необхідні ключі для Secret цього типу.

Для створення TLS Secret за допомогою kubectl використовуйте підкоманду tls:

kubectl create secret tls my-tls-secret \
  --cert=path/to/cert/file \
  --key=path/to/key/file

Пара публічного та приватного ключів повинна існувати заздалегідь. Сертифікат публічного ключа для --cert повинен бути закодований у форматі .PEM і повинен відповідати наданому приватному ключу для --key.

Secrets bootstrap-токенів

Тип Secret bootstrap.kubernetes.io/token призначений для токенів, що використовуються під час процесу ініціалізації вузла. Він зберігає токени, які використовуються для підпису відомих ConfigMaps.

Secret bootstrap-токена зазвичай створюється в просторі імен kube-system і називається у формі bootstrap-token-<token-id>, де <token-id> — це 6-символьний рядок ідентифікатора токена.

Як маніфест Kubernetes, Secret bootstrap-токена може виглядати так:

apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-5emitj
  namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
  auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=
  expiration: MjAyMC0wOS0xM1QwNDozOToxMFo=
  token-id: NWVtaXRq
  token-secret: a3E0Z2lodnN6emduMXAwcg==
  usage-bootstrap-authentication: dHJ1ZQ==
  usage-bootstrap-signing: dHJ1ZQ==

У Secret bootstrap-токена вказані такі ключі в data:

  • token-id: Випадковий рядок з 6 символів як ідентифікатор токена. Обовʼязковий.
  • token-secret: Випадковий рядок з 16 символів як власне секрет токена. Обовʼязковий.
  • description: Зрозумілий людині рядок, що описує, для чого використовується токен. Необовʼязковий.
  • expiration: Абсолютний час UTC, відповідно до RFC3339, що вказує, коли має закінчитися дія токена. Необовʼязковий.
  • usage-bootstrap-<usage>: Логічний прапорець, який вказує додаткове використання bootstrap-токена.
  • auth-extra-groups: Розділений комами список назв груп, від імені яких відбуватиметься автентифікація на додачу до групи system:bootstrappers.

Ви також можете надати значення в полі stringData Secret без кодування в base64:

apiVersion: v1
kind: Secret
metadata:
  # Note how the Secret is named
  name: bootstrap-token-5emitj
  # A bootstrap token Secret usually resides in the kube-system namespace
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
  expiration: "2020-09-13T04:39:10Z"
  # This token ID is used in the name
  token-id: "5emitj"
  token-secret: "kq4gihvszzgn1p0r"
  # This token can be used for authentication
  usage-bootstrap-authentication: "true"
  # and it can be used for signing
  usage-bootstrap-signing: "true"

Примітка:

Поле stringData для Secret погано працює із застосуванням на боці сервера (server-side apply).

Робота з Secrets

Створення Secret

Є кілька способів створення Secret:

Обмеження щодо назв і даних Secret

Назва обʼєкта Secret повинна бути дійсним імʼям DNS-піддомену.

Ви можете вказати поля data і/або stringData під час створення файлу конфігурації для Secret. Поля data і stringData є необовʼязковими. Значення для всіх ключів у полі data повинні бути base64-кодованими рядками. Якщо перетворення у рядок base64 не є бажаним, ви можете вибрати поле stringData, яке приймає довільні рядки як значення.

Ключі data і stringData повинні складатися з буквено-цифрових символів, -, _ або .. Усі пари ключ-значення в полі stringData внутрішньо обʼєднуються в поле data. Якщо ключ зустрічається як у полі data, так і у полі stringData, значення, вказане у полі stringData, має пріоритет.

Обмеження розміру

Розмір окремих Secrets обмежено 1MiB. Це зроблено для того, щоб уникнути створення дуже великих Secrets, які можуть вичерпати памʼять API-сервера та kubelet. Однак створення багатьох менших Secrets також може вичерпати памʼять. Ви можете використовувати квоту ресурсів для обмеження кількості Secrets (або інших ресурсів) у просторі імен.

Редагування Secret

Ви можете редагувати наявний Secret, якщо він не є незмінним. Для редагування Secret скористайтеся одним із таких методів:

Ви також можете редагувати дані у Secret за допомогою інструменту Kustomize. Проте цей метод створює новий обʼєкт Secret з відредагованими даними.

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

Використання Secret

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

Джерела томів Secret перевіряються, щоб переконатися, що вказане посилання на обʼєкт дійсно вказує на обʼєкт типу Secret. Тому Secret повинен бути створений перед будь-якими Podʼами, які залежать від нього.

Якщо Secret не може бути отриманий (можливо, через те, що він не існує, або через тимчасову відсутність зʼєднання з API-сервером), kubelet періодично повторює спробу запуску цього Podʼа. Kubelet також повідомляє про подію (Event) для цього Podʼа, включаючи деталі проблеми отримання Secret.

Необовʼязкові Secrets

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

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      optional: true

Типово Secrets є обовʼязковими. Жоден з контейнерів Podʼа не розпочне роботу, доки всі обовʼязкові Secrets не будуть доступні.

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

Використання Secrets як файлів у Podʼі

Якщо ви хочете отримати доступ до даних з Secret у Podʼі, один зі способів це зробити — щоб Kubernetes зробив значення цього Secret доступним як файл у файловій системі одного або декількох контейнерів Podʼа.

Щоб це зробити, дивіться Створення Podʼа, який має доступ до секретних даних через том.

Коли том містить дані з Secret, а цей Secret оновлюється, Kubernetes відстежує це і оновлює дані в томі, використовуючи підхід кінцевої узгодженості (eventually consistent).

Примітка:

Контейнер, який використовує Secret як том з монтуванням subPath, не отримує автоматичних оновлень Secret.

Kubelet зберігає кеш поточних ключів та значень для Secrets, які використовуються у томах для Podʼів на цьому вузлі. Ви можете налаштувати спосіб, яким kubelet виявляє зміни від кешованих значень. Поле configMapAndSecretChangeDetectionStrategy в конфігурації kubelet контролює стратегію, яку використовує kubelet. Стандартною стратегією є Watch.

Оновлення Secrets можуть передаватися за допомогою механізму watch API (стандартно), на основі кешу з визначеним часом життя або шляхом опитування API-сервера кластера на кожному циклі синхронізації kubelet.

Як результат, загальна затримка від моменту оновлення Secret до моменту, коли нові ключі буде спроєцьовано в Pod, може дорівнювати періоду синхронізації kubelet + затримці поширення кешу, де затримка поширення кешу залежить від обраного типу кешу (у тому самому порядку, що й у попередньому абзаці: затримка поширення watch, налаштований TTL кешу або нуль для прямого опитування).

Використання Secrets як змінних середовища

Для використання Secret в змінних середовища в Podʼі:

  1. Для кожного контейнера у вашій специфікації Podʼа додайте змінну середовища для кожного ключа Secret, який ви хочете використовувати, у поле env[].valueFrom.secretKeyRef.
  2. Змініть свій образ і/або командний рядок так, щоб програма шукала значення у вказаних змінних середовища.

Для отримання інструкцій дивіться Визначення змінних середовища контейнера за допомогою даних Secret.

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

Secrets для завантаження образів контейнерів

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

Використання imagePullSecrets

Поле imagePullSecrets є списком посилань на Secrets у тому самому просторі імен. Ви можете використовувати imagePullSecrets, щоб передати Secret, який містить пароль Docker (або іншого) репозиторію образів, в kubelet. Kubelet використовує цю інформацію для завантаження приватного образу від імені вашого Podʼа. Для отримання додаткової інформації про поле imagePullSecrets дивіться PodSpec API.

Вказування imagePullSecret вручну

Ви можете дізнатися, як вказати imagePullSecrets, переглянувши документацію про образи контейнерів.

Організація автоматичного приєднання imagePullSecrets

Ви можете вручну створити imagePullSecrets та посилатися на них з ServiceAccount. Будь-які Podʼи, створені з цим ServiceAccount або типово з ним, отримають поле imagePullSecrets, встановлене відповідно до цього ServiceAccount. Дивіться Додавання ImagePullSecrets до ServiceAccount для детального пояснення цього процесу.

Використання Secrets зі статичними Podʼами

Ви не можете використовувати ConfigMaps або Secrets зі статичними Podʼами.

Незмінні Secrets

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.21

Kubernetes дозволяє вам позначати певні Secrets (і ConfigMaps) як незмінні. Запобігання змінам даних наявного Secret має такі переваги:

  • захищає вас від випадкових (або небажаних) оновлень, які можуть призвести до відмов застосунків
  • (для кластерів, що широко використовують Secrets, — щонайменше десятки тисяч унікальних монтувань Secret у Podʼи) перехід до незмінних Secrets покращує продуктивність вашого кластера завдяки значному зменшенню навантаження на kube-apiserver. Kubelet не потрібно підтримувати [watch] для будь-яких Secrets, позначених як незмінні.

Позначення Secret як незмінного

Ви можете створити незмінний Secret, встановивши поле immutable в true. Наприклад,

apiVersion: v1
kind: Secret
metadata: ...
data: ...
immutable: true

Ви також можете оновити будь-який наявний змінний Secret, щоб зробити його незмінним.

Примітка:

Як тільки Secret або ConfigMap позначений як незмінний, цю зміну неможливо скасувати чи змінити зміст поля data. Ви можете тільки видалити і знову створити Secret. Наявні Podʼи зберігають точку монтування для видаленого Secret — рекомендується створити ці Podʼи заново.

Інформаційна безпека для Secrets

Хоча ConfigMap і Secret працюють схоже, Kubernetes застосовує додаткові заходи захисту для обʼєктів Secret.

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

Налаштування авторизації впливає на те, як можна отримати доступ до даних Secret у межах простору імен. Наприклад, надання дозволів list або watch для Secrets дозволяє субʼєкту читати всі дані Secret у цьому просторі імен, а не лише ті Secrets, на які явно посилаються його Podʼи. Обмежте доступ до мінімального набору дозволів, необхідних для роботи навантаження, і уникайте надання широких ролей, таких як cluster-admin, якщо це не потрібно для адміністративних цілей.

Також дивіться документацію з авторизації.

Secret буде відправлений на вузол тільки у випадку, якщо на цьому вузлі є Pod, якому він потрібен. Для монтування Secrets у Podʼи kubelet зберігає копію даних у tmpfs, щоб конфіденційні дані не записувалися на постійний носій. Коли Pod, який залежить від Secret, видаляється, kubelet видаляє свою локальну копію конфіденційних даних з Secret.

У Podʼі може бути кілька контейнерів. Типово контейнери, які ви визначаєте, мають доступ тільки до стандартного ServiceAccount та повʼязаного з ним Secret. Вам потрібно явно визначити змінні середовища або відобразити том у контейнері, щоб надати доступ до будь-якого іншого Secret.

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

Налаштування доступу до Secrets за принципом найменших привілеїв

Щоб посилити заходи безпеки навколо Secrets, використовуйте окремі простори імен для ізоляції доступу до змонтованих Secrets.

Попередження:

Будь-які контейнери, які працюють з параметром privileged: true на вузлі, можуть отримати доступ до всіх Secrets, що використовуються на цьому вузлі.

Що далі

7.3 - Керування ресурсами Podʼів та контейнерів

Коли ви визначаєте Pod, ви можете додатково вказати, скільки кожного ресурсу потребує контейнер. Найпоширеніші ресурси, які вказують, — це CPU та памʼять (RAM); є й інші.

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

Запити та обмеження

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

Наприклад, якщо ви встановите запит memory 256 MiB для контейнера, і цей контейнер буде в Podʼі, запланованому на вузол із 8GiB памʼяті та без інших Podʼів, то контейнер може спробувати використати більше оперативної памʼяті.

Обмеження — то вже інша справа. Обидва обмеження cpu та memory застосовуються kubeletʼом (та середовищем виконання контейнерів), і, зрештою, забезпечуються ядром. На вузлах Linux ядро Linux застосовує обмеження за допомогою cgroups. Поведінка застосування обмежень cpu та memory дещо відрізняється.

Обмеження cpu застосовуються через тротлінг CPU (CPU throttling). Коли контейнер наближається до свого обмеження cpu, ядро обмежує доступ до CPU відповідно до обмеження контейнера. Тож обмеження cpu є жорстким обмеженням, що застосовує ядро. Контейнери не можуть використовувати більше CPU, ніж зазначено в їх обмеженні cpu.

Обмеження memory застосовуються ядром через завершення процесів за нестачі памʼяті (OOM kills). Якщо контейнер використовує більше памʼяті, ніж його обмеження memory, ядро може його завершити. Однак завершення відбувається тільки тоді, коли ядро виявляє тиск на памʼять. Тому контейнер, який виділяє забагато памʼяті, може бути завершений не одразу. Це означає, що обмеження memory застосовуються реактивно. Контейнер може використовувати більше памʼяті, ніж його обмеження memory, але в такому разі його може бути завершено.

Примітка:

Існує функція MemoryQoS, яка додає тротлінг памʼяті та необовʼязкове багаторівневе резервування памʼяті на вузлах Linux за допомогою cgroup v2. Докладніше дивіться Memory QoS з cgroup v2.

Примітка:

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

Типи ресурсів

Тип ресурсу має базову одиницю вимірювання, і його можна запитувати, обмежувати або і те, й інше. Kubernetes має такі вбудовані типи ресурсів:

Тип ресурсуОписБазова одиниця
cpuОбчислювальні ресурсиcpu (ядро)
memoryОперативна памʼятьБайти
ephemeral-storageЛокальне тимчасове сховищеБайти
hugepages-<size>Великі сторінки (тільки Linux)Байти

Кластери також можуть надавати розширені ресурси (ресурси з власною назвою, які зазвичай надають втулки пристроїв).

Великі сторінки

Для робочих навантажень на Linux ви можете вказати ресурси великих сторінок (huge pages). Великі сторінки є функцією, специфічною для Linux, де ядро вузла виділяє блоки памʼяті, значно більші за стандартний розмір сторінки.

Наприклад, у системі, де розмір стандартної сторінки становить 4KiB, ви можете вказати обмеження hugepages-2Mi: 80Mi. Якщо контейнер намагається виділити понад 40 великих сторінок по 2MiB (загалом 80 MiB), це виділення завершиться невдачею.

Примітка:

Ресурси hugepages-* не можна виділяти понад наявні (overcommit). Це відрізняється від ресурсів memory та cpu.

CPU та памʼять спільно називаються обчислювальними ресурсами або ресурсами. Обчислювальні ресурси — це вимірювані величини, які можна запитувати, виділяти та використовувати. Вони відрізняються від ресурсів API. Ресурси API, такі як Podʼи та Services, є обʼєктами, які можна читати та змінювати через API-сервер Kubernetes.

Запити та обмеження ресурсів для Podʼа та контейнера

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

  • spec.containers[].resources.limits.cpu
  • spec.containers[].resources.limits.memory
  • spec.containers[].resources.limits.ephemeral-storage
  • spec.containers[].resources.limits.hugepages-<size>
  • spec.containers[].resources.requests.cpu
  • spec.containers[].resources.requests.memory
  • spec.containers[].resources.requests.ephemeral-storage
  • spec.containers[].resources.requests.hugepages-<size>

Хоча ви можете вказувати запити та обмеження тільки для окремих контейнерів, також корисно думати про загальні запити та обмеження ресурсів для Podʼа. Для певного ресурсу запит/обмеження ресурсів Podʼа — це сума запитів/обмежень цього типу для кожного контейнера в Podʼі.

Специфікація ресурсів на рівні Podʼів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.34; (стандартно увімкнено)

За умови, що у вашому кластері увімкнено функціональну можливість PodLevelResources, ви можете вказувати запити та обмеження ресурсів на рівні Podʼа. На рівні Podʼа Kubernetes 1.37 підтримує запити або обмеження ресурсів лише для певних типів ресурсів: cpu та/або memory, та/або hugepages. Завдяки цій функції Kubernetes дозволяє вам декларувати загальний бюджет ресурсів для Podʼа, що є особливо корисним під час роботи з великою кількістю контейнерів, де може бути важко точно визначити індивідуальні потреби у ресурсах. Крім того, це дає змогу контейнерам у межах Podʼа ділитися один з одним ресурсами, що простоюють, покращуючи використання ресурсів.

Для Podʼа ви можете вказати обмеження та запити ресурсів для CPU і памʼяті, включивши таке:

  • spec.resources.limits.cpu
  • spec.resources.limits.memory
  • spec.resources.limits.hugepages-<size>
  • spec.resources.requests.cpu
  • spec.resources.requests.memory
  • spec.resources.requests.hugepages-<size>

Одиниці виміру ресурсів в Kubernetes

Одиниці виміру ресурсів CPU

Обмеження та запити ресурсів CPU вимірюються в одиницях cpu. У Kubernetes 1 одиниця CPU еквівалентна 1 фізичному ядру CPU або 1 віртуальному ядру, залежно від того, чи є вузол фізичним хостом або віртуальною машиною, яка працює всередині фізичної машини.

Допускаються дробові запити. Коли ви визначаєте контейнер зі значенням 0.5 у spec.containers[].resources.requests.cpu, ви запитуєте вдвічі менше часу CPU, ніж якби запросили 1.0 CPU. Для одиниць ресурсів CPU вираз кількості 0.1 еквівалентний виразу 100m, що можна прочитати як "сто millicpu". Деякі люди кажуть "сто міліядер", і це розуміється так само.

Ресурс CPU завжди вказується як абсолютна кількість ресурсу, ніколи як відносна кількість. Наприклад, 500m CPU представляє приблизно ту саму обчислювальну потужність незалежно від того, чи працює цей контейнер на одноядерній, двоядерній або 48-ядерній машині.

Примітка:

Kubernetes не дозволяє вказувати ресурси CPU з точністю вище 1m або 0.001 CPU. Щоб уникнути випадкового використання неприпустимої кількості CPU, корисно вказувати одиниці CPU, використовуючи форму milliCPU замість десяткової форми, коли використовується менше ніж 1 одиниця CPU.

Наприклад, у вас є Pod, який використовує 5m або 0.005 CPU, і ви хочете зменшити його ресурси CPU. Використовуючи десяткову форму, важко помітити, що 0.0005 CPU є неприпустимим значенням, тоді як використовуючи форму milliCPU, легше помітити, що 0.5m є неприпустимим значенням.

Одиниці виміру ресурсів памʼяті

Обмеження та запити для memory вимірюються в байтах. Ви можете вказувати памʼять як звичайне ціле число або як число з фіксованою комою, використовуючи один із таких суфіксів кількості: E, P, T, G, M, k. Ви також можете використовувати еквіваленти степенів двійки: Ei, Pi, Ti, Gi, Mi, Ki. API Kubernetes також дозволяє використовувати суфікс m (для мілібайтів: 1/1000 байта), але вказувати його немає сенсу: завжди потрібно призначати цілі числа байтів або іноді більші блоки, такі як кратні 1 гібібайту.

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

128974848, 129e6, 129M,  128974848000m, 123Mi

Звертайте увагу на регістр суфіксів. "M" означає мегабайти, тоді як "m" означає мілібайти. Якщо ви запитуєте 400m памʼяті, це запит на 0,4 байта. Той, хто вводить це, ймовірно, мав на увазі запросити 400 мебібайтів (400Mi) або 400 мегабайтів (400M).

Приклад ресурсів контейнера

У цьому Podʼі є два контейнери. Обидва контейнери визначені із запитом на 0,25 CPU і 64MiB (226 байт) памʼяті. Кожен контейнер має обмеження в 0,5 CPU та 128MiB памʼяті. Можна сказати, що Pod має запит на 0,5 CPU та 128 MiB памʼяті й обмеження в 1 CPU та 256MiB памʼяті.

---
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

Приклад ресурсів Podʼа

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.34; (стандартно увімкнено)

Цю можливість можна увімкнути за допомогою функціональної можливості PodLevelResources. Цей Pod має явний запит на 1 CPU та 100 MiB памʼяті, а також явне обмеження в 1 CPU та 200 MiB памʼяті. Контейнер pod-resources-demo-ctr-1 має явні запити та обмеження. Однак контейнер pod-resources-demo-ctr-2 просто спільно використовуватиме ресурси, доступні в межах ресурсів Podʼа, оскільки він не має явних запитів і обмежень.

apiVersion: v1
kind: Pod
metadata:
  name: pod-resources-demo
  namespace: pod-resources-example
spec:
  resources:
    limits:
      cpu: "1"
      memory: "200Mi"
    requests:
      cpu: "1"
      memory: "100Mi"
  containers:
  - name: pod-resources-demo-ctr-1
    image: nginx
    resources:
      limits:
        cpu: "0.5"
        memory: "100Mi"
      requests:
        cpu: "0.5"
        memory: "50Mi"
  - name: pod-resources-demo-ctr-2
    image: fedora
    command:
    - sleep
    - inf 

Як плануються Podʼи із запитами ресурсів

Коли ви створюєте Pod, планувальник Kubernetes вибирає вузол, на якому Pod буде запущено. Кожен вузол має максимальну місткість для кожного з типів ресурсів: кількість CPU та обсяг памʼяті, які він може надати для Podʼів. Планувальник гарантує, що для кожного типу ресурсів сума запитів ресурсів запланованих контейнерів буде менше, ніж місткість вузла. Зверніть увагу, що навіть якщо фактичне використання ресурсів CPU або памʼяті на вузлі дуже низьке, планувальник все одно відмовляється розміщувати Pod на вузлі, якщо перевірка місткості не пройшла успішно. Це захищає від нестачі ресурсів на вузлі, коли пізніше збільшується використання ресурсів, наприклад, під час щоденного піку кількості запитів.

Як Kubernetes застосовує запити на ресурси та обмеження

Коли kubelet запускає контейнер як частину Podʼа, він передає запити та обмеження для памʼяті та CPU цього контейнера до середовища виконання контейнерів.

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

  • Обмеження CPU встановлює жорстку верхню межу часу CPU, який може використовувати контейнер. Протягом кожного інтервалу планування (часового відрізка) ядро Linux перевіряє, чи перевищується це обмеження; якщо так, ядро чекає, перш ніж дозволити цій cgroup продовжити виконання.
  • Запит CPU зазвичай визначає вагу. Якщо кілька різних контейнерів (cgroups) хочуть працювати на системі з конкуренцією за ресурси, робочі навантаження з більшими запитами CPU отримують більше часу CPU, ніж робочі навантаження з малими запитами.
  • Запит памʼяті в основному використовується під час планування Podʼа (Kubernetes). На вузлі, що використовує cgroups v2, середовище виконання контейнерів може використовувати запит памʼяті як підказку для встановлення memory.min та memory.low.
  • Обмеження памʼяті встановлює обмеження памʼяті для цієї контрольної групи. Якщо контейнер намагається виділити більше памʼяті, ніж це обмеження, підсистема обробки нестачі памʼяті (out-of-memory) ядра Linux активується і, зазвичай, втручається, зупиняючи один з процесів у контейнері, який намагався виділити памʼять. Якщо цей процес є PID 1 контейнера, і контейнер позначений як такий, що може перезапускатися, Kubernetes перезапускає контейнер.
  • Обмеження памʼяті для Podʼа або контейнера також може застосовуватися до сторінок у томах, що зберігаються в памʼяті, таких як emptyDir. Kubelet відстежує томи emptyDir типу tmpfs як використання памʼяті контейнера, а не як локальне тимчасове сховище. Під час використання emptyDir у памʼяті обовʼязково ознайомтеся з примітками нижче.

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

Контейнеру може бути дозволено або не дозволено перевищувати своє обмеження CPU протягом тривалого часу. Однак середовища виконання контейнерів не завершують Podʼи або контейнери через надмірне використання CPU.

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

Зміна розміру ресурсів контейнера

Після створення Podʼа може знадобитися налаштувати його ресурси CPU або памʼяті відповідно до фактичних моделей використання. Kubernetes пропонує два підходи до зміни розміру ресурсів Podʼа:

Зміна розміру на місці

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.35
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.35. It was first available in the v1.27 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate InPlacePodVerticalScaling, Kubernetes ignores it but does not report any error.

Ви можете змінити requests та limits CPU та памʼяті контейнерів у запущеному Podʼі без його повторного створення. Це називається вертикальним масштабуванням Podʼа на місці або зміною розміру Podʼа на місці. Щоб виконати зміну розміру на місці, оновіть специфікації ресурсів контейнера за допомогою субресурсу Podʼа /resize. Ви можете контролювати, чи потрібно перезапускати контейнер, встановивши поле resizePolicy у специфікації контейнера.

Примітка:

Зміна розміру на місці наразі застосовується до ресурсів на рівні контейнера. Щоб змінити розмір ресурсів на рівні Podʼа, див. Зміна розміру ресурсів CPU та памʼяті Podʼа.

Примітка:

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the InPlacePodVerticalScalingSchedulerPreemption feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли увімкнено функціональну можливість InPlacePodVerticalScalingSchedulerPreemption, відкладені запити на зміну розміру на місці можуть спонукати kube-scheduler витіснити Podʼи з нижчим пріоритетом на призначеному вузлі, щоб звільнити місце для зміни розміру. Для отримання додаткових відомостей див. Витіснення для зміни розміру Podʼа на місці.

Зміна розміру шляхом запуску Podʼів-замінників

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

Детальніше про зміну розміру Podʼів див. Зміна розміру Podʼа. Детальні інструкції щодо зміни розміру на місці див. у розділі Зміна розміру ресурсів CPU та памʼяті, призначених контейнерам. Ви також можете використовувати Vertical Pod Autoscaler для автоматичного управління рекомендаціями щодо ресурсів Podʼа.

Моніторинг використання обчислювальних ресурсів та ресурсів памʼяті

Kubelet повідомляє про використання ресурсів Podʼа як частину status Podʼа.

Якщо у вашому кластері доступні інструменти для моніторингу, то використання ресурсів Podʼа можна отримати або безпосередньо з Metrics API, або з ваших інструментів моніторингу.

Міркування щодо томів emptyDir, що зберігаються в памʼяті

Примітка:

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the InPlacePodVerticalScalingMemoryBackedVolumes feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли увімкнено функціональну можливість InPlacePodVerticalScalingMemoryBackedVolumes, ви можете динамічно змінювати sizeLimit тому emptyDir, що зберігається в памʼяті (medium: Memory), на запущеному Podʼі без повторного створення Podʼа або перезапуску контейнерів. Покрокові інструкції див. у розділі Зміна розміру ресурсів CPU та памʼяті, призначених контейнерам.

Увага:

Якщо ви не вказуєте sizeLimit для тому emptyDir, цей том може використовувати памʼять до обмеження памʼяті цього Podʼа (Pod.spec.containers[].resources.limits.memory). Якщо ви не встановлюєте обмеження памʼяті, Pod не має верхньої межі на споживання памʼяті і може використовувати всю доступну памʼять на вузлі. Kubernetes планує Podʼи на основі запитів на ресурси (Pod.spec.containers[].resources.requests) і не враховує використання памʼяті понад запит під час вирішення, чи може інший Pod розміститися на даному вузлі. Це може призвести до відмови в обслуговуванні та змусити ОС виконати обробку нестачі памʼяті (OOM). Можна створити будь-яку кількість emptyDir, які потенційно можуть використовувати всю доступну памʼять на вузлі, що робить OOM більш ймовірним.

З точки зору управління памʼяттю, є деякі подібності між використанням памʼяті як робочої області процесу і використанням памʼяті для emptyDir, що зберігається в памʼяті. Але під час використання памʼяті як тому, як у випадку з emptyDir, що зберігається в памʼяті, є додаткові моменти, на які слід звернути увагу:

  • Файли, збережені в томі, що зберігається в памʼяті, майже повністю управляються застосунком користувача. На відміну від використання памʼяті як робочої області процесу, ви не можете покладатися на такі речі, як збір сміття на рівні мови програмування.
  • Мета запису файлів в том полягає в збереженні даних або їх передачі між застосунками. Ні Kubernetes, ні ОС не можуть автоматично видаляти файли з тому, тому памʼять, яку займають ці файли, не може бути звільнена, коли система або Pod відчувають тиск на памʼять.
  • Томи emptyDir, що зберігаються в памʼяті, корисні через свою продуктивність, але памʼять зазвичай набагато менша за розміром і набагато дорожча за інші носії, такі як диски або SSD. Використання великої кількості памʼяті для томів emptyDir може вплинути на нормальну роботу вашого Podʼа або всього вузла, тому їх слід використовувати обережно.

Якщо ви адмініструєте кластер або простір імен, ви також можете встановити ResourceQuota, що обмежує використання памʼяті; також може бути корисно визначити LimitRange для додаткового забезпечення обмежень. Якщо ви вказуєте spec.containers[].resources.limits.memory для кожного Podʼа, то максимальний розмір тому emptyDir буде дорівнювати обмеженню памʼяті Podʼа.

Як альтернатива, адміністратор кластера може забезпечити дотримання обмежень на розмір томів emptyDir в нових Podʼах, використовуючи механізм політики, такий як ValidatingAdmissionPolicy.

Локальне тимчасове сховище

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

Моніторинг ресурсів для локального тимчасового сховища

Kubelet може вимірювати, скільки локального тимчасового сховища використовується. Він робить це, якщо ви ввімкнули ізоляцію ємності локального тимчасового сховища.

Kubernetes відстежує обсяг тимчасового сховища, яке використовує Pod, із таких джерел:

  • Запис у шар контейнера, доступний для запису (rootfs), в образи контейнерів або і туди, й туди.
  • Запис у локальні томи emptyDir.
  • Власні логи Podʼа (зазвичай зберігаються в /var/log/pods).
  • Системні файли, керовані Kubernetes, які відображаються в Pod, наприклад /etc/hosts.

Розширені ресурси

Розширені ресурси — це повністю кваліфіковані назви ресурсів поза доменом kubernetes.io. Вони дозволяють операторам кластера оголошувати, а користувачам використовувати ресурси, які не вбудовані в Kubernetes.

Щоб скористатися розширеними ресурсами, потрібно виконати два кроки. По-перше, оператор кластера повинен оголосити розширений ресурс. По-друге, користувачі повинні запитати розширений ресурс у Podʼах.

Керування розширеними ресурсами

Розширені ресурси на рівні вузла

Розширені ресурси на рівні вузла повʼязані з вузлами.

Ресурси, керовані втулками пристроїв

Дивіться Втулок пристроїв щодо того, як оголошувати ресурси, що керуються втулком пристроїв на кожному вузлі.

Інші ресурси

Щоб оголосити новий розширений ресурс на рівні вузла, оператор кластера може надіслати HTTP-запит типу PATCH до API-сервера, щоб вказати доступну кількість в полі status.capacity для вузла у кластері. Після цієї операції поле status.capacity вузла буде містити новий ресурс. Kubelet автоматично й асинхронно оновлює поле status.allocatable, додаючи новий ресурс.

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

Приклад:

Нижче наведено приклад використання curl для формування HTTP-запиту, який оголошує пʼять ресурсів "example.com/foo" на вузлі k8s-node-1, master якого — k8s-master.

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status

Примітка:

У наведеному вище запиті ~1 — це кодування символу / у шляху патча. Значення шляху операції у JSON-Patch інтерпретується як JSON-Pointer. Докладніше дивіться IETF RFC 6901, розділ 3.

Розширені ресурси на рівні кластера

Розширені ресурси на рівні кластера не повʼязані з вузлами. Зазвичай ними керують розширювачі планувальника, які відповідають за споживання ресурсів і квоту ресурсів.

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

Приклад:

Ця конфігурація для політики планувальника вказує на те, що розширений ресурс на рівні кластера "example.com/foo" обробляється розширювачем планувальника.

  • Планувальник відправляє Pod до розширювача планувальника тільки у випадку, якщо Pod запитує "example.com/foo".
  • Поле ignoredByScheduler вказує, що планувальник не перевіряє ресурс "example.com/foo" в своєму предикаті PodFitsResources.
{
  "kind": "Policy",
  "apiVersion": "v1",
  "extenders": [
    {
      "urlPrefix":"<extender-endpoint>",
      "bindVerb": "bind",
      "managedResources": [
        {
          "name": "example.com/foo",
          "ignoredByScheduler": true
        }
      ]
    }
  ]
}

Розподілення розширених ресурсів за допомогою DRA

Розподілення розширених ресурсів за допомогою DRA дозволяє адміністраторам кластерів вказувати extendedResourceName у DeviceClass, після чого пристрої, що відповідають DeviceClass, можуть бути запитані з розширених запитів ресурсів Podʼа. Читайте більше про Розподілення розширених ресурсів за допомогою DRA.

Використання розширених ресурсів

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

API-сервер обмежує кількість розширених ресурсів цілими числами. Приклади дійсних значень: 3, 3000m та 3Ki. Приклади недійсних значень: 0.5 та 1500m (тому що 1500m буде давати в результаті 1.5).

Примітка:

Розширені ресурси замінюють Opaque Integer Resources. Користувачі можуть використовувати будь-який префікс доменного імені, крім kubernetes.io, який зарезервований.

Щоб використовувати розширений ресурс у Podʼі, включіть назву ресурсу як ключ у мапу spec.containers[].resources.limits у специфікації контейнера.

Примітка:

Розширені ресурси не можна виділяти понад наявні (overcommit), тому запит і обмеження мають бути рівними, якщо обидва присутні у специфікації контейнера.

Pod планується лише тоді, коли задоволено всі запити ресурсів, включаючи CPU, памʼять та будь-які розширені ресурси. Pod залишається у стані PENDING, доки запит ресурсу неможливо задовольнити.

Приклад:

Нижче наведено Pod, який запитує 2 CPU та 1 "example.com/foo" (розширений ресурс).

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: myimage
    resources:
      requests:
        cpu: 2
        example.com/foo: 1
      limits:
        example.com/foo: 1

Обмеження PID

Обмеження ідентифікаторів процесів (PID) дозволяють налаштувати kubelet для обмеження кількості PID, яку може використовувати певний Pod. Див. Обмеження PID для отримання інформації.

Усунення несправностей

Мої Podʼи знаходяться в стані очікування з повідомленням події FailedScheduling

Якщо планувальник не може знайти жодного вузла, де може розміститися Pod, Pod залишається незапланованим, поки не буде знайдено місце. Кожного разу, коли планувальник не може знайти місце для Podʼа, створюється подія. Ви можете використовувати kubectl для перегляду подій Podʼа; наприклад:

kubectl describe pod frontend | grep -A 9999999999 Events
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  23s   default-scheduler  0/42 nodes available: insufficient cpu

У наведеному вище прикладі Pod із назвою "frontend" не вдалося запланувати через нестачу ресурсу CPU на будь-якому вузлі. Схожі повідомлення про помилку також можуть вказувати на невдачу через недостатню памʼять (PodExceedsFreeMemory). Загалом, якщо Pod знаходиться в стані очікування з таким типом повідомлення, є кілька речей, які варто спробувати:

  • Додайте більше вузлів у кластер.
  • Завершіть непотрібні Podʼи, щоб звільнити місце для очікуваних Podʼів.
  • Перевірте, що Pod не є більшим, ніж усі вузли. Наприклад, якщо всі вузли мають місткість cpu: 1, то Pod з запитом cpu: 1.1 ніколи не буде запланованим.
  • Перевірте наявність taints на вузлах. Якщо більшість ваших вузлів мають taint, і новий Pod не толерує цей taint, планувальник розглядає розміщення лише на решті вузлів, які не мають цього taint.

Ви можете перевірити місткості вузлів та виділені обсяги ресурсів за допомогою команди kubectl describe nodes. Наприклад:

kubectl describe nodes e2e-test-node-pool-4lw4
Name:            e2e-test-node-pool-4lw4
[ ... lines removed for clarity ...]
Capacity:
 cpu:                               2
 memory:                            7679792Ki
 pods:                              110
Allocatable:
 cpu:                               1800m
 memory:                            7474992Ki
 pods:                              110
[ ... lines removed for clarity ...]
Non-terminated Pods:        (5 in total)
  Namespace    Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------    ----                                  ------------  ----------  ---------------  -------------
  kube-system  fluentd-gcp-v1.38-28bv1               100m (5%)     0 (0%)      200Mi (2%)       200Mi (2%)
  kube-system  coredns-3297075139-61lj3              260m (13%)    0 (0%)      100Mi (1%)       170Mi (2%)
  kube-system  kube-proxy-e2e-test-...               100m (5%)     0 (0%)      0 (0%)           0 (0%)
  kube-system  monitoring-influxdb-grafana-v4-z1m12  200m (10%)    200m (10%)  600Mi (8%)       600Mi (8%)
  kube-system  node-problem-detector-v0.1-fj7m3      20m (1%)      200m (10%)  20Mi (0%)        100Mi (1%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ------------    ----------    ---------------    -------------
  680m (34%)      400m (20%)    920Mi (11%)        1070Mi (13%)

У наведеному вище виводі ви можете побачити, що якщо Pod запитує більше 1,120 CPU або більше 6,23Gi памʼяті, то цей Pod не поміститься на вузлі.

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

Обсяг ресурсів, доступних для Podʼів, менший за місткість вузла, оскільки системні демони використовують частину доступних ресурсів. В API Kubernetes кожен вузол має поле .status.allocatable (див. NodeStatus для деталей).

Поле .status.allocatable описує обсяг ресурсів, доступних для Podʼів на цьому вузлі (наприклад: 15 віртуальних CPU та 7538 MiB памʼяті). Для отримання додаткової інформації про доступні для розподілу (allocatable) ресурси вузла в Kubernetes дивіться Резервування обчислювальних ресурсів для системних демонів.

Ви можете налаштувати квоти ресурсів для обмеження загального обсягу ресурсів, який може споживати простір імен. Kubernetes забезпечує дотримання квот для обʼєктів в конкретному просторі імен, коли існує ResourceQuota в цьому просторі імен. Наприклад, якщо ви призначаєте конкретні простори імен різним командам, ви можете додавати ResourceQuotas в ці простори імен. Встановлення квот ресурсів допомагає запобігти використанню однією командою так багато будь-якого ресурсу, що це впливає на інші команди.

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

Робота мого контейнера завершується примусово

Робота вашого контейнера може бути завершена через нестачу ресурсів. Щоб перевірити, чи контейнер був завершений через досягнення обмеження ресурсів, викличте kubectl describe pod для потрібного Podʼа:

kubectl describe pod simmemleak-hra99

Вивід буде схожий на:

Name:                           simmemleak-hra99
Namespace:                      default
Image(s):                       saadali/simmemleak
Node:                           kubernetes-node-tf0f/10.240.216.66
Labels:                         name=simmemleak
Status:                         Running
Reason:
Message:
IP:                             10.244.2.75
Containers:
  simmemleak:
    Image:  saadali/simmemleak:latest
    Limits:
      cpu:          100m
      memory:       50Mi
    State:          Running
      Started:      Tue, 07 Jul 2019 12:54:41 -0700
    Last State:     Terminated
      Reason:       OOMKilled
      Exit Code:    137
      Started:      Fri, 07 Jul 2019 12:54:30 -0700
      Finished:     Fri, 07 Jul 2019 12:54:33 -0700
    Ready:          False
    Restart Count:  5
Conditions:
  Type      Status
  Ready     False
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  42s   default-scheduler  Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
  Normal  Pulled     41s   kubelet            Container image "saadali/simmemleak:latest" already present on machine
  Normal  Created    41s   kubelet            Created container simmemleak
  Normal  Started    40s   kubelet            Started container simmemleak
  Normal  Killing    32s   kubelet            Killing container with id ead3fb35-5cf5-44ed-9ae1-488115be66c6: Need to kill Pod

У наведеному вище прикладі Restart Count: 5 вказує на те, що контейнер simmemleak у Podʼі був завершений та перезапущений пʼять разів (до цього моменту). Причина OOMKilled показує, що контейнер намагався використовувати більше памʼяті, ніж встановлене йому обмеження.

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

Що далі

7.4 - Організація доступу до кластера за допомогою файлів kubeconfig

Використовуйте файли kubeconfig для організації інформації про кластери, користувачів, простори імен та механізми автентифікації. Інструмент командного рядка kubectl використовує файли kubeconfig, щоб знайти інформацію, необхідну для вибору кластера та звʼязку з API-сервером кластера.

Примітка:

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

Попередження:

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

Типово kubectl шукає файл із назвою config у теці $HOME/.kube. Ви можете вказати інші файли kubeconfig, встановивши змінну середовища KUBECONFIG або встановивши прапорець --kubeconfig.

Для покрокових інструкцій щодо створення та вказування файлів kubeconfig див. Налаштування доступу до кількох кластерів.

Підтримка кількох кластерів, користувачів та механізмів автентифікації

Припустимо, у вас є декілька кластерів, і ваші користувачі та компоненти автентифікуються різними способами. Наприклад:

  • Запущений kubelet може автентифікуватися за допомогою сертифікатів.
  • Користувач може автентифікуватися за допомогою токенів.
  • Адміністратори можуть мати набори сертифікатів, які вони надають окремим користувачам.

За допомогою файлів kubeconfig ви можете організувати ваші кластери, користувачів та простори імен. Ви також можете визначити контексти, щоб швидко та легко перемикатися між кластерами та просторами імен.

Контекст

Елемент context у файлі kubeconfig використовується для групування параметрів доступу під зручною назвою. Кожен контекст має три параметри: кластер, простір імен та користувач. Типово інструмент командного рядка kubectl використовує параметри з поточного контексту для звʼязку з кластером.

Щоб обрати поточний контекст:

kubectl config use-context

Змінна середовища KUBECONFIG

Змінна середовища KUBECONFIG містить список файлів kubeconfig. Для Linux та Mac список розділяється двокрапкою. Для Windows список розділяється крапкою з комою. Змінна середовища KUBECONFIG не є обовʼязковою. Якщо змінна середовища KUBECONFIG не існує, kubectl використовує стандартний файл kubeconfig, $HOME/.kube/config.

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

Обʼєднання файлів kubeconfig

Щоб переглянути вашу конфігурацію, введіть цю команду:

kubectl config view

Як описано раніше, вивід може бути з одного файлу kubeconfig або бути результатом обʼєднання кількох файлів kubeconfig.

Ось правила, які використовує kubectl під час обʼєднання файлів kubeconfig:

  1. Якщо встановлено прапорець --kubeconfig, використовуйте лише вказаний файл. Обʼєднання не здійснюється. Дозволяється лише один екземпляр цього прапорця.

    В іншому випадку, якщо встановлена змінна середовища KUBECONFIG, використовуйте її як список файлів, які потрібно обʼєднати. Обʼєднайте файли, перелічені у змінній середовища KUBECONFIG, за такими правилами:

    • Ігноруйте порожні імена файлів.
    • Виводьте помилки для файлів з вмістом, який не може бути десеріалізований.
    • Перший файл, що встановлює певне значення або ключ мапи, має перевагу.
    • Ніколи не змінюйте значення або ключ мапи. Приклад: зберігайте контекст першого файлу, який встановлює current-context. Приклад: якщо два файли вказують red-user, використовуйте лише значення з red-user першого файлу. Навіть якщо другий файл має неконфліктні записи в red-user, відкиньте їх.

    Для прикладу встановлення змінної середовища KUBECONFIG дивіться Встановлення змінної середовища KUBECONFIG.

    В іншому випадку використовуйте стандартний файл kubeconfig, $HOME/.kube/config, без обʼєднання.

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

    1. Використовуйте прапорець командного рядка --context, якщо він існує.
    2. Використовуйте current-context з обʼєднаних файлів kubeconfig.

    Порожній контекст допускається на цьому етапі.

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

    1. Використовуйте прапорці командного рядка, якщо вони існують: --user або --cluster.
    2. Якщо контекст не порожній, беріть користувача або кластер з контексту.

    Користувач та кластер можуть бути порожніми на цьому етапі.

  4. Визначте фактичну інформацію про кластер, яку слід використовувати. На цьому етапі може бути або не бути інформації про кластер. Побудуйте кожен елемент інформації про кластер на основі цього ланцюжка; перший збіг перемагає:

    1. Використовуйте прапорці командного рядка, якщо вони існують: --server, --certificate-authority, --insecure-skip-tls-verify.
    2. Якщо існують будь-які атрибути інформації про кластер з обʼєднаних файлів kubeconfig, використовуйте їх.
    3. Якщо немає розташування сервера, завершіть роботу з помилкою.
  5. Визначте фактичну інформацію про користувача, яку слід використовувати. Побудуйте інформацію про користувача, використовуючи ті самі правила, що і для інформації про кластер, за винятком того, що дозволяється лише одна техніка автентифікації для кожного користувача:

    1. Використовуйте прапорці командного рядка, якщо вони існують: --client-certificate, --client-key, --username, --password, --token.
    2. Використовуйте поля user з обʼєднаних файлів kubeconfig.
    3. Якщо є дві суперечливі техніки, завершіть роботу з помилкою.
  6. Для будь-якої інформації, що все ще відсутня, використовуйте стандартні значення і, можливо, запитайте інформацію для автентифікації.

Посилання на файли

Посилання на файли та шляхи в файлі kubeconfig є відносними до місця розташування файлу kubeconfig. Посилання на файли в командному рядку є відносними до поточної робочої теки. У файлі $HOME/.kube/config відносні шляхи зберігаються як відносні, а абсолютні — як абсолютні.

Проксі

Ви можете налаштувати kubectl на використання проксі для кожного кластера за допомогою proxy-url у вашому файлі kubeconfig, на зразок такого:

apiVersion: v1
kind: Config

clusters:
- cluster:
    proxy-url: http://proxy.example.org:3128
    server: https://k8s.example.org/k8s/clusters/c-xxyyzz
  name: development

users:
- name: developer

contexts:
- context:
  name: development

Що далі

7.5 - Управління ресурсами для вузлів Windows

Ця сторінка описує відмінності у способах управління ресурсами між Linux та Windows.

На Linux-вузлах використовуються cgroups як межа Podʼа для керування ресурсами. Контейнери створюються всередині цієї межі для ізоляції мережі, процесів та файлової системи. API cgroup для Linux може використовуватися для збору статистики використання CPU, введення/виведення та памʼяті.

Натомість у Windows використовується job object на кожен контейнер з фільтром системного простору імен, щоб обмежити всі процеси в контейнері та забезпечити логічну ізоляцію від хоста. (Обʼєкти завдань є механізмом ізоляції процесів Windows і відрізняються від того, що Kubernetes називає Job).

Не існує можливості запуску контейнера Windows без фільтрації простору імен. Це означає, що системні привілеї не можна отримати в контексті хоста, і, отже, привілейовані контейнери недоступні в Windows. Контейнери не можуть набувати ідентичності хоста, оскільки менеджер облікових записів безпеки (Security Account Manager, SAM) є окремим.

Управління памʼяттю

Windows не має OOM-завершувача процесів (out-of-memory process killer), як у Linux. Windows завжди розглядає всі виділення памʼяті в режимі користувача як віртуальні, і файли підкачки є обовʼязковими.

Вузли Windows не виділяють процесам памʼять понад наявну (overcommit). У підсумку Windows не доходить до стану нестачі памʼяті так, як Linux, і процеси вивантажуються на диск замість завершення через нестачу памʼяті (OOM). Якщо памʼять надмірно виділена і всю фізичну памʼять вичерпано, підкачка може знизити продуктивність.

Управління CPU

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

У Windows kubelet підтримує прапорець командного рядка для встановлення пріоритету планування процесу kubelet: --windows-priorityclass. Цей прапорець дозволяє процесу kubelet отримувати більше часових квантів CPU порівняно з іншими процесами, які виконуються на хості Windows. Додаткову інформацію про допустимі значення та їхнє тлумачення можна знайти за посиланням Класи пріоритетів Windows. Щоб гарантувати, що запущені Podʼи не позбавлять kubelet циклів CPU, встановіть цей прапорець у значення ABOVE_NORMAL_PRIORITY_CLASS або вище.

Резервування ресурсів

Щоб врахувати памʼять і CPU, які використовуються операційною системою, середовищем виконання контейнерів і процесами хоста Kubernetes, такими як kubelet, ви можете (і повинні) зарезервувати ресурси памʼяті та CPU за допомогою прапорців kubelet --kube-reserved та/або --system-reserved. У Windows ці значення використовуються лише для розрахунку доступних для розподілу (allocatable) ресурсів вузла.

Увага:

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

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

У Windows доброю практикою є резервування принаймні 2GiB памʼяті.

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

8 - Політики

Керування безпекою та найкращими практиками за допомогою політик.

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

Застосування політик за допомогою обʼєктів API

Деякі обʼєкти API діють як політики. Ось кілька прикладів:

  • NetworkPolicies можна використовувати для обмеження вхідного та вихідного трафіку робочого навантаження.
  • LimitRanges керують обмеженнями розподілу ресурсів для різних типів обʼєктів.
  • ResourceQuotas обмежують споживання ресурсів для простору імен.

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

Контролер допуску працює в API-сервері та може перевіряти або змінювати запити до API. Деякі контролери допуску застосовують політики. Наприклад, контролер допуску AlwaysPullImages змінює новий Pod, встановлюючи для нього політику завантаження образу Always.

У Kubernetes є кілька вбудованих контролерів допуску, які можна налаштувати за допомогою прапорця --enable-admission-plugins API-сервера.

Детальні відомості про контролери допуску, з повним списком доступних контролерів допуску, ви знайдете в окремому розділі:

Застосування політик за допомогою ValidatingAdmissionPolicy

Політики перевірки допуску дають змогу виконувати налаштовувані перевірки в API-сервері за допомогою мови виразів Common Expression Language (CEL). Наприклад, ValidatingAdmissionPolicy можна використати, щоб заборонити використання теґу образу latest.

ValidatingAdmissionPolicy працює із запитом до API і може використовуватися для блокування, аудиту та попередження користувачів про невідповідні конфігурації.

Детальні відомості про API ValidatingAdmissionPolicy з прикладами ви знайдете в окремому розділі:

Застосування політик за допомогою динамічного контролю допуску

Динамічні контролери допуску (або вебхуки допуску) працюють поза API-сервером як окремі застосунки, які реєструються для отримання запитів вебхуків, щоб виконувати перевірку або зміну запитів до API.

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

Детальні відомості про динамічний контроль допуску ви знайдете в окремому розділі:

Реалізації

Примітка: Цей розділ містить посилання на проєкти сторонніх розробників, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Проєкти вказано в алфавітному порядку. Щоб додати проєкт до цього списку, ознайомтеся з посібником з контенту перед надсиланням змін. Докладніше.

Динамічні контролери допуску, які діють як гнучкі рушії політик, розробляються в екосистемі Kubernetes, серед них:

Застосування політик за допомогою конфігурацій Kubelet

Kubernetes дозволяє налаштовувати Kubelet на кожному робочому вузлі. Деякі конфігурації Kubelet діють як політики:

  • Обмеження та резервування ID процесів використовуються для обмеження та резервування доступних для розподілу PID.
  • Менеджери ресурсів вузлів можуть керувати обчислювальними ресурсами, памʼяттю та ресурсами пристроїв для робочих навантажень, критичних до затримок, і робочих навантажень із високою пропускною здатністю.

8.1 - Діапазони обмежень

Стандартно контейнери запускаються з необмеженими обчислювальними ресурсами у кластері Kubernetes. Використовуючи квоти ресурсів Kubernetes, адміністратори (також відомі як оператори кластера) можуть обмежувати споживання та створення ресурсів кластера (таких як час CPU, памʼять та постійне сховище) у визначеному просторі імен. У межах простору імен Pod може використовувати стільки CPU та памʼяті, скільки дозволяють ResourceQuotas, що застосовуються до цього простору імен. Як оператору кластера або адміністратору на рівні простору імен вам також може бути важливо переконатися, що один обʼєкт не може монополізувати всі доступні ресурси у просторі імен.

LimitRange — це політика для обмеження виділення ресурсів (обмежень і запитів), які можна вказати для кожного відповідного типу обʼєктів (такого як Pod або PersistentVolumeClaim) у просторі імен.

LimitRange надає обмеження, які можуть:

  • Застосовувати мінімальне та максимальне використання обчислювальних ресурсів на Pod або контейнер у просторі імен.
  • Застосовувати мінімальний та максимальний запит на сховище для PersistentVolumeClaim у просторі імен.
  • Застосовувати співвідношення між запитом та обмеженням для ресурсу у просторі імен.
  • Встановлювати стандартний запит/обмеження для обчислювальних ресурсів у просторі імен та автоматично додавати їх до контейнерів під час виконання.

Kubernetes обмежує виділення ресурсів для Podʼів у певному просторі імен, якщо у цьому просторі імен є хоча б один обʼєкт LimitRange.

Назва обʼєкта LimitRange має бути дійсною назвою DNS-піддомену.

Вимоги до обмежень і запитів ресурсів

  • Адміністратор створює LimitRange у просторі імен.
  • Користувачі створюють (або намагаються створити) обʼєкти у цьому просторі імен, такі як Podʼи або PersistentVolumeClaims.
  • По-перше, контролер допуску LimitRange застосовує стандартні значення запиту та обмеження для всіх Podʼів (та їхніх контейнерів), які не задають вимог до обчислювальних ресурсів.
  • По-друге, LimitRange відстежує використання, щоб воно не перевищувало мінімум, максимум і співвідношення ресурсів, визначені в будь-якому LimitRange у цьому просторі імен.
  • Якщо ви намагаєтеся створити або оновити обʼєкт (Pod або PersistentVolumeClaim), який порушує обмеження LimitRange, ваш запит до API-сервера буде відхилено з HTTP-кодом стану 403 Forbidden та повідомленням, що пояснює порушене обмеження.
  • Якщо ви додаєте в простір імен LimitRange, який застосовується до обчислювальних ресурсів, таких як cpu та memory, ви повинні вказати запити або обмеження для цих значень. Інакше система може відхилити створення Podʼа.
  • Перевірка LimitRange відбувається лише на етапі допуску Podʼа, а не для запущених Podʼів. Якщо ви додаєте або змінюєте LimitRange, Podʼи, які вже існують у цьому просторі імен, залишаються без змін.
  • Якщо у просторі імен існує два або більше обʼєктів LimitRange, то не визначено, яке стандартне значення буде застосовано.

LimitRange та перевірки допуску для Podʼів

LimitRange не перевіряє узгодженість стандартних значень, які він застосовує. Це означає, що стандартне значення для limit, встановлене LimitRange, може бути меншим за значення request, вказане для контейнера в специфікації, яку клієнт надсилає до API-сервера. Якщо це станеться, фінальний Pod не буде придатним для планування.

Наприклад, ви визначаєте LimitRange таким маніфестом:

Примітка:

Наведені далі приклади працюють у просторі імен default вашого кластера, оскільки параметр namespace не визначено, а область дії LimitRange обмежена рівнем простору імен. Це означає, що будь-які посилання або операції в цих прикладах взаємодіятимуть з елементами в просторі імен default вашого кластера. Ви можете перевизначити робочий простір імен, налаштувавши namespace у полі metadata.namespace.
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-resource-constraint
spec:
  limits:
  - default: # this section defines default limits
      cpu: 500m
    defaultRequest: # this section defines default requests
      cpu: 500m
    max: # max and min define the limit range
      cpu: "1"
    min:
      cpu: 100m
    type: Container

разом із Podʼом, який оголошує запит ресурсу CPU 700m, але не обмеження:

apiVersion: v1
kind: Pod
metadata:
  name: example-conflict-with-limitrange-cpu
spec:
  containers:
  - name: demo
    image: registry.k8s.io/pause:3.8
    resources:
      requests:
        cpu: 700m

тоді цей Pod не буде заплановано, і він завершиться помилкою, схожою на таку:

Pod "example-conflict-with-limitrange-cpu" is invalid: spec.containers[0].resources.requests: Invalid value: "700m": must be less than or equal to cpu limit

Якщо ви встановите і request, і limit, то цей новий Pod буде успішно заплановано, навіть за наявності того самого LimitRange:

apiVersion: v1
kind: Pod
metadata:
  name: example-no-conflict-with-limitrange-cpu
spec:
  containers:
  - name: demo
    image: registry.k8s.io/pause:3.8
    resources:
      requests:
        cpu: 700m
      limits:
        cpu: 700m

Приклади обмежень ресурсів

Приклади політик, які можна створити за допомогою LimitRange:

  • У кластері з 2 вузлів місткістю 8 GiB RAM та 16 ядер обмежте Podʼи в просторі імен запитом 100m CPU з максимальним обмеженням 500m для CPU та запитом 200Mi для памʼяті з максимальним обмеженням 600Mi для памʼяті.
  • Визначте стандартне обмеження та запит CPU на рівні 150m та стандартний запит памʼяті на рівні 300Mi для контейнерів, що запускаються без запитів CPU та памʼяті у своїх специфікаціях.

У випадку, коли загальні обмеження простору імен менші за суму обмежень Podʼів/контейнерів, може виникнути конкуренція за ресурси. У цьому випадку контейнери або Podʼи не будуть створені.

Ні конкуренція, ні зміни LimitRange не впливають на вже створені ресурси.

Що далі

Приклади використання обмежень дивіться тут:

Контекст та історичну інформацію дивіться в проєктному документі LimitRanger.

8.2 - Квоти ресурсів

Коли кілька користувачів або команд спільно використовують кластер із фіксованою кількістю вузлів, існує ризик, що одна команда використає більше за свою справедливу частку ресурсів.

Квоти ресурсів — це інструмент, за допомогою якого адміністратори розвʼязують цю проблему.

Квота ресурсів, визначена обʼєктом ResourceQuota, задає обмеження загального споживання ресурсів у кожному просторі імен. ResourceQuota також може обмежувати кількість обʼєктів, які можна створити в просторі імен, за типом API, а також загальний обсяг інфраструктурних ресурсів, які можуть споживати обʼєкти API в цьому просторі імен.

Увага:

Ані конкуренція, ані зміни до квоти не вплинуть на вже створені ресурси.

Як працюють ResourceQuotas у Kubernetes

ResourceQuotas працюють так:

  • Різні команди працюють у різних просторах імен. Цей поділ можна забезпечити за допомогою RBAC або будь-якого іншого механізму авторизації.

  • Адміністратор кластера створює принаймні одну ResourceQuota для кожного простору імен.

    • Щоб ці вимоги залишалися чинними, адміністратор кластера також повинен обмежити доступ до видалення або оновлення цієї ResourceQuota, наприклад, визначивши ValidatingAdmissionPolicy.
  • Користувачі створюють ресурси (Podʼи, Services тощо) у просторі імен, а система квот відстежує використання, щоб воно не перевищувало жорстких обмежень ресурсів, визначених у ResourceQuota.

    Ви можете застосувати до ResourceQuota область дії, щоб обмежити, де вона застосовується.

  • Якщо створення або оновлення ресурсу порушує обмеження квоти, панель управління відхиляє цей запит із кодом стану HTTP 403 Forbidden. Помилка містить повідомлення з поясненням обмеження, яке було б порушено.

  • Якщо квоти увімкнено в просторі імен для таких ресурсів, як cpu та memory, користувачі повинні вказувати запити або обмеження для цих значень під час визначення Podʼа; інакше система квот може відхилити створення Podʼа.

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

Примітка:

  • Ви можете визначити LimitRange, щоб примусово встановлювати стандартні значення для Podʼів, які не задають вимог до обчислювальних ресурсів (щоб користувачам не доводилося памʼятати про це).

Часто ви не створюєте Podʼи безпосередньо; наприклад, ви зазвичай створюєте обʼєкт керування робочим навантаженням, такий як Deployment. Якщо ви створюєте Deployment, який намагається використати більше ресурсів, ніж доступно, створення Deployment (або іншого обʼєкта керування робочим навантаженням) буде успішним, але Deployment може бути не в змозі забезпечити існування всіх керованих ним Podʼів. У цьому випадку ви можете перевірити стан Deployment, наприклад, за допомогою kubectl describe, щоб дізнатися, що сталося.

  • Для ресурсів cpu та memory ResourceQuotas вимагають, щоб кожен (новий) Pod у цьому просторі імен встановлював обмеження для цього ресурсу. Якщо ви застосовуєте квоту ресурсів у просторі імен для cpu або memory, ви та інші клієнти повинні вказувати requests або limits для цього ресурсу для кожного нового Podʼа, який ви надсилаєте. Якщо ви цього не зробите, панель управління може відхилити допуск цього Podʼа.
  • Для інших ресурсів: ResourceQuota працює та ігнорує Podʼи в просторі імен, які не встановлюють обмеження або запит для цього ресурсу. Це означає, що ви можете створити новий Pod без обмеження/запиту тимчасового сховища, якщо квота ресурсів обмежує тимчасове сховище цього простору імен.

Ви можете використовувати LimitRange для автоматичного встановлення стандартного запиту для цих ресурсів.

Назва обʼєкта ResourceQuota має бути дійсною назвою DNS-піддомену.

Приклади політик, які можна створити за допомогою просторів імен та квот:

  • У кластері місткістю 32 GiB RAM і 16 ядер дозвольте команді A використовувати 20 GiB і 10 ядер, дозвольте команді B використовувати 10 GiB і 4 ядра, а 2 GiB і 2 ядра залиште в резерві для майбутнього розподілу.
  • Обмежте простір імен "testing" використанням 1 ядра та 1 GiB RAM. Дозвольте простору імен "production" використовувати будь-який обсяг.

У випадку, коли загальна місткість кластера менша за суму квот просторів імен, може виникнути конкуренція за ресурси. Це обробляється за принципом «перший прийшов — перший обслужений».

Увімкнення квоти ресурсів

Підтримка ResourceQuota стандартно увімкнена в багатьох дистрибутивах Kubernetes. Вона увімкнена, коли прапорець --enable-admission-plugins= API-сервера має ResourceQuota серед своїх аргументів.

Квота ресурсів застосовується в певному просторі імен, коли у цьому просторі імен є ResourceQuota.

Типи квот ресурсів

Механізм ResourceQuota дозволяє застосовувати різні види обмежень. У цьому розділі описано типи обмежень, які можна застосовувати.

Квота на інфраструктурні ресурси

Ви можете обмежити загальну суму обчислювальних ресурсів, які можна запитати в певному просторі імен.

Підтримуються такі типи ресурсів:

Назва ресурсуОпис
limits.cpuДля всіх Podʼів у нетермінальному стані сума обмежень CPU не може перевищувати це значення.
limits.memoryДля всіх Podʼів у нетермінальному стані сума обмежень памʼяті не може перевищувати це значення.
requests.cpuДля всіх Podʼів у нетермінальному стані сума запитів CPU не може перевищувати це значення.
requests.memoryДля всіх Podʼів у нетермінальному стані сума запитів памʼяті не може перевищувати це значення.
hugepages-<size>Для всіх Podʼів у нетермінальному стані кількість запитів великих сторінок зазначеного розміру не може перевищувати це значення.
cpuТе саме, що і requests.cpu
memoryТе саме, що і requests.memory

Квота для розширених ресурсів

Крім згаданих вище ресурсів, у випуску 1.10 було додано підтримку квот для розширених ресурсів.

Оскільки overcommit для розширених ресурсів не дозволяється, немає сенсу вказувати і requests, і limits для одного й того самого розширеного ресурсу у квоті. Тому для розширених ресурсів дозволені лише елементи квоти з префіксом requests..

Візьмімо як приклад ресурс GPU: якщо назва ресурсу — nvidia.com/gpu і ви хочете обмежити загальну кількість запитаних GPU в просторі імен до 4, ви можете визначити квоту так:

  • requests.nvidia.com/gpu: 4

Докладніше дивіться в розділі Перегляд та встановлення квот.

Квота для запитів на ресурси DRA

Запити на ресурси DRA (Dynamic Resource Allocation) можуть запитувати ресурси DRA за класом пристроїв. Наприклад, для класу пристроїв із назвою examplegpu, якщо ви хочете обмежити загальну кількість GPU, запитаних у просторі імен, до 4, ви можете визначити квоту так:

  • examplegpu.deviceclass.resource.k8s.io/devices: 4

Коли увімкнено виділення розширених ресурсів за допомогою DRA, той самий клас пристроїв із назвою examplegpu можна запитувати через розширений ресурс або явно, коли в полі ExtendedResourceName класу пристроїв задано, скажімо, example.com/gpu, тоді ви можете визначити квоту так:

  • requests.example.com/gpu: 4

або неявно, використовуючи похідну назву розширеного ресурсу від назви класу пристроїв examplegpu, тоді ви можете визначити квоту так:

  • requests.deviceclass.resource.kubernetes.io/examplegpu: 4

Усі пристрої, запитані через запити на ресурси або розширені ресурси, враховуються у всіх трьох наведених вище квотах. Квота розширеного ресурсу, наприклад requests.example.com/gpu: 4, також враховує пристрої, надані втулком пристроїв.

Докладніше дивіться в розділі Перегляд та встановлення квот.

Квота для сховища

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

Крім того, ви можете обмежити споживання ресурсів сховища на основі повʼязаного StorageClass.

Назва ресурсуОпис
requests.storageДля всіх PersistentVolumeClaims сума запитів на сховище не може перевищувати це значення.
persistentvolumeclaimsЗагальна кількість PersistentVolumeClaims, які можуть існувати у просторі імен.
<storage-class-name>.storageclass.storage.k8s.io/requests.storageДля всіх PersistentVolumeClaims, повʼязаних із <storage-class-name>, сума запитів на сховище не може перевищувати це значення.
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaimsДля всіх PersistentVolumeClaims, повʼязаних із <storage-class-name>, загальна кількість PersistentVolumeClaims, які можуть існувати у просторі імен.

Наприклад, якщо ви хочете встановити квоту для сховища зі StorageClass gold окремо від StorageClass bronze, ви можете визначити квоту так:

  • gold.storageclass.storage.k8s.io/requests.storage: 500Gi
  • bronze.storageclass.storage.k8s.io/requests.storage: 100Gi

Квота для локального тимчасового сховища

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.8
Назва ресурсуОпис
requests.ephemeral-storageДля всіх Podʼів у просторі імен сума запитів на локальне тимчасове сховище не може перевищувати це значення.
limits.ephemeral-storageДля всіх Podʼів у просторі імен сума обмежень на локальне тимчасове сховище не може перевищувати це значення.
ephemeral-storageТе саме, що і requests.ephemeral-storage.

Примітка:

Під час використання середовища виконання контейнерів CRI логи контейнерів зараховуються до квоти тимчасового сховища. Це може призвести до неочікуваного виселення Podʼів, які вичерпали свої квоти сховища.

Докладніше дивіться в розділі Архітектура логів.

Квота на кількість обʼєктів

Ви можете встановити квоту на загальну кількість одного конкретного типу ресурсу в API Kubernetes, використовуючи такий синтаксис:

  • count/<resource>.<group> для ресурсів із груп API, що не належать до основної (non-core)
  • count/<resource> для ресурсів з основної групи API (core)

Наприклад, API PodTemplate входить до основної групи API, тому, якщо ви хочете обмежити кількість обʼєктів PodTemplate у просторі імен, використовуйте count/podtemplates.

Такі типи квот корисні для захисту від вичерпання сховища панелі управління. Наприклад, ви можете обмежити кількість Secrets на сервері, враховуючи їхній великий розмір. Занадто велика кількість Secrets у кластері може фактично завадити запуску серверів і контролерів. Ви можете встановити квоту для Jobs, щоб захиститися від неправильно налаштованого CronJob. CronJobs, які створюють занадто багато Jobs у просторі імен, можуть призвести до відмови в обслуговуванні.

Якщо ви визначаєте квоту таким чином, вона застосовується до API Kubernetes, які є частиною API-сервера, та до будь-яких власних ресурсів, що підтримуються CustomResourceDefinition. Наприклад, щоб створити квоту на власний ресурс widgets у групі API example.com, використовуйте count/widgets.example.com. Якщо ви використовуєте агрегацію API для додавання власних API, які не визначені як CustomResourceDefinitions, основна панель управління Kubernetes не застосовує квоту до агрегованого API. Очікується, що сервер розширення API забезпечуватиме застосування квоти, якщо це доречно для цього власного API.

Загальний синтаксис

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

  • count/pods
  • count/persistentvolumeclaims
  • count/services
  • count/secrets
  • count/configmaps
  • count/deployments.apps
  • count/replicasets.apps
  • count/statefulsets.apps
  • count/jobs.batch
  • count/cronjobs.batch
Спеціалізований синтаксис

Існує інший синтаксис для встановлення такого самого типу квоти, який працює лише для певних типів API. Підтримуються такі типи:

Назва ресурсуОпис
configmapsЗагальна кількість ConfigMaps, які можуть існувати в просторі імен.
persistentvolumeclaimsЗагальна кількість PersistentVolumeClaims, які можуть існувати в просторі імен.
podsЗагальна кількість Podʼів у нетермінальному стані, які можуть існувати в просторі імен. Pod перебуває в термінальному стані, якщо .status.phase in (Failed, Succeeded) є true.
replicationcontrollersЗагальна кількість ReplicationControllers, які можуть існувати в просторі імен.
resourcequotasЗагальна кількість ResourceQuotas, які можуть існувати в просторі імен.
servicesЗагальна кількість Services, які можуть існувати в просторі імен.
services.loadbalancersЗагальна кількість Services типу LoadBalancer, які можуть існувати в просторі імен.
services.nodeportsЗагальна кількість NodePorts, виділених Services типу NodePort чи LoadBalancer, які можуть існувати в просторі імен.
secretsЗагальна кількість Secrets, які можуть існувати в просторі імен.

Наприклад, квота pods рахує та обмежує максимальну кількість pods, створених в одному просторі імен, які не є термінальними. Ви можете встановити квоту pods у просторі імен, щоб уникнути випадку, коли користувач створює багато невеликих Podʼів і вичерпує запас IP-адрес Podʼів у кластері.

Більше прикладів можна знайти в розділі Перегляд та встановлення квот.

Перегляд та встановлення квот

kubectl підтримує створення, оновлення та перегляд квот:

kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    requests.cpu: "1"
    requests.memory: "1Gi"
    limits.cpu: "2"
    limits.memory: "2Gi"
    requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    configmaps: "10"
    persistentvolumeclaims: "4"
    pods: "4"
    replicationcontrollers: "20"
    secrets: "10"
    services: "10"
    services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME                    AGE
compute-resources       30s
object-counts           32s
kubectl describe quota compute-resources --namespace=myspace
Name:                    compute-resources
Namespace:               myspace
Resource                 Used  Hard
--------                 ----  ----
limits.cpu               0     2
limits.memory            0     2Gi
requests.cpu             0     1
requests.memory          0     1Gi
requests.nvidia.com/gpu  0     4
kubectl describe quota object-counts --namespace=myspace
Name:                   object-counts
Namespace:              myspace
Resource                Used    Hard
--------                ----    ----
configmaps              0       10
persistentvolumeclaims  0       4
pods                    0       4
replicationcontrollers  0       20
secrets                 1       10
services                0       10
services.loadbalancers  0       2

kubectl також підтримує квоту на кількість обʼєктів для всіх стандартних ресурсів простору імен, використовуючи синтаксис count/<resource>.<group>:

kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name:                         test
Namespace:                    myspace
Resource                      Used  Hard
--------                      ----  ----
count/deployments.apps        1     2
count/pods                    2     3
count/replicasets.apps        1     4
count/secrets                 1     4

Квота та місткість кластера

ResourceQuotas не залежать від місткості кластера. Вони виражаються в абсолютних одиницях. Отже, якщо ви додаєте вузли до свого кластера, це не дає автоматично кожному простору імен можливості споживати більше ресурсів.

Іноді можуть знадобитися більш складні політики, такі як:

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

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

Зверніть увагу, що квота ресурсів розподіляє сукупні ресурси кластера, але не створює обмежень щодо вузлів: Podʼи з кількох просторів імен можуть працювати на одному вузлі.

Області дії квоти

Кожна квота може мати повʼязаний набір scopes. Квота вимірюватиме використання ресурсу лише тоді, коли він відповідає перетину перелічених областей дії.

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

Kubernetes 1.37 підтримує такі області дії:

ОбластьОпис
BestEffortВідповідає Podʼам із якістю обслуговування best-effort.
CrossNamespacePodAffinityВідповідає Podʼам, які мають правила (анти)спорідненості Podʼів між просторами імен.
NotBestEffortВідповідає Podʼам, які не мають якості обслуговування best-effort.
NotTerminatingВідповідає Podʼам, де .spec.activeDeadlineSeconds є nil.
PriorityClassВідповідає Podʼам, які посилаються на вказаний клас пріоритету.
TerminatingВідповідає Podʼам, де .spec.activeDeadlineSeconds >= 0.
VolumeAttributesClassВідповідає PersistentVolumeClaims, які посилаються на вказаний клас атрибутів тому.

ResourceQuotas із заданою областю дії також можуть мати необовʼязкове поле scopeSelector. Ви визначаєте один або кілька виразів відповідності, які вказують operators і, якщо це доречно, набір values для відповідності. Наприклад:

  scopeSelector:
    matchExpressions:
      - scopeName: BestEffort # Відповідає Podʼам із якістю обслуговування best-effort
        operator: Exists # необовʼязково; для області дії BestEffort "Exists" мається на увазі

scopeSelector підтримує такі значення в полі operator:

  • In
  • NotIn
  • Exists
  • DoesNotExist

Якщо оператором є In або NotIn, поле values повинно мати щонайменше одне значення. Наприклад:

  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values:
          - middle

Якщо оператором є Exists або DoesNotExist, поле values НЕ повинно бути вказане.

Область дії для Podʼів Best effort

Ця область дії відстежує лише квоту, спожиту Podʼами. Вона відповідає лише Podʼам, які мають клас QoS best-effort.

operator для scopeSelector повинен бути Exists.

Область дії для Podʼів Not-best-effort

Ця область дії відстежує лише квоту, спожиту Podʼами. Вона відповідає лише Podʼам, які мають клас QoS Guaranteed або Burstable.

operator для scopeSelector повинен бути Exists.

Область дії для Podʼів Non-terminating

Ця область дії відстежує лише квоту, спожиту Podʼами, які не завершуються. operator для scopeSelector повинен бути Exists.

Pod вважається таким, що не завершується, якщо поле .spec.activeDeadlineSeconds не встановлено.

Ви можете використовувати ResourceQuota з цією областю дії для керування такими ресурсами:

  • count.pods
  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

Область дії Podʼів Terminating

Ця область дії відстежує лише квоту, спожиту Podʼами, які завершуються. operator для scopeSelector повинен бути Exists.

Pod вважається таким, що завершується, якщо поле .spec.activeDeadlineSeconds встановлено на будь-яке число.

Ви можете використовувати ResourceQuota з цією областю дії для керування такими ресурсами:

  • count.pods
  • pods
  • cpu
  • memory
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

Область дії спорідненості Podʼів між просторами імен

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

Ви можете використовувати область дії квоти CrossNamespacePodAffinity, щоб обмежити, які простори імен можуть мати Podʼи з правилами спорідненості, що охоплюють кілька просторів імен. Зокрема, вона контролює, яким Podʼам дозволено встановлювати поля namespaces або namespaceSelector у правилах (анти)спорідненості Podʼів.

Може бути бажаним не дозволяти користувачам використовувати правила спорідненості між просторами імен, оскільки Pod з обмеженнями антиспорідненості може не дати Podʼам з усіх інших просторів імен бути запланованими в домені відмов.

За допомогою цієї області дії ви (як адміністратор кластера) можете заборонити певним просторам імен (наприклад, foo-ns у наведеному нижче прикладі) мати Podʼи, які використовують спорідненість Podʼів між просторами імен. Для цього створіть у цьому просторі імен обʼєкт ResourceQuota з областю дії CrossNamespacePodAffinity та жорстким обмеженням 0:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: disable-cross-namespace-affinity
  namespace: foo-ns
spec:
  hard:
    pods: "0"
  scopeSelector:
    matchExpressions:
    - scopeName: CrossNamespacePodAffinity
      operator: Exists

Якщо ви хочете стандартно заборонити використання namespaces та namespaceSelector і дозволити його лише для певних просторів імен, ви можете налаштувати CrossNamespacePodAffinity як обмежений ресурс, встановивши для прапорця kube-apiserver --admission-control-config-file шлях до такого конфігураційного файлу:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: CrossNamespacePodAffinity
        operator: Exists

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

Область дії PriorityClass

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.17

ResourceQuota з областю дії PriorityClass відповідає лише тим Podʼам, які мають певний клас пріоритету, і лише якщо будь-який scopeSelector у специфікації квоти вибирає певний Pod.

Podʼи можна створювати з певним пріоритетом. Ви можете контролювати споживання системних ресурсів Podʼом на основі його пріоритету, використовуючи поле scopeSelector у специфікації квоти.

Коли область дії квоти задано для PriorityClass за допомогою поля scopeSelector, ResourceQuota може відстежувати (і обмежувати) лише такі ресурси:

  • pods
  • cpu
  • memory
  • ephemeral-storage
  • limits.cpu
  • limits.memory
  • limits.ephemeral-storage
  • requests.cpu
  • requests.memory
  • requests.ephemeral-storage

Приклад

Цей приклад створює ResourceQuota, яка відповідає Podʼам з певними пріоритетами. Приклад працює так:

  • Podʼи в кластері мають один із трьох PriorityClasses: "low", "medium", "high".
    • Якщо ви хочете спробувати це, використовуйте тестовий кластер і налаштуйте ці три PriorityClasses, перш ніж продовжувати.
  • Для кожного пріоритету створюється один обʼєкт квоти.

Перегляньте цей набір ResourceQuotas:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-high
spec:
  hard:
    cpu: "1000"
    memory: "200Gi"
    pods: "10"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["high"]
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-medium
spec:
  hard:
    cpu: "10"
    memory: "20Gi"
    pods: "10"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["medium"]
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-low
spec:
  hard:
    cpu: "5"
    memory: "10Gi"
    pods: "10"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["low"]

Застосуйте YAML за допомогою kubectl create.

kubectl create -f https://k8s.io/examples/policy/quota.yaml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created

Перевірте, що квота Used дорівнює 0, за допомогою команди kubectl describe quota.

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     1k
memory      0     200Gi
pods        0     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

Створіть Pod з пріоритетом "high".

apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
  - name: high-priority
    image: ubuntu
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "10Gi"
        cpu: "500m"
      limits:
        memory: "10Gi"
        cpu: "500m"
  priorityClassName: high

Створіть Pod:

kubectl create -f https://k8s.io/examples/policy/high-priority-pod.yaml

Перевірте, що статистика "Used" для квоти пріоритету "high", pods-high, змінилася, а інші дві квоти залишилися без змін.

kubectl describe quota
Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         500m  1k
memory      10Gi  200Gi
pods        1     10


Name:       pods-low
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     5
memory      0     10Gi
pods        0     10


Name:       pods-medium
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     10
memory      0     20Gi
pods        0     10

Стандартне обмеження споживання PriorityClass

Може бути бажаним, щоб Podʼи з певним пріоритетом, наприклад "cluster-services", дозволялися в просторі імен тоді й лише тоді, коли існує відповідний обʼєкт квоти.

За допомогою цього механізму оператори можуть обмежувати використання певних високопріоритетних класів обмеженою кількістю просторів імен, і не кожен простір імен зможе стандартно споживати ці класи пріоритету.

Для цього потрібно використати прапорець kube-apiserver --admission-control-config-file, щоб передати шлях до такого конфігураційного файлу:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: ResourceQuotaConfiguration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: PriorityClass
        operator: In
        values: ["cluster-services"]

Потім створіть обʼєкт квоти ресурсів у просторі імен kube-system:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pods-cluster-services
spec:
  scopeSelector:
    matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["cluster-services"]
kubectl apply -f https://k8s.io/examples/policy/priority-class-resourcequota.yaml -n kube-system
resourcequota/pods-cluster-services created

У цьому випадку створення Podʼа буде дозволено, якщо:

  1. priorityClassName Podʼа не вказано.
  2. priorityClassName Podʼа має значення, відмінне від cluster-services.
  3. priorityClassName Podʼа встановлено на cluster-services, Pod створюється у просторі імен kube-system і пройшов перевірку квоти ресурсів.

Запит на створення Podʼа буде відхилено, якщо його priorityClassName встановлено на cluster-services і Pod створюється в просторі імен, відмінному від kube-system.

Область дії VolumeAttributesClass

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.36
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.36. It was first available in the v1.29 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate VolumeAttributesClass, Kubernetes ignores it but does not report any error.

Ця область дії відстежує лише квоту, спожиту PersistentVolumeClaims.

PersistentVolumeClaims можна створювати з певним VolumeAttributesClass і змінювати після створення. Ви можете контролювати споживання ресурсів сховища PVC на основі повʼязаних VolumeAttributesClasses, використовуючи поле scopeSelector у специфікації квоти.

PVC посилається на повʼязаний VolumeAttributesClass через такі поля:

  • spec.volumeAttributesClassName
  • status.currentVolumeAttributesClassName
  • status.modifyVolumeStatus.targetVolumeAttributesClassName

Відповідна ResourceQuota зіставляється й споживається лише тоді, коли ResourceQuota має scopeSelector, який вибирає PVC.

Коли область дії квоти задано для класу атрибутів тому за допомогою поля scopeSelector, обʼєкт квоти може відстежувати лише такі ресурси:

  • persistentvolumeclaims
  • requests.storage

Детальніше про це читайте в розділі Обмеження використання сховища.

Що далі

8.3 - Обмеження та резервування ID процесів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.20

Kubernetes дозволяє обмежувати кількість ідентифікаторів процесів (PID), які може використовувати Pod. Також можна зарезервувати певну кількість доступних для розподілу PID для кожного вузла для використання операційною системою та демонами (а не Podʼами).

Ідентифікатори процесів (PID) є фундаментальним ресурсом на вузлах. Досягти обмеження на кількість завдань, не досягнувши жодних інших обмежень ресурсів, дуже просто, і це може призвести до нестабільної роботи хост-машини.

Адміністраторам кластерів потрібні механізми, щоб гарантувати, що Podʼи, які працюють у кластері, не зможуть спричинити вичерпання PID, яке перешкоджає роботі демонів хоста (таких як kubelet або kube-proxy, а потенційно й середовище виконання контейнерів). Крім того, важливо обмежувати PID між Podʼами, щоб вони мали обмежений вплив на інші робочі навантаження на тому самому вузлі.

Примітка:

На деяких інсталяціях Linux операційна система стандартно встановлює низьке обмеження PID, наприклад 32768. Розгляньте можливість збільшення значення /proc/sys/kernel/pid_max.

Ви можете налаштувати kubelet для обмеження кількості PID, які може споживати конкретний Pod. Наприклад, якщо ОС хоста вашого вузла налаштовано на використання максимум 262144 PID і очікується, що на ньому розміщуватиметься менше ніж 250 Podʼів, можна надати кожному Podʼу бюджет у 1000 PID, щоб запобігти вичерпанню загальної кількості доступних PID на вузлі. Якщо адміністратор хоче застосувати overcommit для PID, як для CPU чи памʼяті, це теж можливо, але з певними додатковими ризиками. У будь-якому випадку один Pod не зможе вивести з ладу всю машину. Цей вид обмеження ресурсів допомагає запобігти впливу простих форк-бомб на роботу всього кластера.

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

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

Обмеження PID є важливим доповненням до запитів і обмежень обчислювальних ресурсів. Однак ви вказуєте його інакше: замість того, щоб визначати обмеження ресурсу Podʼа в .spec Podʼа, ви налаштовуєте обмеження як параметр kubelet. Обмеження PID, визначені в Podʼі, наразі не підтримуються.

Увага:

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

Обмеження PID вузла

Kubernetes дозволяє зарезервувати певну кількість ідентифікаторів процесів для системного використання. Щоб налаштувати резервування, використовуйте параметр pid=<кількість> у параметрах командного рядка kubelet --system-reserved та --kube-reserved. Вказане вами значення оголошує, що вказану кількість ідентифікаторів процесів буде зарезервовано відповідно для системи в цілому та для системних демонів Kubernetes.

Обмеження PID на рівні Podʼа

Kubernetes дозволяє обмежити кількість процесів, запущених у Podʼі. Ви вказуєте це обмеження на рівні вузла, а не налаштовуєте його як обмеження ресурсів для певного Podʼа. Кожен вузол може мати власне обмеження PID. Щоб налаштувати обмеження, ви можете вказати параметр командного рядка kubelet --pod-max-pids або встановити PodPidsLimit у конфігураційному файлі kubelet.

Виселення на основі PID

Ви можете налаштувати kubelet так, щоб він починав завершувати Pod, коли той поводиться некоректно та споживає аномальну кількість ресурсів. Ця можливість називається виселенням (eviction). Ви можете налаштувати обробку випадків нестачі ресурсів для різних сигналів виселення. Використовуйте сигнал виселення pid.available, щоб налаштувати поріг кількості PID, які використовує Pod. Ви можете встановити мʼякі та жорсткі політики виселення. Однак навіть із жорсткою політикою виселення, якщо кількість PID зростає дуже швидко, вузол усе ще може перейти в нестабільний стан, досягнувши обмеження PID вузла. Значення сигналу виселення обчислюється періодично і НЕ забезпечує дотримання обмеження.

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

Що далі

9 - Планування, витіснення та виселення

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

Планування

Порушення роботи Podʼів

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

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

9.1 - Запуск кількох планувальників

Kubernetes дозволяє запускати кілька планувальників в одному кластері.

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

Стратегії розгортання

Під час запуску кількох планувальників ви повинні вирішити, як вони взаємодіють один з одним та з вузлами кластера. Існує три поширені сценарії розгортання:

Висока доступність (репліки у стані очікування)

Якщо ви хочете запустити єдиний логічний планувальник (такий як kube-scheduler), але забезпечити його доступність у разі збою, ви повинні запустити кілька реплік планувальника та увімкнути вибір лідера (leader election).

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

Щоб дізнатися, як це налаштувати, див. Увімкнення вибору лідера.

Кілька профілів планувальника або екземплярів

Якщо ви хочете використовувати різні конфігурації або правила планування для різних робочих навантажень, ви можете налаштувати один екземпляр kube-scheduler з кількома профілями планування або запустити кілька окремих екземплярів kube-scheduler.

  • Кілька профілів в одному екземплярі: Один процес kube-scheduler може запускати кілька профілів (кожен з унікальною назвою планувальника). Оскільки ці профілі працюють в одному процесі та спільно використовують одну чергу планування та кеш, вони не приймають конфліктних рішень щодо планування. Ви все ще можете використовувати спорідненість вузлів, якщо хочете ізолювати робочі навантаження, спрямовуючи кожен профіль до певного пулу вузлів.
  • Кілька окремих екземплярів: Якщо ви запускаєте кілька окремих процесів kube-scheduler (розгортання), вони не координуються між собою і можуть приймати конфліктні рішення щодо планування (наприклад, виділяти ресурси вузла понад наявні). Щоб цього уникнути, кожен екземпляр планувальника має націлюватися на неперетинну множину вузлів за допомогою спорідненості вузлів.

Поєднання kube-scheduler з власними планувальниками

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

Що далі

9.2 - Планувальник Kubernetes

У Kubernetes планування означає забезпечення відповідності Podʼів вузлам, щоб Kubelet міг їх запустити.

Огляд планування

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

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

kube-scheduler

kube-scheduler є стандартним планувальником для Kubernetes і працює як частина панелі управління. kube-scheduler розроблено так, що, якщо ви хочете і вам треба, ви можете написати власний компонент планування і використовувати його замість стандартного.

Kube-scheduler вибирає оптимальний вузол для запуску нових або ще не запланованих Podʼів. Оскільки контейнери в Podʼах, і самі Podʼи, можуть мати різні вимоги, планувальник відфільтровує будь-які вузли, які не відповідають конкретним потребам планування Podʼа. Крім того, через API можна вказати вузол для Podʼа під час його створення, але це робиться тільки у виняткових випадках.

У кластері вузли, які відповідають вимогам планування для Podʼа, називаються придатними (feasible) вузлами. Якщо жоден з вузлів не підходить, Pod залишається незапланованим до тих пір, поки планувальник не зможе розмістити його.

Планувальник знаходить придатні вузли для Podʼів, а потім виконує набір функцій для оцінки таких вузлів і вибирає вузол з найвищою оцінкою серед придатних для запуску Podʼа. Потім планувальник повідомляє API-сервер про це рішення в процесі, який називається привʼязкою (binding).

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

Вибір вузла в kube-scheduler

kube-scheduler вибирає вузол для Podʼа в два етапи:

  1. Фільтрація.
  2. Оцінювання.

На етапі фільтрації визначається набір вузлів, на яких можна розмістити Pod. Наприклад, фільтр PodFitsResources перевіряє, чи має вузол-кандидат достатньо доступних ресурсів, щоб задовольнити конкретні запити Podʼа на ресурси. Після цього етапу список вузлів містить придатні вузли; часто їх буде більше одного. Якщо список порожній, цей Pod (поки що) неможливо запланувати.

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

На останньому етапі kube-scheduler призначає Pod вузлу з найвищим рейтингом. Якщо є більше одного вузла з однаковими оцінками, kube-scheduler вибирає один з них випадковим чином.

Є два підтримуваних способи налаштування поведінки фільтрації та оцінювання планувальника:

  1. Політики планування дозволяють налаштувати Предикати для фільтрації та Пріоритети для оцінювання.
  2. Профілі планування дозволяють налаштувати втулки, які реалізують різні етапи планування, включаючи: QueueSort, Filter, Score, Bind, Reserve, Permit та інші. Ви також можете налаштувати kube-scheduler для запуску різних профілів.

Що далі

9.3 - Призначення Podʼів вузлам

Ви можете обмежити Pod так, щоб він міг запускатися лише на конкретних вузлах або віддавав перевагу запуску на певних вузлах. Є кілька способів зробити це, і всі рекомендовані підходи використовують селектори міток для полегшення вибору. Зазвичай вам не потрібно встановлювати жодні обмеження такого роду; планувальник автоматично робить розумне розміщення (наприклад, розподіл Podʼів по вузлах так, щоб не розміщувати Podʼи на вузлі з недостатньою кількістю вільних ресурсів). Однак є обставини, коли ви можете захотіти контролювати, на якому вузлі розгортається Pod, наприклад, щоб переконатися, що Pod потрапляє на вузол з прикріпленим до нього SSD, або щоб розмістити в одній зоні доступності Podʼи двох різних сервісів, які багато взаємодіють.

Ви можете використовувати будь-який з наступних методів для вибору місця, де Kubernetes планує конкретні Podʼи:

Мітки вузлів

Як і багато інших обʼєктів Kubernetes, вузли мають мітки. Ви можете додавати мітки вручну. Kubernetes також заповнює стандартний набір міток на всіх вузлах кластера.

Примітка:

Значення цих міток залежить від хмарного провайдера, і їх надійність не гарантується. Наприклад, значення kubernetes.io/hostname може бути таким самим, як назва вузла, у деяких середовищах і відрізнятися в інших середовищах.

Ізоляція/обмеження вузлів

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

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

Втулок допуску NodeRestriction перешкоджає kubelet встановлювати або змінювати мітки з префіксом node-restriction.kubernetes.io/.

Щоб скористатися цим префіксом міток для ізоляції вузлів:

  1. Переконайтеся, що ви використовуєте авторизатор вузлів і увімкнули втулок допуску NodeRestriction.
  2. Додайте мітки з префіксом node-restriction.kubernetes.io/ на ваші вузли, і використовуйте ці мітки у ваших селекторах вузлів. Наприклад, example.com.node-restriction.kubernetes.io/fips=true або example.com.node-restriction.kubernetes.io/pci-dss=true.

nodeSelector

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

Дивіться Призначення Podʼів вузлам для отримання додаткової інформації.

Спорідненість та антиспорідненість

nodeSelector є найпростішим способом обмежити Podʼи вузлами з певними мітками. Спорідненість та антиспорідненість розширюють типи обмежень, які ви можете визначити. Деякі з переваг спорідненості та антиспорідненості включають:

  • Мова (анти)спорідненості є більш виразною. nodeSelector вибирає лише вузли з усіма вказаними мітками. (Анти)спорідненість дає вам більший контроль над логікою вибору.
  • Ви можете вказати, що правило є soft або preferred, щоб планувальник все одно запланував Pod, навіть якщо не може знайти відповідний вузол.
  • Ви можете обмежити Pod, використовуючи мітки на інших Podʼах, які працюють на вузлі (або в іншому топологічному домені), а не лише мітки вузла, що дозволяє визначати правила для того, які Podʼи можуть бути розміщені разом на вузлі.

Можливість affinity складається з двох типів спорідненості:

  • Спорідненість вузла працює подібно до поля nodeSelector, але є виразнішою і дозволяє вказувати мʼякі правила.
  • (Анти)спорідненість між Podʼами дозволяє обмежувати Podʼи відносно міток інших Podʼів.

Спорідненість вузлів

Спорідненість вузлів концептуально подібна до nodeSelector і дозволяє вам обмежувати, на яких вузлах може бути запланований ваш Pod на основі міток вузла. Існують два типи спорідненості вузла:

  • requiredDuringSchedulingIgnoredDuringExecution: Планувальник не може запланувати Pod, якщо правило не виконується. Це працює подібно до nodeSelector, але з більш виразним синтаксисом.
  • preferredDuringSchedulingIgnoredDuringExecution: Планувальник намагається знайти вузол, який відповідає правилу. Якщо відповідний вузол недоступний, планувальник все одно планує Pod.

Примітка:

У попередніх типах IgnoredDuringExecution означає, що якщо мітки вузла змінюються після того, як Kubernetes запланував Pod, Pod продовжує працювати.

Ви можете вказати спорідненість вузлів, використовуючи поле .spec.affinity.nodeAffinity в специфікації вашого Podʼа.

Наприклад, розгляньте наступну специфікацію Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - antarctica-east1
            - antarctica-west1
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:3.8

У цьому прикладі застосовуються наступні правила:

  • Вузол повинен мати мітку з ключем topology.kubernetes.io/zone, а значення цієї мітки повинно бути або antarctica-east1, або antarctica-west1.
  • Вузол бажано має мітку з ключем another-node-label-key, а значення another-node-label-value.

Ви можете використовувати поле operator, щоб вказати логічний оператор, який Kubernetes буде використовувати під час інтерпретації правил. Ви можете використовувати In, NotIn, Exists, DoesNotExist, Gt і Lt.

Прочитайте розділ Оператори, щоб дізнатися більше про те, як вони працюють.

NotIn та DoesNotExist дозволяють визначати поведінку антиспорідненості. Як альтернативу ви можете використовувати taints вузлів для відштовхування Podʼів від конкретних вузлів.

Примітка:

Якщо ви вказуєте як nodeSelector, так і nodeAffinity, обидва вони повинні задовольнятися для того, щоб Pod був запланований на вузол.

Якщо ви вказуєте кілька умов в nodeSelectorTerms, повʼязаних з типами nodeAffinity, то Pod може бути запланований на вузол, якщо одна з вказаних умов може бути задоволеною (умови зʼєднуються логічним OR).

Якщо ви вказуєте кілька виразів в одному полі matchExpressions, повʼязаному з умовою в nodeSelectorTerms, то Pod може бути запланований на вузол лише в тому випадку, якщо всі вирази задовольняються (вирази зʼєднуються логічним AND).

Дивіться Призначення Podʼів вузлам з використанням Node Affinity для отримання додаткової інформації.

Вага спорідненості вузлів

Ви можете вказати weight (вагу) від 1 до 100 для кожного екземпляра спорідненості типу preferredDuringSchedulingIgnoredDuringExecution. Коли планувальник знаходить вузли, які відповідають усім іншим вимогам планування Podʼа, планувальник проходить по кожному правилу preferred, якому задовольняє вузол, і додає значення weight для цього виразу до суми.

Кінцева сума додається до оцінки інших функцій пріоритету для вузла. Вузли з найвищою загальною оцінкою отримують пріоритет, коли планувальник приймає рішення щодо планування Podʼа.

Наприклад, розгляньте наступну специфікацію Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: with-affinity-preferred-weight
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: label-1
            operator: In
            values:
            - key-1
      - weight: 50
        preference:
          matchExpressions:
          - key: label-2
            operator: In
            values:
            - key-2
  containers:
  - name: with-node-affinity
    image: registry.k8s.io/pause:3.8

Якщо існують два можливих вузли, які відповідають правилу preferredDuringSchedulingIgnoredDuringExecution, один з міткою label-1:key-1, а інший з міткою label-2:key-2, планувальник бере до уваги weight кожного вузла і додає вагу до інших оцінок для цього вузла, і планує Pod на вузол з найвищою кінцевою оцінкою.

Примітка:

Якщо ви хочете, щоб Kubernetes успішно запланував Podʼи в цьому прикладі, вам потрібно мати наявні вузли з міткою kubernetes.io/os=linux.

Спорідненість вузлів для кожного профілю планування

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.20

Під час налаштування кількох профілів планування ви можете повʼязати профіль зі спорідненістю вузлів, що є корисним, якщо профіль застосовується лише до певного набору вузлів. Для цього додайте addedAffinity до поля args втулка NodeAffinity у конфігурації планувальника. Наприклад:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo

addedAffinity застосовується до всіх Podʼів, які встановлюють .spec.schedulerName в foo-scheduler, на додачу до NodeAffinity, вказаного в PodSpec. Тобто, щоб відповідати Podʼу, вузли мають задовольняти addedAffinity та .spec.NodeAffinity Podʼа.

Оскільки addedAffinity не є видимим для кінцевих користувачів, його поведінка може бути неочікуваною для них. Використовуйте мітки вузлів, які мають чіткий взаємозвʼязок із назвою профілю планувальника.

Примітка:

Контролер DaemonSet, який створює Podʼи для DaemonSet, не підтримує профілі планування. Коли контролер DaemonSet створює Podʼи, стандартний планувальник Kubernetes розміщує ці Podʼи та дотримується будь-яких правил nodeAffinity в контролері DaemonSet.

Спорідненість та антиспорідненість між Podʼами

Спорідненість та антиспорідненість між Podʼами дозволяють обмежити, на яких вузлах ваші Podʼи можуть бути заплановані на основі міток Podʼів, які вже працюють на цьому вузлі, а не міток вузлів.

Типи спорідненості та антиспорідненості між Podʼами

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

Ви виражаєте ці правила (Y) як селектори міток з необовʼязковим повʼязаним списком просторів імен. Podʼи є обʼєктами з простором імен в Kubernetes, тому мітки Podʼів також неявно мають простори імен. Будь-які селектори міток для міток Podʼа повинні вказувати простори імен, в яких Kubernetes має переглядати ці мітки.

Ви виражаєте топологічний домен (X), використовуючи topologyKey, який є ключем для мітки вузла, яку система використовує для позначення домену. Для прикладу дивіться у Відомі мітки, анотації та taints.

Примітка:

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

Примітка:

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

Подібно до спорідненості вузлів, існують два типи спорідненості та антиспорідненості Podʼа:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

Наприклад, ви можете використовувати спорідненість requiredDuringSchedulingIgnoredDuringExecution, щоб повідомити планувальнику про те, що потрібно розмістити Podʼи двох сервісів в одній зоні хмарного провайдера, оскільки вони взаємодіють між собою дуже часто. Так само ви можете використовувати антиспорідненість preferredDuringSchedulingIgnoredDuringExecution, щоб розподілити Podʼи сервісу по різних зонах хмарного провайдера.

Для використання спорідненості між Podʼами використовуйте поле affinity.podAffinity в специфікації Podʼа. Для антиспорідненості між Podʼами використовуйте поле affinity.podAntiAffinity в специфікації Podʼа.

Поведінка планування

Під час планування нового Podʼа планувальник Kubernetes оцінює правила спорідненості/антиспорідненості Podʼа в контексті поточного стану кластера:

  1. Жорсткі обмеження (фільтрація вузлів):

    • podAffinity.requiredDuringSchedulingIgnoredDuringExecution та podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution:
      • Планувальник гарантує, що новий Pod буде призначено вузлам, які задовольняють ці обовʼязкові правила спорідненості та антиспорідненості, на основі наявних Podʼів.
  2. Мʼякі обмеження (оцінювання):

    • podAffinity.preferredDuringSchedulingIgnoredDuringExecution та podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution:
      • Планувальник оцінює вузли на основі того, наскільки добре вони відповідають цим бажаним правилам спорідненості та антиспорідненості, щоб оптимізувати розміщення Podʼів.
  3. Ігноровані поля:

    • Наявні в Podʼах podAffinity.preferredDuringSchedulingIgnoredDuringExecution:
      • Ці бажані правила спорідненості не враховуються під час прийняття рішення щодо планування нових Podʼів.
    • Наявні в Podʼах podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution:
      • Аналогічно, під час планування ігноруються бажані правила антиспорідненості наявних Podʼів.

Планування групи Podʼів зі спорідненістю між собою

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

Приклад спорідненості Podʼа

Розгляньте наступну специфікацію Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: registry.k8s.io/pause:3.8

У цьому прикладі визначено одне правило спорідненості Podʼа та одне правило антиспорідненості Podʼа. Правило спорідненості Podʼа використовує «hard» requiredDuringSchedulingIgnoredDuringExecution, тоді як правило антиспорідненості використовує «soft» preferredDuringSchedulingIgnoredDuringExecution.

Правило спорідненості вказує, що планувальник може розмістити Pod лише на вузлі, який належить до певної зони, де інші Podʼи мають мітку security=S1. Наприклад, якщо у нас є кластер із призначеною зоною, скажімо, «Zone V», що складається з вузлів з міткою topology.kubernetes.io/zone=V, планувальник може призначити Pod на будь-який вузол у Zone V, якщо принаймні один Pod у Zone V вже має мітку security=S1. І навпаки, якщо в Zone V немає Podʼів з мітками security=S1, планувальник не призначить Pod з прикладу ні на один вузол в цій зоні.

Правило антиспорідненості вказує, що планувальник повинен уникати призначення Podʼа на вузол, якщо цей вузол належить до певної зони, де інші Podʼи мають мітку security=S2. Наприклад, якщо у нас є кластер із призначеною зоною, скажімо, «Zone R», що складається з вузлів з міткою topology.kubernetes.io/zone=R, планувальник повинен уникати призначення Podʼа на будь-який вузол у Zone R, якщо принаймні один Pod у Zone R вже має мітку security=S2. І навпаки, правило антиспорідненості не впливає на планування у Zone R, якщо немає Podʼів з мітками security=S2.

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

В полі operator для спорідненості та антиспорідненості Podʼа можна використовувати значення In, NotIn, Exists та DoesNotExist.

Для отримання додаткової інформації про те, як це працює, перегляньте Оператори.

У принципі, topologyKey може бути будь-яким допустимим ключем мітки з такими винятками з причин продуктивності та безпеки:

  • Для спорідненості та антиспорідненості Podʼа порожнє поле topologyKey не дозволяється як для requiredDuringSchedulingIgnoredDuringExecution, так і для preferredDuringSchedulingIgnoredDuringExecution.
  • Для правил антиспорідненості Podʼа requiredDuringSchedulingIgnoredDuringExecution контролер допуску LimitPodHardAntiAffinityTopology обмежує topologyKey до kubernetes.io/hostname. Ви можете змінити або вимкнути контролер допуску, якщо хочете дозволити власні топології.

Крім labelSelector та topologyKey, ви можете за бажанням вказати список просторів імен, з якими labelSelector повинен зіставлятися, використовуючи поле namespaces на тому ж рівні, що й labelSelector та topologyKey. Якщо поле відсутнє або порожнє, namespaces стандартно відповідає простору імен Podʼа, де зʼявляється визначення спорідненості/антиспорідненості.

Селектор простору імен

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

Ви також можете вибирати відповідні простори імен за допомогою namespaceSelector, який є запитом міток до набору просторів імен. Умова спорідненості застосовується до просторів імен, вибраних як namespaceSelector, так і полем namespaces. Зверніть увагу, що порожній namespaceSelector ({}) відповідає всім просторам імен, тоді як null або порожній список namespaces і null namespaceSelector відповідають простору імен Podʼа, де визначено правило.

matchLabelKeys

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.33
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.33. It was first available in the v1.29 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate MatchLabelKeysInPodAffinity, Kubernetes ignores it but does not report any error.

Примітка:

Поле matchLabelKeys перебуває на рівні бета-версії та є стандартно увімкненим в Kubernetes 1.37. Якщо ви бажаєте його вимкнути, вам потрібно зробити це явним чином за допомогою вимкнення функціональної можливості MatchLabelKeysInPodAffinity.

У Kubernetes є необовʼязкове поле matchLabelKeys для спорідненості або антиспорідненості Podʼа. Це поле вказує ключі для міток, які повинні відповідати міткам вхідного Podʼа під час задоволення спорідненості (антиспорідненості) Podʼа.

Ці ключі використовуються для отримання значень з міток Podʼа; ці ключ-значення міток поєднуються (використовуючи AND) з обмеженнями відповідно до поля labelSelector. Обʼєднане фільтрування вибирає набір наявних Podʼів, які будуть враховуватися під час розрахунку спорідненості (антиспорідненості) Podʼа.

Увага:

Не рекомендується використовувати matchLabelKeys з мітками, які можуть оновлюватися безпосередньо на Podʼах. Навіть якщо ви редагуєте мітку Podʼів, вказану у matchLabelKeys, безпосередньо, (тобто не через deployment), kube-apiserver не відобразить оновлення мітки в обʼєднаному labelSelector.

Поширеним випадком є використання matchLabelKeys разом із pod-template-hash (встановленим у Podʼах, керованих як частина Deployment, де значення унікальне для кожної ревізії). Використання pod-template-hash в matchLabelKeys дозволяє вам спрямовувати Podʼи, які належать до тієї ж ревізії, що й вхідний Pod, щоб поетапне оновлення не порушувало спорідненість.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: application-server
...
spec:
  template:
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - database
            topologyKey: topology.kubernetes.io/zone
            # Тільки Podʼи з певного розгортання беруться до уваги під час обчислення спорідненості Podʼа.
            # Якщо ви оновите Deployment, Podʼи-замінники дотримуватимуться своїх власних правил спорідненості
            # (якщо вони визначені в новому шаблоні Podʼа).
            matchLabelKeys:
            - pod-template-hash

mismatchLabelKeys

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.33
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.33. It was first available in the v1.29 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate MatchLabelKeysInPodAffinity, Kubernetes ignores it but does not report any error.

Примітка:

Поле mismatchLabelKeys перебуває на рівні бета-версії та є стандартно увімкненим в Kubernetes 1.37. Якщо ви бажаєте його вимкнути, вам потрібно зробити це явним чином за допомогою вимкнення функціональної можливості MatchLabelKeysInPodAffinity.

Kubernetes включає необовʼязкове поле mismatchLabelKeys для спорідненості або антиспорідненості Podʼа. Це поле вказує ключі для міток, які не повинні мати збігу з мітками вхідного Podʼа, під час задоволення спорідненості чи антиспорідненості Podʼа.

Увага:

Не рекомендується використовувати mismatchLabelKeys з мітками, які можуть оновлюватися безпосередньо на Podʼах. Навіть якщо ви відредагуєте мітку Podʼів, вказану у mismatchLabelKeys, безпосередньо (тобто не через deployment), kube-apiserver не відобразить оновлення мітки в обʼєднаному labelSelector.

Один з прикладів використання — це забезпечення того, що Podʼи будуть розміщені в топологічному домені (вузол, зона і т. д.), де розміщені лише Podʼи від того ж орендаря або команди. Іншими словами, ви хочете уникнути запуску Podʼів від двох різних орендарів в одному топологічному домені одночасно.

apiVersion: v1
kind: Pod
metadata:
  labels:
    # Припустимо, що всі відповідні Podʼи мають встановлену мітку "tenant"
    tenant: tenant-a
...
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      # переконаємось, що Podʼи, повʼязані з цим орендарем, потрапляють у відповідний пул вузлів
      - matchLabelKeys:
          - tenant
        labelSelector: {}
        topologyKey: node-pool
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      # переконаємось, що Podʼи, повʼязані з цим орендарем, не можуть розміщуватися на вузлах,
      # які використовуються для іншого орендаря
      - mismatchLabelKeys:
        - tenant # незалежно від значення мітки "tenant" для цього Podʼа, заборонити
                 # розміщення на вузлах у будь-якому пулі, де працює будь-який Pod
                 # від іншого орендаря.
        labelSelector:
          # Ми повинні мати labelSelector, який обирає лише Podʼи з міткою tenant,
          # інакше цей Pod матиме антиспорідненість до Podʼів з daemonsetʼів, наприклад,
          # які не повинні мати мітку tenant.
          matchExpressions:
          - key: tenant
            operator: Exists
        topologyKey: node-pool

Ще кілька практичних прикладів

Спорідненість та антиспорідненість між Podʼами можуть бути ще кориснішими, коли вони використовуються з колекціями вищого рівня, такими як ReplicaSets, StatefulSets, Deployments і т. д. Ці правила дозволяють налаштувати спільне розташування набору робочих навантажень у визначеній топології; наприклад, віддаючи перевагу розміщенню двох повʼязаних Podʼів на одному вузлі.

Наприклад: уявіть кластер з трьох вузлів. Ви використовуєте кластер для запуску вебзастосунку, а також кешу в памʼяті (наприклад, Redis). Для цього прикладу також припустімо, що затримка між вебзастосунком і кешем має бути настільки низькою, наскільки це практично можливо. Ви можете використовувати спорідненість та антиспорідненість між Podʼами, щоб розміщувати вебсервери разом із кешем, наскільки це можливо.

У наступному прикладі Deployment для кешу Redis репліки отримують мітку app=store. Правило podAntiAffinity повідомляє планувальникові уникати розміщення кількох реплік з міткою app=store на одному вузлі. Це створює кожен кеш на окремому вузлі.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

У наступному прикладі Deployment для вебсерверів створює репліки з міткою app=web-store. Правило спорідненості Podʼа повідомляє планувальнику розмістити кожну репліку на вузлі, де є Pod з міткою app=store. Правило антиспорідненості Podʼа повідомляє планувальнику ніколи не розміщати кілька серверів app=web-store на одному вузлі.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine

Створення цих двох попередніх Deploymentʼів призводить до наступної структури кластера, де кожен вебсервер розміщений разом із кешем, на трьох окремих вузлах.

node-1node-2node-3
webserver-1webserver-2webserver-3
cache-1cache-2cache-3

Загальний ефект полягає в тому, що кожен екземпляр кешу ймовірно використовується одним клієнтом, який працює на тому ж самому вузлі. Цей підхід спрямований на мінімізацію як перекосу (нерівномірного навантаження), так і затримки.

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

nodeName

nodeName є більш прямим способом вибору вузла, ніж спорідненість або nodeSelector. nodeName — це поле в специфікації Podʼа. Якщо поле nodeName не порожнє, планувальник ігнорує Pod, і kubelet на названому вузлі намагається розмістити Pod на цьому вузлі. Використання nodeName переважає використання nodeSelector або правил спорідненості та антиспорідненості.

Деякі з обмежень використання nodeName для вибору вузлів:

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

Попередження:

nodeName призначений для використання власними планувальниками або у більш складних випадках, де вам потрібно обійти будь-які налаштовані планувальники. Обхід планувальників може призвести до Podʼів зі збоєм, якщо призначені вузли перевантажені. Ви можете використовувати спорідненість вузлів або поле nodeSelector, щоб призначити Pod до певного вузла без обходу планувальників.

Нижче наведено приклад специфікації Podʼа з використанням поля nodeName:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

Наведений вище Pod буде запущено лише на вузлі kube-01.

nominatedNodeName

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

nominatedNodeName може використовуватись для зовнішніх компонентів, щоб номінувати вузол для Podʼа в очікуванні. Ця номінація працює за принципом best-effort: її може бути проігноровано, якщо планувальник визначить, що Pod не може перейти до номінованого вузла.

Крім того, це поле може бути (пере)записане планувальником:

  • Якщо планувальник знаходить вузол для номінації через витіснення.
  • Якщо планувальник вирішує, куди йде Pod, і переміщує його в цикл привʼязки.
    • Зверніть увагу, що в цьому випадку nominatedNodeName встановлюється лише тоді, коли Pod повинен пройти через точки розширення WaitOnPermit або PreBind.

Ось приклад статусу Podʼа з використанням поля nominatedNodeName:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
...
status:
  nominatedNodeName: kube-01

Обмеження топології розміщення Podʼів

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

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

Мітки топології Podʼів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

Podʼи успадковують мітки топології (topology.kubernetes.io/zone та topology.kubernetes.io/region) від призначеного їм вузла, якщо такі мітки присутні. Ці мітки можна потім використовувати через Downward API, щоб забезпечити робоче навантаження інформацією про топологію вузлів.

Ось приклад Podʼа, що використовує Downward API для своєї зони та регіону:

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-topology-labels
spec:
  containers:
    - name: app
      image: alpine
      command: ["sh", "-c", "env"]
      env:
        - name: MY_ZONE
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['topology.kubernetes.io/zone']
        - name: MY_REGION
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['topology.kubernetes.io/region']

Оператори

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

ОператорПоведінка
InЗначення мітки присутнє у заданому наборі рядків
NotInЗначення мітки не міститься у заданому наборі рядків
ExistsМітка з цим ключем існує на обʼєкті
DoesNotExistНа обʼєкті не існує мітки з цим ключем

Наступні оператори можна використовувати лише з nodeAffinity.

ОператорПоведінка
GtЗначення поля буде інтерпретовано як ціле число, і це ціле число, отримане в результаті обробки значення мітки, визначеної цим селектором, буде більшим ніж це ціле число.
LtЗначення поля буде інтерпретовано як ціле число, і це ціле число, отримане в результаті обробки значення мітки, визначеної цим селектором, буде меншим ніж це ціле число.

Примітка:

Оператори Gt та Lt не працюватимуть з нецілими значеннями. Якщо вказане значення не вдається розібрати як ціле число, Pod не вдасться запланувати. Крім того, Gt та Lt не доступні для podAffinity.

Що далі

9.4 - Накладні витрати, повʼязані з роботою Podʼів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

Коли ви запускаєте Pod на вузлі, сам Pod потребує певної кількості системних ресурсів. Ці ресурси додаються до ресурсів, необхідних для запуску контейнерів всередині Podʼа. У Kubernetes накладні витрати Podʼа (Pod Overhead) — це спосіб обліку ресурсів, які використовуються інфраструктурою Podʼа, понад запити та обмеження контейнерів.

У Kubernetes накладні витрати Podʼа встановлюються під час допуску відповідно до накладних витрат, повʼязаних із RuntimeClass Podʼа.

Накладні витрати Podʼа враховуються на додачу до суми запитів ресурсів контейнерів під час планування Podʼа. Так само kubelet включатиме накладні витрати Podʼа під час визначення розміру cgroup Podʼа і під час ранжування Podʼів для виселення.

Налаштування накладних витрат Podʼа

Вам потрібно переконатися, що використовується RuntimeClass, який визначає поле overhead.

Приклад використання

Для роботи з накладними витратами Podʼів вам потрібен RuntimeClass, який визначає поле overhead. Наприклад, ви можете використати таке визначення RuntimeClass із середовищем виконання контейнерів з віртуалізацією (у цьому прикладі Kata Containers у поєднанні з монітором віртуальної машини Firecracker), яке використовує приблизно 120MiB на Pod для віртуальної машини та гостьової ОС:

# Вам потрібно змінити цей приклад, щоб назва та накладні витрати ресурсів на Pod
# відповідали тим, що додає середовище виконання контейнерів у вашому кластері.
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata-fc
handler: kata-fc
overhead:
  podFixed:
    memory: "120Mi"
    cpu: "250m"

Робочі навантаження, створені із зазначенням обробника RuntimeClass kata-fc, враховуватимуть накладні витрати памʼяті та CPU в обчисленнях квот ресурсів, під час планування на вузли, а також під час визначення розміру cgroup Podʼа.

Розгляньмо запуск наведеного прикладу робочого навантаження, test-pod:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  runtimeClassName: kata-fc
  containers:
  - name: busybox-ctr
    image: busybox:1.28
    stdin: true
    tty: true
    resources:
      limits:
        cpu: 500m
        memory: 100Mi
  - name: nginx-ctr
    image: nginx
    resources:
      limits:
        cpu: 1500m
        memory: 100Mi

Примітка:

Якщо у визначенні Podʼа вказані лише limits, kubelet виведе requests з цих обмежень і встановить їх такими ж, як і визначені limits.

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

Після того, як контролер допуску RuntimeClass вніс зміни, ви можете перевірити оновлене значення накладних витрат Podʼа:

kubectl get pod test-pod -o jsonpath='{.spec.overhead}'

Вивід:

map[cpu:250m memory:120Mi]

Якщо визначено ResourceQuota, то враховується сума запитів контейнерів, а також поле overhead.

Коли kube-scheduler вирішує, на якому вузлі слід запускати новий Pod, він бере до уваги overhead цього Podʼа, а також суму запитів контейнерів для цього Podʼа. Для цього прикладу планувальник додає запити та overhead, а потім шукає вузол, на якому є 2,25 CPU та 320 MiB вільної памʼяті.

Після того, як Pod заплановано на вузол, kubelet на цьому вузлі створює нову cgroup для Podʼа. Саме в цьому Podʼі базове середовище виконання контейнерів створюватиме контейнери.

Якщо для кожного контейнера визначено обмеження ресурсу (Guaranteed QoS або Burstable QoS з визначеними обмеженнями), то kubelet встановлює верхнє обмеження для cgroup Podʼа, повʼязаної з цим ресурсом (cpu.cfs_quota_us для CPU та memory.limit_in_bytes для памʼяті). Це верхнє обмеження базується на сумі обмежень контейнерів плюс поле overhead, визначене в PodSpec.

Для CPU, якщо Pod має Guaranteed або Burstable QoS, то kubelet встановлює cpu.shares на основі суми запитів контейнерів плюс поле overhead, визначене в PodSpec.

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

kubectl get pod test-pod -o jsonpath='{.spec.containers[*].resources.limits}'

Загальні запити контейнерів становлять 2000m CPU та 200MiB памʼяті:

map[cpu: 500m memory:100Mi] map[cpu:1500m memory:100Mi]

Порівняйте це з тим, що бачить вузол:

kubectl describe node | grep test-pod -B2

Вивід показує запити для 2250m CPU та 320MiB памʼяті. Запити включають накладні витрати Podʼа:

  Namespace    Name       CPU Requests  CPU Limits   Memory Requests  Memory Limits  AGE
  ---------    ----       ------------  ----------   ---------------  -------------  ---
  default      test-pod   2250m (56%)   2250m (56%)  320Mi (1%)       320Mi (1%)     36m

Перевірка обмежень cgroup Podʼа

Перевірте cgroup памʼяті Podʼа на вузлі, де працює робоче навантаження. У наступному прикладі використовується crictl на вузлі, який надає інтерфейс командного рядка для сумісних із CRI середовищ виконання контейнерів. Це складний приклад, що демонструє поведінку накладних витрат Podʼа, і не очікується, що користувачам потрібно буде безпосередньо перевіряти cgroups на вузлі.

Спочатку на конкретному вузлі визначте ідентифікатор Podʼа:

# Виконайте це на вузлі, де заплановано Pod
POD_ID="$(sudo crictl pods --name test-pod -q)"

З цього можна визначити шлях cgroup Podʼа:

# Виконайте це на вузлі, де заплановано Pod
sudo crictl inspectp -o=json $POD_ID | grep cgroupsPath

Отриманий шлях cgroup включає контейнер pause Podʼа. Cgroup рівня Podʼа розташована на одну теку вище.

  "cgroupsPath": "/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/7ccf55aee35dd16aca4189c952d83487297f3cd760f1bbf09620e206e7d0c27a"

У цьому конкретному випадку шлях cgroup Podʼа — kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2. Перевірте налаштування памʼяті cgroup рівня Podʼа:

# Виконайте це на вузлі, де заплановано Pod.
# Також змініть назву cgroup, щоб вона відповідала cgroup, виділеній для вашого Podʼа.
 cat /sys/fs/cgroup/memory/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/memory.limit_in_bytes

Це 320 MiB, як і очікувалося:

335544320

Спостережуваність

Деякі метрики kube_pod_overhead_* доступні у kube-state-metrics для ідентифікації використання накладних витрат Podʼа та спостереження за стабільністю робочих навантажень, які працюють з визначеними накладними витратами.

Що далі

9.5 - Готовність Podʼів до планування

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.30

Podʼи вважалися готовими до планування відразу після створення. Планувальник Kubernetes виконує всі необхідні дії для знаходження вузлів для розміщення всіх Podʼів, що очікують. Однак на практиці деякі Podʼи можуть перебувати в стані «бракує необхідних ресурсів» («miss-essential-resources») протягом тривалого періоду. Ці Podʼи фактично створюють непотрібне навантаження на планувальник (і на залежні від нього інтегратори, такі як Cluster AutoScaler).

Шляхом вказання/видалення поля .spec.schedulingGates для Podʼа ви можете контролювати, коли Pod готовий до розгляду для планування.

Налаштування schedulingGates Podʼа

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


stateDiagram-v2

    s1: Pod створено
    s2: Очікування планування Podʼа
    s3: Pod готовий до планування
    s4: Pod виконується
    if: шлюзи планування порожні?
    [*] --> s1
    s1 --> if
    s2 --> if: шлюз
планування
видалено if --> s2: ні if --> s3: так s3 --> s4 s4 --> [*] classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; class s1,s2,s3,s4,if k8s

Схема. schedulingGates Podʼа

Приклад використання

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

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  schedulingGates:
  - name: example.com/foo
  - name: example.com/bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.6

Після створення Podʼа ви можете перевірити його стан за допомогою:

kubectl get pod test-pod

Вивід показує, що він перебуває в стані SchedulingGated:

NAME       READY   STATUS            RESTARTS   AGE
test-pod   0/1     SchedulingGated   0          7s

Ви також можете перевірити його поле schedulingGates, запустивши:

kubectl get pod test-pod -o jsonpath='{.spec.schedulingGates}'

Вивід:

[{"name":"example.com/foo"},{"name":"example.com/bar"}]

Щоб повідомити планувальнику, що цей Pod готовий до планування, ви можете повністю видалити його schedulingGates шляхом повторного застосування зміненого маніфеста:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.6

Ви можете перевірити, чи очищено schedulingGates, виконавши:

kubectl get pod test-pod -o jsonpath='{.spec.schedulingGates}'

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

kubectl get pod test-pod -o wide

Враховуючи те, що test-pod не запитує жодних ресурсів CPU/памʼяті, очікується, що стан цього Podʼа перейде з попереднього SchedulingGated в Running:

NAME       READY   STATUS    RESTARTS   AGE   IP         NODE
test-pod   1/1     Running   0          15s   10.0.0.4   node-2

Спостережуваність

Метрика scheduler_pending_pods має нову мітку "gated", щоб відрізняти, чи були спроби запланувати Pod, але він був визначений як непридатний для планування, чи він явно позначений як не готовий для планування. Ви можете використовувати scheduler_pending_pods{queue="gated"} для перевірки результату метрики.

Змінювані директиви планування Podʼа

Ви можете змінювати директиви планування Podʼа, коли вони мають шлюзи планування, з певними обмеженнями. У загальних рисах, ви можете лише робити директиви планування Podʼа жорсткішими. Іншими словами, оновлені директиви призведуть до можливості розміщення Podʼів тільки на підмножині вузлів, з якими вони раніше мали збіг. Конкретніше, правила для оновлення директив планування Podʼа такі:

  1. Для .spec.nodeSelector дозволяються лише додавання. Якщо відсутнє, його можна встановити.

  2. Для spec.affinity.nodeAffinity, якщо nil, тоді дозволяється встановлення будь-чого.

  3. Якщо NodeSelectorTerms був порожнім, дозволено встановлення. Якщо не порожній, тоді дозволяються лише додавання NodeSelectorRequirements до matchExpressions або fieldExpressions, а зміни наявних matchExpressions і fieldExpressions не будуть дозволені. Це тому, що умови в .requiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms обʼєднуються через OR, тоді як вирази в nodeSelectorTerms[].matchExpressions та nodeSelectorTerms[].fieldExpressions обʼєднуються через AND.

  4. Для .preferredDuringSchedulingIgnoredDuringExecution всі оновлення дозволені. Це повʼязано з тим, що бажані умови не є авторитетними, і тому контролери політики не перевіряють ці умови.

Що далі

9.6 - Обмеження топології розміщення Podʼів

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

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

Мотивація

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

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

Під час масштабування та запуску більшої кількості Podʼів важливою стає інша проблема. Уявіть, що у вас є три вузли, на кожному з яких працює по пʼять Podʼів. Вузли мають достатню місткість для запуску такої кількості реплік; проте клієнти, які взаємодіють із цим робочим навантаженням, розподілені по трьох різних центрах обробки даних (або інфраструктурних зонах). Тепер вас менше турбує відмова одного вузла, але ви помічаєте, що затримка вища, ніж вам хотілося б, і ви платите за мережеві витрати, повʼязані з передаванням мережевого трафіку між різними зонами.

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

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

Поле topologySpreadConstraints

У API Pod є поле spec.topologySpreadConstraints. Використання цього поля виглядає так:

---
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  # Налаштувати обмеження топології розміщення
  topologySpreadConstraints:
    - maxSkew: <ціле число>
      minDomains: <ціле число> # необовʼязково
      topologyKey: <рядок>
      whenUnsatisfiable: <рядок>
      labelSelector: <обʼєкт>
      matchLabelKeys: <список> # необовʼязково; бета з v1.27
      nodeAffinityPolicy: [Honor|Ignore] # необовʼязково; бета з v1.26
      nodeTaintsPolicy: [Honor|Ignore] # необовʼязково; бета з v1.26
  ### інші поля Pod тут

Примітка:

Для заданої пари значень topologyKey та whenUnsatisfiable може бути лише один topologySpreadConstraint. Наприклад, якщо ви визначили topologySpreadConstraint, що використовує topologyKey "kubernetes.io/hostname" і значення whenUnsatisfiable "DoNotSchedule", ви можете додати інший topologySpreadConstraint для topologyKey "kubernetes.io/hostname" лише в тому випадку, якщо використаєте інше значення whenUnsatisfiable.

Додаткову інформацію про це поле можна отримати, виконавши команду kubectl explain Pod.spec.topologySpreadConstraints або звернувшись до розділу планування довідки API для Pod.

Визначення обмеження топології розміщення

Ви можете визначити один або кілька записів topologySpreadConstraints, щоб вказати kube-scheduler, як розмістити кожний вхідний Pod відносно наявних Podʼів у вашому кластері. Ці поля такі:

  • maxSkew описує ступінь нерівномірності розподілу Podʼів. Ви повинні вказати це поле, а його значення має бути більшим за нуль. Його семантика відрізняється залежно від значення whenUnsatisfiable:

    • якщо ви виберете whenUnsatisfiable: DoNotSchedule, то maxSkew визначає максимально допустиму різницю між кількістю відповідних Podʼів у цільовій топології та глобальним мінімумом (мінімальною кількістю відповідних Podʼів у прийнятному домені або нулем, якщо кількість прийнятних доменів менша за MinDomains). Наприклад, якщо у вас є 3 зони, де кількість відповідних Podʼів становить 2, 2 та 1, а maxSkew встановлено на 1, то глобальний мінімум дорівнює 1.
    • якщо ви виберете whenUnsatisfiable: ScheduleAnyway, планувальник надає вищий пріоритет топологіям, які допомогли б зменшити перекіс.
  • minDomains вказує мінімальну кількість прийнятних доменів. Це поле є необовʼязковим. Домен — це певний екземпляр топології. Прийнятний домен — це домен, вузли якого відповідають селектору вузлів.

    Примітка:

    До Kubernetes v1.30 поле minDomains було доступне лише за умови, що функціональну можливість MinDomainsInPodTopologySpread увімкнено (стандартно увімкнена починаючи з v1.28). У старіших кластерах Kubernetes її може бути явно вимкнено, або поле може бути недоступним.
    • Значення minDomains має бути більшим за 0, якщо його вказано. Ви можете вказати minDomains лише разом із whenUnsatisfiable: DoNotSchedule.
    • Коли кількість прийнятних доменів з відповідними ключами топології менша за minDomains, під час розміщення Podʼів за топологією глобальний мінімум вважається рівним 0, а потім виконується обчислення skew. Глобальний мінімум — це мінімальна кількість відповідних Podʼів у прийнятному домені або нуль, якщо кількість прийнятних доменів менша за minDomains.
    • Коли кількість прийнятних доменів з відповідними ключами топології дорівнює minDomains або більша за нього, це значення не впливає на планування.
    • Якщо ви не вказуєте minDomains, обмеження поводиться так, ніби minDomains дорівнює 1.
  • topologyKey — ключ міток вузла. Вузли, які мають мітку з цим ключем та однаковими значеннями, вважаються такими, що належать до однієї топології. Кожен екземпляр топології (іншими словами, пара <ключ, значення>) називається доменом. Планувальник намагатиметься розмістити збалансовану кількість Podʼів у кожному домені. Також ми визначаємо прийнятний домен як домен, вузли якого відповідають вимогам nodeAffinityPolicy та nodeTaintsPolicy.

  • whenUnsatisfiable вказує, що робити з Podʼом, якщо він не задовольняє обмеження топології розміщення:

    • DoNotSchedule (стандартно) вказує планувальнику не планувати його.
    • ScheduleAnyway вказує планувальнику все одно його планувати, надаючи пріоритет вузлам, що мінімізують перекіс.
  • labelSelector використовується для пошуку відповідних Podʼів. Podʼи, які відповідають цьому селектору міток, враховуються для визначення кількості Podʼів у відповідному топологічному домені. Докладніше дивіться в розділі Селектори міток.

  • matchLabelKeys — це список ключів міток Podʼа для вибору групи Podʼів, для яких буде обчислено перекіс розподілу. Під час створення Podʼа kube-apiserver використовує ці ключі для пошуку значень у мітках вхідного Podʼа, і ці мітки ключ-значення буде обʼєднано з наявним labelSelector. Один і той самий ключ не може бути одночасно в matchLabelKeys і в labelSelector. matchLabelKeys не можна встановити, якщо не встановлено labelSelector. Ключі, яких немає в мітках Podʼа, буде проігноровано. Значення null або порожній список означає відповідність лише labelSelector.

    Увага:

    Не рекомендується використовувати matchLabelKeys з мітками, які можуть бути оновлені безпосередньо в Podʼах. Навіть якщо ви безпосередньо редагуєте мітку Podʼа, вказану в matchLabelKeys (тобто ви редагуєте Pod, а не Deployment), kube-apiserver не відображає оновлення мітки в обʼєднаному labelSelector.

    З matchLabelKeys вам не потрібно оновлювати pod.spec між різними ревізіями. Контролеру/оператору достатньо встановити різні значення для того самого ключа мітки для різних ревізій. Наприклад, якщо ви налаштовуєте Deployment, ви можете використовувати мітку з ключем pod-template-hash, яка автоматично додається контролером Deployment, щоб розрізняти різні ревізії в одному Deployment.

        topologySpreadConstraints:
            - maxSkew: 1
              topologyKey: kubernetes.io/hostname
              whenUnsatisfiable: DoNotSchedule
              labelSelector:
                matchLabels:
                  app: foo
              matchLabelKeys:
                - pod-template-hash
    

    Примітка:

    Поле matchLabelKeys є полем рівня бета і стандартно увімкнене у 1.27. Ви можете вимкнути його, вимкнувши функціональну можливість MatchLabelKeysInPodTopologySpread.

    До v1.34 matchLabelKeys оброблявся неявно. Починаючи з v1.34, мітки ключ-значення, що відповідають matchLabelKeys, явно обʼєднуються з labelSelector. Ви можете вимкнути це й повернутися до попередньої поведінки, вимкнувши функціональну можливість MatchLabelKeysInPodTopologySpreadSelectorMerge у kube-apiserver.

  • nodeAffinityPolicy вказує, як ми будемо обробляти nodeAffinity/nodeSelector Podʼа під час обчислення перекосу топології розміщення Podʼів. Варіанти:

    • Honor: до розрахунків включаються лише вузли, які відповідають nodeAffinity/nodeSelector.
    • Ignore: nodeAffinity/nodeSelector ігноруються. До розрахунків включаються всі вузли.

    Якщо це значення є null, поведінка еквівалентна політиці Honor.

    Примітка:

    Поле nodeAffinityPolicy було полем рівня бета з v1.26 і стало загальнодоступним (GA) у v1.33. У бета-версії воно стандартно увімкнене, і ви можете вимкнути його, вимкнувши функціональну можливість NodeInclusionPolicyInPodTopologySpread.
  • nodeTaintsPolicy вказує, як ми будемо обробляти taints вузлів під час обчислення перекосу топології розміщення Podʼів. Варіанти:

    • Honor: включаються вузли без taints, а також вузли з taints, для яких вхідний Pod має toleration.
    • Ignore: taints вузла ігноруються. Включаються всі вузли.

    Якщо це значення є null, поведінка еквівалентна політиці Ignore.

    Примітка:

    Поле nodeTaintsPolicy було полем рівня бета з v1.26 і стало загальнодоступним (GA) у v1.33. У бета-версії воно стандартно увімкнене, і ви можете вимкнути його, вимкнувши функціональну можливість NodeInclusionPolicyInPodTopologySpread.

Коли Pod визначає більше одного topologySpreadConstraint, ці обмеження поєднуються за допомогою логічної операції AND: kube-scheduler шукає для вхідного Podʼа вузол, який задовольняє всі налаштовані обмеження.

Мітки вузлів

Обмеження топології розміщення ґрунтуються на мітках вузлів, які визначають топологічні домени кожного вузла. Наприклад, вузол може мати такі мітки:

  region: us-east-1
  zone: us-east-1a

Примітка:

Для стислості в цьому прикладі не використовуються відомі ключі міток topology.kubernetes.io/zone та topology.kubernetes.io/region. Однак рекомендується використовувати саме ці зареєстровані ключі міток, а не приватні (некваліфіковані) ключі міток region та zone, які використовуються тут.

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

Припустимо, у вас є кластер із 4 вузлами з такими мітками:

NAME    STATUS   ROLES    AGE     VERSION   LABELS
node1   Ready    <none>   4m26s   v1.16.0   node=node1,zone=zoneA
node2   Ready    <none>   3m58s   v1.16.0   node=node2,zone=zoneA
node3   Ready    <none>   3m17s   v1.16.0   node=node3,zone=zoneB
node4   Ready    <none>   2m43s   v1.16.0   node=node4,zone=zoneB

Тоді кластер логічно виглядає так:

graph TB
    subgraph "zoneB"
        n3(Node3)
        n4(Node4)
    end
    subgraph "zoneA"
        n1(Node1)
        n2(Node2)
    end

    classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
    classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
    classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
    class n1,n2,n3,n4 k8s;
    class zoneA,zoneB cluster;

Узгодженість

Вам слід встановити однакові обмеження топології розміщення Podʼів для всіх Podʼів у групі.

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

Вам потрібен механізм, що забезпечує узгоджені мітки на всіх вузлах топологічного домену (наприклад, регіону хмарного провайдера). Щоб вам не доводилося додавати мітки до вузлів вручну, більшість кластерів автоматично заповнюють відомі мітки, такі як kubernetes.io/hostname. Перевірте, чи підтримує це ваш кластер.

Приклади обмежень топології розміщення

Приклад: одне обмеження топології розміщення

Припустимо, у вас є кластер із 4 вузлами, де 3 Podʼи з міткою foo: bar розташовані на вузлах node1, node2 та node3 відповідно:

graph BT
    subgraph "zoneB"
        p3(Pod) --> n3(Node3)
        n4(Node4)
    end
    subgraph "zoneA"
        p1(Pod) --> n1(Node1)
        p2(Pod) --> n2(Node2)
    end

    classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
    classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
    classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
    class n1,n2,n3,n4,p1,p2,p3 k8s;
    class zoneA,zoneB cluster;

Якщо ви хочете, щоб вхідний Pod був рівномірно розподілений разом із наявними Podʼами по зонах, ви можете використати маніфест, схожий на такий:

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

У цьому маніфесті topologyKey: zone означає, що рівномірний розподіл буде застосовуватися лише до вузлів, які мають мітку zone: <будь-яке значення> (вузли, які не мають мітки zone, буде пропущено). Поле whenUnsatisfiable: DoNotSchedule вказує планувальнику залишити вхідний Pod в очікуванні, якщо планувальник не може знайти спосіб задовольнити обмеження.

Якщо планувальник розмістить цей вхідний Pod у зоні A, розподіл Podʼів стане [3, 1]. Це означає, що фактичний перекіс становить 2 (обчислюється як 3 - 1), що порушує maxSkew: 1. Щоб задовольнити обмеження та умови цього прикладу, вхідний Pod може бути розміщений лише на вузлі в зоні B:

graph BT
    subgraph "zoneB"
        p3(Pod) --> n3(Node3)
        p4(mypod) --> n4(Node4)
    end
    subgraph "zoneA"
        p1(Pod) --> n1(Node1)
        p2(Pod) --> n2(Node2)
    end

    classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
    classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
    classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
    class n1,n2,n3,n4,p1,p2,p3 k8s;
    class p4 plain;
    class zoneA,zoneB cluster;

або

graph BT
    subgraph "zoneB"
        p3(Pod) --> n3(Node3)
        p4(mypod) --> n3
        n4(Node4)
    end
    subgraph "zoneA"
        p1(Pod) --> n1(Node1)
        p2(Pod) --> n2(Node2)
    end

    classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
    classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
    classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
    class n1,n2,n3,n4,p1,p2,p3 k8s;
    class p4 plain;
    class zoneA,zoneB cluster;

Ви можете змінити специфікацію Podʼа, щоб вона відповідала різним вимогам:

  • Змініть maxSkew на більше значення, наприклад 2, щоб вхідний Pod також можна було розмістити в зоні A.
  • Змініть topologyKey на node, щоб рівномірно розподілити Podʼи по вузлах, а не по зонах. У наведеному вище прикладі, якщо maxSkew залишиться 1, вхідний Pod може бути розміщений лише на вузлі node4.
  • Змініть whenUnsatisfiable: DoNotSchedule на whenUnsatisfiable: ScheduleAnyway, щоб вхідний Pod завжди був придатним для планування (за умови, що вимоги інших API планування задоволено). Однак перевага надається розміщенню в топологічному домені, який має менше відповідних Podʼів. (Памʼятайте, що ця перевага нормалізується разом з іншими внутрішніми пріоритетами планування, такими як коефіцієнт використання ресурсів.)

Приклад: кілька обмежень топології розміщення

Цей приклад базується на попередньому. Припустимо, у вас є кластер із 4 вузлами, де 3 наявні Podʼи з міткою foo: bar розташовані на вузлах node1, node2 і node3 відповідно:

graph BT
    subgraph "zoneB"
        p3(Pod) --> n3(Node3)
        n4(Node4)
    end
    subgraph "zoneA"
        p1(Pod) --> n1(Node1)
        p2(Pod) --> n2(Node2)
    end

    classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
    classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
    classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
    class n1,n2,n3,n4,p1,p2,p3 k8s;
    class p4 plain;
    class zoneA,zoneB cluster;

Ви можете поєднати два обмеження топології розміщення, щоб контролювати розподіл Podʼів як за вузлами, так і за зонами:

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  - maxSkew: 1
    topologyKey: node
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

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

Приклад: конфліктні обмеження топології розміщення

Кілька обмежень можуть призвести до конфліктів. Припустимо, у вас є кластер із 3 вузлами у 2 зонах:

graph BT
    subgraph "zoneB"
        p4(Pod) --> n3(Node3)
        p5(Pod) --> n3
    end
    subgraph "zoneA"
        p1(Pod) --> n1(Node1)
        p2(Pod) --> n1
        p3(Pod) --> n2(Node2)
    end

    classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
    classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
    classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
    class n1,n2,n3,n4,p1,p2,p3,p4,p5 k8s;
    class zoneA,zoneB cluster;

Якщо ви застосуєте two-constraints.yaml (маніфест із попереднього прикладу) до цього кластера, ви побачите, що Pod mypod залишається у стані Pending. Це відбувається тому, що для задоволення першого обмеження Pod mypod може бути розміщений лише в зоні B; тоді як згідно з другим обмеженням Pod mypod може бути запланований лише на вузол node2. Перетин двох обмежень дає порожню множину, і планувальник не може розмістити Pod.

Щоб вийти з цієї ситуації, ви можете або збільшити значення maxSkew, або змінити одне з обмежень, щоб воно використовувало whenUnsatisfiable: ScheduleAnyway. Залежно від обставин, ви також можете вирішити видалити наявний Pod вручну — наприклад, якщо ви зʼясовуєте, чому розгортання виправлення помилки не просувається.

Взаємодія зі спорідненістю вузлів та селекторами вузлів

Планувальник пропустить вузли, що не відповідають, в обчисленнях перекосу, якщо у вхідного Podʼа визначено spec.nodeSelector або spec.affinity.nodeAffinity.

Приклад: обмеження топології розміщення зі спорідненістю вузлів

Припустимо, у вас є кластер із 5 вузлами, розташованими в зонах від A до C:

graph BT
    subgraph "zoneB"
        p3(Pod) --> n3(Node3)
        n4(Node4)
    end
    subgraph "zoneA"
        p1(Pod) --> n1(Node1)
        p2(Pod) --> n2(Node2)
    end

classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n1,n2,n3,n4,p1,p2,p3 k8s;
class p4 plain;
class zoneA,zoneB cluster;
graph BT
    subgraph "zoneC"
        n5(Node5)
    end

classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
class n5 k8s;
class zoneC cluster;

і ви знаєте, що зону C потрібно виключити. У цьому випадку ви можете скласти маніфест, як наведено нижче, щоб Pod mypod був розміщений у зоні B, а не в зоні C. Так само Kubernetes враховує і spec.nodeSelector.

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: zone
            operator: NotIn
            values:
            - zoneC
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

Неявні домовленості

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

  • Відповідними кандидатами можуть бути лише ті Podʼи, що перебувають у тому самому просторі імен, що й вхідний Pod.

  • Планувальник розглядає лише ті вузли, у яких одночасно присутні всі topologySpreadConstraints[*].topologyKey. Вузли, у яких відсутній будь-який з цих topologyKeys, пропускаються. Це означає, що:

    1. будь-які Podʼи, розташовані на цих пропущених вузлах, не впливають на обчислення maxSkew — у наведеному вище прикладі, припустимо, що вузол node1 не має мітки "zone", тоді 2 Podʼи буде проігноровано, тому вхідний Pod буде заплановано в зону A.
    2. вхідний Pod не має шансів бути запланованим на такі вузли — у наведеному вище прикладі, припустимо, що вузол node5 має мітку з помилкою zone-typo: zoneC (і не має встановленої мітки zone). Після приєднання вузла node5 до кластера його буде пропущено, і Podʼи цього робочого навантаження не плануватимуться на нього.
  • Враховуйте, що станеться, якщо topologySpreadConstraints[*].labelSelector вхідного Podʼа не відповідає його власним міткам. У наведеному вище прикладі, якщо ви видалите мітки вхідного Podʼа, його все ще можна розмістити на вузлах у зоні B, оскільки обмеження все ще виконуються. Проте після цього розміщення ступінь незбалансованості кластера залишається без змін — зона A все ще має 2 Podʼи з міткою foo: bar, а зона B має 1 Pod з міткою foo: bar. Якщо це не те, що ви очікуєте, оновіть topologySpreadConstraints[*].labelSelector робочого навантаження, щоб він відповідав міткам у шаблоні Podʼа.

Стандартні обмеження на рівні кластера

Для кластера можна встановити стандартні обмеження топології розміщення. Стандартні обмеження топології розміщення застосовуються до Podʼа тоді й лише тоді, коли:

  • Він не визначає жодних обмежень у своєму .spec.topologySpreadConstraints.
  • Він належить до Service, ReplicaSet, StatefulSet або ReplicationController.

Стандартні обмеження можна встановити як частину аргументів втулка PodTopologySpread у профілі планування. Обмеження задаються за допомогою того самого API, що описаний вище, за винятком того, що labelSelector має бути порожнім. Селектори обчислюються на основі Services, ReplicaSets, StatefulSets або ReplicationControllers, до яких належить Pod.

Приклад конфігурації може виглядати так:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
    pluginConfig:
      - name: PodTopologySpread
        args:
          defaultConstraints:
            - maxSkew: 1
              topologyKey: topology.kubernetes.io/zone
              whenUnsatisfiable: ScheduleAnyway
          defaultingType: List

Вбудовані стандартні обмеження

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

Якщо ви не налаштовуєте жодних стандартних обмежень на рівні кластера для топології розміщення Podʼів, то kube-scheduler діє так, ніби ви вказали такі стандартні обмеження топології:

defaultConstraints:
  - maxSkew: 3
    topologyKey: "kubernetes.io/hostname"
    whenUnsatisfiable: ScheduleAnyway
  - maxSkew: 5
    topologyKey: "topology.kubernetes.io/zone"
    whenUnsatisfiable: ScheduleAnyway

Також застарілий втулок SelectorSpread, який забезпечує еквівалентну поведінку, стандартно вимкнений.

Примітка:

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

Якщо ви не очікуєте, що на ваших вузлах будуть встановлені обидві мітки kubernetes.io/hostname та topology.kubernetes.io/zone, визначте власні обмеження замість використання стандартних обмежень Kubernetes.

Якщо ви не хочете використовувати стандартні обмеження топології розміщення Podʼів для вашого кластера, ви можете вимкнути їх, встановивши defaultingType у List і залишивши порожнім defaultConstraints у конфігурації втулка PodTopologySpread:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
    pluginConfig:
      - name: PodTopologySpread
        args:
          defaultConstraints: []
          defaultingType: List

Порівняння з podAffinity та podAntiAffinity

У Kubernetes спорідненість та антиспорідненість між Podʼами контролюють те, як Podʼи плануються відносно один одного — щільніше або більш розрізнено.

podAffinity
притягує Podʼи; ви можете спробувати упакувати будь-яку кількість Podʼів у відповідні топологічні домени.
podAntiAffinity
відштовхує Podʼи. Якщо ви встановите режим requiredDuringSchedulingIgnoredDuringExecution, то в один топологічний домен може бути запланований лише один Pod; якщо ви виберете preferredDuringSchedulingIgnoredDuringExecution, то втратите можливість примусово забезпечити дотримання обмеження.

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

Докладнішу інформацію див. у розділі Motivation пропозиції щодо вдосконалення про обмеження топології розміщення Podʼів.

Відомі обмеження

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

    Ви можете використовувати інструмент, такий як Descheduler, для перебалансування розподілу Podʼів.

  • Відповідні Podʼи на вузлах із taints враховуються. Див. Issue 80921.

  • Планувальник не має попереднього знання про всі зони або інші топологічні домени кластера. Вони визначаються на основі наявних вузлів у кластері. Це може призвести до проблем у кластерах з автомасштабуванням, коли пул вузлів (або група вузлів) масштабується до нуля вузлів, а ви очікуєте масштабування кластера, оскільки в цьому випадку ці топологічні домени не будуть враховуватися, поки в них не зʼявиться хоча б один вузол.

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

  • Podʼи, які не відповідають власному labelSelector, створюють «Podʼи-привиди» (ghost pods). Якщо мітки Podʼа не відповідають labelSelector у його обмеженні топології розміщення, Pod не враховує себе в обчисленнях розподілу. Це означає:

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

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

Що далі

  • У статті блогу Introducing PodTopologySpread докладно пояснюється maxSkew, а також розглядаються деякі приклади розширеного використання.
  • Прочитайте розділ планування довідки API для Pod.

9.7 - Taints та Tolerations

Спорідненість вузлів (node affinity) — це властивість Podʼів, яка притягує їх до набору вузлів (або як перевага, або як жорстка вимога). Taints є протилежністю — вони дозволяють вузлу відштовхувати набір Podʼів.

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

Taints та tolerations працюють разом, щоб Podʼи не планувалися на непридатні вузли. На вузол накладається один або кілька taints; це означає, що вузол не повинен приймати жодних Podʼів, які не толерують ці taints.

Концепції

Ви додаєте taint до вузла за допомогою kubectl taint. Наприклад,

kubectl taint nodes node1 key1=value1:NoSchedule

додає taint до вузла node1. Taint має ключ key1, значення value1 і ефект taint NoSchedule. Це означає, що жодний Pod не зможе розміститись на node1, якщо він не має відповідної toleration.

Щоб видалити taint, доданий командою вище, можна виконати:

kubectl taint nodes node1 key1=value1:NoSchedule-

Ви вказуєте toleration для Podʼа в PodSpec. Обидві наступні tolerations «відповідають» taint, створеному за допомогою команди kubectl taint вище, тому Pod з будь-якою з них зможе розміститись на node1:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
tolerations:
- key: "key1"
  operator: "Exists"
  effect: "NoSchedule"

Стандартний планувальник Kubernetes бере до уваги taints та tolerations під час вибору вузла для запуску певного Podʼа. Проте, якщо ви вручну вказуєте .spec.nodeName для Podʼа, ця дія оминає планувальник; Pod тоді привʼязується до вузла, на який ви його призначили, навіть якщо на вибраному вами вузлі є taints типу NoSchedule. Якщо це трапиться і на вузлі також встановлено taint типу NoExecute, kubelet виселить Pod, якщо не встановлено відповідну toleration.

Ось приклад Podʼа, у якого визначено деякі tolerations:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

Стандартне значення для operator — Equal.

Toleration «відповідає» taint, якщо ключі однакові та ефекти однакові, і:

  • operator — Exists (у цьому випадку не слід вказувати value), або
  • operator — Equal, а значення мають бути рівні.

Примітка:

Є два спеціальних випадки:

Якщо key порожній, то operator має бути Exists, що відповідає всім ключам і значенням. Зверніть увагу, що effect все одно має збігатися.

Порожній effect відповідає всім ефектам з ключем key1.

У наведеному вище прикладі використовувався effect NoSchedule. Також можна використовувати effect PreferNoSchedule.

Дозволені значення для поля effect:

NoExecute
Це впливає на Podʼи, які вже запущені на вузлі, таким чином:
  • Podʼи, які не толерують taint, негайно виселяються
  • Podʼи, які толерують taint, не вказуючи tolerationSeconds в їхній специфікації toleration, залишаються привʼязаними назавжди
  • Podʼи, які толерують taint із вказаним tolerationSeconds, залишаються привʼязаними протягом зазначеного часу. Після закінчення цього часу контролер життєвого циклу вузла виселяє Podʼи з вузла.
NoSchedule
На вузол із taint не буде заплановано нові Podʼи, якщо вони не мають відповідної toleration. Podʼи, які вже працюють на вузлі, не виселяються.
PreferNoSchedule
PreferNoSchedule — це «preference» або «soft» варіант NoSchedule. Панель управління спробує уникнути розміщення на вузлі Podʼа, який не толерує taint, але це не гарантовано.

На один вузол можна накласти кілька taints, а на один Pod — кілька tolerations. Kubernetes обробляє кілька taints і tolerations подібно до фільтра: спочатку беруться всі taints вузла, потім ігноруються ті, для яких Pod має відповідну toleration; решта неігнорованих taints мають зазначені ефекти на Pod. Зокрема,

  • якщо є принаймні один неігнорований taint з ефектом NoSchedule, тоді Kubernetes не буде планувати Pod на цей вузол
  • якщо немає неігнорованих taints з ефектом NoSchedule, але є принаймні один неігнорований taint з ефектом PreferNoSchedule, тоді Kubernetes спробує не планувати Pod на цей вузол
  • якщо є принаймні один неігнорований taint з ефектом NoExecute, тоді Pod буде виселено з вузла (якщо він вже працює на вузлі), і він не буде плануватися на вузол (якщо він ще не працює на вузлі).

Наприклад, уявіть, що ви накладаєте taint на вузол таким чином

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

І Pod має дві tolerations:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

У цьому випадку Pod не зможе плануватися на вузол, оскільки відсутня toleration, яка відповідає третьому taint. Але він зможе продовжувати працювати, якщо він вже працює на вузлі, коли до нього додається taint, оскільки третій taint — єдиний з трьох, який не толерується Podʼом.

Зазвичай, якщо до вузла додається taint з ефектом NoExecute, то будь-які Podʼи, які не толерують taint, будуть негайно виселені, а Podʼи, які толерують taint, ніколи не будуть виселені. Однак toleration з ефектом NoExecute може вказувати необовʼязкове поле tolerationSeconds, яке визначає, як довго Pod буде привʼязаний до вузла після додавання taint. Наприклад,

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

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

Оператори числового порівняння

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.35; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the TaintTolerationComparisonOperators feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Окрім Equal та Exists, ви можете використовувати числові оператори порівняння (Gt та Lt) для зіставлення taints із цілими значеннями. Це корисно для планування на основі порогових значень, наприклад для зіставлення вузлів за рівнем надійності або рівнем SLA.

  • Gt спрацьовує, коли значення taint більше за значення toleration.
  • Lt спрацьовує, коли значення taint менше за значення toleration.

Для числових операторів значення toleration та taint повинні бути дійсними цілими числами. Якщо одне зі значень не може бути розібране як ціле число, toleration не збігається.

Примітка:

Коли ви створюєте Pod, який використовує оператори tolerations Gt або Lt, API-сервер перевіряє, чи є значення toleration дійсними цілими числами. Значення taint на вузлах не перевіряються під час реєстрації вузлів. Якщо вузол має нечислове значення taint (наприклад, servicelevel.organization.example/agreed-service-level=high:NoSchedule), Podʼи з числовими операторами порівняння не відповідатимуть цьому taint і не зможуть плануватися на цьому вузлі.

Наприклад, якщо на вузли накладено taint зі значенням, що представляє угоду про рівень обслуговування (SLA):

kubectl taint nodes node1 servicelevel.organization.example/agreed-service-level=950:NoSchedule

Pod може толерувати вузли з SLA більше 900:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-numeric-toleration
  labels:
    env: test
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent
  tolerations:
    - key: "servicelevel.organization.example/agreed-service-level"
      operator: "Gt"
      value: "900"
      effect: "NoSchedule"

Ця toleration відповідає taint на node1, оскільки 950 > 900 (значення taint більше за значення toleration для оператора Gt). Аналогічно, ви можете використовувати оператор Lt для зіставлення taints, де значення taint менше за значення toleration:

tolerations:
- key: "servicelevel.organization.example/agreed-service-level"
  operator: "Lt"
  value: "1000"
  effect: "NoSchedule"

Примітка:

Під час використання числових операторів порівняння:

  • Значення toleration та taint повинні бути дійсними 64-бітними цілими числами зі знаком (нульові цифри на початку (наприклад, "0550") не допускаються).
  • Якщо значення не може бути розібране як ціле число, toleration не збігається.
  • Числові оператори працюють з усіма ефектами taint: NoSchedule, PreferNoSchedule та NoExecute.
  • Для PreferNoSchedule з числовими операторами: якщо toleration Podʼа не задовольняє числове порівняння (наприклад, значення taint < значення toleration під час використання Gt), планувальник надає вузлу нижчий пріоритет, але все одно може планувати там, якщо немає кращих варіантів.

Попередження:

Перед вимкненням функціональної можливості TaintTolerationComparisonOperators:

  • Ви повинні ідентифікувати всі робочі навантаження, що використовують оператори Gt або Lt, щоб уникнути гарячих циклів контролера.
  • Оновіть усі шаблони контролерів робочих навантажень, щоб замість них використовувати оператори Equal або Exists.
  • Видаліть усі Podʼи в стані очікування, які використовують оператори Gt або Lt.
  • Стежте за метрикою apiserver_request_total, щоб виявити сплески помилок перевірки.

Приклади використання

Taints та tolerations є гнучким способом спрямовувати Podʼи геть від вузлів або виселяти Podʼи, які не повинні працювати. Деякі з варіантів використання:

  • Виділені вузли: Якщо ви хочете виділити певний набір вузлів для виключного використання конкретним набором користувачів, ви можете додати taint на ці вузли (наприклад, kubectl taint nodes nodename dedicated=groupName:NoSchedule) і потім додати відповідну toleration до їхніх Podʼів (це найпростіше зробити за допомогою власного контролера допуску). Podʼам із tolerations буде дозволено використовувати вузли з taints (виділені), а також будь-які інші вузли в кластері. Якщо ви хочете виділити вузли для них та забезпечити, щоб вони використовували лише виділені вузли, то вам слід додатково додати мітку, аналогічну taint, до того ж набору вузлів (наприклад, dedicated=groupName), і контролер допуску повинен додатково додати спорідненість вузлів, щоб вимагати, щоб Podʼи могли плануватися лише на вузли з міткою dedicated=groupName.

  • Вузли зі спеціальним обладнанням: У кластері, де невелика підмножина вузлів має спеціалізоване обладнання (наприклад, GPU), бажано утримувати Podʼи, які не потребують спеціалізованого обладнання, поза цими вузлами, щоб залишити місце для Podʼів, що надійдуть пізніше й дійсно потребують спеціалізованого обладнання. Це можна зробити, накладаючи taints на вузли зі спеціалізованим обладнанням (наприклад, kubectl taint nodes nodename special=true:NoSchedule або kubectl taint nodes nodename special=true:PreferNoSchedule) і додаючи відповідну toleration до Podʼів, які використовують спеціалізоване обладнання. Як і у випадку з виділеними вузлами, найпростіше застосовувати tolerations за допомогою власного контролера допуску. Наприклад, рекомендується використовувати Розширені ресурси для представлення спеціального обладнання, накладати на вузли зі спеціальним обладнанням taint з назвою розширеного ресурсу і запускати контролер допуску ExtendedResourceToleration. Тепер, оскільки на вузлах є taints, жоден Pod без toleration не буде плануватися на них. Але коли ви надсилаєте Pod, який запитує розширений ресурс, контролер допуску ExtendedResourceToleration автоматично додасть правильну toleration до Podʼа і цей Pod буде плануватися на вузли зі спеціальним обладнанням. Це забезпечить, що ці вузли зі спеціальним обладнанням виділені для Podʼів, які запитують таке обладнання, і вам не потрібно вручну додавати tolerations до ваших Podʼів.

  • Виселення на основі taint: Поведінка виселення, що налаштовується для кожного Podʼа, коли є проблеми з вузлом, яка описана в наступному розділі.

Виселення на основі taint

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.18

Контролер вузлів автоматично накладає taint на вузол, коли виконуються певні умови. Вбудованими є такі taints:

  • node.kubernetes.io/not-ready: Вузол не готовий. Це відповідає тому, що NodeCondition Ready має значення "False".
  • node.kubernetes.io/unreachable: Вузол недоступний для контролера вузлів. Це відповідає тому, що NodeCondition Ready має значення "Unknown".
  • node.kubernetes.io/memory-pressure: На вузлі є тиск на памʼять.
  • node.kubernetes.io/disk-pressure: На вузлі є тиск на диск.
  • node.kubernetes.io/pid-pressure: На вузлі є тиск на PID.
  • node.kubernetes.io/network-unavailable: Мережа вузла недоступна.
  • node.kubernetes.io/unschedulable: Вузол непридатний для планування.
  • node.cloudprovider.kubernetes.io/uninitialized: Коли kubelet запускається із «зовнішнім» хмарним провайдером, цей taint накладається на вузол, щоб позначити його як непридатний до використання. Після того як контролер з cloud-controller-manager ініціалізує цей вузол, kubelet видаляє цей taint.

Якщо вузол потрібно очистити, контролер вузлів або kubelet додає відповідні taints з ефектом NoExecute. Цей ефект стандартно додається для taints node.kubernetes.io/not-ready та node.kubernetes.io/unreachable. Якщо стан несправності повертається до норми, kubelet або контролер вузлів можуть видалити відповідні taints.

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

Примітка:

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

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

Наприклад, ви можете довго зберігати застосунок з великою кількістю локального стану, привʼязаного до вузла, у разі розділу мережі, сподіваючись, що розділ відновиться, і, таким чином, можна уникнути виселення Podʼа. Toleration, яку ви встановили для цього Podʼа, може виглядати так:

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

Примітка:

Kubernetes автоматично додає toleration для node.kubernetes.io/not-ready та node.kubernetes.io/unreachable з tolerationSeconds=300, якщо тільки ви або контролер не встановили ці tolerations явно.

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

Podʼи DaemonSet створюються з tolerations NoExecute для таких taints без tolerationSeconds:

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

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

Примітка:

Контролер вузлів відповідав за додавання taints до вузлів і виселення Podʼів. Але після 1.29 реалізація виселення на основі taint була переміщена з контролера вузлів в окремий і незалежний компонент під назвою taint-eviction-controller. Користувачі можуть за бажанням вимкнути виселення на основі taint, встановивши --controllers=-taint-eviction-controller в kube-controller-manager.

Накладання taints на вузли за станами

Панель управління, використовуючи контролер вузла, автоматично створює taints з ефектом NoSchedule для станів вузла.

Планувальник перевіряє taints, а не стани вузла, коли він приймає рішення про планування. Це забезпечує те, що стани вузла не впливають безпосередньо на планування. Наприклад, якщо стан вузла DiskPressure активний, панель управління додає taint node.kubernetes.io/disk-pressure і не планує нові Podʼи на уражений вузол. Якщо стан вузла MemoryPressure активний, панель управління додає taint node.kubernetes.io/memory-pressure.

Ви можете ігнорувати стани вузла для новостворених Podʼів, додавши відповідні tolerations Podʼів. Панель управління також додає toleration node.kubernetes.io/memory-pressure на Podʼи, які мають клас якості обслуговування інший, ніж BestEffort. Це тому, що Kubernetes вважає Podʼи у класах QoS Guaranteed або Burstable (навіть Podʼи без встановленого запиту на памʼять) здатними впоратися з тиском на памʼять, тоді як нові Podʼи BestEffort не плануються на уражений вузол.

Контролер DaemonSet автоматично додає наступні tolerations NoSchedule для всіх демонів, щоб запобігти порушенню роботи DaemonSets.

  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/disk-pressure
  • node.kubernetes.io/pid-pressure (1.14 або пізніше)
  • node.kubernetes.io/unschedulable (1.10 або пізніше)
  • node.kubernetes.io/network-unavailable (тільки для мережі хоста)

Додавання цих tolerations забезпечує сумісність з попередніми версіями. Ви також можете додавати довільні tolerations до DaemonSets.

Taints та tolerations пристроїв

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

Що далі

9.8 - Фреймворк планування

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.19

Фреймворк планування — це архітектура на основі втулків для планувальника Kubernetes. Вона складається з набору API «втулків», які компілюються безпосередньо в планувальник. Ці API дозволяють реалізувати більшість можливостей планування у вигляді втулків, зберігаючи при цьому «ядро» планування легким та зручним у підтримці. Для отримання більш технічної інформації про дизайн фреймворку зверніться до проєктної пропозиції фреймворку планування.

Робочий процес фреймворку

Фреймворк планування визначає кілька точок розширення. Втулки планувальника реєструються для виклику в одній або кількох таких точках розширення. Деякі з цих втулків можуть змінювати рішення щодо планування, а деякі є суто інформаційними.

Кожна спроба запланувати один Pod розділяється на дві фази: цикл планування та цикл привʼязки.

Цикл планування та цикл привʼязки

Цикл планування вибирає вузол для Podʼа, а цикл привʼязки застосовує це рішення до кластера. Разом цикл планування та цикл привʼязки називаються «контекстом планування».

Цикли планування виконуються послідовно, тоді як цикли привʼязки можуть виконуватися паралельно.

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

Інтерфейси

Наступне зображення показує контекст планування Podʼа та інтерфейси, які надає фреймворк планування.

Один втулок може реалізовувати кілька інтерфейсів для виконання складніших завдань або завдань зі збереженням стану.

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

Точки розширення фреймворку планування

PreEnqueue

Ці втулки викликаються перед додаванням Podʼів до внутрішньої активної черги, де Podʼи відзначаються як готові до планування.

Тільки коли всі втулки PreEnqueue повертають Success, Pod допускається до активної черги. В іншому випадку він поміщається у внутрішній список непридатних для планування Podʼів і не отримує стан Unschedulable.

Для отримання докладнішої інформації про те, як працюють внутрішні черги планувальника, прочитайте Черга планування в kube-scheduler.

EnqueueExtension

EnqueueExtension — це інтерфейс, де втулок може контролювати, чи потрібно повторно спробувати планування Podʼів, відхилених цим втулком, на основі змін у кластері. Втулки, які реалізують PreEnqueue, PreFilter, Filter, Reserve або Permit, повинні реалізувати цей інтерфейс.

QueueingHint

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.34; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.34. It was first available in the v1.28 release.

QueueingHint — це функція зворотного виклику для вирішення того, чи можна повторно поставити Pod до активної черги або черги backoff. Вона виконується кожного разу, коли в кластері відбувається певний вид події або зміна. Коли QueueingHint виявляє, що подія може зробити Pod придатним для планування, Pod поміщається в активну чергу або чергу backoff, щоб планувальник повторно спробував запланувати Pod.

QueueSort

Ці втулки використовуються для сортування Podʼів у черзі планування. Втулок сортування черги, по суті, надає функцію Less(Pod1, Pod2). Одночасно може бути увімкнений лише один втулок сортування черги.

PreFilter

Ці втулки використовуються для попередньої обробки інформації про Pod або для перевірки певних умов, яким мають відповідати кластер або Pod. Якщо втулок PreFilter повертає помилку, цикл планування переривається.

Filter

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

PostFilter

Ці втулки викликаються після фази фільтрації, але лише тоді, коли не було знайдено жодного придатного вузла для Podʼа. Втулки викликаються в налаштованому порядку. Якщо будь-який втулок postFilter позначає вузол як Schedulable, інші втулки не будуть викликані. Типовою реалізацією PostFilter є витіснення, яка намагається зробити Pod придатним для планування, витісняючи інші Podʼи.

PreScore

Ці втулки використовуються для виконання «попередньої оцінки», яка генерує спільний стан для використання втулками Score. Якщо втулок попередньої оцінки повертає помилку, цикл планування переривається.

Score

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

Оцінка місткості

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно увімкнено)

У версії 1.32 для підтримки сховищ зі статичним наданням використовувалася функціональна можливість VolumeCapacityPriority. Починаючи з версії 1.33, нова функціональна можливість StorageCapacityScoring замінює стару VolumeCapacityPriority з додатковою підтримкою сховищ з динамічним наданням. Коли StorageCapacityScoring увімкнено, втулок VolumeBinding у kube-scheduler розширено, щоб оцінювати вузли на основі місткості сховища на кожному з них. Ця можливість застосовується до томів CSI, які підтримують місткість сховища, включно з локальним сховищем, яке підтримується драйвером CSI.

NormalizeScore

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

Наприклад, припустимо, що втулок BlinkingLightScorer ранжує вузли на основі того, скільки лампочок, що блимають, в них є.

func ScoreNode(_ *v1.pod, n *v1.Node) (int, error) {
    return getBlinkingLightCount(n)
}

Однак максимальна кількість лампочок, що блимають, може бути невеликою порівняно з NodeScoreMax. Щоб виправити це, BlinkingLightScorer також повинен реєструватися для цієї точки розширення.

func NormalizeScores(scores map[string]int) {
    highest := 0
    for _, score := range scores {
        highest = max(highest, score)
    }
    for node, score := range scores {
        scores[node] = score*NodeScoreMax/highest
    }
}

Якщо будь-який втулок NormalizeScore повертає помилку, цикл планування припиняється.

Примітка:

Втулки, які бажають виконати «pre-reserve», повинні використовувати точку розширення NormalizeScore.

Reserve

Втулок, який реалізує інтерфейс Reserve, має два методи, а саме Reserve та Unreserve, які підтримують дві інформаційні фази планування, які називаються Reserve та Unreserve, відповідно. Втулки, які підтримують стан часу виконання (так звані «втулки зі станом»), повинні використовувати ці фази, щоб отримувати від планувальника сповіщення про те, що ресурси на вузлі резервуються або звільняються для вказаного Podʼа.

Фаза резервування (Reserve) відбувається перед тим, як планувальник фактично привʼязує Pod до призначеного вузла. Вона існує для запобігання станам гонитви, поки планувальник чекає на успішну привʼязку. Метод Reserve кожного втулка Reserve може бути успішним або невдалим; якщо один виклик методу Reserve не вдасться, наступні втулки не виконуються, і фаза резервування вважається невдалою. Якщо метод Reserve всіх втулків успішний, фаза резервування вважається успішною, виконується решта циклу планування та циклу привʼязки.

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

Увага:

Реалізація методу Unreserve у втулках Reserve повинна бути ідемпотентною і не може зазнавати невдачі.

Permit

Втулки Permit викликаються в кінці циклу планування для кожного Podʼа, щоб запобігти або затримати привʼязку до вузла-кандидата. Втулок дозволу може робити одну з трьох речей:

  1. approve
    Якщо всі втулки Permit схвалюють Pod, він відправляється для привʼязки.

  2. deny
    Якщо будь-який втулок Permit відхиляє Pod, він повертається до черги планування. Це спричинить запуск фази Unreserve у втулках Reserve.

  3. wait (із тайм-аутом)
    Якщо втулок Permit повертає «wait», тоді Pod залишається у внутрішньому списку Podʼів, що «очікують», і цикл привʼязки цього Podʼа починається, але безпосередньо блокується, поки він не буде схвалений. Якщо виникає тайм-аут, wait стає deny і Pod повертається до черги планування, спричиняючи запуск фази Unreserve у втулках Reserve.

Примітка:

Хоча будь-який втулок може мати доступ до списку Podʼів, що «очікують», та схвалювати їх (див. FrameworkHandle), ми очікуємо, що лише втулки Permit схвалюватимуть привʼязку зарезервованих Podʼів, які перебувають у стані «очікування». Після схвалення Podʼа він відправляється до фази PreBind.

PreBind

Ці втулки використовуються для виконання будь-яких необхідних операцій перед привʼязкою Podʼа. Наприклад, втулок PreBind може надавати мережевий том і монтувати його на цільовому вузлі перед тим, як дозволити Podʼу запускатися там.

Якщо будь-який втулок PreBind повертає помилку, Pod відхиляється і повертається до черги планування.

Bind

Ці втулки використовуються для привʼязки Podʼа до вузла. Втулки Bind не будуть викликані, поки всі втулки PreBind не завершаться. Кожен втулок привʼязки викликається в налаштованому порядку. Втулок привʼязки може вибрати, чи обробляти вказаний Pod. Якщо втулок привʼязки обирає Pod для опрацювання, решта втулків привʼязки пропускаються.

PostBind

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

API втулків

API втулків складається з двох кроків. По-перше, втулки повинні зареєструватися та отримати налаштування, а потім вони використовують інтерфейси точок розширення. Інтерфейси точок розширення мають такий вигляд.

type Plugin interface {
    Name() string
}

type QueueSortPlugin interface {
    Plugin
    Less(*v1.pod, *v1.pod) bool
}

type PreFilterPlugin interface {
    Plugin
    PreFilter(context.Context, *framework.CycleState, *v1.pod) error
}

// ...

Налаштування втулків

Ви можете увімкнути або вимкнути втулки у конфігурації планувальника. Якщо ви використовуєте Kubernetes v1.18 або пізнішу версію, більшість втулків планування використовуються та є стандартно увімкненими.

Окрім стандартних втулків, ви також можете реалізувати власні втулки планування та налаштувати їх разом зі стандартними втулками. Ви можете дізнатися більше на scheduler-plugins.

Якщо ви використовуєте Kubernetes v1.18 або пізнішу версію, ви можете налаштувати набір втулків як профіль планувальника, а потім визначити кілька профілів для різних видів робочих навантажень. Дізнайтеся більше про кілька профілів.

9.9 - Групове планування

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Групове планування забезпечує, щоб група Podʼів була запланована з дотриманням принципу «все або нічого». Якщо кластер не може вмістити всю групу (або визначену мінімальну кількість Podʼів, вказану в minCount), жоден з Podʼів не привʼязується до вузла.

Примітка:

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

Ця можливість залежить від PodGroup API. Переконайтесь, що увімкнено функціональну можливість GenericWorkload та групу API scheduling.k8s.io/v1beta1 у кластері.

Як це працює

Коли втулок GangScheduling увімкнено, планувальник змінює життєвий цикл Podʼів, що належать до PodGroup, для якого встановлено політику планування типу gang. Для кожного PodGroup цей процес відбувається за такими кроками:

  1. Планувальник утримує Podʼи у фазі PreEnqueue, доки:

    • Вказаний обʼєкт PodGroup існує.
    • Кількість Podʼів, створених для PodGroup (як уже запланованих, так і незапланованих), принаймні дорівнює minCount.

    PodGroup не потрапляє до активної черги планування, доки не будуть виконані обидві умови.

  2. Після досягнення кворуму планувальник намагається знайти розміщення для всіх незапланованих Podʼів у групі. Він використовує цикл планування PodGroup, щоб прийняти одне, атомарне рішення щодо планування. Втулок GangScheduling реалізує точку розширення PlacementFeasible, яка викликається для кожного оцінюваного Podʼа під час циклу. Це використовується, щоб визначити, чи виконується обмеження minCount, шляхом порівняння кількості успішно розміщених Podʼів (включно з тими, що вже були заплановані в попередніх циклах) зі значенням minCount.

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

Ієрархічне групове планування з CompositePodGroups

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли увімкнено функціональну можливість CompositePodGroup та групу API scheduling.k8s.io/v1alpha3, групове планування поширює свою підтримку на CompositePodGroups.

На відміну від PodGroups, які групують Podʼи, CompositePodGroups групують дочірні групи разом — або PodGroups, або інші CompositePodGroups. CompositePodGroup визначає політику планування, яка застосовується до його дочірніх груп під час планування:

  • політика gang з полем minGroupCount, яке вказує мінімальну кількість дочірніх груп (або обʼєктів CompositePodGroup, або PodGroup), які повинні бути заплановані разом як єдиний атомарний блок.
  • політика basic, яка вказує, що дочірні групи можна планувати незалежно.

Політика gang корисна для багатокомпонентних робочих навантажень, які потребують планування «все або нічого» для кількох дочірніх груп, забезпечуючи, що мінімальна кількість дочірніх груп планується разом. Прикладом робочого навантаження з такими потребами є репліковане навчання AI.

Політика basic може використовуватися для робочих навантажень, що складаються з кількох груп Podʼів, кожну з яких можна планувати як незалежну групу, наприклад, для робочих навантажень інференсу AI.

Ієрархічний кворум

Втулок GangScheduling затримує потрапляння кореневого обʼєкта CompositePodGroup до активної черги планування на етапі PreEnqueue доти, доки він не задовольнить вимоги ієрархічного кворуму. Цей кворум оцінюється знизу вгору — від кінцевих обʼєктів PodGroup аж до кореневого обʼєкта CompositePodGroup:

  • Кінцевий PodGroup задовольняє кворум тоді і лише тоді, коли обʼєкт PodGroup існує та може потенційно задовольнити критерії своєї політики планування:
    • Для політики gang: принаймні minCount його складових Podʼів було створено.
    • Для політики basic: принаймні один з його складових Podʼів було створено.
  • CompositePodGroup задовольняє кворум тоді і лише тоді, коли обʼєкт CompositePodGroup існує та може потенційно задовольнити критерії своєї політики планування:
    • Для політики gang: принаймні minGroupCount його безпосередніх дочірніх груп задовольняють кворум.
    • Для політики basic: принаймні одна з його безпосередніх дочірніх груп задовольняє кворум.
  • Загальний ієрархічний кворум задовольняється тоді і лише тоді, коли кореневий CompositePodGroup задовольняє кворум.

Зрештою, кореневий CompositePodGroup допускається до активної черги планування тоді і лише тоді, коли він задовольняє ієрархічний кворум та існує щонайменше один Pod, що очікує на планування та належить до одного з його нащадків PodGroups.

Здійсненність розміщення

Метод PlacementFeasible втулка GangScheduling підтримує оцінювання як для PodGroups, так і для CompositePodGroups. Він викликається циклом планування до початку оцінювання дочірніх груп та після оцінювання кожної дочірньої групи CompositePodGroup.

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

Що далі

9.10 - Налаштування продуктивності планувальника

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.14

kube-scheduler — стандартний планувальник для Kubernetes. Він відповідає за розміщення Podʼів на вузлах кластера.

Вузли кластера, які відповідають вимогам планування Podʼа, називаються придатними (feasible) вузлами для Podʼа. Планувальник знаходить придатні вузли для Podʼа, а потім виконує набір функцій для оцінки цих вузлів, вибираючи вузол з найвищою оцінкою серед придатних для запуску Podʼа. Потім планувальник повідомляє API-сервер про це рішення в процесі, що називається привʼязкою (Binding).

На цій сторінці пояснюються оптимізації налаштування продуктивності, які є актуальними для великих кластерів Kubernetes.

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

Це налаштування задається через параметр kube-scheduler percentageOfNodesToScore. Цей параметр KubeSchedulerConfiguration визначає поріг для планування вузлів у вашому кластері.

Налаштування порога

Опція percentageOfNodesToScore приймає цілі числові значення від 0 до 100. Значення 0 є спеціальним числом, яке позначає, що kube-scheduler повинен використовувати вбудоване стандартне значення. Якщо ви встановлюєте percentageOfNodesToScore більше 100, kube-scheduler діє так, ніби ви встановили значення 100.

Щоб змінити значення, відредагуйте файл конфігурації kube-scheduler і перезапустіть планувальник. У багатьох випадках файл конфігурації можна знайти за шляхом /etc/kubernetes/config/kube-scheduler.yaml.

Після внесення цих змін ви можете виконати

kubectl get pods -n kube-system | grep kube-scheduler

щоб перевірити, що компонент kube-scheduler працює належним чином.

Поріг оцінки вузлів

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

Ви вказуєте поріг для того, яка кількість вузлів є достатньою, як цілий відсоток від усіх вузлів у вашому кластері. Kube-scheduler перетворює це в ціле число вузлів. Під час планування, якщо kube-scheduler визначив достатню кількість придатних вузлів, щоб перевищити налаштований відсоток, він припиняє пошук додаткових придатних вузлів і переходить до фази оцінки.

У розділі Як планувальник проходиться по вузлах детально описано цей процес.

Стандартний поріг

Якщо ви не вказуєте поріг, Kubernetes розраховує значення за допомогою лінійної формули, яка дає 50% для кластера зі 100 вузлами та 10% для кластера з 5000 вузлів. Нижня межа для автоматичного значення — 5%.

Це означає, що kube-scheduler завжди оцінює принаймні 5% вашого кластера, незалежно від розміру кластера, якщо ви явно не встановили percentageOfNodesToScore менше ніж 5.

Якщо ви хочете, щоб планувальник оцінював всі вузли у вашому кластері, встановіть percentageOfNodesToScore на 100.

Приклад

Нижче наведено приклад конфігурації, яка встановлює percentageOfNodesToScore на 50%.

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
  provider: DefaultProvider

...

percentageOfNodesToScore: 50

Налаштування percentageOfNodesToScore

percentageOfNodesToScore повинен бути значенням від 1 до 100, а стандартне значення розраховується на основі розміру кластера. Також існує жорстко задане мінімальне значення в 100 вузлів.

Примітка:

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

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

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

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

Вам слід уникати встановлення percentageOfNodesToScore дуже низьким, щоб kube-scheduler не приймав часто погані рішення щодо розміщення Podʼа. Уникайте встановлення відсотка нижче 10%, якщо тільки пропускна здатність планувальника не є критичною для вашого застосунку та оцінка вузлів не є важливою. Іншими словами, ви віддаєте перевагу запуску Podʼа на будь-якому вузлі, який буде придатним.

Як планувальник проходиться по вузлах

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

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

Якщо вузли розташовані в кількох зонах, планувальник проходиться по вузлах у різних зонах, щоб вузли з різних зон враховувалися під час перевірок придатності. Наприклад, розгляньте шість вузлів у двох зонах:

Zone 1: Node 1, Node 2, Node 3, Node 4
Zone 2: Node 5, Node 6

Планувальник оцінює придатність вузлів у такому порядку:

Node 1, Node 5, Node 2, Node 6, Node 3, Node 4

Після проходження всіх вузлів він повертається до вузла 1.

Увімкнення опортуністичної пакетної обробки

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.35; (стандартно увімкнено)

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

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

Загалом ця можливість працює так:

  1. Планувальник планує pod-1 і кешує результат планування.
  2. Планувальник планує pod-2, 3, ... з кешованими результатами.
  3. Термін дії кешу спливає через 0,5 секунди. Планувальник планує наступний Pod, який створює новий кеш.

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

Ми застосовуємо це пакетне планування до конкретних Podʼів, які:

  1. Не мають спорідненості/антиспорідненості між Podʼами
  2. Не мають обмежень топології розміщення
  3. Не мають DRA (тобто не мають жодних ResourceClaims)
  4. Не запитують розширені ресурси, які підтримуються DRA

Також, щоб увімкнути цю можливість, конфігурація планувальника повинна:

  1. Вимкнути стандартні обмеження топології розміщення (встановити порожнє значення)
  2. Встановити IgnorePreferredTermsOfExistingPods InterPodAffinityArgs на true, щоб зробити пакетну обробку більш ефективною

Зверніть увагу, що в таких випадках:

  1. Наявні Podʼи використовують обмеження спорідненості Podʼів, які відповідають будь-яким міткам запланованих Podʼів, можливість може не принести користі.
  2. Використовуються власні втулки, які повинні реалізовувати точку розширення Signature.

Очікується, що обмеження та умови будуть змінюватися в майбутніх версіях.

Що далі

9.11 - Витіснення з урахуванням робочого навантаження

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Примітка:

У v1.36 логіка витіснення з урахуванням робочого навантаження вмикалася функціональною можливістю WorkloadAwarePreemption. Цю функціональну можливість було обʼєднано з функціональною можливістю GenericWorkload у v1.37.

Витіснення з урахуванням робочого навантаження (Workload-aware preemption) вводить механізм витіснення, спеціально розроблений для PodGroups. Коли PodGroup не може бути запланований, планувальник використовує логіку витіснення, яка намагається зробити можливим планування цього PodGroup. Цей підхід використовується виключно під час планування PodGroup і замінює стандартний механізм витіснення для Podʼів із цього PodGroup.

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

Ця можливість повʼязана з Груповим плануванням та залежить від Workload API. Переконайтеся, що у кластері увімкнено групу API scheduling.k8s.io/v1beta1.

Як це працює

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

  1. Домен по всьому кластеру: Замість оцінки витіснення вузол за вузлом, планувальник оцінює весь кластер як єдиний домен. Він обирає набір жертв на кількох вузлах, які можна видалити, щоб звільнити достатньо місця для планування PodGroup-витіснювача.

  2. Ієрархія важливості жертв: Планувальник визначає, які одиниці витіснення (окремі Podʼи або PodGroups) є більш критичними і повинні бути захищені від витіснення, використовуючи сувору ієрархію:

    • Пріоритет: Одиниці з вищим пріоритетом завжди важливіші.
    • Тип робочого навантаження: PodGroups вважаються важливішими за окремі Podʼи з тим самим пріоритетом.
    • Розмір групи (PodGroups): Якщо обидві одиниці є PodGroups, той, що має більше членів (більший розмір), вважається важливішим.
    • Час запуску: Одиниці, які були запущені раніше, є важливішими.
  3. Пріоритет і режим порушення групи Podʼів: Планувальник враховує конкретний пріоритет і режим порушення PodGroup для оцінки того, чи можна витісняти його Podʼи під час подій витіснення і як саме.

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

Примітка:

Під час планування одного Podʼа застосовується стандартне витіснення Podʼів. У v1.36, коли планувальник виконує стандартне витіснення для одного Podʼа і намагається витіснити Pod, що належить до PodGroup, він не враховує поля priority або disruptionMode цього PodGroup. Це обмеження більше не застосовується у v1.37.

Алгоритм помилування

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

  1. Додає Podʼи-жертви назад до їхніх вузлів та CycleStates Podʼів-витіснювачів.
  2. Для кожного Podʼа в PodGroup (у тому ж порядку, що й в алгоритмі планування):
    • Запускає втулки Filter для Podʼа на його запропонованому вузлі
    • Додає Pod до його запропонованого вузла
    • Запускає втулки Reserve для Podʼа на його запропонованому вузлі

Якщо для кожного Podʼа фільтрація проходить успішно, Podʼи-жертви залишаються на своїх вузлах.

Якщо фільтрація не вдається принаймні для одного Podʼа, Podʼи-жертви видаляються з CycleStates та вузла.

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

Витіснення для CompositePodGroups

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість CompositePodGroup та групу API scheduling.k8s.io/v1alpha3 увімкнено, витіснення з урахуванням робочого навантаження також забезпечує підтримку CompositePodGroups.

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

CompositePodGroups також можуть бути обрані як жертви витіснення. Процес вибору жертв скориговано, щоб враховувати CompositePodGroups наступним чином:

  1. Ієрархія важливості жертв:

    • CompositePodGroups вважаються важливішими за окремі PodGroups з тим самим пріоритетом.
    • Для двох CompositePodGroups з однаковим пріоритетом важливішим вважається той, що має більше членів (більший розмір).
  2. Режим порушення: Подібно до PodGroups, CompositePodGroups вказують режим порушення, який визначає, як слід обробляти їхні дочірні групи під час витіснення.

Окрім витіснення з урахуванням робочого навантаження, CompositePodGroups можуть бути обрані як жертви витіснення стандартним витісненням Podʼів під час циклу планування Podʼів, поряд із PodGroups та Podʼами. Стандартне витіснення Podʼів поділяє логіку ієрархії важливості жертв із витісненням з урахуванням робочого навантаження та враховує поле disruptionMode CompositePodGroups.

Що далі

9.12 - Пакування ресурсів

Примітка:

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

У втулку планування NodeResourcesFit kube-scheduler є дві стратегії оцінювання, які підтримують пакування ресурсів: MostAllocated та RequestedToCapacityRatio.

Увімкнення пакування ресурсів за допомогою стратегії MostAllocated

Стратегія MostAllocated оцінює вузли на основі використання ресурсів, віддаючи перевагу тим, у яких виділення вище. Для кожного типу ресурсів ви можете встановити вагу, щоб змінити його вплив на оцінку вузла.

Щоб встановити стратегію MostAllocated для втулка NodeResourcesFit, використовуйте конфігурацію планувальника, подібну до наступної:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- pluginConfig:
  - args:
      scoringStrategy:
        resources:
        - name: cpu
          weight: 1
        - name: memory
          weight: 1
        - name: intel.com/foo
          weight: 3
        - name: intel.com/bar
          weight: 3
        type: MostAllocated
    name: NodeResourcesFit

За допомогою цієї конфігурації вузли оцінюються за зваженим середнім використанням усіх чотирьох ресурсів. Оскільки intel.com/foo та intel.com/bar мають вагу 3 порівняно з 1 для CPU та памʼяті, використання цих розширених ресурсів має втричі більший вплив на кінцеву оцінку вузла. Планувальник обирає вузол з найвищою оцінкою, прагнучи розміщувати Podʼи на вузлах з високим використанням ресурсів. Це допомагає підготуватися до зменшення кількості найменш використовуваних вузлів.

Щоб дізнатися більше про інші параметри та їх стандартну конфігурацію, див. документацію API для NodeResourcesFitArgs.

Увімкнення пакування ресурсів за допомогою стратегії RequestedToCapacityRatio

Стратегія RequestedToCapacityRatio дозволяє користувачам вказати ресурси разом з вагами для кожного ресурсу для оцінювання вузлів на основі відношення запиту до місткості. Це дозволяє користувачам пакувати розширені ресурси, використовуючи відповідні параметри для покращення використання дефіцитних ресурсів у великих кластерах. Вона віддає перевагу вузлам згідно з налаштованою функцією виділених ресурсів. Поведінкою RequestedToCapacityRatio в функції оцінювання NodeResourcesFit можна керувати за допомогою поля scoringStrategy. У межах поля scoringStrategy ви можете налаштувати два параметри: requestedToCapacityRatio та resources. Параметр shape в requestedToCapacityRatio дозволяє користувачу налаштувати функцію як least requested або most requested на основі значень utilization та score. Параметр resources охоплює як name ресурсу, що враховується під час оцінювання, так і відповідну weight, яка визначає вагу кожного ресурсу.

Нижче наведено приклад конфігурації, яка встановлює поведінку пакування для розширених ресурсів intel.com/foo та intel.com/bar за допомогою поля requestedToCapacityRatio.

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- pluginConfig:
  - args:
      scoringStrategy:
        resources:
        - name: intel.com/foo
          weight: 3
        - name: intel.com/bar
          weight: 3
        requestedToCapacityRatio:
          shape:
          - utilization: 0
            score: 0
          - utilization: 100
            score: 10
        type: RequestedToCapacityRatio
    name: NodeResourcesFit

В цьому прикладі лише розширені ресурси intel.com/foo та intel.com/bar зазначені в resources. Тому втулок NodeResourcesFit оцінює вузли виключно на основі використання цих двох ресурсів; CPU та памʼять не впливають на оцінку від цього втулка. Оскільки налаштована shape присвоює вищу оцінку зі збільшенням використання (score: 0 за utilization: 0, зростаючи до score: 10 за utilization: 100), планувальник віддає перевагу вузлам, де більше цих розширених ресурсів вже використовується, пакуючи запити на них на якомога меншу кількість вузлів.

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

Посилання на файл KubeSchedulerConfiguration з прапорцем kube-scheduler --config=/path/to/config/file передасть конфігурацію планувальнику.

Щоб дізнатися більше про інші параметри та їх стандартну конфігурацію, див. документацію API для NodeResourcesFitArgs.

Налаштування функції оцінювання

Параметр shape використовується для визначення поведінки функції RequestedToCapacityRatio.

shape:
  - utilization: 0
    score: 0
  - utilization: 100
    score: 10

Вищезазначені аргументи надають вузлу score 0, якщо utilization дорівнює 0%, та 10 для utilization 100%, що дозволяє пакування ресурсів. Щоб увімкнути least requested, значення оцінки потрібно обернути таким чином.

shape:
  - utilization: 0
    score: 10
  - utilization: 100
    score: 0

resources є необовʼязковим параметром, який стандартно має значення:

resources:
  - name: cpu
    weight: 1
  - name: memory
    weight: 1

Він може бути використаний для додавання розширених ресурсів таким чином:

resources:
  - name: intel.com/foo
    weight: 5
  - name: cpu
    weight: 3
  - name: memory
    weight: 1

Параметр weight є необовʼязковим та встановлюється в 1, якщо його не вказано. Також weight не може бути відʼємним.

Оцінка вузла для розподілу місткості

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

Запитані ресурси:

intel.com/foo : 2
memory: 256MB
cpu: 2

Ваги ресурсів:

intel.com/foo : 5
memory: 1
cpu: 3

FunctionShapePoint {{0, 0}, {100, 10}}

Специфікація вузла 1:

Available:
  intel.com/foo: 4
  memory: 1 GB
  cpu: 8

Used:
  intel.com/foo: 1
  memory: 256MB
  cpu: 1

Оцінка вузла:

intel.com/foo  = resourceScoringFunction((2+1),4)
               = (100 - ((4-3)*100/4))
               = (100 - 25)
               = 75                       # запитано + використано = 75% * доступно
               = rawScoringFunction(75)
               = 7                        # floor(75/10)

memory         = resourceScoringFunction((256+256),1024)
               = (100 -((1024-512)*100/1024))
               = 50                       # запитано + використано = 50% * доступно
               = rawScoringFunction(50)
               = 5                        # floor(50/10)

cpu            = resourceScoringFunction((2+1),8)
               = (100 -((8-3)*100/8))
               = 37.5                     # запитано + використано = 37.5% * доступно
               = rawScoringFunction(37.5)
               = 3                        # floor(37.5/10)

NodeScore   =  ((7 * 5) + (5 * 1) + (3 * 3)) / (5 + 1 + 3)
            =  5

Специфікація вузла 2:

Available:
  intel.com/foo: 8
  memory: 1GB
  cpu: 8
Used:
  intel.com/foo: 2
  memory: 512MB
  cpu: 6

Оцінка вузла:

intel.com/foo  = resourceScoringFunction((2+2),8)
               =  (100 - ((8-4)*100/8)
               =  (100 - 50)
               =  50
               =  rawScoringFunction(50)
               = 5

memory         = resourceScoringFunction((256+512),1024)
               = (100 -((1024-768)*100/1024))
               = 75
               = rawScoringFunction(75)
               = 7

cpu            = resourceScoringFunction((2+6),8)
               = (100 -((8-8)*100/8))
               = 100
               = rawScoringFunction(100)
               = 10

NodeScore   =  ((5 * 5) + (7 * 1) + (10 * 3)) / (5 + 1 + 3)
            =  7

Що далі

9.13 - Планування PodGroup

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

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

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

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

Ця можливість залежить від Workload API. Переконайтеся, що у кластері увімкнено групу API scheduling.k8s.io/v1beta1.

Цикл планування PodGroup

Щоб підтримати планування групи Podʼів разом, kube-scheduler використовує цикл планування PodGroup. Замість обробки Podʼів окремо та утримання їх на етапі WaitOnPermit, планувальник оцінює всю групу Podʼів, що очікують і належать до конкретного PodGroup, колективно. Замість виконання окремих циклів планування для кожного Podʼа він оцінює здійсненність для всієї групи, а потім переходить безпосередньо до фази привʼязки.

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

  1. Знімок стану кластера: Коли планувальник починає оцінювати PodGroup, він робить один знімок стану кластера, який зберігається протягом усього циклу. Це забезпечує консистентність оцінки для всієї групи та запобігає станам гонитви з іншими подіями.

  2. Пошук здійсненних розміщень: Планувальник запускає алгоритм планування PodGroup, щоб знайти дійсні розміщення на вузлах для Podʼів у групі.

  3. Атомарне рішення: Залежно від результату алгоритму, рішення про планування застосовується атомарно для всього PodGroup.

    • Success: Якщо планувальник знаходить достатньо ресурсів та дійсні розміщення для Podʼів (наприклад, задовольняючи обмеження minCount для групового планування), ці Podʼи переходять безпосередньо до циклу привʼязки до обраних вузлів. Будь-які залишкові непридатні для планування Podʼи в групі повторно ставляться безпосередньо в активну чергу планування без backoff, зберігаючи свою попередню мітку часу, щоб вони були негайно переоцінені для спроби витіснення.

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

    • Failure: Якщо планувальник не може знайти достатньо ресурсів, щоб PodGroup став здійсненним (наприклад, не вдається задовольнити обмеження minCount), весь PodGroup вважається непридатним для планування.

      Планувальник намагається зробити PodGroup придатним для планування, запускаючи точку розширення PodGroupPostFilter. Точка розширення PodGroupPostFilter втулка DefaultPreemption виконує витіснення з урахуванням робочого навантаження. Якщо будь-який втулок, що реалізує точку розширення PodGroupPostFilter, повертає Success, жодні інші втулки для точки розширення PodGroupPostFilter не виконуються.

      Навіть коли втулок PodGroupPostFilter повертає Success, жоден Pod не привʼязується до вузла; натомість усі повертаються до черги планування в надії, що дії, виконані PodGroupPostFilter, зроблять PodGroup придатним для планування в наступному циклі. Застосовується стандартна логіка backoff планування, що дозволяє повторити спробу пізніше.

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

Алгоритм планування PodGroup

Стандартний алгоритм планування PodGroup значною мірою покладається на базовий алгоритм планування на основі Podʼів. Він послідовно обробляє всі Podʼи і для кожного з них виконує таке:

  1. Знаходить придатний вузол за допомогою стандартних фаз фільтрації та оцінки для кожного Podʼа.

    • Якщо Pod підходить, він тимчасово призначається та резервується на обраному вузлі до завершення роботи алгоритму планування.
    • Якщо Pod не підходить, планувальник не запускає точку розширення PostFilter. Натомість він покладається на точку розширення PodGroupPostFilter, яка виконується для всього PodGroup.
  2. Перевіряє, чи відповідають придатні для планування Podʼи критеріям планування групи (наприклад, minCount для групового планування), викликаючи точку розширення PlacementFeasible після оцінки кожного Podʼа в групі на основі сукупних результатів планування. Якщо для будь-якого Podʼа повертається статус Success, PodGroup вважається здійсненним. Якщо алгоритм обробляє всі Podʼи без досягнення статусу Success або якщо під час цього циклу не заплановано жодного Podʼа, PodGroup вважається непридатним для планування.

Алгоритм планування розміщення

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.36; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the TopologyAwareWorkloadScheduling feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

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

Алгоритм проходить три основні фази для цього PodGroup:

Фаза 1: Генерація кандидатів на розміщення

Генерує кандидатів на розміщення (підмножини вузлів, які теоретично можуть бути придатними для призначення PodGroup), наприклад, на основі обмежень планування PodGroup (які можуть бути визначені в обʼєкті PodGroup).

Ця фаза виконується в точці розширення PlacementGeneratePlugin.

Фаза 2: Фільтрація на рівні Podʼів і перевірка здійсненності

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

Фаза 3: Оцінка та вибір розміщення

Оцінює всі здійсненні розміщення, щоб вибрати оптимальний домен для PodGroup.

Ця фаза виконується в точці розширення PlacementScorePlugin.

Обмеження

Алгоритм планування PodGroup покладається на специфічне сортування Podʼів і може не знайти дійсне розміщення, яке могло б бути виявлене під час обробки Podʼів групи в іншому порядку. Зокрема:

  • Для базових однорідних груп Podʼів (тобто тих, де всі Podʼи мають однакові вимоги до планування і не мають взаємозалежностей між Podʼами, таких як спорідненість, антиспорідненість або обмеження топології розміщення), очікується, що алгоритм знайде розміщення, якщо воно існує.

  • Для різнорідних груп Podʼів знаходження відповідного розміщення не гарантується.

  • Для груп Podʼів із взаємозалежностями між Podʼами знаходження відповідного розміщення не гарантується.

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

Для забезпечення послідовної роботи протягом усього циклу алгоритм вимагає, щоб усі Podʼи, що належать до одного PodGroup, мали однакове поле .spec.schedulerName. Ця вимога перевіряється перед початком циклу, і PodGroup відхиляється, якщо ця умова не виконується.

Ієрархічне планування з CompositePodGroups

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість CompositePodGroup та групу API scheduling.k8s.io/v1alpha3 увімкнено, планувальник розширює цикл планування PodGroup для підтримки багаторівневих ієрархій груп.

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

Виконання циклу ієрархічного планування

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

Щойно кореневий CompositePodGroup задовольняє ієрархічний кворум, він потрапляє в активну чергу планування, з якої його може витягнути цикл планування. Цикл планування для CompositePodGroups виглядає так:

  1. Уніфікований знімок кластера та перевірка: Планувальник робить знімок ресурсів кластера, щоб відобразити останній спостережений стан кластера. Він перевіряє, що форма ієрархії груп, витягнутої з черги, збігається зі знімком ієрархії, та перевіряє узгодженість конфігурації ієрархії (таку як однакові .spec.schedulerName та пріоритет серед Podʼів-членів).

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

  2. Генерація кандидатів на розміщення зверху вниз: На кожному рівні ієрархії, перед оцінкою дочірніх груп, планувальник викликає втулки PlacementGeneratePlugin, щоб згенерувати кандидатів на розміщення (підмножини вузлів) для групи, що оцінюється.

    Кандидати на розміщення для дочірнього CompositePodGroup або кінцевого PodGroup генеруються виключно з підмножини вузлів, що належать до поточного оцінюваного розміщення батьківської групи. Для кореневого CompositePodGroup кандидати на розміщення генеруються по всіх доступних вузлах кластера.

  3. Рекурсивна симуляція піддерева та перевірка здійсненності: Планувальник оцінює кожного кандидата на розміщення для CompositePodGroup, тимчасово припускаючи це розміщення у знімку кластера та рекурсивно плануючи його дочірні групи:

    • Рекурсивний обхід: Планувальник обходить дочірні групи в попередньо відсортованому порядку, викликаючи себе рекурсивно від CompositePodGroup вниз до кінцевих обʼєктів PodGroup. Для кожного кінцевого PodGroup планувальник запускає алгоритм планування розміщення, обмежений кандидатом на розміщення батьківської групи, щоб зробити попередні призначення Podʼів у памʼяті.
    • Перевірки здійсненності: Після оцінки кожної дочірньої групи планувальник викликає втулки PlacementFeasible, щоб визначити, чи може політика планування батьківської групи все ще бути виконана:
      • Якщо обмеження політики залишаються досяжними (або вже задоволені), планувальник продовжує оцінювати наступні сусідні групи.
      • Якщо обмеження політики більше не можуть бути задоволені, планувальник негайно перериває оцінку цього CompositePodGroup та скасовує всі попередні призначення Podʼів у памʼяті, зроблені для цієї групи.
    • Відкат симуляції: Після симуляції кандидата на розміщення (незалежно від успіху чи невдачі) планувальник скасовує попередні резервування вузлів, зроблені під час цієї симуляції, перед оцінкою наступного кандидата на розміщення.
  4. Оцінка розміщення та фіксація піддерева: Після того, як усі кандидати на розміщення для CompositePodGroup були оцінені:

    • Оцінка: Якщо один або кілька кандидатів на розміщення є здійсненними, планувальник викликає втулки PlacementScorePlugin, щоб оцінити всіх здійсненних кандидатів на розміщення. Втулки оцінки оцінюють обʼєднані запропоновані призначення Podʼів по всіх дочірніх кінцевих обʼєктах PodGroup у піддереві та вибирають розміщення з найвищою загальною оцінкою.

    • Фіксація: Після вибору виграшного розміщення планувальник фіксує (припускає) попередні призначення Podʼів, що відповідають цьому оптимальному розміщенню, у памʼяті. Це гарантує, що наступні оцінки сусідніх груп або оцінки на рівні батьківської групи спостерігають узгоджені, оптимальні рішення планування для цього піддерева.

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

  5. Атомарна привʼязка: Якщо рекурсивна оцінка на кореневому рівні успішна та принаймні один Pod був успішно запланований, планувальник фіксує призначення Podʼів, переходячи до циклу привʼязки.

    В іншому випадку весь CompositePodGroup вважається непридатним для планування.

Обмеження

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

Крім того, оскільки планувальник оцінює ієрархії груп за допомогою жадібного підходу:

  • Знаходження дійсного розміщення не гарантується у всіх випадках, навіть якщо воно існує в кластері.
  • Навіть коли планувальник успішно знаходить дійсне розміщення для ієрархії CompositePodGroup, це розміщення не гарантовано є оптимальним по всьому кластеру.

Стани PodGroup

Після завершення циклу планування PodGroup планувальник оновлює стани в status.conditions PodGroup:

  • PodGroupInitiallyScheduled: повідомляє, чи був PodGroup успішно запланований вперше.

PodGroupInitiallyScheduled

Коли цикл планування успішний, стан встановлюється в True з причиною Scheduled. Для PodGroup з політикою gang це означає, що було розміщено принаймні minCount Podʼів.

Коли планування не вдається, стан встановлюється в False з однією з таких причин:

  • Unschedulable — групу не вдалося розмістити через обмеження ресурсів, правила спорідненості або антиспорідненості, або недостатню місткість для групи.
  • SchedulerError — планування не вдалося через внутрішню помилку планувальника (наприклад, під час розбору обмежень планування, таких як nodeAffinity).

Після того, як стан встановлено в True, він ніколи не змінюється.

Ви можете перевірити стани за допомогою:

kubectl get podgroup <name> -o jsonpath='{.status.conditions}'

Стани CompositePodGroup

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

API CompositePodGroup також надає поле status.conditions.

Однак у v1.37 планувальник не заповнює це поле.

Що далі

9.14 - Планування робочих навантажень з урахуванням топології

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.36; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the TopologyAwareWorkloadScheduling feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Планування з урахуванням топології (Topology-Aware Scheduling, TAS) є алгоритмом планування розміщення, який дозволяє знайти оптимальне розміщення для розглянутого PodGroup, гарантуючи, що всі Podʼи будуть розміщені разом у межах одного топологічного домену. Користувачі можуть налаштувати TAS відповідно до своїх конкретних потреб, змінюючи конфігурацію втулків TAS.

Фреймворк планування: налаштування втулків TAS

Планувальник включає нові та розширені вбудовані втулки, які реалізують точки розширення TAS:

  • TopologyPlacement: Реалізує інтерфейс PlacementGeneratePlugin. Він генерує кандидатів на розміщення, групуючи вузли на основі різних значень запитуваного ключа key топології (визначеного в PodGroup).

  • NodeResourcesFit: Розширено для реалізації інтерфейсу PlacementScorePlugin. Використовуючи подібну логіку до стандартного пакування Podʼів, він оцінює розміщення на основі коефіцієнта використання ресурсів на всіх вузлах у розміщенні. Використовується стратегія MostAllocated для максимізації використання ресурсів у розміщенні, а також успадковуються ваги ресурсів із стандартних налаштувань втулка pod-by-pod.

  • PodGroupPodsCount: Реалізує інтерфейс PlacementScorePlugin. Він оцінює кандидатів на розміщення на основі загальної кількості Podʼів у PodGroup, які можна успішно запланувати.

Налаштування ваг втулків та ваг ресурсів пакування

Стандартно втулки NodeResourcesFit та PodGroupPodsCount налаштовані з однаковими вагами (обидві стандартно мають вагу 1), щоб підтримувати хороший баланс між логікою пакування Podʼів та плануванням максимальної кількості Podʼів.

Ви можете змінити ці ваги або ваги ресурсів у стратегії пакування у вашій конфігурації KubeSchedulerConfiguration. Ось приклад фрагмента, який показує, як змінити ваги для обох втулків і як перевизначити ваги ресурсів для NodeResourcesFit. Остання зміна буде застосована як до алгоритмів планування pod-by-pod, так і до алгоритмів оцінки розміщення:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: default-scheduler
    plugins:
      placementScore:
        enabled:
          # 1) Зміна стандартних ваг втулків оцінки розміщення
          - name: NodeResourcesFit
            weight: 2
          - name: PodGroupPodsCount
            weight: 5
    pluginConfig:
      - name: NodeResourcesFit
        args:
          # 2) Зміна ваг ресурсів для обох алгоритмів оцінки pod-by-pod та розміщення
          scoringStrategy:
            # Тип буде враховуватися лише під час планування pod-by-pod. Оцінка розміщення завжди
            # використовує стратегію MostAllocated
            type: LeastAllocated
            # Ваги ресурсів будуть використовуватися як в алгоритмах планування pod-by-pod, так і в алгоритмах оцінки розміщення
            resources:
              - name: cpu
                weight: 2
              - name: memory
                weight: 3

Багаторівневі розміщення топології

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість CompositePodGroup та групу API scheduling.k8s.io/v1alpha3 увімкнено, втулки планування з урахуванням топології розширюють свою підтримку на багаторівневі ієрархії CompositePodGroup. Ці втулки викликаються для CompositePodGroups під час ієрархічного планування.

Генерація кандидатів на розміщення

Для робочих навантажень, визначених з ієрархією CompositePodGroup, втулок TopologyPlacement генерує кандидатів на розміщення зверху вниз по ієрархії груп шляхом послідовного поділу:

  • Для кореневого CompositePodGroup втулок TopologyPlacement генерує кандидатів на розміщення по всіх доступних вузлах кластера, групуючи вузли на основі різних значень запитуваного ключа key топології.
  • Для дочірнього CompositePodGroup або кінцевого PodGroup втулок TopologyPlacement генерує кандидатів на розміщення, обмежених розміщенням, прийнятим батьківською групою. Він поділяє набір вузлів із розміщення батьківської групи, групуючи ці вузли на основі запитуваного ключа key топології дочірньої групи.

Примітка:

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

Аналогічно, якщо коренева група не вказує жодного обмеження топології, втулок генерує одного кандидата на розміщення, що відповідає всім доступним вузлам у кластері. Це також справедливо для однорівневих робочих навантажень, що використовують API PodGroup, де обмеження топології не вказано.

Оцінка розміщення

Під час оцінки кандидата на розміщення для CompositePodGroup втулки оцінки застосовують логіку, подібну до однорівневого випадку PodGroup:

  • PodGroupPodsCount: Оцінює кандидатів на розміщення на основі загальної кількості Podʼів (як уже запланованих, так і щойно прийнятих) по всіх дочірніх кінцевих PodGroups цього CompositePodGroup. Кандидати на розміщення, здатні вмістити більшу загальну кількість Podʼів по підієрархії, отримують вищі оцінки.
  • NodeResourcesFit: Агрегує запити ресурсів усіх запропонованих Podʼів по всіх дочірніх PodGroups цього CompositePodGroup та оцінює використання ресурсів по всіх вузлах у межах домену кандидата на розміщення.

Що далі

9.15 - Пріоритет та витіснення Podʼів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.14

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

Попередження:

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

Детальніше дивіться стандартне обмеження споживання Priority Class.

Як використовувати пріоритет та витіснення

Для використання пріоритету та витіснення:

  1. Додайте один чи кілька PriorityClasses.

  2. Створіть Podʼи з параметром priorityClassName, встановленим на один з доданих PriorityClasses. Звісно, вам не потрібно створювати Podʼи безпосередньо; зазвичай ви додаєте priorityClassName до шаблону Podʼа обʼєкта колекції, такого як Deployment.

Продовжуйте читати для отримання додаткової інформації про ці кроки.

PriorityClass

PriorityClass є обʼєктом, що не належить до простору імен, і визначає зіставлення назви класу пріоритету з цілим значенням пріоритету. Назва вказується в полі name метаданих обʼєкта PriorityClass. Значення вказується в обовʼязковому полі value. Чим вище значення, тим вищий пріоритет. Назва обʼєкта PriorityClass повинна бути дійсною назвою піддомену DNS, і вона не може мати префікс system-.

Обʼєкт PriorityClass може мати будь-яке 32-розрядне ціле значення, яке менше або дорівнює 1 мільярду. Це означає, що діапазон значень для обʼєкта PriorityClass становить від -2147483648 до 1000000000 включно. Більші числа зарезервовані для вбудованих PriorityClass, які представляють критичні системні Podʼи. Адміністратор кластера повинен створити один обʼєкт PriorityClass для кожного потрібного йому зіставлення.

Примітка:

Kubernetes вже поставляється з двома PriorityClasses: system-cluster-critical та system-node-critical. Ці спільні класи використовуються для забезпечення того, що критичні компоненти завжди плануються першими. Для Kubernetes v1.37 їхні значення пріоритету становлять 2000000000 для system-cluster-critical і 2000001000 для system-node-critical.

PriorityClass також має два необовʼязкові поля: globalDefault та description. Поле globalDefault вказує, що значення цього PriorityClass повинно використовуватися для Podʼів без priorityClassName. У системі може існувати лише один PriorityClass з globalDefault, встановленим в true. Якщо немає PriorityClass із встановленим globalDefault, пріоритет Podʼів без priorityClassName дорівнює нулю.

Поле description є довільним рядком. Воно призначене для сповіщення користувачів кластера про те, коли вони повинні використовувати цей PriorityClass.

Примітки щодо PodPriority та наявних кластерів

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

  • Додавання PriorityClass з globalDefault, встановленим в true, не змінює пріоритети наявних Podʼів. Значення такого PriorityClass використовується тільки для Podʼів, створених після додавання PriorityClass.

  • Якщо ви видаляєте PriorityClass, наявні Podʼи, які використовують назву видаленого PriorityClass, залишаються незмінними, але ви не можете створювати більше Podʼів, які використовують назву видаленого PriorityClass.

Приклад PriorityClass

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

PriorityClass без витіснення

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.24

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

Podʼи без витіснення все ще можуть бути витіснені іншими Podʼами з високим пріоритетом.

Стандартне значення preemptionPolicy — PreemptLowerPriority, що дозволяє Podʼам цього PriorityClass витісняти Podʼи з нижчим пріоритетом (це наявна стандартна поведінка). Якщо preemptionPolicy встановлено в Never, Podʼи в цьому PriorityClass не витіснятимуть інші Podʼи.

Прикладом використання є робочі навантаження науки про дані. Користувач може надіслати завдання, якому він хоче дати пріоритет перед іншими робочими навантаженнями, але не бажає втрачати наявну роботу, витісняючи запущені Podʼи. Завдання з високим пріоритетом із preemptionPolicy: Never буде заплановано перед іншими Podʼами в черзі, щойно достатньо ресурсів кластера «природним чином» звільниться.

Приклад PriorityClass без витіснення

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "This priority class will not cause other pods to be preempted."

Пріоритет Podʼа

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

Наведений нижче YAML — це приклад конфігурації Podʼа, який використовує PriorityClass, створений у попередньому прикладі. Контролер допуску пріоритетів перевіряє специфікацію та визначає пріоритет Podʼа як 1000000.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

Вплив пріоритету Podʼа на порядок планування

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

PodGroups та порядок планування

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість GenericWorkload увімкнено, PodGroups чергуються з окремими Podʼами в черзі планування. Обʼєкт PodGroup має власне поле priority, яке використовується для впорядкування відносно інших Podʼів та PodGroups. Деталі щодо того, як встановити пріоритет PodGroup, доступні на сторінці Порушення та пріоритет групи Podʼів.

CompositePodGroups та порядок планування

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CompositePodGroup feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість CompositePodGroup увімкнено, планувальник розглядає наступні обʼєкти як одиниці планування, які можуть бути поставлені в чергу:

  • Самостійні Podʼи — Podʼи, які не є частиною жодного PodGroup,
  • Самостійні PodGroups — PodGroups, які не вказують ParentCompositePodGroup,
  • Кореневі CompositePodGroups — CompositePodGroups, які не вказують ParentCompositePodGroup.

Ці обʼєкти вказують значення поля priority, яке використовується для визначення їх позиції в активній черзі планування.

Витіснення

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

Витіснення PodGroup

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the GenericWorkload feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість GenericWorkload увімкнено, PodGroups можуть брати участь у витісненні як ініціатор або як жертва. Коли PodGroup ініціює витіснення, він дотримується логіки витіснення з урахуванням робочого навантаження, щоб витіснити інші Podʼи та PodGroups і звільнити місце для себе. Коли Pod ініціює витіснення, PodGroup може стати жертвою. У цьому випадку поле priority PodGroup використовується для впорядкування відносно інших потенційних жертв, а поле disruptionMode використовується для визначення поведінки PodGroup під час порушення. Детальний опис цих полів доступний на сторінці Порушення та пріоритет групи Podʼів.

Інформація, доступна користувачеві

Коли Pod P витісняє один або кілька Podʼів на вузлі N, поле nominatedNodeName статусу Podʼа P встановлюється в назву вузла N. Це поле допомагає планувальнику відстежувати ресурси, зарезервовані для Podʼа P, і також надає користувачам інформацію про витіснення в їх кластерах.

Зверніть увагу, що Pod P не обовʼязково планується на «номінований вузол». Планувальник завжди спочатку спробує «номінований вузол», перед тим як перевіряти будь-які інші вузли. Після того, як Podʼи-жертви витіснено, вони отримують свій період коректного завершення. Якщо під час очікування на завершення роботи Podʼів-жертв стає доступним інший вузол, планувальник може використати інший вузол для планування Podʼа P. В результаті поля nominatedNodeName та nodeName специфікації Podʼа не завжди збігаються. Крім того, якщо планувальник витісняє Podʼи на вузлі N, а потім надходить Pod вищого пріоритету, ніж Pod P, планувальник може надати вузол N новому Podʼу вищого пріоритету. У такому випадку планувальник очищає nominatedNodeName Podʼа P. Цим самим планувальник робить Pod P придатним для витіснення Podʼів на іншому вузлі.

Обмеження витіснення

Коректне завершення роботи жертв витіснення

Коли Podʼи витісняються, жертви отримують свій період коректного завершення. У них є стільки часу, скільки вказано в цьому періоді, для завершення своєї роботи та виходу. Якщо вони цього не роблять, їх робота завершується примусово. Цей період коректного завершення створює проміжок часу між моментом, коли планувальник витісняє Podʼи, і моментом, коли Pod (P) в очікуванні може бути запланований на вузол (N). Тим часом планувальник продовжує планувати інші Podʼи в очікуванні. Поки жертви завершують роботу самі або примусово, планувальник намагається запланувати Podʼи з черги очікування. Тому зазвичай є проміжок часу між моментом, коли планувальник витісняє жертв, і моментом, коли Pod P буде заплановано. Щоб зменшити цей проміжок, можна встановити період коректного завершення для Podʼів з нижчим пріоритетом на нуль або мале число.

PodDisruptionBudget підтримується, але не гарантується

PodDisruptionBudget (PDB) дозволяє власникам застосунків обмежувати кількість Podʼів реплікованого застосунку, які одночасно недоступні через добровільні порушення. Kubernetes підтримує PDB під час витіснення Podʼів, але дотримання PDB відбувається за принципом best-effort. Планувальник намагається знайти жертв, PDB яких не буде перевищено в результаті витіснення, але якщо жодної такої жертви не знайдено, витіснення все одно відбудеться, і Podʼи з нижчим пріоритетом будуть видалені, попри перевищення їхнього PDB.

Спорідненість між Podʼами для Podʼів з нижчим пріоритетом

Вузол розглядається для витіснення тільки тоді, коли відповідь на це питання ствердна: «Якщо всі Podʼи з нижчим пріоритетом, ніж Pod в очікуванні, будуть видалені з вузла, чи може Pod в очікуванні бути запланований на цьому вузлі?»

Примітка:

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

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

Рекомендованим рішенням для цієї проблеми є створення спорідненості між Podʼами лише з Podʼами з рівним або вищим пріоритетом.

Міжвузлове витіснення

Припустимо, що вузол N розглядається для витіснення, щоб запланувати Pod P в очікуванні на N. Планування P на N може стати здійсненним лише у випадку витіснення Podʼа на іншому вузлі. Ось приклад:

  • Pod P розглядається для вузла N.
  • Pod Q запущений на іншому вузлі в тій же зоні, що й вузол N.
  • Pod P має антиспорідненість по всій зоні з Podʼом Q (з topologyKey: topology.kubernetes.io/zone).
  • Інших випадків антиспорідненості між Podʼом P та іншими Podʼами в зоні немає.
  • Щоб запланувати Pod P на вузлі N, Pod Q може бути витіснений, але планувальник не виконує міжвузлове витіснення. Отже, Pod P буде вважатися непридатним для планування на вузлі N.

Якщо Pod Q буде видалено зі свого вузла, порушення антиспорідненості Podʼа буде усунено, і Pod P, можливо, можна буде запланувати на вузлі N.

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

Витіснення для зміни розміру Podʼа на місці

СТАН ФУНКЦІОНАЛУ: Alpha since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the InPlacePodVerticalScalingSchedulerPreemption feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Коли функціональну можливість InPlacePodVerticalScalingSchedulerPreemption увімкнено, витіснення також застосовується, коли запит на зміну розміру запущеного Podʼа на місці має статус Deferred (тобто запитана зміна розміру тимчасово неможлива, але може стати можливою пізніше) через недостатню місткість вузла. Деталі щодо станів зміни розміру дивіться в статусі зміни розміру Podʼа.

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

Витіснення для зміни розміру Podʼа на місці відрізняється від стандартного витіснення під час розміщення Podʼа кількома ключовими способами:

  • Обмеження вузла: Витіснення суворо обмежене поточним призначеним вузлом Podʼа. Планувальник не оцінює інші вузли-кандидати по всьому кластеру.
  • Обчислення дельти ресурсів: Необхідна для витіснення місткість обчислюється на основі дельти ресурсів (різниці між бажаними та поточно виділеними ресурсами), а не загального запиту Podʼа.
  • Блокування ресурсів: Запитані ресурси Podʼа, розмір якого змінюється, вважаються вже спожитими для обліку ресурсів планувальника на цьому вузлі, не даючи іншим Podʼам нижчого пріоритету їх використовувати, доки зміна розміру не буде виконана або kubelet не видалить статус Deferred.
  • Статус номінованого вузла: Поле nominatedNodeName у статусі Podʼа не встановлюється, оскільки Pod вже призначений і працює на своєму цільовому вузлі.
  • Виконання kubelet: Планувальник ініціює витіснення, щоб звільнити місткість вузла, але не привʼязує Pod повторно. Kubelet виявляє щойно звільнену місткість на вузлі та виконує фактичну зміну розміру на місці.
  • Політика витіснення на рівні вузла: Витіснення можна вимкнути спеціально для змін розміру на місці на конкретних вузлах за допомогою поля spec.podPreemptionPolicy.disableResizePreemption специфікації Node. На відміну від стандартного витіснення, яке можна вимкнути лише для кожного класу пріоритету за допомогою preemptionPolicy: Never, це дозволяє операторам або системам автомасштабування вимикати витіснення на окремих вузлах.

Для отримання додаткових деталей про зміну розміру Podʼа на місці дивіться Зміна розміру CPU та памʼяті, призначених контейнерам.

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

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

Це налаштовується за допомогою поля spec.podPreemptionPolicy у специфікації Node. Якщо список disableResizePreemption не порожній, витіснення, спричинене зміною розміру, вимикається на цьому вузлі, і kube-scheduler пропускатиме спроби витіснення для будь-яких відкладених змін розміру, що виконуються на цьому вузлі.

Ось приклад конфігурації Node:

apiVersion: v1
kind: Node
metadata:
  name: resizable-node
spec:
  podPreemptionPolicy:
    disableResizePreemption:
      - autoscaling.k8s.io/cluster-autoscaler
      - autoscaling.k8s.io/vpa-updater

Обмеження та правила перевірки:

  • Формат мітки: Кожен запис у списку disableResizePreemption повинен відповідати стандартному формату ключа мітки Kubernetes (наприклад, префікс піддомену DNS та назва, розділені /).
  • Максимальна кількість: Список може містити максимум 20 записів.
  • Непорожній список: Політика виконується (витіснення вимикається), якщо список містить принаймні один запис. Якщо список порожній або поле podPreemptionPolicy опущено, витіснення для зміни розміру на місці увімкнено на вузлі.

Розвʼязання проблем

Пріоритет та витіснення Podʼа можуть мати небажані побічні ефекти. Ось кілька прикладів потенційних проблем і способів їх вирішення.

Podʼи витісняються безпідставно

Витіснення видаляє наявні Podʼи з кластера під тиском на ресурси, щоб звільнити місце для високопріоритетних Podʼів в очікуванні. Якщо ви помилково встановили високі пріоритети для певних Podʼів, ці ненавмисно високопріоритетні Podʼи можуть спричинити витіснення у вашому кластері. Пріоритет Podʼа вказується встановленням поля priorityClassName в специфікації Podʼа. Потім ціле значення пріоритету визначається і заповнюється у поле priority podSpec.

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

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

Podʼи витісняються, але витіснювач не планується

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

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

Це очікувана поведінка: Pod з вищим пріоритетом повинен зайняти місце Podʼа з нижчим пріоритетом.

Podʼи з вищим пріоритетом витісняються раніше за Podʼи з нижчим пріоритетом

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

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

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

Взаємодія між пріоритетом Podʼа та якістю обслуговування

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

kubelet використовує пріоритет для визначення порядку Podʼів для виселення внаслідок тиску на вузол. Ви можете використовувати клас QoS для оцінки порядку, в якому найімовірніше будуть виселятися Podʼи. kubelet ранжує Podʼи для виселення на основі таких факторів:

  1. Чи перевищує використання дефіцитного ресурсу запити
  2. Пріоритет Podʼа
  3. Обсяг використання ресурсів в порівнянні із запитами

Дивіться Вибір Podʼа для виселення kubelet для отримання додаткової інформації.

Виселення kubelet внаслідок тиску на вузол не виселяє Podʼи, коли їх використання не перевищує їх запити. Якщо Pod з нижчим пріоритетом не перевищує свої запити, він не буде виселений. Інший Pod з вищим пріоритетом, який перевищує свої запити, може бути виселений.

Що далі

9.16 - Виселення внаслідок тиску на вузол

Виселення внаслідок тиску на вузол — це процес, під час якого kubelet проактивно завершує роботу Podʼів, щоб звільнити ресурси на вузлах.

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

Під час виселення внаслідок тиску на вузол kubelet встановлює фазу для вибраних Podʼів в значення Failed та завершує роботу Podʼа.

Виселення внаслідок тиску на вузол не є тим самим, що й виселення, ініційоване API.

kubelet не враховує ваш налаштований PodDisruptionBudget або параметр terminationGracePeriodSeconds Podʼа. Якщо ви використовуєте порогові значення мʼякого виселення, kubelet дотримується вашого налаштованого evictionMaxPodGracePeriod. Якщо ви використовуєте порогові значення жорсткого виселення, kubelet використовує період коректного завершення 0s (негайне вимкнення) для завершення роботи.

Поведінка самозцілення

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

Якщо Podʼами керує обʼєкт керування робочим навантаженням (такий як StatefulSet або Deployment), який замінює Podʼи зі збоєм, панель управління (kube-controller-manager) створює нові Podʼи на місці виселених Podʼів.

Самозцілення статичних Podʼів

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

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

Сигнали та пороги виселення

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

  • Сигнали виселення
  • Пороги виселення
  • Інтервали моніторингу

Сигнали виселення

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

Kubelet використовує такі сигнали виселення:

Сигнал виселенняОписLinux Only
memory.availablememory.available := node.status.capacity[memory] - node.stats.memory.workingSet
nodefs.availablenodefs.available := node.stats.fs.available
nodefs.inodesFreenodefs.inodesFree := node.stats.fs.inodesFree•
imagefs.availableimagefs.available := node.stats.runtime.imagefs.available
imagefs.inodesFreeimagefs.inodesFree := node.stats.runtime.imagefs.inodesFree•
containerfs.availablecontainerfs.available := node.stats.runtime.containerfs.available
containerfs.inodesFreecontainerfs.inodesFree := node.stats.runtime.containerfs.inodesFree•
pid.availablepid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc•

У цій таблиці стовпець Опис показує, як kubelet отримує значення сигналу. Кожен сигнал підтримує відсоткове або літеральне значення. Kubelet обчислює відсоткове значення відносно загальної місткості, повʼязаної із сигналом.

Сигнали памʼяті

На вузлах Linux значення для memory.available походить з cgroupfs замість таких інструментів, як free -m. Це важливо, оскільки free -m не працює у контейнері, і якщо користувачі використовують можливість ресурсів вузла, доступних для розподілу, рішення про вичерпання ресурсів приймаються локально в частині ієрархії cgroup, що належить Podʼам кінцевих користувачів, а також на кореневому вузлі. Цей скрипт або скрипт cgroupv2 відтворює той самий набір кроків, які kubelet виконує для обчислення memory.available. Kubelet виключає зі свого обчислення inactive_file (кількість байтів памʼяті, що має файлове резервне сховище, у неактивному списку LRU), оскільки припускає, що цю памʼять можна звільнити під тиском.

Примітка:

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно увімкнено)

На вузлах із налаштованими hugepages kubelet віднімає загальну місткість hugepages вузла від memory.available, яку використовує менеджер виселення. Без цього коригування зарезервована під hugepages оперативна памʼять завищує AvailableBytes, оскільки контролер cgroup памʼяті не відстежує виділення hugetlb у своєму робочому наборі. Це може затримати виселення та призвести до OOM-завершень. Щоб відновити попередню поведінку, вимкніть функціональну можливість HugepageAwareEviction.

На Windows-вузлах значення memory.available отримується з глобального рівня комітів памʼяті вузла (запитується через системний виклик GetPerformanceInfo()) шляхом віднімання глобального значення CommitTotal від CommitLimit вузла. Зверніть увагу, що значення CommitLimit може змінюватися, якщо змінюється розмір файлу підкачки вузла!

Сигнали файлової системи

Kubelet розпізнає три конкретні ідентифікатори файлової системи, які можна використовувати із сигналами виселення (<identifier>.inodesFree або <identifier>.available):

  1. nodefs: Основна файлова система вузла, використовується для локальних дискових томів, томів emptyDir, які не підтримуються памʼяттю, зберігання логів, ефемерного зберігання та іншого. Наприклад, nodefs містить /var/lib/kubelet.

  2. imagefs: Необовʼязкова файлова система, яку середовища виконання контейнерів можуть використовувати для зберігання образів контейнерів (які є шарами тільки для читання). Якщо окремої файлової системи containerfs немає, файлова система образів також зберігає шари контейнера, доступні для запису.

  3. containerfs: Необовʼязкова файлова система, яку середовища виконання контейнерів можуть використовувати для зберігання записуваних шарів контейнерів. Коли використовується containerfs, файлова система imagefs може бути розділена так, щоб зберігати лише образи (шари тільки для читання) і більше нічого.

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

Примітка:

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.31; (стандартно увімкнено)

Можливість split image filesystem додає нові сигнали виселення, пороги та метрики для containerfs. Щоб використовувати containerfs, у випуску Kubernetes v1.37 потрібно увімкнути функціональну можливість KubeletSeparateDiskGC. Для Kubernetes v1.37 підтримку файлової системи containerfs пропонує лише CRI-O (v1.29 або вище).

Kubelet підтримує три поширені схеми розміщення файлових систем контейнерів:

  • Усе зберігається на єдиній nodefs, яку також називають «rootfs» або просто «root». У цій схемі nodefs, imagefs та containerfs посилаються на одну й ту саму базову файлову систему.

  • Сховище середовища виконання контейнерів розміщено на окремому диску, відокремленому від кореневої файлової системи. У цій схемі imagefs та containerfs посилаються на одну й ту саму базову файлову систему, яка зберігає як шари образів, так і записувані шари контейнерів. Це часто називають файловою системою «split disk» (або «separate disk»).

  • Записувані шари контейнерів розміщено на nodefs, а образи контейнерів (шари лише для читання) зберігаються на окремій imagefs. У цій схемі containerfs та nodefs посилаються на одну й ту саму базову файлову систему. Це часто називають файловою системою «split image».

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

Kubelet не підтримує інші файлові системи контейнерів або конфігурації зберігання і наразі не підтримує кілька файлових систем для образів і контейнерів.

Застарілі можливості збору сміття kubelet

Деякі можливості збору сміття kubelet застаріли на користь виселення:

Наявний прапорецьОбґрунтування
--maximum-dead-containersзастаріло, щойно старі логи зберігаються поза контекстом контейнера
--maximum-dead-containers-per-containerзастаріло, щойно старі логи зберігаються поза контекстом контейнера
--minimum-container-ttl-durationзастаріло, щойно старі логи зберігаються поза контекстом контейнера

Пороги виселення

Ви можете вказати власні пороги виселення для kubelet, які він буде використовувати під час прийняття рішень про виселення. Ви можете налаштувати мʼякі та жорсткі пороги виселення.

Пороги виселення мають формат [eviction-signal][operator][quantity], де:

  • eviction-signal — це сигнал виселення, який використовується.
  • operator — це оператор відношення, який ви хочете використати, наприклад, < (менше ніж).
  • quantity — це кількість порогу виселення, наприклад, 1Gi. Значення quantity повинно відповідати представленню кількості, яке використовується в Kubernetes. Ви можете використовувати як літеральні значення, так і відсотки (%).

Наприклад, якщо вузол має загальний обсяг памʼяті 10GiB і ви хочете ініціювати виселення, коли доступна памʼять опуститься нижче 1GiB, ви можете визначити поріг виселення як memory.available<10% або memory.available<1Gi (використовувати обидва варіанти одночасно не можна).

Мʼякі пороги виселення

Мʼякий поріг виселення поєднує в собі поріг виселення з обовʼязковим пільговим періодом, вказаним адміністратором. Kubelet не виселяє Podʼи до закінчення пільгового періоду. Kubelet повертає помилку під час запуску, якщо ви не вказали пільговий період.

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

Ви можете використовувати такі налаштування у файлі конфігурації kubelet:

  • evictionSoft: Набір порогів виселення, наприклад, memory.available<1.5Gi, які можуть ініціювати виселення Podʼів, якщо вони утримуються протягом вказаного пільгового періоду.
  • evictionSoftGracePeriod: Набір пільгових періодів для виселення, наприклад, memory.available=1m30s, які визначають, як довго мʼякий поріг виселення повинен утримуватися, перш ніж буде ініційовано виселення Podʼа.
  • evictionMaxPodGracePeriod: Максимально дозволений період коректного завершення (у секундах) для використання під час завершення Podʼів у відповідь на досягнення мʼякого порогу виселення.

Жорсткі пороги виселення

Жорсткий поріг виселення не має пільгового періоду. Коли досягнуто жорсткого порогу виселення, kubelet негайно завершує роботу Podʼів без коректного завершення, щоб звільнити дефіцитний ресурс.

Ви можете використовувати поле evictionHard для налаштування набору жорстких порогів виселення, наприклад memory.available<1Gi.

Kubelet має наступні стандартні жорсткі пороги виселення:

  • memory.available<100Mi (для вузлів Linux)
  • memory.available<500Mi (для вузлів Windows)
  • nodefs.available<10%
  • imagefs.available<15%
  • nodefs.inodesFree<5% (для вузлів Linux)
  • imagefs.inodesFree<5% (для вузлів Linux)

Ці стандартні значення жорстких порогів виселення будуть встановлені лише у випадку, якщо жоден з параметрів не змінено. Якщо ви зміните значення будь-якого параметра, то значення інших параметрів не будуть успадковані як стандартні та будуть встановлені в нуль. Щоб надати власні значення, ви повинні вказати всі пороги відповідно. Ви також можете встановити параметр MergeDefaultEvictionSettings у true у файлі конфігурації kubelet. Якщо встановити true і змінити будь-який параметр, то інші параметри успадкують свої стандартні значення, а не 0.

Стандартні пороги виселення для containerfs.available і containerfs.inodesFree (для Linux-вузлів) будуть встановлені таким чином:

  • Якщо containerfs та nodefs посилаються на одну й ту саму базову файлову систему, то пороги для containerfs встановлюються такими ж, як і для nodefs.

  • Якщо containerfs та imagefs посилаються на одну й ту саму базову файлову систему, то пороги для containerfs встановлюються такими ж, як і для imagefs.

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

Інтервал моніторингу виселення

Kubelet оцінює пороги виселення, виходячи з налаштованого housekeeping-interval, стандартне значення якого — 10s.

Стани вузла

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

Kubelet зіставляє сигнали виселення зі станами вузла таким чином:

Стан вузлаСигнал виселенняОпис
MemoryPressurememory.availableДоступна памʼять на вузлі досягла порогового значення для виселення
DiskPressurenodefs.available, nodefs.inodesFree, imagefs.available, imagefs.inodesFree, containerfs.available, або containerfs.inodesFreeДоступне місце на диску та inode на кореневій файловій системі вузла, файловій системі образів або файловій системі контейнерів досягли порогового значення для виселення
PIDPressurepid.availableДоступні ідентифікатори процесів на (Linux) вузлі зменшилися нижче порогового значення для виселення

Панель управління також зіставляє ці стани вузла на taints.

Kubelet оновлює стани вузла відповідно до налаштованого nodeStatusUpdateFrequency, стандартне значення якого — 10s.

Коливання станів вузла

У деяких випадках вузли коливаються вище та нижче мʼяких порогів виселення, не утримуючись протягом визначених пільгових періодів. Це призводить до того, що стан вузла, про який повідомляється, постійно перемикається між true та false, що призводить до поганих рішень щодо виселення.

Для захисту від коливань можна використовувати прапорець eviction-pressure-transition-period, який контролює, як довго kubelet повинен чекати перш ніж перевести стан вузла в інше значення. Період переходу має стандартне значення 5m.

Звільнення ресурсів на рівні вузла

Kubelet намагається звільнити ресурси на рівні вузла перед виселенням Podʼів кінцевих користувачів.

Коли вузол повідомляє про стан DiskPressure, kubelet звільняє ресурси на рівні вузла на основі файлових систем на вузлі.

Без imagefs або containerfs

Якщо на вузлі є лише файлова система nodefs, яка досягає порогів виселення, kubelet звільняє місце на диску в такому порядку:

  1. Виконує збір сміття для мертвих Podʼів і контейнерів.
  2. Видаляє непотрібні образи.

З використанням imagefs

Якщо на вузлі є окрема файлова система imagefs, яку використовують середовища виконання контейнерів, то kubelet робить таке:

  • Якщо файлова система nodefs досягає порогів виселення, то kubelet виконує збір сміття для мертвих Podʼів і контейнерів.
  • Якщо файлова система imagefs досягає порогів виселення, то kubelet видаляє всі невикористані образи.

З imagefs та containerfs

Якщо на вузлі налаштована окрема containerfs разом із файловою системою imagefs, які використовують середовища виконання контейнерів, то kubelet намагатиметься звільнити ресурси таким чином:

  • Якщо файлова система containerfs досягає порогів виселення, kubelet виконує збір сміття для мертвих Podʼів і контейнерів.

  • Якщо файлова система imagefs досягає порогів виселення, kubelet видаляє всі невикористані образи.

Вибір Podʼів для виселення kubelet

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

Kubelet використовує наступні параметри для визначення порядку виселення Podʼів:

  1. Чи перевищує використання ресурсів Podʼа їх запити
  2. Пріоритет Podʼа
  3. Використання ресурсів Podʼа в порівнянні з запитами

В результаті kubelet ранжує та виселяє Podʼи в наступному порядку:

  1. Podʼи BestEffort або Burstable, де використання перевищує запити. Ці Podʼи виселяються на основі їх пріоритету, а потім за тим, наскільки їхнє використання перевищує запит.
  2. Podʼи Guaranteed та Burstable, де використання менше за запити, виселяються останніми, на основі їх пріоритету.

Примітка:

Kubelet не використовує клас QoS Podʼа для визначення порядку виселення. Ви можете використовувати клас QoS для оцінки найбільш ймовірного порядку виселення Podʼів під час звільнення ресурсів, таких як памʼять. Класифікація QoS не застосовується до запитів EphemeralStorage, тому вищезазначений сценарій не буде застосовуватися, якщо вузол, наприклад, перебуває під DiskPressure.

Podʼи Guaranteed гарантовані лише тоді, коли для всіх контейнерів вказано запити та обмеження, і вони є однаковими. Ці Podʼи ніколи не будуть виселятися через споживання ресурсів іншим Podʼом. Якщо системний демон (наприклад, kubelet та journald) споживає більше ресурсів, ніж було зарезервовано за допомогою розподілів systemReserved або kubeReserved, і на вузлі залишилися лише Guaranteed або Burstable Podʼи, які використовують менше ресурсів, ніж запити, то kubelet повинен вибрати Pod для виселення для збереження стабільності вузла та обмеження впливу нестачі ресурсів на інші Podʼи. У цьому випадку він вибере Podʼи з найнижчим пріоритетом першими.

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

Коли kubelet виселяє Podʼи у відповідь на вичерпання inode або ідентифікаторів процесів, він використовує відносний пріоритет Podʼів для визначення порядку виселення, оскільки для inode та PID не існує запитів.

Kubelet сортує Podʼи по-різному залежно від того, чи є на вузлі окрема файлова система imagefs чи containerfs:

Без imagefs або containerfs (nodefs і imagefs використовують одну і ту ж файлову систему)

  • Якщо nodefs ініціює виселення, kubelet сортує Podʼи на основі їх загального використання диска (локальні томи + логи та записуваний шар усіх контейнерів).

З imagefs (nodefs і imagefs — окремі файлові системи)

  • Якщо nodefs ініціює виселення, kubelet сортує Podʼи на основі використання nodefs (локальні томи + логи всіх контейнерів).

  • Якщо imagefs ініціює виселення, kubelet сортує Podʼи на основі використання записуваного шару всіх контейнерів.

З imagefs та containerfs (imagefs та containerfs розділені)

  • Якщо containerfs ініціює виселення, kubelet сортує Podʼи на основі використання containerfs (локальні томи + логи та записуваний шар усіх контейнерів).

  • Якщо imagefs ініціює виселення, kubelet сортує Podʼи на основі ранжування storage of images, яке відображає використання диска для даного образу.

Мінімальне звільнення ресурсів під час виселення

Примітка:

Станом на Kubernetes v1.37 ви не можете встановити власне значення для метрики containerfs.available. Конфігурація для цієї конкретної метрики буде встановлена автоматично, щоб відображати значення, задані для nodefs або imagefs, залежно від конфігурації.

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

Ви можете використовувати поле evictionMinimumReclaim у файлі конфігурації kubelet для налаштування мінімальної кількості звільнення для кожного ресурсу. Коли kubelet помічає нестачу ресурсу, він продовжує звільняти цей ресурс, доки не звільнить кількість, яку ви вказали.

Наприклад, наступна конфігурація встановлює мінімальні кількості звільнення:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
  memory.available: "500Mi"
  nodefs.available: "1Gi"
  imagefs.available: "100Gi"
evictionMinimumReclaim:
  memory.available: "0Mi"
  nodefs.available: "500Mi"
  imagefs.available: "2Gi"

У цьому прикладі, якщо сигнал nodefs.available досягає порога виселення, kubelet звільняє ресурс, доки сигнал не досягне порога в 1GiB, а потім продовжує звільняти мінімальну кількість 500MiB, доки доступний обсяг сховища nodefs не досягне 1,5GiB.

Аналогічно, kubelet намагається звільнити ресурс imagefs, доки значення imagefs.available не досягне 102Gi, що представляє 102GiB доступного сховища для образів контейнерів. Якщо кількість сховища, яку може звільнити kubelet, менша за 2GiB, kubelet нічого не звільняє.

Стандартно evictionMinimumReclaim дорівнює 0 для всіх ресурсів.

Поведінка в разі вичерпання памʼяті на вузлі

Якщо вузол зазнає події out of memory (OOM) до того, як kubelet зможе звільнити памʼять, вузол залежить від реагування oom_killer.

Kubelet встановлює значення oom_score_adj для кожного контейнера на основі QoS для Podʼа.

Якість обслуговуванняoom_score_adj
Guaranteed-997
BestEffort1000
Burstablemin(max(2, 1000 - (1000 × memoryRequestBytes) / machineMemoryCapacityBytes), 999)

Примітка:

Kubelet також встановлює значення oom_score_adj рівне -997 для будь-яких контейнерів у Podʼах, які мають пріоритет system-node-critical.

Якщо kubelet не може звільнити памʼять до того, як вузол зазнає OOM, oom_killer обчислює оцінку oom_score на основі відсотка памʼяті, яку він використовує на вузлі, а потім додає oom_score_adj, щоб отримати ефективну оцінку oom_score для кожного контейнера. Потім він примусово завершує роботу контейнера з найвищою оцінкою.

Це означає, що спочатку припиняється робота контейнерів у Podʼах низького QoS, які споживають велику кількість памʼяті в порівнянні з їх запитами на планування.

На відміну від виселення Podʼа, якщо роботу контейнера було припинено через вичерпання памʼяті, kubelet може перезапустити його відповідно до його restartPolicy.

Поради

У наступних розділах описано найкращі практики конфігурації виселення.

Ресурси, доступні для планування, та політики виселення

Під час конфігурації kubelet з політикою виселення слід переконатися, що планувальник не розміщуватиме Podʼи, які спричинять виселення через те, що вони одразу створюють тиск на памʼять.

Розгляньте такий сценарій:

  • Обсяг памʼяті вузла: 10GiB
  • Оператор бажає зарезервувати 10% обсягу памʼяті для системних демонів (ядро, kubelet тощо)
  • Оператор бажає виселяти Podʼи за використання памʼяті на рівні 95%, щоб зменшити кількість випадків OOM у системі.

Для цього файл конфігурації kubelet заповнюється таким чином:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
  memory.available: "500Mi"
systemReserved:
  memory: "1.5Gi"

У цій конфігурації systemReserved резервує 1,5GiB памʼяті для системи, що становить 10% від загальної памʼяті + кількість порогу виселення.

Вузол може досягти порогу виселення, якщо Pod використовує більше, ніж його запит, або якщо система використовує більше 1GiB памʼяті, що знижує значення memory.available нижче 500MiB і спричиняє спрацювання порогу.

DaemonSets та виселення внаслідок тиску на вузол

Пріоритет Podʼа — це основний фактор під час прийняття рішень про виселення. Якщо вам не потрібно, щоб kubelet виселяв Podʼи, які належать до DaemonSet, встановіть цим Podʼам достатньо високий пріоритет, вказавши відповідний priorityClassName в специфікації Podʼа. Ви також можете використовувати нижчий пріоритет або стандартне значення, щоб дозволити запускати Podʼи з цього DaemonSet тільки тоді, коли є достатньо ресурсів.

Відомі проблеми

У наступних розділах описані відомі проблеми, повʼязані з обробкою вичерпання ресурсів.

kubelet може помітити тиск на памʼять не одразу

Стандартно kubelet опитує cAdvisor, щоб регулярно збирати статистику використання памʼяті. Якщо використання памʼяті швидко зростає протягом цього інтервалу, kubelet може не помітити MemoryPressure достатньо швидко, і OOM killer все одно буде викликано.

Ви можете використовувати поле конфігурації kernelMemcgNotification, щоб увімкнути API сповіщення memcg в kubelet і отримувати повідомлення негайно, коли перетинається поріг.

Якщо ви прагнете не екстремального використання, а розумного рівня overcommit, робочим обхідним рішенням цієї проблеми є використання полів конфігурації kubeReserved та systemReserved для виділення памʼяті для системи.

active_file памʼять не вважається доступною памʼяттю

В Linux ядро відстежує кількість байтів файлової памʼяті в активному списку LRU (least recently used) як статистику active_file. Kubelet розглядає області памʼяті active_file як такі, що не підлягають звільненню. Для робочих навантажень, що інтенсивно використовують локальне сховище з блоковою підтримкою, включаючи ефемерне локальне сховище, кеші ядра для файлів і блоків означають, що багато нещодавно використаних сторінок кеша, ймовірно, будуть враховані як active_file. Якщо достатньо цих буферів блоків ядра перебувають на активному списку LRU, kubelet може сприйняти це як високе використання ресурсів та накласти на вузол taint тиску на памʼять, що ініціює виселення Podʼів.

Для отримання додаткової інформації дивіться https://github.com/kubernetes/kubernetes/issues/43916.

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

Що далі

9.17 - Можливості, оголошені вузлом

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.37
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.37. It was first available in the v1.35 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate NodeDeclaredFeatures, Kubernetes ignores it but does not report any error.

Вузли Kubernetes використовують оголошені можливості для повідомлення про доступність конкретних можливостей, які є новими або вмикаються через feature gate. Компоненти панелі управління використовують цю інформацію для прийняття кращих рішень. Kube-scheduler за допомогою втулка NodeDeclaredFeatures гарантує, що Podʼи розміщуються тільки на вузлах, які явно підтримують можливості, необхідні для Podʼа. Крім того, контролер допуску NodeDeclaredFeatureValidator перевіряє оновлення Podʼів на відповідність оголошеним можливостям вузла.

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

Як це працює

Оголошена можливість — це рядок, який вузол перелічує в полі .status.declaredFeatures обʼєкта Node. Кожна можливість, яку можна оголосити, ідентифікує можливість на рівні вузла, яка все ще проходить через етапи розвитку можливостей Kubernetes.

  1. Звіт про можливості Kubelet: під час запуску kubelet на кожному вузлі виявляє, які керовані можливості Kubernetes наразі увімкнені, і повідомляє про них у полі .status.declaredFeatures вузла. У це поле включаються лише можливості, що активно розробляються.

  2. Фільтрування планувальника: стандартний планувальник kube-scheduler використовує втулок NodeDeclaredFeatures. Цей втулок:

    • На етапі PreFilter перевіряє PodSpec, щоб визначити набір можливостей вузла, необхідних для Podʼа.
    • На етапі Filter перевіряє, чи можливості, перелічені в .status.declaredFeatures вузла, відповідають вимогам, визначеним для Podʼа. Podʼи не плануються на вузлах, які не мають необхідних можливостей.

    Власні планувальники також можуть використовувати поле .status.declaredFeatures для застосування подібних обмежень.

  3. Контроль допуску: Контролер допуску NodeDeclaredFeatureValidator може відхиляти Podʼи, які вимагають можливостей, не оголошених вузлом, до якого вони привʼязані, запобігаючи проблемам під час оновлення Podʼів.

  4. Допуск Kubelet: Як останній запобіжний захід, kubelet повторно перевіряє вимоги Podʼа до можливостей на відповідність можливостям, доступним на його вузлі, перед запуском Podʼа, і відхиляє Pod, якщо необхідна можливість відсутня.

  5. Очищення після GA: Коли можливість стає доступною на всіх вузлах кластера (після того, як можливість переходить до GA і підтримувана розбіжність версій між панеллю управління та вузлами минула), вузли припиняють оголошувати можливість. Це досягається шляхом встановлення максимальної версії (MaxVersion) для кожної оголошеної можливості: kubeletʼи новіші за цю версію припиняють перелічувати можливість в .status.declaredFeatures, а планувальник і контролер допуску вважають можливість загальнодоступною та припиняють її перевіряти. Можливість зрештою видаляється з набору оголошених можливостей у рамках стандартного очищення можливостей після GA.

Приклад оголошеної можливості

Оголошена можливість RestartAllContainersOnContainerExits вказує, що вузол підтримує перезапуск усіх контейнерів у Podʼі на місці. Коли функціональну можливість RestartAllContainersOnContainerExits увімкнено для kubelet, kubelet оголошує цю можливість у статусі свого Node:

apiVersion: v1
kind: Node
metadata:
  name: example-node
status:
  declaredFeatures:
  - RestartAllContainersOnContainerExits

Pod вимагає цю можливість, якщо один із його контейнерів вказує правило перезапуску з дією RestartAllContainers:

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
  - name: main
    image: registry.k8s.io/busybox:1.27.2
    restartPolicy: Never     # Якщо визначено правила, необхідно вказати політику перезапуску контейнера
    restartPolicyRules:      # Перезапустити весь Pod на місці за коду виходу 42
    - action: RestartAllContainers
      exitCodes:
        operator: In
        values: [42]

Під час планування цього Podʼа kube-scheduler розглядає лише вузли, які перелічують RestartAllContainersOnContainerExits у своєму .status.declaredFeatures. У кластері, де лише деякі вузли мають увімкнену цю функціональну можливість (наприклад, у середині оновлення кластера), це запобігає призначенню Podʼа вузлу, чий kubelet ігнорував би правило перезапуску.

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

Що далі

10 - Керування ресурсами

Як Kubernetes представляє, запитує, виділяє та обмежує ресурси, які споживають робочі навантаження.

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

10.1 - Менеджери ресурсів

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

Менеджер топології

This is a stable feature in Kubernetes, and has been since the 1.27 release. You can no longer toggle this feature (the associated feature gate has been removed).

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

Менеджер CPU

This is a stable feature in Kubernetes, and has been since the 1.26 release. You can no longer toggle this feature (the associated feature gate has been removed).

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

Політики призначення CPU Podʼам

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

Стандартно kubelet використовує квоту CFS для застосування обмежень CPU Podʼів. Коли на вузлі працює багато Podʼів, залежних від CPU, робоче навантаження може переміщуватися між різними ядрами CPU залежно від того, чи зазнає Pod тротлінгу і які ядра CPU доступні на момент планування. Багато робочих навантажень не чутливі до цієї міграції і тому нормально працюють без будь-якого втручання.

Однак для робочих навантажень, де спорідненість кешу CPU та затримка планування суттєво впливають на продуктивність, kubelet дозволяє використовувати альтернативні політики керування CPU, щоб визначати деякі вподобання щодо розміщення на вузлі. Це реалізовано за допомогою менеджера CPU та його політики. Доступні дві політики:

  • none: політика none явно вмикає наявну стандартну схему спорідненості CPU, не забезпечуючи спорідненості понад те, що автоматично робить планувальник ОС. Обмеження використання CPU для Podʼів Guaranteed та Podʼів Burstable застосовуються за допомогою квоти CFS.
  • static: політика static дозволяє контейнерам у Podʼах Guaranteed із цілочисельними requests CPU отримувати доступ до ексклюзивних CPU на вузлі. Ця ексклюзивність забезпечується за допомогою контролера cgroup cpuset.

Примітка:

Системні сервіси, такі як середовище виконання контейнерів і сам kubelet, можуть і надалі працювати на цих ексклюзивних CPU. Ексклюзивність поширюється лише на інші Podʼи.

Менеджер CPU не підтримує вимкнення та ввімкнення CPU під час роботи.

Політика static

Політика static забезпечує детальніше керування CPU та ексклюзивне призначення CPU. Ця політика керує спільним пулом CPU, який спочатку містить усі CPU вузла. Кількість CPU, доступних для ексклюзивного виділення, дорівнює загальній кількості CPU вузла за вирахуванням резервувань CPU, заданих конфігурацією kubelet. CPU, зарезервовані цими параметрами, беруться в цілій кількості з початкового спільного пулу за зростанням фізичного ідентифікатора ядра. Цей спільний пул є набором CPU, на яких працюють усі контейнери в Podʼах BestEffort та Burstable. Контейнери в Podʼах Guaranteed із дробовими requests CPU також працюють на CPU зі спільного пулу. Ексклюзивні CPU отримують лише контейнери, які є частиною Podʼа Guaranteed і мають цілочисельні requests CPU.

Примітка:

kubelet вимагає резервування CPU більше за нуль, коли ввімкнено політику static. Це тому, що нульове резервування CPU дозволило б спільному пулу стати порожнім.

Коли Podʼи Guaranteed, контейнери яких відповідають вимогам статичного призначення, плануються на вузол, CPU вилучаються зі спільного пулу та розміщуються в cpuset контейнера. Квота CFS не використовується для обмеження використання CPU цими контейнерами, оскільки їхнє використання обмежене самим доменом планування. Іншими словами, кількість CPU в cpuset контейнера дорівнює цілочисельному limit CPU, вказаному в специфікації Podʼа. Це статичне призначення підвищує спорідненість CPU та зменшує кількість перемикань контексту через тротлінг для робочого навантаження, залежного від CPU.

Розгляньмо контейнери в наведених нижче специфікаціях Podʼів:

spec:
  containers:
  - name: nginx
    image: nginx

Наведений вище Pod працює в класі QoS BestEffort, оскільки не вказано жодних requests чи limits ресурсів. Він працює в спільному пулі.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

Наведений вище Pod працює в класі QoS Burstable, оскільки requests ресурсів не дорівнюють limits, а кількість cpu не вказана. Він працює в спільному пулі.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "100Mi"
        cpu: "1"

Наведений вище Pod працює в класі QoS Burstable, оскільки requests ресурсів не дорівнюють limits. Він працює в спільному пулі.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "200Mi"
        cpu: "2"

Наведений вище Pod працює в класі QoS Guaranteed, оскільки requests дорівнюють limits. І обмеження ресурсу CPU контейнера є цілим числом, більшим або рівним одиниці. Контейнер nginx отримує 2 ексклюзивні CPU.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "1.5"
      requests:
        memory: "200Mi"
        cpu: "1.5"

Наведений вище Pod працює в класі QoS Guaranteed, оскільки requests дорівнюють limits. Але обмеження ресурсу CPU контейнера є дробовим. Він працює в спільному пулі.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"

Наведений вище Pod працює в класі QoS Guaranteed, оскільки вказано лише limits, а requests прирівнюються до limits, коли їх не вказано явно. І обмеження ресурсу CPU контейнера є цілим числом, більшим або рівним одиниці. Контейнер nginx отримує 2 ексклюзивні CPU.

Параметри політики static

Ось доступні параметри політики керування CPU static, перелічені в алфавітному порядку:

align-by-socket (alpha, стандартно приховано)
Вирівнювання CPU за межею фізичного пакета / сокета, а не за логічними межами NUMA (доступно з Kubernetes v1.25)
distribute-cpus-across-cores (alpha, стандартно приховано)
Розподіл віртуальних ядер, які іноді називають апаратними потоками, по різних фізичних ядрах (доступно з Kubernetes v1.31)
distribute-cpus-across-numa (beta, стандартно видимо)
Розподіл CPU по різних доменах NUMA з метою рівномірного балансу між вибраними доменами (доступно з Kubernetes v1.23)
full-pcpus-only (GA, стандартно видимо)
Завжди виділяти повні фізичні ядра (доступно з Kubernetes v1.22, GA з Kubernetes v1.33)
strict-cpu-reservation (GA, стандартно видимо)
Запобігання запуску всіх Podʼів незалежно від їхнього класу якості обслуговування на зарезервованих CPU (доступно з Kubernetes v1.32, GA з Kubernetes v1.35)
prefer-align-cpus-by-uncorecache (GA, стандартно видимо)
Вирівнювання CPU за межею кешу uncore (Last-Level) за принципом best-effort (доступно з Kubernetes v1.32)

Ви можете вмикати або вимикати групи параметрів залежно від їхнього рівня зрілості за допомогою таких функціональних можливостей:

  • CPUManagerPolicyBetaOptions (стандартно увімкнено). Вимкніть, щоб приховати параметри рівня beta.
  • CPUManagerPolicyAlphaOptions (стандартно вимкнено). Увімкніть, щоб показати параметри рівня alpha.

Вам все одно потрібно ввімкнути кожен параметр за допомогою поля cpuManagerPolicyOptions у файлі конфігурації kubelet.

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

full-pcpus-only

Якщо вказано параметр політики full-pcpus-only, політика static завжди виділятиме повні фізичні ядра. Стандартно, без цього параметра, політика static виділяє CPU за принципом найкращого збігу з урахуванням топології. На системах з увімкненим SMT політика може виділяти окремі віртуальні ядра, які відповідають апаратним потокам. Це може призвести до того, що різні контейнери використовуватимуть ті самі фізичні ядра; така поведінка, у свою чергу, сприяє проблемі шумних сусідів. З увімкненим параметром kubelet допустить Pod лише тоді, коли запит CPU всіх його контейнерів можна задовольнити виділенням повних фізичних ядер. Якщо Pod не проходить допуск, він переходить у стан Failed із повідомленням SMTAlignmentError.

distribute-cpus-across-numa

Якщо вказано параметр політики distribute-cpus-across-numa, політика static рівномірно розподілятиме CPU між вузлами NUMA у випадках, коли для виконання виділення потрібно більше одного вузла NUMA. Стандартно CPUManager розміщує CPU на одному вузлі NUMA, доки він не заповниться, а залишкові CPU просто переходять на наступний вузол NUMA. Це може спричинити небажані вузькі місця в паралельному коді, що використовує барʼєри (та подібні примітиви синхронізації), оскільки такий код зазвичай працює лише так швидко, як його найповільніший робочий процес (який сповільнюється через те, що принаймні на одному вузлі NUMA доступно менше CPU). Рівномірно розподіляючи CPU між вузлами NUMA, розробники застосунків можуть легше забезпечити, щоб жоден робочий процес не страждав від ефектів NUMA більше за інші, покращуючи загальну продуктивність таких застосунків.

align-by-socket

Якщо вказано параметр політики align-by-socket, CPU вважатимуться вирівняними за межею сокета під час вирішення того, як виділити CPU контейнеру. Стандартно CPUManager вирівнює виділення CPU за межею NUMA, що може призвести до зниження продуктивності, якщо для виконання виділення CPU потрібно взяти більш ніж з одного вузла NUMA. Хоча він намагається забезпечити виділення всіх CPU з мінімальної кількості вузлів NUMA, немає гарантії, що ці вузли NUMA будуть на одному сокеті. Вказавши CPUManager явно вирівнювати CPU за межею сокета, а не NUMA, ми можемо уникнути таких проблем. Зверніть увагу, що цей параметр політики несумісний із політикою single-numa-node TopologyManager і не застосовується до апаратного забезпечення, де кількість сокетів більша за кількість вузлів NUMA.

distribute-cpus-across-cores

Якщо вказано параметр політики distribute-cpus-across-cores, політика static намагатиметься виділяти віртуальні ядра (апаратні потоки) на різних фізичних ядрах. Стандартно CPUManager намагається розмістити CPU на якомога меншій кількості фізичних ядер, що може призвести до конкуренції між CPU на одному фізичному ядрі та спричинити вузькі місця продуктивності. Увімкнення політики distribute-cpus-across-cores забезпечує розподіл CPU на якомога більшій кількості фізичних ядер, зменшуючи конкуренцію на одному фізичному ядрі та покращуючи загальну продуктивність. Однак важливо зазначити, що ця стратегія може бути менш ефективною за високого навантаження системи. У таких умовах користь від зменшення конкуренції зменшується. Натомість стандартна поведінка може допомогти зменшити накладні витрати на міжядерну комунікацію, потенційно забезпечуючи кращу продуктивність за високого навантаження.

strict-cpu-reservation

Параметр reservedSystemCPUs у KubeletConfiguration або застарілий параметр командного рядка kubelet --reserved-cpus визначає явний набір CPU для системних демонів ОС і демонів Kubernetes. Докладніше про цей параметр читайте на сторінці Явно зарезервований список CPU. Стандартно ця ізоляція реалізована лише для Podʼів guaranteed із цілочисельними запитами CPU, але не для Podʼів burstable та best-effort (і Podʼів guaranteed із дробовими запитами CPU). Допуск лише порівнює запити CPU з доступними для розподілу CPU. Оскільки обмеження CPU вище за запит, стандартна поведінка дозволяє Podʼам burstable та best-effort вичерпати місткість reservedSystemCPUs і спричинити голодування сервісів ОС хоста в реальних розгортаннях. Якщо ввімкнено параметр політики strict-cpu-reservation, політика static не дозволить жодному робочому навантаженню використовувати ядра CPU, вказані в reservedSystemCPUs.

prefer-align-cpus-by-uncorecache

Якщо вказано політику prefer-align-cpus-by-uncorecache, політика static виділятиме ресурси CPU для окремих контейнерів так, щоб усі CPU, призначені контейнеру, використовували той самий блок кешу uncore (також відомий як кеш останнього рівня, Last-Level Cache або LLC). Стандартно CPUManager щільно розподіляє CPU, що може призвести до того, що контейнери отримають CPU з кількох кешів uncore. Цей параметр дозволяє CPUManager виділяти CPU так, щоб максимізувати ефективне використання кешу uncore. Виділення виконується за принципом best-effort з метою розмістити якомога більше CPU в межах одного кешу uncore. Якщо вимога контейнера до CPU перевищує місткість CPU одного кешу uncore, CPUManager мінімізує кількість використаних кешів uncore, щоб підтримувати оптимальне вирівнювання. Окремі робочі навантаження можуть виграти в продуктивності завдяки зменшенню затримки між кешами та впливу шумних сусідів на рівні кешу. Якщо CPUManager не може оптимально вирівняти CPU, хоча вузол має достатньо ресурсів, контейнер однаково буде допущено зі стандартною щільною поведінкою.

Менеджер памʼяті

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.32; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.32. It was first available in the v1.21 release.

Менеджер памʼяті — це компонент kubelet, який забезпечує ексклюзивне виділення ресурсів памʼяті. Для прийняття рішень щодо призначення ресурсів він консультується з менеджером топології. Щоб дізнатися більше, прочитайте Керування політиками управління памʼяттю на вузлі.

Політики призначення памʼяті Podʼам

Менеджер памʼяті Kubernetes виділяє ресурси оперативної памʼяті (памʼять і, за потреби, великі сторінки Linux) для Podʼів із класом QoS Guaranteed.

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

Крім того, менеджер памʼяті забезпечує виділення запитаної Podʼом памʼяті з мінімальної кількості вузлів NUMA.

Щоб дізнатися більше, прочитайте Керування політиками управління памʼяттю на вузлі.

Менеджер пристроїв

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.26

Менеджер пристроїв — це компонент kubelet, який виділяє апаратні пристрої Podʼам за допомогою API втулків пристроїв. Для прийняття рішень щодо призначення ресурсів він консультується з менеджером топології, використовуючи інформацію про топологію, надану втулками пристроїв. Щоб дізнатися більше, прочитайте Інтеграція втулків пристроїв з менеджером топології.

Менеджери ресурсів на рівні Podʼів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the PodLevelResourceManagers feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Підтримка ресурсів на рівні Podʼів для менеджерів ресурсів kubelet (топології, CPU та памʼяті) дозволяє менеджерам ресурсів використовувати .spec.resources безпосередньо для рішень щодо вирівнювання за NUMA та ексклюзивного виділення.

Щоб дізнатися більше, перегляньте окрему сторінку концепції Менеджери ресурсів на рівні Podʼів або прочитайте, як призначати ресурси CPU та памʼяті на рівні Podʼів.

Що далі

10.2 - Менеджери ресурсів на рівні Podʼів

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.37; (стандартно вимкнено)
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the PodLevelResourceManagers feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Підтримка ресурсів на рівні Podʼів для наявних менеджерів ресурсів (топології, CPU та памʼяті) розширює їх для обробки специфікацій ресурсів на рівні Podʼів. Коли цю можливість увімкнено (через функціональні можливості PodLevelResources та PodLevelResourceManagers), менеджери ресурсів можуть використовувати .spec.resources безпосередньо як основу для своїх рішень щодо виділення, переходячи від суто поконтейнерної моделі виділення до моделі, орієнтованої на Pod. Ця схема розділення впроваджує гнучкішу й потужнішу модель керування ресурсами, особливо для чутливих до продуктивності робочих навантажень. Вона дозволяє визначати гібридні моделі виділення, у яких деякі контейнери в Podʼі отримують ексклюзивні, вирівняні за NUMA ресурси, тоді як інші спільно використовують решту ресурсів зі спільного пулу на рівні Podʼа.

Щоб на практиці налаштувати менеджери ресурсів kubelet з ресурсами на рівні Podʼів і спостерігати за поведінкою виділення, скористайтеся навчальним матеріалом Використання ресурсів на рівні Podʼів з менеджерами ресурсів kubelet.

Щоб зрозуміти менеджери ресурсів на рівні Podʼів, корисно порівняти їх із традиційною моделлю, орієнтованою на контейнери. Раніше виділення ресурсів kubelet працювало суто за принципом «все або нічого»: щоб отримати ексклюзивні, вирівняні за NUMA ресурси для вашого робочого навантаження, кожен контейнер у Podʼі мав бути Guaranteed (із запитами, що дорівнюють обмеженням і для CPU, і для памʼяті).

Менеджери ресурсів на рівні Podʼів використовують .spec.resources, щоб забезпечити гнучке розділення на основі налаштованої області дії менеджера топології:

  • Область дії pod: kubelet виділяє та вирівнює за NUMA єдину «бульбашку» Podʼа для всього Podʼа на основі .spec.resources. Контейнери, які запитують ексклюзивні виділення, вирізають окремі частки всередині цієї «бульбашки», тоді як усі інші контейнери спільно використовують решту місткості «бульбашки» у спільному пулі, ізольованому на рівні Podʼа.
  • Область дії container: Вмикає гібридну модель виділення. kubelet дозволяє окремим контейнерам отримувати ексклюзивні, вирівняні за NUMA ресурси безпосередньо з доступного для розподілу пулу вузла, водночас використовуючи верхню межу .spec.resources Podʼа для обмеження сукупного споживання. Це дозволяє sidecar-контейнерам працювати в загальному спільному пулі вузла, не вимагаючи, щоб кожен контейнер у Podʼі був Guaranteed.

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

Глосарій

Специфікація ресурсів на рівні Podʼа
Бюджет ресурсів, визначений на рівні Podʼа в .spec.resources, який задає сукупні запити та обмеження для всього Podʼа.
Контейнер Guaranteed
Контейнер, запити ресурсів якого дорівнюють його обмеженням і для CPU (ексклюзивне виділення CPU вимагає додатного цілого значення), і для памʼяті. Відповідно до наявної поведінки kubelet, це робить контейнер придатним для ексклюзивного виділення ресурсів менеджерами ресурсів.
Ексклюзивна частка
Виділена частина ресурсів (наприклад, конкретні CPU або сторінки памʼяті), призначена виключно одному контейнеру, що забезпечує ізоляцію від інших контейнерів.
Спільний пул Podʼа
Підмножина виділених ресурсів Podʼа, яка залишається після резервування всіх ексклюзивних часток. Ці ресурси спільно використовуються всіма контейнерами в Podʼі, які не отримують ексклюзивного виділення. Хоча контейнери в цьому пулі спільно використовують ресурси один з одним, вони суворо ізольовані від ексклюзивних часток і загального спільного пулу вузла.

Як працюють менеджери ресурсів на рівні Podʼів

Менеджери ресурсів CPU та Memory працюють по-різному залежно від налаштованої області дії менеджера топології.

Область дії pod менеджера топології та ресурси на рівні Podʼів

Коли область дії менеджера топології встановлено в pod, kubelet виконує єдине вирівнювання за NUMA для всього Podʼа на основі бюджету ресурсів, визначеного в .spec.resources.

Отриманий вирівняний за NUMA пул ресурсів потім розділяється:

  1. Ексклюзивні частки: Контейнери, які визначають ресурси Guaranteed (запити дорівнюють обмеженням і для CPU, і для памʼяті, а запит CPU є додатним цілим числом), отримують ексклюзивні частки із загального виділення Podʼа.
  2. Спільний пул Podʼа: Решта ресурсів утворює спільний пул для всіх інших контейнерів у Podʼі, які не отримують ексклюзивного виділення. Хоча контейнери в цьому пулі спільно використовують ресурси один з одним, вони суворо ізольовані від ексклюзивних часток і загального спільного пулу вузла.

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

Це дозволяє розміщувати разом контейнери, які потребують ексклюзивних ресурсів (наприклад, високопродуктивний основний застосунок), з тими, які цього не потребують (наприклад, sidecar-контейнери для логування або моніторингу), і все це в межах одного вирівняного за NUMA Podʼа.

Розгляньмо контейнери в наведеній нижче специфікації Podʼа, де область дії менеджера топології — pod, а Pod має загальний бюджет у 4 CPU. main-app запитує ексклюзивну частку у 2 CPU, тоді як sidecar-контейнери спільно використовують решту 2 CPU у спільному пулі Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: pod-scope-mixed
  annotations:
    kubernetes.io/description: "A pod demonstrating pod-level scope where one container gets exclusive resources and others share the remaining pod resources in a shared pool."
spec:
  # At Pod level, the Pod has CPU request equal to limits and memory request
  # also equal to memory limits. The main-app container meets the requirements
  # for the Guaranteed QoS class at container level, and the sidecar containers
  # don't specify any resource request. Under pod scope, this means that the
  # kubelet could statically assign 4 CPUs to the overall Pod, of which 2 are
  # assigned exclusively to the main-app container, and the remaining 2 are
  # shared by the sidecars in the pod's shared pool.
  resources:
    requests:
      cpu: "4"
      memory: "4Gi"
    limits:
      cpu: "4"
      memory: "4Gi"
  initContainers:
  - name: metrics-sidecar
    # Note: This is a placeholder image for demonstration purposes, not an
    #actual metrics helper.
    image: registry.k8s.io/pause:3.9
    restartPolicy: Always
  - name: logging-sidecar
    # Note: This is a placeholder image for demonstration purposes, not an
    # actual logging agent.
    image: registry.k8s.io/pause:3.9
    restartPolicy: Always
  containers:
  - name: main-app
    # Note: This is a placeholder image for demonstration purposes.
    image: registry.k8s.io/pause:3.9
    resources:
      requests:
        cpu: "2"
        memory: "2Gi"
      limits:
        cpu: "2"
        memory: "2Gi"

Важливі міркування:

Під час використання ресурсів на рівні Podʼів з областю дії pod менеджера топології є кілька важливих міркувань:

  • Обмеження порожнього спільного пулу: Ця конфігурація не дозволяє специфікації Podʼа, які призвели б до порожнього спільного пулу Podʼа, якщо є контейнери, які потребують такого пулу. Якщо сума запитів ресурсів усіх контейнерів, які є Guaranteed, точно дорівнює загальному бюджету ресурсів, і є принаймні один інший контейнер, який потребує спільного пулу, kubelet відхиляє Pod під час допуску.

    Наприклад, наведений нижче Pod запитує бюджет на рівні Podʼа у 4 CPU. main-app потребує ексклюзивних 3 CPU, а metrics-sidecar — ексклюзивного 1 CPU. Оскільки у спільному пулі для logging-sidecar залишається 0 CPU, kubelet відхиляє цей Pod (та сама перевірка застосовується до памʼяті):

    apiVersion: v1
      kind: Pod
      metadata:
        name: empty-shared-pool
        annotations:
          kubernetes.io/description: "A pod demonstrating a configuration that is rejected because exclusive containers consume the entire pod resource budget, leaving no resources for the remaining container in the shared pool."
      spec:
        # At Pod level, the Pod has CPU request equal to limits and memory request
        # also equal to memory limits. The main-app and metrics-sidecar containers
        # meet the requirements for the Guaranteed QoS class at container level, and
        # the logging-sidecar container doesn't specify any resource request. Because
        # the Guaranteed containers consume the entire pod resource budget,
        # leaving 0 CPUs for the shared pool required by logging-sidecar, this pod
        # will be rejected at admission.
        resources:
          requests:
            cpu: "4"
            memory: "4Gi"
          limits:
            cpu: "4"
            memory: "4Gi"
        initContainers:
        - name: metrics-sidecar
          # Note: This is a placeholder image for demonstration purposes, not an
          # actual metrics helper.
          image: registry.k8s.io/pause:3.9
          restartPolicy: Always
          resources:
            requests:
              cpu: "1"
              memory: "1Gi"
            limits:
              cpu: "1"
              memory: "1Gi"
        - name: logging-sidecar
          # Note: This is a placeholder image for demonstration purposes, not an
          # actual logging agent.
          image: registry.k8s.io/pause:3.9
          restartPolicy: Always
        containers:
        - name: main-app
          # Note: This is a placeholder image for demonstration purposes.
          image: registry.k8s.io/pause:3.9
          resources:
            requests:
              cpu: "3"
              memory: "3Gi"
            limits:
              cpu: "3"
              memory: "3Gi"
      
  • Втрачені ресурси: Будь-які ресурси, надмірно виділені під час використання області дії pod (сума запитів контейнерів менша за бюджет на рівні Podʼа, і немає контейнерів спільного пулу, або контейнери спільного пулу не повністю використовують решту), залишаються призначеними та зарезервованими для Podʼа, фактично марнуючись протягом усього виконання Podʼа.

  • Постійний пул: Загальний пул ресурсів Podʼа (вирівнювання за NUMA та загальна зарезервована місткість) є постійним. Якщо контейнер спільного пулу аварійно завершується та перезапускається, загальне резервування ресурсів Podʼа залишається надійно закріпленим на вузлі. Вузол повертає ресурси до свого загального пулу лише тоді, коли завершується весь Pod.

Область дії container менеджера топології та ресурси на рівні Podʼів

Коли область дії менеджера топології встановлено в container, kubelet оцінює кожен контейнер окремо для ексклюзивного виділення.

Якщо весь Pod досягає класу QoS Guaranteed (завдяки відповідним значенням у .spec.resources на рівні Podʼа), ви можете поєднувати контейнери:

  • Контейнери з власними запитами Guaranteed отримують ексклюзивні, вирівняні за NUMA ресурси.
  • Інші контейнери в Podʼі, які не визначають запити Guaranteed, працюють у спільному пулі вузла.
  • На сукупне споживання ресурсів усіма контейнерами все одно діють обмеження .spec.resources Podʼа.

Ця область дії корисна, коли у вас є інфраструктурний sidecar-контейнер, який має бути вирівняний за конкретним вузлом NUMA для доступу до пристроїв, тоді як основне робоче навантаження може працювати в загальному спільному пулі вузла.

Розгляньмо контейнери в наведеній нижче специфікації Podʼа, де область дії менеджера топології — container, а Pod представляє робоче навантаження з інфраструктурним sidecar-контейнером і двома робочими процесами застосунку із загальним бюджетом у 4 CPU. infrastructure-sidecar отримує ексклюзивну, вирівняну за NUMA частку у 2 CPU. Два робочі процеси застосунку (worker-1 та worker-2) працюють у загальному спільному пулі вузла:

apiVersion: v1
kind: Pod
metadata:
  name: container-scope-mixed
  annotations:
    kubernetes.io/description: "A pod demonstrating container-level scope where one container gets exclusive resources and others run in the node's shared pool."
spec:
  # At Pod level, the Pod has CPU request equal to limits and memory request
  # also equal to memory limits. The infrastructure-sidecar container meets the
  # requirements for the Guaranteed QoS class at container level, and the worker
  # containers don't specify any resource request. Under container scope, the
  # kubelet evaluates containers individually for exclusive allocation. This
  # means the infrastructure-sidecar gets an exclusive 2 CPU slice, while the
  # worker containers run in the node's general shared pool, all while bounded
  # by the overall pod limits.
  resources:
    requests:
      cpu: "4"
      memory: "4Gi"
    limits:
      cpu: "4"
      memory: "4Gi"
  initContainers:
  - name: infrastructure-sidecar
    # Note: This is a placeholder image for demonstration purposes, not an
    # actual infrastructure helper.
    image: registry.k8s.io/pause:3.9
    restartPolicy: Always
    resources:
      requests:
        cpu: "2"
        memory: "2Gi"
      limits:
        cpu: "2"
        memory: "2Gi"
  containers:
  - name: worker-1
    # Note: This is a placeholder image for demonstration purposes.
    image: registry.k8s.io/pause:3.9
  - name: worker-2
    # Note: This is a placeholder image for demonstration purposes.
    image: registry.k8s.io/pause:3.9

Квота CPU (CFS)

Під час виконання змішаних робочих навантажень у межах Podʼа kubelet забезпечує ізоляцію по-різному залежно від виділення:

  • Ексклюзивні контейнери: Для контейнерів з ексклюзивними частками CPU застосування квоти CPU CFS вимкнено, що дозволяє їм працювати без тротлінгу планувальником Linux.
  • Контейнери спільного пулу Podʼа: Для контейнерів у спільному пулі Podʼа квоти CPU CFS увімкнено, що гарантує, що вони не споживають більше за залишковий бюджет Podʼа, і не дає їм заважати ексклюзивним контейнерам.

Постійний пул і перезапуски

Загальний пул ресурсів Podʼа (вирівнювання за NUMA та загальна зарезервована місткість) є постійним. Якщо контейнер у спільному пулі Podʼа аварійно завершується та перезапускається, загальне резервування ресурсів Podʼа залишається надійно закріпленим на вузлі. Вузол повертає ресурси до свого загального пулу лише тоді, коли завершується весь Pod.

Пониження версії kubelet та контрольні точки стану

У Kubernetes 1.36 увімкнення PodLevelResourceManagers оновило внутрішні файли контрольних точок стану kubelet (cpu_manager_state та memory_manager_state) до формату, який старіші версії kubelet не можуть завантажити. Якщо ви понизите версію kubelet 1.36 після активного використання, старіший kubelet не зможе запуститися; ви маєте виконати drain вузла, видалити ці файли контрольних точок і перезапустити kubelet.

У Kubernetes 1.37 файли контрольних точок використовують формат, сумісний із майбутніми версіями, щоб запобігти збоям запуску під час пониження версії, хоча версії kubelet 1.36 не відновлюють активні призначення ресурсів на рівні Podʼів. Докладні відомості про формати контрольних точок і відновлення дивіться в довіднику менеджерів ресурсів на рівні Podʼів.

Спостережуваність і метрики

Ви можете контролювати поведінку та справність менеджерів ресурсів як для виділень на рівні контейнерів, так і на рівні Podʼів за допомогою таких метрик kubelet (увімкнених через функціональну можливість PodLevelResourceManagers):

  • resource_manager_allocations_total: підраховує загальну кількість ексклюзивних виділень ресурсів, виконаних менеджером. Мітка source ("pod" або "node") розрізняє виділення з пулу на рівні вузла та попередньо виділеного пулу на рівні Podʼа.
  • resource_manager_allocation_errors_total: підраховує помилки, що виникають під час ексклюзивного виділення ресурсів, розрізняючи їх за передбачуваним джерелом source виділення ("pod" або "node").
  • resource_manager_container_assignments: відстежує сукупну кількість контейнерів, яким буде надано конкретний тип призначення ресурсів. Мітка assignment_type ("node_exclusive", "pod_exclusive", "pod_shared") забезпечує видимість того, скільки контейнерів працюють з ексклюзивними ресурсами (з пулу вузла або Podʼа) порівняно зі спільним пулом на рівні Podʼа.

PodResources API

У Kubernetes 1.37 локальний для вузла gRPC API PodResources kubelet включає виділення ресурсів на рівні Podʼів, коли увімкнено PodLevelResourceManagers. Локальні для вузла агенти моніторингу та втулки пристроїв можуть запитувати призначення верхнього рівня для Podʼів (cpu_ids та memory), уникаючи подвійного підрахунку виділень на рівні контейнерів.

Повні схеми API, маски полів і таблиці звітування за областями дії дивіться в довіднику менеджерів ресурсів на рівні Podʼів.

Обмеження та застереження

  • Функціональність реалізована лише для політики static менеджера CPU та політики Static менеджера памʼяті. Зверніть увагу, що політика BestEffort не підтримується для менеджера памʼяті.
  • Ця можливість підтримується лише на вузлах Linux. На вузлах Windows менеджери ресурсів для виділень на рівні Podʼів нічого не робитимуть (no-op).

Що далі

11 - Windows у Kubernetes

Kubernetes підтримує вузли, на яких працює Microsoft Windows.

Kubernetes підтримує робочі вузли, що працюють на Linux або Microsoft Windows.

🛇 Цей елемент посилається на сторонній проєкт або продукт, який не є частиною Kubernetes. Докладніше

CNCF та її материнська організація Linux Foundation дотримуються нейтрального щодо постачальників підходу до сумісності. Ви можете приєднати свій сервер Windows до кластера Kubernetes як робочий вузол.

Ви можете встановити та налаштувати kubectl у Windows незалежно від операційної системи, яку ви використовуєте у своєму кластері.

Якщо ви використовуєте вузли Windows, ви можете прочитати:

або, для загального огляду, прочитайте:

11.1 - Контейнери Windows у Kubernetes

Застосунки Windows становлять значну частину сервісів та застосунків, що працюють у багатьох організаціях. Контейнери Windows дають змогу інкапсулювати процеси та пакувати залежності, що спрощує використання практик DevOps і дотримання патернів cloud native для застосунків Windows.

Організаціям, що інвестували в застосунки на базі Windows і Linux, не потрібно шукати окремі оркестратори для керування своїми робочими навантаженнями, що підвищує операційну ефективність їхніх розгортань незалежно від операційної системи.

Вузли Windows у Kubernetes

Щоб увімкнути оркестрування контейнерів Windows у Kubernetes, додайте вузли Windows до вашого наявного кластера Linux. Планування контейнерів Windows у Podʼах у Kubernetes подібне до планування контейнерів на базі Linux.

Щоб запускати контейнери Windows, ваш кластер Kubernetes має містити кілька операційних систем. Хоча панель управління можна запускати лише на Linux, ви можете розгортати робочі вузли, що працюють як на Windows, так і на Linux.

Вузли Windows підтримуються за умови, що операційною системою є Windows Server 2022 або Windows Server 2025.

У цьому документі термін контейнери Windows означає контейнери Windows з ізоляцією на рівні процесів. Kubernetes не підтримує запуск контейнерів Windows з ізоляцією Hyper-V.

Сумісність та обмеження

Деякі можливості вузлів доступні лише за використання певного середовища виконання контейнерів; інші недоступні на вузлах Windows, зокрема:

  • HugePages: не підтримуються для контейнерів Windows
  • Привілейовані контейнери: не підтримуються для контейнерів Windows. Контейнери HostProcess пропонують схожу функціональність.
  • TerminationGracePeriod: вимагає containerD

Підтримуються не всі можливості спільних просторів імен. Докладніше дивіться в розділі Сумісність API.

Докладніше про версії Windows, з якими тестується Kubernetes, дивіться в розділі Сумісність версій операційної системи Windows.

З точки зору API та kubectl контейнери Windows поводяться майже так само, як контейнери на базі Linux. Проте існують деякі помітні відмінності в ключовій функціональності, описані в цьому розділі.

Порівняння з Linux

Ключові елементи Kubernetes працюють у Windows так само, як і в Linux. У цьому розділі описано кілька ключових абстракцій робочих навантажень і те, як вони відображаються у Windows.

  • Podʼи

    Pod — це базовий будівельний блок Kubernetes: найменша та найпростіша одиниця в обʼєктній моделі Kubernetes, яку ви створюєте або розгортаєте. Не можна розгортати контейнери Windows і Linux в одному Podʼі. Усі контейнери в Podʼі плануються на один вузол, де кожен вузол представляє певну платформу та архітектуру. З контейнерами Windows підтримуються такі можливості, властивості та події Podʼа:

    • Один або кілька контейнерів на Pod з ізоляцією процесів та спільним використанням томів

    • Поля status Podʼа

    • Проби готовності, життєздатності та запуску

    • Хуки життєвого циклу контейнера postStart та preStop

    • ConfigMap, Secrets: як змінні середовища або томи

    • Томи emptyDir

    • Монтування іменованих каналів хоста

    • Обмеження ресурсів

    • Поле OS:

      Значення поля .spec.os.name має бути встановлено у windows, щоб вказати, що поточний Pod використовує контейнери Windows.

      Якщо ви встановлюєте поле .spec.os.name у windows, ви не повинні встановлювати такі поля в .spec цього Podʼа:

      • spec.hostPID
      • spec.hostIPC
      • spec.securityContext.seLinuxOptions
      • spec.securityContext.seccompProfile
      • spec.securityContext.fsGroup
      • spec.securityContext.fsGroupChangePolicy
      • spec.securityContext.sysctls
      • spec.shareProcessNamespace
      • spec.securityContext.runAsUser
      • spec.securityContext.runAsGroup
      • spec.securityContext.supplementalGroups
      • spec.containers[*].securityContext.seLinuxOptions
      • spec.containers[*].securityContext.seccompProfile
      • spec.containers[*].securityContext.capabilities
      • spec.containers[*].securityContext.readOnlyRootFilesystem
      • spec.containers[*].securityContext.privileged
      • spec.containers[*].securityContext.allowPrivilegeEscalation
      • spec.containers[*].securityContext.procMount
      • spec.containers[*].securityContext.runAsUser
      • spec.containers[*].securityContext.runAsGroup

      У наведеному вище списку символи підстановки (*) позначають усі елементи списку. Наприклад, spec.containers[*].securityContext стосується обʼєкта SecurityContext для всіх контейнерів. Якщо вказано будь-яке з цих полів, API-сервер не прийме цей Pod.

  • Ресурси робочих навантажень, зокрема:

    • ReplicaSet
    • Deployment
    • StatefulSet
    • DaemonSet
    • Job
    • CronJob
    • ReplicationController
  • Services. Докладніше дивіться в розділі Балансування навантаження та Services.

Podʼи, ресурси робочих навантажень та Services є критично важливими елементами для керування робочими навантаженнями Windows у Kubernetes. Однак самі по собі вони недостатні для належного керування життєвим циклом робочих навантажень Windows у динамічному середовищі cloud native.

Параметри командного рядка для kubelet

Деякі параметри командного рядка kubelet поводяться у Windows інакше, як описано нижче:

  • Параметр --windows-priorityclass дозволяє встановлювати пріоритет планування процесу kubelet (див. Керування ресурсами CPU)
  • Прапорці --kube-reserved, --system-reserved та --eviction-hard оновлюють NodeAllocatable
  • Виселення за допомогою --enforce-node-allocable не реалізовано
  • Під час роботи на вузлі Windows kubelet не має обмежень памʼяті або CPU. --kube-reserved та --system-reserved лише віднімаються від NodeAllocatable і не гарантують ресурсів для робочих навантажень. Докладніше дивіться в розділі Керування ресурсами для вузлів Windows.
  • Стан PIDPressure не реалізовано
  • kubelet не вживає дій щодо виселення через OOM

Сумісність API

Існують тонкі відмінності в роботі API Kubernetes для Windows, зумовлені ОС та середовищем виконання контейнерів. Деякі властивості робочих навантажень було розроблено для Linux, і вони не працюють у Windows.

На високому рівні відрізняються такі концепції ОС:

  • Ідентичність — Linux використовує ідентифікатор користувача (UID) та ідентифікатор групи (GID), які подаються як цілі числа. Імена користувачів і груп не є канонічними — це лише псевдоніми для UID+GID у /etc/groups або /etc/passwd. Windows використовує довший двійковий ідентифікатор безпеки (SID), який зберігається в базі даних Windows Security Access Manager (SAM). Ця база даних не є спільною між хостом і контейнерами або між контейнерами.
  • Права доступу до файлів — Windows використовує список керування доступом на основі (SID), тоді як POSIX-системи, такі як Linux, використовують бітову маску на основі дозволів обʼєкта та UID+GID, а також необовʼязкові списки керування доступом.
  • Шляхи до файлів — у Windows прийнято використовувати \ замість /. Бібліотеки вводу-виводу Go зазвичай приймають обидва варіанти й просто забезпечують їхню роботу, але коли ви задаєте шлях або командний рядок, що інтерпретується всередині контейнера, може знадобитися \.
  • Сигнали — інтерактивні застосунки Windows обробляють завершення інакше і можуть реалізовувати один або кілька таких механізмів:
    • UI-потік обробляє чітко визначені повідомлення, зокрема WM_CLOSE.
    • Консольні застосунки обробляють Ctrl-C або Ctrl-break за допомогою обробника керування (Control Handler).
    • Служби реєструють функцію обробника керування службою (Service Control Handler), яка може приймати коди керування SERVICE_CONTROL_STOP.

Коди виходу контейнера дотримуються тієї самої домовленості: 0 означає успіх, а ненульове значення — помилку. Конкретні коди помилок у Windows та Linux можуть відрізнятися. Однак коди виходу, які передають компоненти Kubernetes (kubelet, kube-proxy), залишаються незмінними.

Сумісність полів для специфікацій контейнера

У цьому списку наведено відмінності в роботі специфікацій контейнерів Podʼа між Windows та Linux:

  • Великі сторінки (huge pages) не реалізовані в середовищі виконання контейнерів Windows і недоступні. Вони потребують наявності привілею користувача, який не налаштовується для контейнерів.
  • requests.cpu та requests.memory — запити віднімаються від доступних ресурсів вузла, тому їх можна використовувати, щоб уникнути надмірного виділення ресурсів вузла. Проте вони не можуть гарантувати ресурси на вузлі з надмірно виділеними ресурсами. Як найкращу практику їх слід застосовувати до всіх контейнерів, якщо оператор хоче повністю уникнути надмірного виділення ресурсів.
  • securityContext.allowPrivilegeEscalation — неможливо у Windows; жодні можливості (capabilities) не підключені
  • securityContext.capabilities — можливості POSIX (capabilities) не реалізовані у Windows
  • securityContext.privileged — Windows не підтримує привілейовані контейнери, замість них використовуйте Контейнери HostProcess
  • securityContext.procMount — Windows не має файлової системи /proc
  • securityContext.readOnlyRootFilesystem — неможливо у Windows; для роботи реєстру та системних процесів усередині контейнера потрібен доступ на запис
  • securityContext.runAsGroup — неможливо у Windows, оскільки немає підтримки GID
  • securityContext.runAsNonRoot — це налаштування перешкоджатиме запуску контейнерів як ContainerAdministrator, який є найближчим еквівалентом користувача root у Windows.
  • securityContext.runAsUser — використовуйте runAsUserName замість цього
  • securityContext.seLinuxOptions — неможливо у Windows, оскільки SELinux специфічний для Linux
  • terminationMessagePath — тут є певні обмеження, оскільки Windows не підтримує зіставлення окремих файлів. Стандартне значення — /dev/termination-log, і воно працює, бо цей шлях стандартно не існує у Windows.

Сумісність полів для специфікацій Podʼа

У цьому списку наведено відмінності в роботі специфікацій Podʼа між Windows та Linux:

  • hostIPC та hostpid — спільне використання простору імен хоста неможливе у Windows
  • hostNetwork — використання мережі хоста неможливе у Windows
  • dnsPolicy — встановлення dnsPolicy Podʼа на ClusterFirstWithHostNet не підтримується у Windows, оскільки мережа хоста не надається. Podʼи завжди працюють з мережею контейнера.
  • podSecurityContext див. нижче
  • shareProcessNamespace — це бета-можливість, яка залежить від просторів імен Linux, не реалізованих у Windows. Windows не може спільно використовувати простори імен процесів або кореневу файлову систему контейнера. Спільною може бути лише мережа.
  • terminationGracePeriodSeconds — це не повністю реалізовано в Docker у Windows, див. issue на GitHub. Наразі процес ENTRYPOINT отримує CTRL_SHUTDOWN_EVENT, потім Windows стандартно чекає 5 секунд і нарешті завершує всі процеси звичайним для Windows способом вимкнення. Стандартні 5 секунд насправді задані в реєстрі Windows усередині контейнера, тому їх можна перевизначити під час збирання контейнера.
  • volumeDevices — це бета-можливість, яка не реалізована у Windows. Windows не може підключати сирі блочні пристрої до Podʼів.
  • volumes
    • Якщо ви визначаєте том emptyDir, ви не можете встановити для нього джерело memory.
  • Ви не можете увімкнути mountPropagation для монтування томів, оскільки це не підтримується у Windows.

Доступ до мережі хоста

Kubernetes від v1.26 до v1.32 містив альфа-підтримку запуску Podʼів Windows у мережевому просторі імен хоста.

Kubernetes v1.37 не містить функціональної можливості WindowsHostNetwork і не підтримує запуск Podʼів Windows у мережевому просторі імен хоста.

Сумісність полів для контексту безпеки Podʼа

Лише поля securityContext.runAsNonRoot та securityContext.windowsOptions із полів securityContext Podʼа працюють у Windows.

Виявлення проблем вузла

Механізм виявлення проблем вузла (див. Моніторинг справності вузла) має попередню підтримку для Windows. Для отримання додаткової інформації відвідайте сторінку проєкту на GitHub.

Контейнер pause

У Podʼі Kubernetes спочатку створюється інфраструктурний контейнер, або контейнер «pause», щоб розмістити контейнер. У Linux для cgroups та просторів імен, що утворюють Pod, потрібен процес, який підтримує їхнє подальше існування; цей процес забезпечує pause. Контейнери, які належать до одного Podʼа, зокрема інфраструктурні та робочі контейнери, мають спільну мережеву кінцеву точку (та сама адреса IPv4 та/або IPv6, ті самі простори мережевих портів). Kubernetes використовує контейнери pause, щоб робочі контейнери могли аварійно завершуватися або перезапускатися без втрати мережевої конфігурації.

Kubernetes супроводжує багатоархітектурний образ із підтримкою Windows. Для Kubernetes v1.37.0 рекомендований образ pause — registry.k8s.io/pause:3.6. Вихідний код доступний на GitHub.

Microsoft супроводжує інший багатоархітектурний образ із підтримкою Linux та Windows amd64, який можна знайти як mcr.microsoft.com/oss/kubernetes/pause:3.6. Цей образ зібрано з того самого вихідного коду, що й образ, який супроводжує Kubernetes, але всі бінарні файли Windows підписані authenticode компанією Microsoft. Проєкт Kubernetes рекомендує використовувати образ, який супроводжує Microsoft, якщо ви розгортаєте у промисловому або подібному до промислового середовищі, що потребує підписаних бінарних файлів.

Середовища виконання контейнерів

Вам потрібно встановити середовище виконання контейнерів на кожний вузол у кластері, щоб Podʼи могли там працювати.

Із Windows працюють такі середовища виконання контейнерів:

Примітка: Цей розділ містить посилання на проєкти сторонніх розробників, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Проєкти вказано в алфавітному порядку. Щоб додати проєкт до цього списку, ознайомтеся з посібником з контенту перед надсиланням змін. Докладніше.

ContainerD

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.20

Ви можете використовувати ContainerD версії 1.4.0+ як середовище виконання контейнерів для вузлів Kubernetes, які працюють на Windows.

Дізнайтеся, як встановити ContainerD на вузол Windows.

Примітка:

Є відоме обмеження під час використання GMSA з containerd для доступу до мережевих ресурсів Windows, яке потребує виправлення ядра.

Mirantis Container Runtime

Mirantis Container Runtime (MCR) доступне як середовище виконання контейнерів для всіх версій Windows Server 2019 та новіших.

Для отримання додаткової інформації дивіться Встановлення MCR на серверах Windows.

Сумісність версій операційної системи Windows

На вузлах Windows діють суворі правила сумісності: версія операційної системи хоста повинна збігатися з версією операційної системи базового образу контейнера.

Для Kubernetes v1.37 сумісність операційних систем для вузлів Windows (та Podʼів) така:

Випуск LTSC Windows Server
Windows Server 2022
Windows Server 2025

Також застосовується політика версійної розбіжності Kubernetes.

Рекомендації та важливі аспекти апаратного забезпечення

Примітка: Цей розділ містить посилання на проєкти сторонніх розробників, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Проєкти вказано в алфавітному порядку. Щоб додати проєкт до цього списку, ознайомтеся з посібником з контенту перед надсиланням змін. Докладніше.

Примітка:

Наведені тут апаратні характеристики слід розглядати як розумні стандартні значення. Вони не є мінімальними вимогами чи конкретними рекомендаціями для промислових середовищ. Залежно від вимог вашого робочого навантаження ці значення може знадобитися скоригувати.
  • 64-бітний процесор із 4 ядрами CPU або більше, здатний підтримувати віртуалізацію
  • 8 GB або більше оперативної памʼяті
  • 50 GB або більше вільного місця на диску

Найновішу інформацію про мінімальні апаратні вимоги дивіться на сторінці Вимоги до апаратного забезпечення для Windows Server у документації Microsoft. Рекомендації щодо вибору ресурсів для промислових робочих вузлів дивіться в розділі Промислові робочі вузли в документації Kubernetes.

Для оптимізації ресурсів системи, якщо графічний інтерфейс не потрібен, може бути краще використовувати інсталяцію Windows Server без варіанта встановлення Windows Desktop Experience, оскільки така конфігурація зазвичай звільняє більше ресурсів системи.

Оцінюючи дисковий простір для робочих вузлів Windows, врахуйте, що образи контейнерів Windows зазвичай більші за образи контейнерів Linux: розмір одного образу становить від 300 MB до понад 10 GB. Крім того, зверніть увагу, що диск C: у контейнерах Windows стандартно має віртуальний вільний розмір 20 GB: це не фактично зайнятий простір, а розмір диска, до якого може вирости один контейнер під час використання локального сховища на хості. Докладніше дивіться на сторінці Контейнери у Windows — документація про сховище контейнерів.

Отримання допомоги та усунення несправностей

Основним джерелом допомоги з усунення несправностей у вашому кластері Kubernetes має бути сторінка Усунення несправностей.

Додаткову допомогу з усунення несправностей, специфічну для Windows, наведено в цьому розділі. Логи є важливою складовою усунення несправностей у Kubernetes. Обовʼязково додавайте їх щоразу, коли звертаєтеся по допомогу до інших учасників. Дотримуйтесь інструкцій у настановах щодо збору логів від SIG Windows.

Повідомлення про проблеми та запити на нові можливості

Якщо у вас є щось схоже на помилку або ви хочете подати запит на нову можливість, дотримуйтесь настанов для учасників SIG Windows, щоб створити новий issue. Спочатку перегляньте список issues на випадок, якщо про проблему вже повідомляли, і додайте коментар про свій досвід та додаткові логи. Канал SIG Windows у Slack Kubernetes також є чудовим способом отримати початкову підтримку та ідеї щодо усунення несправностей перед створенням issue.

Перевірка правильності роботи кластера Windows

Проєкт Kubernetes надає специфікацію Windows Operational Readiness разом зі структурованим набором тестів. Цей набір поділено на дві групи тестів, базову та розширену, кожна з яких містить категорії, спрямовані на тестування конкретних областей. Його можна використовувати для перевірки всієї функціональності системи Windows та гібридної системи (разом із вузлами Linux) з повним покриттям.

Щоб налаштувати проєкт на щойно створеному кластері, дивіться інструкції в посібнику проєкту.

Інструменти розгортання

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

Проєкт Kubernetes cluster API також надає засоби для автоматизації розгортання вузлів Windows.

Канали поширення Windows

Для докладного пояснення каналів поширення Windows дивіться документацію Microsoft.

Інформацію про різні канали обслуговування Windows Server, зокрема їхні моделі підтримки, можна знайти на сторінці Канали обслуговування Windows Server.

11.2 - Посібник із запуску контейнерів Windows у Kubernetes

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

Важливо зауважити, що створення та розгортання Services і робочих навантажень у Kubernetes відбувається майже однаково для контейнерів Linux та Windows. Команди kubectl для взаємодії з кластером ідентичні. Приклади на цій сторінці наведено, щоб ви могли швидко почати роботу з контейнерами Windows.

Цілі

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

Перш ніж ви розпочнете

Ви вже повинні мати доступ до кластера Kubernetes, який містить робочий вузол, на якому працює Windows Server.

Початок роботи: Розгортання робочого навантаження Windows

Наведений нижче приклад файлу YAML розгортає простий вебсервер, що працює в контейнері Windows.

Створіть маніфест із назвою win-webserver.yaml з таким вмістом:

---
apiVersion: v1
kind: Service
metadata:
  name: win-webserver
  labels:
    app: win-webserver
spec:
  ports:
    # порт, на якому має працювати цей Service
    - port: 80
      targetPort: 80
  selector:
    app: win-webserver
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: win-webserver
  name: win-webserver
spec:
  replicas: 2
  selector:
    matchLabels:
      app: win-webserver
  template:
    metadata:
      labels:
        app: win-webserver
      name: win-webserver
    spec:
     containers:
      - name: windowswebserver
        image: mcr.microsoft.com/windows/servercore:ltsc2019
        command:
        - powershell.exe
        - -command
        - "<#code used from https://gist.github.com/19WAS85/5424431#> ; $$listener = New-Object System.Net.HttpListener ; $$listener.Prefixes.Add('http://*:80/') ; $$listener.Start() ; $$callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($$listener.IsListening) { ;$$context = $$listener.GetContext() ;$$requestUrl = $$context.Request.Url ;$$clientIP = $$context.Request.RemoteEndPoint.Address ;$$response = $$context.Response ;Write-Host '' ;Write-Host('> {0}' -f $$requestUrl) ;  ;$$count = 1 ;$$k=$$callerCounts.Get_Item($$clientIP) ;if ($$k -ne $$null) { $$count += $$k } ;$$callerCounts.Set_Item($$clientIP, $$count) ;$$ip=(Get-NetAdapter | Get-NetIpAddress); $$header='<html><body><H1>Windows Container Web Server</H1>' ;$$callerCountsString='' ;$$callerCounts.Keys | % { $$callerCountsString+='<p>IP {0} callerCount {1} ' -f $$ip[1].IPAddress,$$callerCounts.Item($$_) } ;$$footer='</body></html>' ;$$content='{0}{1}{2}' -f $$header,$$callerCountsString,$$footer ;Write-Output $$content ;$$buffer = [System.Text.Encoding]::UTF8.GetBytes($$content) ;$$response.ContentLength64 = $$buffer.Length ;$$response.OutputStream.Write($$buffer, 0, $$buffer.Length) ;$$response.Close() ;$$responseStatus = $$response.StatusCode ;Write-Host('< {0}' -f $$responseStatus)  } ; "
     nodeSelector:
      kubernetes.io/os: windows

Примітка:

Зіставлення портів також підтримується, але для простоти в цьому прикладі порт 80 контейнера відкрито безпосередньо для Service.
  1. Перевірте, що всі вузли справні:

    kubectl get nodes
    
  2. Розгорніть Service та спостерігайте за оновленнями Podʼів:

    kubectl apply -f win-webserver.yaml
    kubectl get pods -o wide -w
    

    Коли Service розгорнуто правильно, обидва Podʼи позначаються як Ready. Щоб вийти з команди спостереження, натисніть Ctrl+C.

  3. Перевірте успішність розгортання. Для перевірки:

    • Кілька Podʼів показуються з вузла панелі управління Linux, скористайтеся kubectl get pods
    • Звʼязок між вузлом і Podʼом через мережу, за допомогою curl перевірте порт 80 IP-адрес ваших Podʼів з вузла панелі управління Linux, щоб перевірити відповідь вебсервера
    • Звʼязок між Podʼами, пінг між Podʼами (і між хостами, якщо у вас більше одного вузла Windows) за допомогою kubectl exec
    • Звʼязок між Service і Podʼом, за допомогою curl перевірте віртуальну IP-адресу Service (її видно в kubectl get services) з вузла панелі управління Linux та з окремих Podʼів
    • Виявлення Service, за допомогою curl перевірте назву Service зі стандартним суфіксом DNS Kubernetes
    • Вхідне підключення, за допомогою curl перевірте NodePort з вузла панелі управління Linux або з машин поза кластером
    • Вихідне підключення, за допомогою curl перевірте зовнішні IP-адреси зсередини Podʼа, використовуючи kubectl exec

Примітка:

Хости контейнерів Windows не можуть отримати доступ до IP-адрес Services, запланованих на них, через поточні обмеження платформи мережевого стека Windows. Доступ до IP-адрес Services мають лише Podʼи Windows.

Спостережуваність

Збір логів робочих навантажень

Логи — важливий елемент спостережуваності; вони дозволяють користувачам отримувати інформацію про роботу робочих навантажень і є ключовою складовою усунення несправностей. Оскільки контейнери Windows та робочі навантаження всередині контейнерів Windows поводяться інакше, ніж контейнери Linux, користувачам було важко збирати логи, що обмежувало операційну видимість. Робочі навантаження Windows, наприклад, зазвичай налаштовані на логування в ETW (Event Tracing for Windows) або надсилання записів до журналу подій застосунку. LogMonitor, інструмент Microsoft з відкритим кодом, є рекомендованим способом моніторингу налаштованих джерел логів усередині контейнера Windows. LogMonitor підтримує моніторинг журналів подій, провайдерів ETW та власних логів застосунків, перенаправляючи їх у STDOUT для споживання через kubectl logs <pod>.

Дотримуйтеся інструкцій на сторінці LogMonitor на GitHub, щоб скопіювати його бінарні файли та файли конфігурації до всіх ваших контейнерів і додати необхідні точки входу, щоб LogMonitor міг надсилати ваші логи у STDOUT.

Налаштування користувача контейнера

Використання налаштовуваних імен користувачів контейнера

Контейнери Windows можна налаштувати для запуску своїх точок входу та процесів з іменами користувачів, відмінними від стандартних для образу. Дізнайтеся більше про це тут.

Керування ідентичністю робочих навантажень за допомогою службових облікових записів з груповим керуванням

Робочі навантаження контейнерів Windows можна налаштувати для використання службових облікових записів з груповим керуванням (Group Managed Service Accounts, GMSA). Службові облікові записи з груповим керуванням — це особливий тип облікових записів Active Directory, які забезпечують автоматичне керування паролями, спрощене керування іменами принципалів служб (service principal name, SPN) та можливість делегувати керування іншим адміністраторам на кількох серверах. Контейнери, налаштовані з GMSA, можуть отримувати доступ до зовнішніх ресурсів домену Active Directory, використовуючи ідентичність, налаштовану за допомогою GMSA. Дізнайтеся більше про налаштування та використання GMSA для контейнерів Windows тут.

Taints та tolerations

Користувачам необхідно використовувати певну комбінацію taints та селекторів вузлів, щоб планувати робочі навантаження Linux та Windows на вузли з відповідною ОС. Рекомендований підхід викладено нижче; одна з його головних цілей — не порушувати сумісність для наявних робочих навантажень Linux.

Ви можете (і повинні) встановлювати .spec.os.name для кожного Podʼа, щоб вказати операційну систему, для якої призначені контейнери в цьому Podʼі. Для Podʼів, які запускають контейнери Linux, встановіть .spec.os.name у linux. Для Podʼів, які запускають контейнери Windows, встановіть .spec.os.name у windows.

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

Значення .spec.os.name не впливає на планування Podʼів Windows, тому taints та tolerations (або селектори вузлів) усе ще потрібні, щоб Podʼи Windows потрапляли на відповідні вузли Windows.

Забезпечення розміщення робочих навантажень, специфічних для ОС, на відповідному хості контейнерів

Користувачі можуть забезпечити планування контейнерів Windows на відповідний хост за допомогою taints та tolerations. Усі вузли Kubernetes, на яких працює Kubernetes 1.37, мають такі стандартні мітки:

  • kubernetes.io/os = [windows|linux]
  • kubernetes.io/arch = [amd64|arm64|...]

Якщо специфікація Podʼа не вказує nodeSelector, такий як "kubernetes.io/os": windows, Pod може бути запланований на будь-який хост, Windows або Linux. Це може бути проблемою, оскільки контейнер Windows може працювати лише на Windows, а контейнер Linux — лише на Linux. Найкраща практика для Kubernetes 1.37 — використовувати nodeSelector.

Однак у багатьох випадках користувачі вже мають велику кількість розгортань для контейнерів Linux, а також екосистему готових конфігурацій, таких як Helm-чарти спільноти, і випадки програмної генерації Podʼів, наприклад за допомогою операторів. У таких ситуаціях ви можете вагатися, чи вносити зміни до конфігурації, щоб додати поля nodeSelector до всіх Podʼів і шаблонів Podʼів. Альтернативою є використання taints. Оскільки kubelet може встановлювати taints під час реєстрації, його можна легко змінити так, щоб він автоматично додавав taint лише під час роботи на Windows.

Наприклад: --register-with-taints='os=windows:NoSchedule'

Якщо додати taint до всіх вузлів Windows, на них нічого не буде заплановано (зокрема й наявні Podʼи Linux). Щоб Pod Windows було заплановано на вузол Windows, йому потрібні і nodeSelector, і відповідна toleration для вибору Windows.

nodeSelector:
    kubernetes.io/os: windows
    node.kubernetes.io/windows-build: '10.0.20348'
tolerations:
    - key: "os"
      operator: "Equal"
      value: "windows"
      effect: "NoSchedule"

Робота з кількома версіями Windows в одному кластері

Версія Windows Server, яку використовує кожен Pod, повинна збігатися з версією вузла. Якщо ви хочете використовувати кілька версій Windows Server в одному кластері, то вам слід встановити додаткові мітки вузлів та поля nodeSelector.

Kubernetes автоматично додає мітку, node.kubernetes.io/windows-build щоб це спростити.

Ця мітка відображає старший і молодший номери версії Windows та номер збірки, які повинні збігатися для сумісності. Ось значення для кожної версії Windows Server:

Назва продуктуВерсія
Windows Server 202210.0.20348
Windows Server 202510.0.26100

Спрощення за допомогою RuntimeClass

RuntimeClass можна використовувати для спрощення роботи з taints та tolerations. Адміністратор кластера може створити обʼєкт RuntimeClass, який інкапсулює ці taints та tolerations.

  1. Збережіть цей файл як runtimeClasses.yml. Він містить відповідний nodeSelector для ОС Windows, архітектури та версії.

    ---
    apiVersion: node.k8s.io/v1
    kind: RuntimeClass
    metadata:
      name: windows-2019
    handler: example-container-runtime-handler
    scheduling:
      nodeSelector:
        kubernetes.io/os: 'windows'
        kubernetes.io/arch: 'amd64'
        node.kubernetes.io/windows-build: '10.0.20348'
      tolerations:
      - effect: NoSchedule
        key: os
        operator: Equal
        value: "windows"
    
  2. Виконайте команду kubectl create -f runtimeClasses.yml з правами адміністратора кластера.

  3. Додайте runtimeClassName: windows-2019 до відповідних специфікацій Podʼів.

    Наприклад:

    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: iis-2019
      labels:
        app: iis-2019
    spec:
      replicas: 1
      template:
        metadata:
          name: iis-2019
          labels:
            app: iis-2019
        spec:
          runtimeClassName: windows-2019
          containers:
          - name: iis
            image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
            resources:
              limits:
                cpu: 1
                memory: 800Mi
              requests:
                cpu: 100m
                memory: 300Mi
            ports:
              - containerPort: 80
      selector:
        matchLabels:
          app: iis-2019
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: iis
    spec:
      type: LoadBalancer
      ports:
      - protocol: TCP
        port: 80
      selector:
        app: iis-2019
    

12 - Розширення можливостей Kubernetes

Різні способи зміни поведінки вашого кластера Kubernetes.

Kubernetes добре налаштовується та розширюється. Тому потреба робити форк або надсилати патчі до коду проєкту Kubernetes виникає рідко.

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

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

Конфігурація

Файли конфігурації та аргументи командного рядка описано в розділі Довідник онлайн-документації, де для кожного бінарного файлу є окрема сторінка:

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

Вбудовані API політик, такі як ResourceQuota, NetworkPolicy та керування доступом на основі ролей (RBAC), є вбудованими API Kubernetes, які надають декларативно налаштовувані параметри політик. Ці API зазвичай доступні навіть у сервісах Kubernetes, що надаються провайдером, та в керованих інсталяціях Kubernetes. Вбудовані API політик дотримуються тих самих домовленостей, що й інші ресурси Kubernetes, такі як Podʼи. Коли ви використовуєте стабільний API політик, ви отримуєте переваги визначеної політики підтримки, як і для інших API Kubernetes. З цих причин, де це доречно, рекомендується використовувати API політик, а не файли конфігурації та аргументи командного рядка.

Розширення

Розширення — це програмні компоненти, які розширюють Kubernetes і глибоко з ним інтегруються. Вони адаптують його для підтримки нових типів ресурсів і нових видів обладнання.

Багато адміністраторів кластерів використовують екземпляри Kubernetes, що надаються провайдером, або встановлені з дистрибутива. У таких кластерах розширення вже попередньо встановлено. Тому більшості користувачів Kubernetes не потрібно встановлювати розширення, і ще меншій кількості потрібно створювати нові.

Шаблони розширень

Kubernetes спроєктовано для автоматизації шляхом написання клієнтських програм. Будь-яка програма, яка читає та/або записує дані в API Kubernetes, може забезпечувати корисну автоматизацію. Автоматизація може працювати як у кластері, так і поза ним. Дотримуючись настанов із цього документа, ви зможете писати високодоступну та надійну автоматизацію. Автоматизація зазвичай працює з будь-яким кластером Kubernetes, зокрема з кластерами, що надаються провайдером, і керованими інсталяціями.

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

Контролер є клієнтом API Kubernetes. Коли Kubernetes є клієнтом і звертається до віддаленого сервісу, Kubernetes називає це вебхуком. Віддалений сервіс називається бекендом вебхука. Як і власні контролери, вебхуки додають точку відмови.

Примітка:

Поза Kubernetes термін «вебхук» зазвичай означає механізм асинхронних сповіщень, де виклик вебхука є одностороннім сповіщенням іншій системі чи компоненту. В екосистемі Kubernetes «вебхуками» часто називають навіть синхронні HTTP-виклики.

У моделі вебхуків Kubernetes надсилає мережевий запит до віддаленого сервісу. В альтернативній моделі бінарних втулків Kubernetes виконує бінарний файл (програму). Бінарні втулки використовуються в kubelet (наприклад, втулки сховища CSI та мережеві втулки CNI), а також у kubectl (дивіться Розширення kubectl за допомогою втулків).

Точки розширення

На цій діаграмі показано точки розширення в кластері Kubernetes та клієнти, які до нього звертаються.

Символічне представлення семи пронумерованих точок розширення для Kubernetes.

Точки розширення Kubernetes

Пояснення до діаграми

  1. Користувачі часто взаємодіють з API Kubernetes через kubectl. Втулки налаштовують поведінку клієнтів. Існують загальні розширення, які можна застосовувати до різних клієнтів, а також спеціальні способи розширення kubectl.

  2. API-сервер обробляє всі запити. Кілька типів точок розширення в API-сервері дозволяють автентифікувати запити або блокувати їх на основі вмісту, редагувати вміст та обробляти видалення. Про це йдеться в розділі Розширення доступу до API.

  3. API-сервер обслуговує різні види ресурсів. Вбудовані види ресурсів, такі як pods, визначені проєктом Kubernetes і не можуть бути змінені. Про розширення API Kubernetes читайте в розділі Розширення API.

  4. Планувальник Kubernetes вирішує, на яких вузлах розмістити Podʼи. Існує кілька способів розширити планування, вони описані в розділі Розширення планувальника.

  5. Значну частину поведінки Kubernetes реалізовано програмами, які називаються контролерами і є клієнтами API-сервера. Контролери часто використовуються разом із власними ресурсами. Докладніше читайте в розділах Поєднання нових API з автоматизацією та Зміна вбудованих ресурсів.

  6. kubelet працює на серверах (вузлах) і допомагає Podʼам виглядати як віртуальні сервери з власними IP-адресами в мережі кластера. Мережеві втулки дозволяють різні реалізації мережі Podʼів.

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

    kubelet також монтує та демонтує томи для Podʼів та їхніх контейнерів. Ви можете використовувати втулки сховища, щоб додати підтримку нових видів сховища та інших типів томів.

Блок-схема вибору точки розширення

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

Блок-схема із запитаннями про варіанти використання та настановами для тих, хто реалізує розширення. Зелені кола означають «так», червоні — «ні».

Блок-схема для вибору підходу до розширення


Розширення клієнта

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

Якщо ви хочете розширити інструмент kubectl, прочитайте Розширення kubectl за допомогою втулків.

Розширення API

Визначення власних ресурсів

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

Докладніше про власні ресурси дивіться на сторінці Власні ресурси.

Шар агрегації API

Ви можете використовувати шар агрегації API Kubernetes, щоб інтегрувати API Kubernetes з додатковими сервісами, такими як Metrics API.

Поєднання нових API з автоматизацією

Поєднання API власного ресурсу та циклу управління називається шаблоном контролера. Якщо ваш контролер заміняє оператора-людину, який розгортає інфраструктуру на основі бажаного стану, то він також може дотримуватися шаблону Operator. Шаблон Operator використовується для керування конкретними застосунками; зазвичай це застосунки, які зберігають стан і потребують уважного керування.

Ви також можете створювати власні API та цикли управління, які керують іншими ресурсами, такими як сховище, або визначають політики (наприклад, обмеження контролю доступу).

Зміна вбудованих ресурсів

Коли ви розширюєте API Kubernetes, додаючи власні ресурси, додані ресурси завжди потрапляють до нової групи API. Ви не можете замінити чи змінити наявні групи API. Додавання API не дає прямого впливу на поведінку наявних API (таких як Podʼи), тоді як розширення доступу до API це дозволяють.

Розширення доступу до API

Коли запит потрапляє до API-сервера Kubernetes, він спочатку автентифікується, потім авторизується, а потім проходить різні типи контролю допуску (деякі запити насправді не автентифікуються й обробляються особливим чином). Докладніше про цей процес дивіться в розділі Керування доступом до API Kubernetes.

Кожен крок процесу автентифікації/авторизації в Kubernetes пропонує точки розширення.

Автентифікація

Автентифікація зіставляє заголовки або сертифікати в усіх запитах з іменем користувача клієнта, який робить запит.

Kubernetes підтримує кілька вбудованих методів автентифікації. Він також може працювати за проксі-сервером автентифікації та надсилати токен із заголовка Authorization: до віддаленого сервісу для перевірки (вебхук автентифікації), якщо вбудовані методи вам не підходять.

Авторизація

Авторизація визначає, чи можуть певні користувачі читати, записувати та виконувати інші операції з ресурсами API. Вона працює на рівні цілих ресурсів і не розрізняє їх за довільними полями обʼєктів.

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

Динамічний контроль допуску

Після авторизації запит, якщо це операція запису, також проходить кроки контролю допуску. На додаток до вбудованих кроків є кілька розширень:

  • Вебхук Image Policy обмежує, які образи можна запускати в контейнерах.
  • Для прийняття довільних рішень щодо контролю допуску можна використовувати загальний вебхук допуску. Вебхуки допуску можуть відхиляти створення або оновлення. Деякі вебхуки допуску змінюють дані вхідного запиту до того, як Kubernetes обробить його далі.

Розширення інфраструктури

Втулки пристроїв

Втулки пристроїв дозволяють вузлу виявляти нові ресурси вузла (на додаток до вбудованих, таких як cpu та memory) за допомогою втулка пристроїв.

Втулки сховища

Втулки Container Storage Interface (CSI) дають змогу розширити Kubernetes підтримкою нових типів томів. Томи можуть спиратися на надійне зовнішнє сховище, надавати тимчасове сховище або пропонувати інтерфейс лише для читання до інформації, використовуючи парадигму файлової системи.

Kubernetes також містить підтримку втулків FlexVolume, які застаріли з Kubernetes v1.23 (на користь CSI).

Втулки FlexVolume дозволяють користувачам монтувати типи томів, які Kubernetes не підтримує нативно. Коли ви запускаєте Pod, який залежить від сховища FlexVolume, kubelet викликає бінарний втулок для монтування тому. Більше подробиць про цей підхід є в архівній проєктній пропозиції FlexVolume.

Kubernetes Volume Plugin FAQ for Storage Vendors містить загальну інформацію про втулки сховища.

Мережеві втулки

Вашому кластеру Kubernetes потрібен мережевий втулок, щоб мати робочу мережу Podʼів і підтримувати інші аспекти мережевої моделі Kubernetes.

Мережеві втулки дозволяють Kubernetes працювати з різними мережевими топологіями та технологіями.

Втулки kubelet image credential provider

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.26

Постачальники облікових даних образів kubelet (kubelet image credential providers) — це втулки, які дозволяють kubelet динамічно отримувати облікові дані реєстрів образів. Ці облікові дані потім використовуються під час завантаження образів із реєстрів образів контейнерів, які відповідають конфігурації.

Втулки можуть взаємодіяти із зовнішніми сервісами або використовувати локальні файли для отримання облікових даних. Завдяки цьому kubelet не потребує статичних облікових даних для кожного реєстру й може підтримувати різні методи та протоколи автентифікації.

Подробиці налаштування втулка дивіться на сторінці Налаштування kubelet image credential provider.

Розширення планувальника

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

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

Ви можете контролювати, які втулки планування активні, або привʼязувати набори втулків до різних іменованих профілів планувальника. Ви також можете написати власний втулок, який інтегрується з однією або кількома точками розширення kube-scheduler.

Нарешті, вбудований компонент kube-scheduler підтримує вебхук, який дозволяє віддаленому HTTP-бекенду (розширенню планувальника) фільтрувати та/або пріоритизувати вузли, які kube-scheduler обирає для Podʼа.

Примітка:

За допомогою вебхука розширення планувальника ви можете впливати лише на фільтрування та пріоритизацію вузлів; інші точки розширення через інтеграцію вебхука недоступні.

Що далі

12.1 - Розширення API Kubernetes

Власні ресурси є розширенням API Kubernetes. Kubernetes надає два способи додавання власних ресурсів до вашого кластера:

  • Механізм CustomResourceDefinition (CRD) дозволяє декларативно визначити новий власний API із вказаними вами групою API, видом (kind) та схемою. Панель управління Kubernetes обслуговує ваш власний ресурс і забезпечує його зберігання. CRD дозволяють створювати нові типи ресурсів для вашого кластера без написання та запуску власного API-сервера.
  • Шар агрегації розташований за основним API-сервером, який діє як проксі. Така організація називається агрегацією API (AA) і дозволяє надавати спеціалізовані реалізації для ваших власних ресурсів, написавши та розгорнувши власний API-сервер. Основний API-сервер делегує вашому API-серверу запити до вказаних вами власних API, роблячи їх доступними для всіх своїх клієнтів.

12.1.1 - Власні ресурси

Власні ресурси — це екземпляри типів ресурсів, доданих до Kubernetes через розширення API. На цій сторінці розглядається, коли додавати власний ресурс до вашого кластера Kubernetes, а коли використовувати окремий сервіс. На ній описано два методи додавання власних ресурсів і те, як вибрати між ними.

Власні ресурси

Ресурс — це точка доступу в API Kubernetes, яка зберігає колекцію обʼєктів API певного виду; наприклад, вбудований ресурс pods містить колекцію обʼєктів Pod.

Власний ресурс — це розширення API Kubernetes, яке не обовʼязково доступне в стандартній інсталяції Kubernetes. Він є налаштуванням конкретної інсталяції Kubernetes. Однак багато основних функцій Kubernetes тепер побудовані з використанням власних ресурсів, що робить Kubernetes більш модульним.

Власні ресурси можуть зʼявлятися та зникати в запущеному кластері через динамічну реєстрацію, і адміністратори кластера можуть оновлювати власні ресурси незалежно від самого кластера. Щойно власний ресурс встановлено, користувачі можуть створювати його обʼєкти та отримувати до них доступ за допомогою kubectl, так само як для вбудованих ресурсів, таких як Pods.

Власні контролери

Самі по собі власні ресурси дозволяють зберігати та отримувати структуровані дані. Коли ви поєднуєте власний ресурс з власним контролером, власні ресурси надають справжній декларативний API.

Декларативний API Kubernetes забезпечує розділення обовʼязків. Ви оголошуєте бажаний стан вашого ресурсу. Контролер Kubernetes підтримує поточний стан обʼєктів Kubernetes відповідно до оголошеного вами бажаного стану. Це відрізняється від імперативного API, де ви вказуєте серверу, що робити.

Ви можете розгортати та оновлювати власний контролер на запущеному кластері, незалежно від життєвого циклу кластера. Власні контролери можуть працювати з будь-яким видом ресурсів, але вони особливо ефективні в поєднанні з власними ресурсами. Шаблон Operator поєднує власні ресурси та власні контролери. Ви можете використовувати власні контролери, щоб закодувати доменні знання про конкретні застосунки в розширення API Kubernetes.

Чи слід додавати власний ресурс до мого кластера Kubernetes?

Під час створення нового API подумайте, чи агрегувати ваш API з API кластера Kubernetes, чи залишити ваш API незалежним.

Розгляньте агрегацію API, якщо:Віддайте перевагу незалежному API, якщо:
Ваш API є Декларативним.Ваш API не відповідає моделі Декларативного API.
Ви хочете, щоб ваші нові типи були доступні для читання та запису за допомогою kubectl.Підтримка kubectl не є необхідною.
Ви хочете бачити свої нові типи в UI Kubernetes, такому як dashboard, поряд із вбудованими типами.Підтримка UI Kubernetes не є необхідною.
Ви розробляєте новий API.Ви вже маєте програму, яка обслуговує ваш API та працює добре.
Ви готові прийняти обмеження формату, які Kubernetes накладає на шляхи ресурсів REST, такі як групи API та простори імен. (Дивіться Огляд API.)Вам потрібні специфічні шляхи REST для сумісності з уже визначеним REST API.
Ваші ресурси природно обмежені кластером або просторами імен кластера.Ресурси, обмежені кластером або простором імен, погано підходять; вам потрібен контроль над специфікою шляхів ресурсів.
Ви хочете повторно використовувати можливості підтримки API Kubernetes.Вам не потрібні ці можливості.

Декларативні API

У декларативному API зазвичай:

  • Ваш API складається з відносно невеликої кількості відносно невеликих обʼєктів (ресурсів).
  • Обʼєкти визначають конфігурацію застосунків або інфраструктури.
  • Обʼєкти оновлюються відносно рідко.
  • Люди часто мають читати та записувати обʼєкти.
  • Основні операції з обʼєктами — це CRUD (створення, читання, оновлення та видалення).
  • Транзакції між обʼєктами не потрібні: API представляє бажаний стан, а не точний стан.

Імперативні API не є декларативними. Ознаки того, що ваш API може не бути декларативним:

  • Клієнт каже «зробіть це», а потім отримує синхронну відповідь, коли це виконано.
  • Клієнт каже «зробіть це», потім отримує ідентифікатор операції й повинен перевіряти окремий обʼєкт Operation, щоб визначити завершення запиту.
  • Ви говорите про віддалені виклики процедур (RPC).
  • Пряме зберігання великих обсягів даних; наприклад, понад кілька kB на обʼєкт або понад тисячі обʼєктів.
  • Потрібен доступ із високою пропускною здатністю (стабільно десятки запитів на секунду).
  • Зберігання даних кінцевих користувачів (наприклад, зображень, PII тощо) або інших великомасштабних даних, які обробляють застосунки.
  • Природні операції з обʼєктами не є CRUD-подібними.
  • API нелегко змоделювати як обʼєкти.
  • Ви вирішили подавати операції в очікуванні за допомогою ідентифікатора операції або обʼєкта операції.

Чи слід використовувати ConfigMap або власний ресурс?

Використовуйте ConfigMap, якщо виконується будь-яка з таких умов:

  • Існує наявний, добре задокументований формат файлу конфігурації, такий як mysql.cnf або pom.xml.
  • Ви хочете помістити всю конфігурацію в один ключ ConfigMap.
  • Файл конфігурації використовується насамперед програмою, що працює в Podʼі вашого кластера, для її самоналаштування.
  • Споживачі файлу воліють отримувати його через файл у Podʼі або змінну середовища в Podʼі, а не через API Kubernetes.
  • Ви хочете виконувати поетапні оновлення за допомогою Deployment тощо, коли файл оновлюється.

Примітка:

Використовуйте Secret для конфіденційних даних, який є схожим на ConfigMap, але безпечнішим.

Використовуйте власний ресурс (CRD або агрегований API), якщо виконується більшість із таких умов:

  • Ви хочете використовувати клієнтські бібліотеки та CLI Kubernetes для створення та оновлення нового ресурсу.
  • Ви хочете отримати підтримку на верхньому рівні від kubectl; наприклад, kubectl get my-object object-name.
  • Ви хочете побудувати нову автоматизацію, яка стежить за оновленнями нового обʼєкта, а потім виконує CRUD інших обʼєктів, або навпаки.
  • Ви хочете написати автоматизацію, яка обробляє оновлення обʼєкта.
  • Ви хочете використовувати домовленості API Kubernetes, такі як .spec, .status та .metadata.
  • Ви хочете, щоб обʼєкт був абстракцією над колекцією контрольованих ресурсів або узагальненням інших ресурсів.

Додавання власних ресурсів

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

  • CRD прості, і їх можна створити без будь-якого програмування.
  • Агрегація API потребує програмування, але дає більше контролю над поведінкою API, наприклад над тим, як зберігаються дані та як виконується перетворення між версіями API.

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

Агреговані API — це підлеглі API-сервери, розташовані за основним API-сервером, який діє як проксі. Така організація називається агрегацією API (AA). Для користувачів API Kubernetes виглядає розширеним.

CRD дозволяють користувачам створювати нові типи ресурсів без додавання ще одного API-сервера. Щоб використовувати CRD, не потрібно розуміти агрегацію API.

Незалежно від способу встановлення нові ресурси називаються власними ресурсами (Custom Resources), щоб відрізняти їх від вбудованих ресурсів Kubernetes (таких як Podʼи).

Примітка:

Уникайте використання власного ресурсу як сховища даних застосунків, кінцевих користувачів або моніторингу: архітектурні рішення, які зберігають дані застосунків в API Kubernetes, зазвичай занадто тісно повʼязані.

З архітектурного погляду архітектури застосунків cloud native надають перевагу слабкому звʼязуванню між компонентами. Якщо частина вашого робочого навантаження потребує допоміжного сервісу для своєї звичайної роботи, запустіть цей допоміжний сервіс як компонент або використовуйте його як зовнішній сервіс. Таким чином ваше робоче навантаження не залежатиме від API Kubernetes у своїй звичайній роботі.

CustomResourceDefinitions

API-ресурс CustomResourceDefinition дозволяє вам визначати власні ресурси. Визначення обʼєкта CRD створює новий власний ресурс із вказаними вами назвою та схемою. API Kubernetes обслуговує та обробляє зберігання вашого власного ресурсу. Назва самого обʼєкта CRD має бути дійсною назвою DNS-піддомену, утвореною з визначеної назви ресурсу та його групи API; докладніше дивіться як створити CRD. Крім того, назва обʼєкта, вид/ресурс якого визначено CRD, також має бути дійсною назвою DNS-піддомену.

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

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

Агрегація API-серверів

Зазвичай кожен ресурс в API Kubernetes потребує коду, який обробляє REST-запити та керує постійним зберіганням обʼєктів. Основний API-сервер Kubernetes обробляє вбудовані ресурси, такі як pods та services, а також може узагальнено обробляти власні ресурси через CRD.

Шар агрегації дозволяє вам надавати спеціалізовані реалізації для ваших власних ресурсів, написавши та розгорнувши власний API-сервер. Основний API-сервер делегує запити вашому API-серверу для власних ресурсів, які ви обробляєте, роблячи їх доступними для всіх своїх клієнтів.

Вибір методу для додавання власних ресурсів

CRD простіші у використанні. Агреговані API більш гнучкі. Виберіть метод, який найкраще відповідає вашим потребам.

Зазвичай CRD підходять, якщо:

  • У вас небагато полів
  • Ви використовуєте ресурс у своїй компанії або як частину невеликого проєкту з відкритим кодом (на відміну від комерційного продукту)

Порівняння простоти використання

CRD простіше створювати, ніж агреговані API.

CRDАгрегований API
Не потребує програмування. Користувачі можуть вибрати будь-яку мову для контролера CRD.Потребує програмування та збирання бінарного файлу й образу.
Немає додаткового сервісу для запуску; CRD обробляються API-сервером.Додатковий сервіс, який потрібно створити і який може зазнати збою.
Після створення CRD постійна підтримка не потрібна. Усі виправлення помилок надходять разом зі звичайними оновленнями Kubernetes Master.Може знадобитися періодично отримувати виправлення помилок з основного репозиторію (upstream), перезбирати й оновлювати агрегований API-сервер.
Не потрібно керувати кількома версіями вашого API; наприклад, коли ви контролюєте клієнта для цього ресурсу, ви можете оновлювати його синхронно з API.Потрібно керувати кількома версіями вашого API; наприклад, коли розробляєте розширення для спільного використання.

Розширені можливості та гнучкість

Агреговані API пропонують більш розширені можливості API та налаштування інших можливостей; наприклад, рівня зберігання.

МожливістьОписCRDАгрегований API
ВалідаціяДопомагає користувачам уникати помилок і дозволяє вам розвивати API незалежно від ваших клієнтів. Ці можливості найбільш корисні, коли є багато клієнтів, які не можуть оновитися одночасно.Так. Більшість валідацій можна вказати в CRD за допомогою валідації OpenAPI v3.0. Функціональна можливість CRDValidationRatcheting дозволяє ігнорувати збої валідацій, вказаних за допомогою OpenAPI, якщо частина ресурсу, яка не пройшла валідацію, не змінювалася. Будь-які інші валідації підтримуються шляхом додавання валідаційного вебхука.Так, довільні перевірки
Встановлення стандартних значеньДив. вищеТак, або через ключове слово default валідації OpenAPI v3.0 (GA в 1.17), або через змінювальний вебхук (хоча він не виконуватиметься під час читання старих обʼєктів з etcd).Так
Підтримка кількох версійДозволяє обслуговувати той самий обʼєкт через дві версії API. Може спростити такі зміни API, як перейменування полів. Менш важливо, якщо ви контролюєте версії клієнтів.ТакТак
Власне сховищеЯкщо вам потрібне сховище з іншим режимом продуктивності (наприклад, база даних часових рядів замість сховища ключ-значення) або ізоляція для безпеки (наприклад, шифрування конфіденційної інформації тощо)НіТак
Власна бізнес-логікаВиконання довільних перевірок або дій під час створення, читання, оновлення або видалення обʼєктаТак, за допомогою вебхуків.Так
Субресурс масштабуванняДозволяє таким системам, як HorizontalPodAutoscaler та PodDisruptionBudget, взаємодіяти з вашим новим ресурсомТакТак
Субресурс статусуДозволяє детальний контроль доступу, коли користувач записує розділ spec, а контролер — розділ status. Дозволяє збільшувати Generation обʼєкта під час зміни даних власного ресурсу (потребує окремих розділів spec і status у ресурсі)ТакТак
Інші субресурсиДодавання операцій, відмінних від CRUD, таких як "logs" або "exec".НіТак
strategic-merge-patchНові точки доступу підтримують PATCH з Content-Type: application/strategic-merge-patch+json. Корисно для оновлення обʼєктів, які можуть бути змінені як локально, так і сервером. Докладніше див. «Оновлення обʼєктів API на місці за допомогою kubectl patch»НіТак
Protocol BuffersНовий ресурс підтримує клієнтів, які хочуть використовувати Protocol BuffersНіТак
Схема OpenAPIЧи є схема OpenAPI (swagger) для типів, яку можна динамічно отримати із сервера? Чи захищений користувач від помилок у назвах полів завдяки тому, що можна встановлювати лише дозволені поля? Чи примусово перевіряються типи (іншими словами, чи неможливо помістити int у поле string)?Так, на основі схеми валідації OpenAPI v3.0 (GA в 1.16).Так
Назва екземпляраЧи накладає цей механізм розширення будь-які обмеження на назви обʼєктів, вид/ресурс яких визначено таким чином?Так, назва такого обʼєкта має бути дійсною назвою DNS-піддомену.Ні

Спільні можливості

Створюючи власний ресурс, через CRD або AA, ви отримуєте багато можливостей для вашого API порівняно з його реалізацією поза платформою Kubernetes:

МожливістьЩо вона робить
CRUDНові точки доступу підтримують базові операції CRUD через HTTP та kubectl
WatchНові точки доступу підтримують операції Watch Kubernetes через HTTP
DiscoveryКлієнти, такі як kubectl та dashboard, автоматично пропонують операції переліку, відображення та редагування полів для ваших ресурсів
json-patchНові точки доступу підтримують PATCH з Content-Type: application/json-patch+json
merge-patchНові точки доступу підтримують PATCH з Content-Type: application/merge-patch+json
HTTPSНові точки доступу використовують HTTPS
Вбудована автентифікаціяДоступ до розширення використовує основний API-сервер (шар агрегації) для автентифікації
Вбудована авторизаціяДоступ до розширення може повторно використовувати авторизацію основного API-сервера; наприклад, RBAC.
ЗавершувачіБлокування видалення ресурсів розширення, доки не відбудеться зовнішнє очищення.
Вебхуки допускуВстановлення стандартних значень і валідація ресурсів розширення під час будь-якої операції створення, оновлення або видалення.
Відображення в UI/CLIKubectl та dashboard можуть відображати ресурси розширення.
Невстановлене та порожнєКлієнти можуть відрізняти невстановлені поля від полів із нульовим значенням.
Генерація клієнтських бібліотекKubernetes надає загальні клієнтські бібліотеки, а також інструменти для генерації клієнтських бібліотек для конкретних типів.
Мітки та анотаціїЗагальні метадані обʼєктів, які інструменти вміють редагувати для основних і власних ресурсів.

Підготовка до встановлення власного ресурсу

Перш ніж додавати власний ресурс до вашого кластера, слід врахувати кілька моментів.

Код сторонніх розробників і нові точки відмови

Хоча створення CRD саме по собі не додає нових точок відмови (наприклад, через запуск коду сторонніх розробників на вашому API-сервері), пакети (наприклад, Charts) або інші набори для встановлення часто містять CRD, а також Deployment з кодом сторонніх розробників, який реалізує бізнес-логіку нового власного ресурсу.

Встановлення агрегованого API-сервера завжди передбачає запуск нового Deployment.

Сховище

Власні ресурси споживають простір сховища так само, як і ConfigMaps. Створення занадто великої кількості власних ресурсів може перевантажити простір сховища вашого API-сервера.

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

Агреговані API-сервери можуть використовувати те саме сховище, що й основний API-сервер; у такому разі застосовується те саме застереження.

Автентифікація, авторизація та аудит

CRD завжди використовують ту саму автентифікацію, авторизацію та логування аудиту, що й вбудовані ресурси вашого API-сервера.

Якщо ви використовуєте RBAC для авторизації, більшість ролей RBAC не надають доступу до нових ресурсів (окрім ролі cluster-admin або будь-якої ролі, створеної з wildcard-правилами). Вам потрібно явно надати доступ до нових ресурсів. CRD та агреговані API часто постачаються з новими визначеннями ролей для типів, які вони додають.

Агреговані API-сервери можуть використовувати або не використовувати ту саму автентифікацію, авторизацію та аудит, що й основний API-сервер.

Доступ до власного ресурсу

Клієнтські бібліотеки Kubernetes можна використовувати для доступу до власних ресурсів. Не всі клієнтські бібліотеки підтримують власні ресурси. Клієнтські бібліотеки Go та Python підтримують.

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

  • kubectl
  • Динамічного клієнта Kubernetes.
  • REST-клієнта, який ви напишете.
  • Клієнта, згенерованого за допомогою інструментів генерації клієнтів Kubernetes (генерація клієнта є складним завданням, але деякі проєкти можуть постачати клієнта разом із CRD або AA).

Селектори полів власних ресурсів

Селектори полів дозволяють клієнтам вибирати власні ресурси за значенням одного чи кількох полів ресурсу.

Усі власні ресурси підтримують селектори полів metadata.name та metadata.namespace.

Поля, оголошені в CustomResourceDefinition, також можна використовувати із селекторами полів, якщо вони вказані в полі spec.versions[*].selectableFields у CustomResourceDefinition.

Поля власних ресурсів, доступні для вибору

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.32; (стандартно увімкнено)
More information about this feature

This is a stable feature in Kubernetes, and has been since version 1.32. It was first available in the v1.30 release.

Поле spec.versions[*].selectableFields у CustomResourceDefinition можна використовувати, щоб оголосити, які ще поля власного ресурсу можна використовувати в селекторах полів.

У цьому прикладі поля .spec.color та .spec.size додаються як поля, доступні для вибору.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: shirts.stable.example.com
spec:
  group: stable.example.com
  scope: Namespaced
  names:
    plural: shirts
    singular: shirt
    kind: Shirt
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              color:
                type: string
              size:
                type: string
    selectableFields:
    - jsonPath: .spec.color
    - jsonPath: .spec.size
    additionalPrinterColumns:
    - jsonPath: .spec.color
      name: Color
      type: string
    - jsonPath: .spec.size
      name: Size
      type: string

Після цього за допомогою селекторів полів можна отримати лише ресурси зі значенням color blue:

kubectl get shirts.stable.example.com --field-selector spec.color=blue

Вивід має бути таким:

NAME       COLOR  SIZE
example1   blue   S
example2   blue   M

Що далі

12.1.2 - Шар агрегації API Kubernetes

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

Шар агрегації відрізняється від Custom Resource Definitions, які є способом зробити так, щоб kube-apiserver розпізнавав нові види обʼєктів.

Шар агрегації

Шар агрегації працює в одному процесі з kube-apiserver. Поки ресурс розширення не зареєстровано, шар агрегації нічого не робить. Щоб зареєструвати API, ви додаєте обʼєкт APIService, який «заявляє права» на шлях URL в API Kubernetes. Після цього шар агрегації проксіюватиме все, що надсилається на цей шлях API (наприклад, /apis/myextension.mycompany.io/v1/…), до зареєстрованого APIService.

Найпоширеніший спосіб реалізації APIService — запуск API-сервера розширення в Podʼах, які працюють у вашому кластері. Якщо ви використовуєте API-сервер розширення для керування ресурсами у своєму кластері, API-сервер розширення (також пишеться як "extension-apiserver") зазвичай працює в парі з одним або кількома контролерами. Бібліотека apiserver-builder надає кістяк як для API-серверів розширення, так і для відповідних контролерів.

Затримки відповіді

API-сервери розширення повинні мати мережевий звʼязок із малою затримкою до kube-apiserver і від нього. Запити виявлення мають проходити шлях туди й назад від kube-apiserver за пʼять секунд або менше.

Якщо ваш API-сервер розширення не може відповідати цій вимозі щодо затримки, розгляньте зміни, які дозволять їй відповідати.

Що далі

Або дізнайтеся, як розширити API Kubernetes за допомогою Custom Resource Definitions.

12.2 - Розширення для обчислень, сховища та мережі

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

  • Втулки сховища CSI та FlexVolume

    Втулки Container Storage Interface (CSI) дають змогу розширити Kubernetes підтримкою нових типів томів. Томи можуть спиратися на надійне зовнішнє сховище, надавати тимчасове сховище або пропонувати інтерфейс лише для читання до інформації, використовуючи парадигму файлової системи.

    Kubernetes також містить підтримку втулків FlexVolume, які застаріли з Kubernetes v1.23 (на користь CSI).

    Втулки FlexVolume дозволяють користувачам монтувати типи томів, які Kubernetes не підтримує нативно. Коли ви запускаєте Pod, який залежить від сховища FlexVolume, kubelet викликає бінарний втулок для монтування тому. Більше подробиць про цей підхід є в архівній проєктній пропозиції FlexVolume.

    Kubernetes Volume Plugin FAQ for Storage Vendors містить загальну інформацію про втулки сховища.

  • Втулки пристроїв

    Втулки пристроїв дозволяють вузлу виявляти нові можливості вузла (на додаток до вбудованих ресурсів вузла, таких як cpu та memory) і надавати ці власні локальні можливості вузла Podʼам, які їх запитують.

  • Мережеві втулки

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

    Kubernetes 1.37 сумісний із мережевими втулками CNI.

12.2.1 - Мережеві втулки

Kubernetes (з версії 1.3 до останньої 1.37 і, ймовірно, надалі) дозволяє використовувати втулки Container Network Interface (CNI) для мережі кластера. Ви повинні використовувати втулок CNI, сумісний з вашим кластером, який відповідає вашим потребам. У ширшій екосистемі Kubernetes доступні різні втулки (як з відкритим, так і із закритим кодом).

Втулок CNI потрібен для реалізації мережевої моделі Kubernetes.

Ви повинні використовувати втулок CNI, сумісний зі специфікацією CNI v0.4.0 або новішими випусками. Проєкт Kubernetes рекомендує використовувати втулок, сумісний зі специфікацією CNI v1.0.0 (втулки можуть бути сумісними з кількома версіями специфікації).

Встановлення

Середовище виконання контейнерів (Container Runtime) у контексті мережі — це демон на вузлі, налаштований для надання сервісів CRI для kubelet. Зокрема, середовище виконання контейнерів повинно бути налаштоване на завантаження втулків CNI, необхідних для реалізації мережевої моделі Kubernetes.

Примітка:

До Kubernetes 1.24 втулками CNI також можна було керувати через kubelet за допомогою параметрів командного рядка cni-bin-dir та network-plugin. Ці параметри командного рядка було вилучено в Kubernetes 1.24, і керування CNI більше не входить до сфери відповідальності kubelet.

Дивіться Усунення помилок, повʼязаних із втулками CNI, якщо ви стикаєтеся з проблемами після вилучення dockershim.

Конкретну інформацію про те, як середовище виконання контейнерів керує втулками CNI, дивіться в документації цього середовища виконання контейнерів, наприклад:

Конкретну інформацію про те, як встановити втулок CNI та керувати ним, дивіться в документації цього втулка або мережевого провайдера.

Вимоги до мережевих втулків

Loopback CNI

Крім втулка CNI, встановленого на вузлах для реалізації мережевої моделі Kubernetes, Kubernetes також вимагає, щоб середовища виконання контейнерів надавали інтерфейс loopback lo, який використовується для кожної пісочниці (пісочниці Podʼів, пісочниці віртуальних машин тощо). Реалізувати інтерфейс loopback можна, повторно використавши втулок CNI loopback або розробивши для цього власний код (дивіться цей приклад від CRI-O).

Підтримка hostPort

Мережевий втулок CNI підтримує hostPort. Ви можете використовувати офіційний втулок portmap, який пропонує команда втулків CNI, або власний втулок із функціональністю portMapping.

Якщо ви хочете увімкнути підтримку hostPort, ви повинні вказати portMappings capability у своєму cni-conf-dir. Наприклад:

{
  "name": "k8s-pod-network",
  "cniVersion": "0.4.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true},
      "externalSetMarkChain": "KUBE-MARK-MASQ"
    }
  ]
}

Підтримка формування трафіку

Експериментальна можливість

Мережевий втулок CNI також підтримує формування вхідного та вихідного трафіку Podʼів. Ви можете використовувати офіційний втулок bandwidth, який пропонує команда втулків CNI, або власний втулок із функціональністю контролю пропускної здатності.

Якщо ви хочете увімкнути підтримку формування трафіку, ви повинні додати втулок bandwidth до файлу конфігурації CNI (стандартно /etc/cni/net.d) і переконатися, що його бінарний файл є у вашій теці бінарних файлів CNI (стандартно /opt/cni/bin).

{
  "name": "k8s-pod-network",
  "cniVersion": "0.4.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    }
  ]
}

Тепер ви можете додати анотації kubernetes.io/ingress-bandwidth та kubernetes.io/egress-bandwidth до вашого Podʼа. Наприклад:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/ingress-bandwidth: 1M
    kubernetes.io/egress-bandwidth: 1M
...

Що далі

12.2.2 - Втулки пристроїв

Втулки пристроїв дозволяють налаштувати кластер із підтримкою пристроїв або ресурсів, які потребують специфічного для постачальника налаштування, наприклад GPU, NIC, FPGA або енергонезалежної основної памʼяті.
СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.26

Kubernetes надає фреймворк втулків пристроїв, який ви можете використовувати для оголошення системних апаратних ресурсів для Kubelet.

Замість того, щоб змінювати код самого Kubernetes, постачальники можуть реалізувати втулок пристроїв, який ви розгортаєте вручну або як DaemonSet. До цільових пристроїв належать GPU, високопродуктивні NIC, FPGA, адаптери InfiniBand та інші подібні обчислювальні ресурси, які можуть потребувати специфічної для постачальника ініціалізації та налаштування.

Реєстрація втулка пристроїв

kubelet надає сервіс gRPC Registration:

service Registration {
	rpc Register(RegisterRequest) returns (Empty) {}
}

Втулок пристроїв може зареєструвати себе в kubelet через цей сервіс gRPC. Під час реєстрації втулок пристроїв повинен надіслати:

  • Назву свого Unix-сокета.
  • Версію API втулків пристроїв, для якої його було зібрано.
  • ResourceName, який він хоче оголошувати. Тут ResourceName повинен відповідати схемі іменування розширених ресурсів у вигляді vendor-domain/resourcetype. (Наприклад, NVIDIA GPU оголошується як nvidia.com/gpu.)

Після успішної реєстрації втулок пристроїв надсилає kubelet список пристроїв, якими він керує, і тоді kubelet відповідає за оголошення цих ресурсів API-серверу як частини оновлення стану вузла. Наприклад, після того як втулок пристроїв зареєструє hardware-vendor.example/foo в kubelet і повідомить про два справні пристрої на вузлі, статус вузла оновлюється, щоб оголосити, що на вузлі встановлено 2 пристрої "Foo" і вони доступні.

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

  • Розширені ресурси підтримуються лише як цілочисельні ресурси, і для них неможливий overcommit.
  • Пристрої не можна спільно використовувати між контейнерами.

Приклад

Припустимо, що в кластері Kubernetes працює втулок пристроїв, який оголошує ресурс hardware-vendor.example/foo на певних вузлах. Ось приклад Podʼа, який запитує цей ресурс для запуску демонстраційного робочого навантаження:

---
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
spec:
  containers:
    - name: demo-container-1
      image: registry.k8s.io/pause:3.8
      resources:
        limits:
          hardware-vendor.example/foo: 2
#
# Цей Pod потребує 2 пристроїв hardware-vendor.example/foo
# і може бути запланований лише на вузол, який може задовольнити
# цю потребу.
#
# Якщо на вузлі доступно більше 2 таких пристроїв, то
# залишок буде доступний для використання іншими Podʼами.

Реалізація втулка пристроїв

Загальний робочий процес втулка пристроїв складається з таких кроків:

  1. Ініціалізація. Під час цієї фази втулок пристроїв виконує специфічні для постачальника ініціалізацію та налаштування, щоб пристрої перебували в стані готовності.

  2. Втулок запускає сервіс gRPC з Unix-сокетом за шляхом хоста /var/lib/kubelet/device-plugins/ (цей шлях жорстко закодовано, і на нього не впливають --root-dir kubelet або будь-яка інша конфігурація), який реалізує такі інтерфейси:

    service DevicePlugin {
          // GetDevicePluginOptions повертає параметри для передавання менеджеру пристроїв.
          rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    
          // ListAndWatch повертає потік списку пристроїв
          // Кожного разу, коли змінюється стан пристрою або пристрій зникає, ListAndWatch
          // повертає новий список
          rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    
          // Allocate викликається під час створення контейнера, щоб втулок
          // пристроїв міг виконати операції, специфічні для пристрою, і повідомити kubelet
          // про кроки, потрібні, щоб зробити пристрій доступним у контейнері
          rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    
          // GetPreferredAllocation повертає набір пріоритетних пристроїв для виділення
          // зі списку доступних. Не гарантується, що отримане пріоритетне виділення
          // буде тим, яке зрештою виконає devicemanager. Воно лише допомагає devicemanager
          // приймати обґрунтованіші рішення про виділення, коли це можливо.
          rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    
          // PreStartContainer викликається, якщо це вказано втулком пристроїв під час фази реєстрації,
          // перед кожним запуском контейнера. Втулок пристроїв може виконати специфічні для пристрою операції,
          // такі як скидання пристрою перед наданням пристроїв контейнеру.
          rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
    }
    

    Примітка:

    Втулки не зобовʼязані надавати корисні реалізації GetPreferredAllocation() або PreStartContainer(). Прапорці, що вказують на доступність цих викликів, якщо такі є, слід встановлювати в повідомленні DevicePluginOptions, яке повертає виклик GetDevicePluginOptions(). kubelet завжди викликатиме GetDevicePluginOptions(), щоб дізнатися, які необовʼязкові функції доступні, перш ніж викликати будь-яку з них безпосередньо.
  3. Втулок реєструється в kubelet через Unix-сокет за шляхом хоста /var/lib/kubelet/device-plugins/kubelet.sock.

    Примітка:

    Послідовність робочого процесу є важливою. Для успішної реєстрації втулок МАЄ почати обслуговування сервісу gRPC до реєстрації в kubelet.
  4. Після успішної реєстрації втулок пристроїв працює в режимі обслуговування, під час якого він постійно відстежує справність пристроїв і повідомляє kubelet про будь-які зміни стану пристрою. Він також відповідає за обслуговування запитів gRPC Allocate. Під час Allocate втулок пристроїв може виконувати підготовку, специфічну для пристрою; наприклад, очищення GPU або ініціалізацію QRNG. Якщо операції успішні, втулок пристроїв повертає AllocateResponse, який містить конфігурації середовища виконання контейнерів для доступу до виділених пристроїв. kubelet передає цю інформацію середовищу виконання контейнерів.

    AllocateResponse містить нуль або більше обʼєктів ContainerAllocateResponse. У них втулок пристроїв визначає зміни, які потрібно внести у визначення контейнера, щоб надати доступ до пристрою. Ці зміни включають:

    • Анотації
    • вузли пристроїв
    • змінні середовища
    • монтування
    • повні назви пристроїв CDI

    Примітка:

    Обробка повних назв пристроїв CDI менеджером пристроїв потребує, щоб функціональну можливість DevicePluginCDIDevices було увімкнено і для kubelet, і для kube-apiserver. Її було додано як альфа-можливість у Kubernetes v1.28, вона перейшла до бета у v1.29 і до GA у v1.31.

Обробка перезапусків kubelet

Очікується, що втулок пристроїв виявлятиме перезапуски kubelet і повторно реєструватиметься в новому екземплярі kubelet. Новий екземпляр kubelet під час запуску видаляє всі наявні Unix-сокети в /var/lib/kubelet/device-plugins (жорстко закодований шлях для втулків пристроїв). Втулок пристроїв може відстежувати видалення свого Unix-сокета й повторно реєструватися після такої події.

Втулок пристроїв та несправні пристрої

Бувають випадки, коли пристрої виходять з ладу або вимикаються. Обовʼязок втулка пристроїв у такому випадку — повідомити kubelet про ситуацію за допомогою API ListAndWatchResponse.

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

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

До Kubernetes v1.31, щоб дізнатися, чи повʼязаний Pod із несправним пристроєм, потрібно було використовувати PodResources API.

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.36; (стандартно увімкнено)

Коли функціональна можливість ResourceHealthStatus увімкнена (бета, стандартно увімкнена з v1.36), до статусу кожного контейнера в .status кожного Podʼа додається поле allocatedResourcesStatus. Поле allocatedResourcesStatus надає інформацію про справність кожного пристрою, призначеного контейнеру. Кожен запис про справність ресурсу може містити необовʼязкове поле message з додатковим зрозумілим для людини контекстом про стан справності, наприклад подробиці помилок або причини відмови.

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

Розгортання втулка пристроїв

Ви можете розгорнути втулок пристроїв як DaemonSet, як пакунок для операційної системи вузла або вручну.

Канонічна тека /var/lib/kubelet/device-plugins (жорстко закодована в kubelet) потребує привілейованого доступу, тому втулок пристроїв повинен працювати у привілейованому контексті безпеки. Якщо ви розгортаєте втулок пристроїв як DaemonSet, теку /var/lib/kubelet/device-plugins потрібно змонтувати як Том у PodSpec втулка.

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

Сумісність API

Раніше схема керування версіями вимагала, щоб версія API втулків пристроїв точно відповідала версії kubelet. Відтоді як у v1.12 ця можливість перейшла до бета, це більше не є жорсткою вимогою. API має версії й залишається стабільним із переходу цієї можливості до бета. Завдяки цьому оновлення kubelet мають бути безшовними, але до стабілізації в API все ще можуть відбуватися зміни, тож не гарантовано, що оновлення нічого не зламають.

Примітка:

Хоча компонент Kubernetes Device Manager є загальнодоступною можливістю, API втулків пристроїв не є стабільним. Інформацію про API втулків пристроїв та сумісність версій дивіться на сторінці Версії API втулків пристроїв.

Як проєкт, Kubernetes рекомендує розробникам втулків пристроїв:

  • Стежити за змінами API втулків пристроїв у майбутніх випусках.
  • Підтримувати кілька версій API втулків пристроїв для зворотної та прямої сумісності.

Щоб запускати втулки пристроїв на вузлах, які потрібно оновити до випуску Kubernetes з новішою версією API втулків пристроїв, оновіть свої втулки пристроїв до підтримки обох версій, перш ніж оновлювати ці вузли. Такий підхід забезпечить безперервну роботу виділення пристроїв під час оновлення.

Моніторинг ресурсів втулка пристроїв

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.28

Для моніторингу ресурсів, наданих втулками пристроїв, агенти моніторингу повинні мати змогу виявляти набір пристроїв, які використовуються на вузлі, та отримувати метадані, що описують, з яким контейнером має бути повʼязана метрика. Метрики Prometheus, які надають агенти моніторингу пристроїв, мають відповідати настановам щодо інструментування Kubernetes, ідентифікуючи контейнери за допомогою міток prometheus pod, namespace та container.

kubelet надає сервіс gRPC для виявлення пристроїв, що використовуються, та надання метаданих про ці пристрої:

// PodResourcesLister — це сервіс, який надається kubelet, який надає інформацію про
// ресурси вузла, використані контейнерами та Podʼами на вузлі
service PodResourcesLister {
    rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {}
    rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse) {}
    rpc Get(GetPodResourcesRequest) returns (GetPodResourcesResponse) {}
}

Точка доступу gRPC List

Точка доступу List надає інформацію про ресурси запущених Podʼів із такими подробицями, як ідентифікатори виключно виділених CPU, ідентифікатор пристрою, яким його повідомили втулки пристроїв, та ідентифікатор вузла NUMA, де виділено ці пристрої. Також для машин на базі NUMA вона містить інформацію про памʼять і великі сторінки (hugepages), зарезервовані для контейнера.

Починаючи з Kubernetes v1.27, точка доступу List може надавати інформацію про ресурси запущених Podʼів, виділені в ResourceClaims через API DynamicResourceAllocation. Починаючи з Kubernetes v1.34, ця можливість стандартно увімкнена.

// ListPodResourcesResponse — це відповідь, повернута функцією List
message ListPodResourcesResponse {
    repeated PodResources pod_resources = 1;
}

// PodResources містить інформацію про ресурси вузла, призначені для Podʼа
message PodResources {
    string name = 1;
    string namespace = 2;
    repeated ContainerResources containers = 3;
}

// ContainerResources містить інформацію про ресурси, призначені для контейнера
message ContainerResources {
    string name = 1;
    repeated ContainerDevices devices = 2;
    repeated int64 cpu_ids = 3;
    repeated ContainerMemory memory = 4;
    repeated DynamicResource dynamic_resources = 5;
}

// ContainerMemory містить інформацію про памʼять та великі сторінки, призначені для контейнера
message ContainerMemory {
    string memory_type = 1;
    uint64 size = 2;
    TopologyInfo topology = 3;
}

// Topology описує апаратну топологію ресурсу
message TopologyInfo {
        repeated NUMANode nodes = 1;
}

// NUMA представлення NUMA-вузла
message NUMANode {
        int64 ID = 1;
}

// ContainerDevices містить інформацію про пристрої, призначені для контейнера
message ContainerDevices {
    string resource_name = 1;
    repeated string device_ids = 2;
    TopologyInfo topology = 3;
}

// DynamicResource містить інформацію про пристрої, призначені для контейнера за допомогою Dynamic Resource Allocation
message DynamicResource {
    string class_name = 1;
    string claim_name = 2;
    string claim_namespace = 3;
    repeated ClaimResource claim_resources = 4;
}

// ClaimResource містить інформацію про ресурси у втулках
message ClaimResource {
    repeated CDIDevice cdi_devices = 1 [(gogoproto.customname) = "CDIDevices"];
}

// CDIDevice визначає інформацію про пристрій CDI
message CDIDevice {
    // Повністю кваліфіковане імʼя пристрою CDI
    // наприклад: vendor.com/gpu=gpudevice1
    // див. більше деталей в специфікації CDI:
    // https://github.com/container-orchestrated-devices/container-device-interface/blob/main/SPEC.md
    string name = 1;
}

Примітка:

cpu_ids у ContainerResources у точці доступу List відповідають виключно виділеним CPU, призначеним певному контейнеру. Якщо мета — оцінити CPU, які належать до спільного пулу, точку доступу List потрібно використовувати разом із точкою доступу GetAllocatableResources, як пояснено нижче:

  1. Викличте GetAllocatableResources, щоб отримати список усіх доступних для розподілу CPU
  2. Викличте GetCpuIds для всіх ContainerResources у системі
  3. Відніміть усі CPU з викликів GetCpuIds від результату виклику GetAllocatableResources

Точка доступу gRPC GetAllocatableResources

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.28

GetAllocatableResources надає інформацію про ресурси, початково доступні на робочому вузлі. Вона надає більше інформації, ніж kubelet експортує в APIServer.

Примітка:

GetAllocatableResources слід використовувати лише для оцінки доступних для розподілу ресурсів на вузлі. Якщо мета — оцінити вільні/нерозподілені ресурси, її слід використовувати разом із точкою доступу List(). Результат, отриманий від GetAllocatableResources, залишатиметься тим самим, доки не зміняться базові ресурси, доступні kubelet. Це відбувається рідко, але коли відбувається (наприклад, hotplug/hotunplug, зміни справності пристроїв), клієнт має викликати точку доступу GetAllocatableResources.

Однак виклику точки доступу GetAllocatableResources недостатньо в разі оновлення CPU та/або памʼяті: щоб відобразити правильні значення місткості та доступних для розподілу ресурсів, kubelet потрібно перезапустити.

// AllocatableResourcesResponses містить інформацію про всі пристрої, відомі kubelet
message AllocatableResourcesResponse {
    repeated ContainerDevices devices = 1;
    repeated int64 cpu_ids = 2;
    repeated ContainerMemory memory = 3;
}

ContainerDevices справді надають інформацію про топологію, вказуючи, до яких комірок NUMA привʼязаний пристрій. Комірки NUMA ідентифікуються за допомогою непрозорого цілочисельного ідентифікатора, значення якого відповідає тому, що повідомляють втулки пристроїв, коли реєструються в kubelet.

Сервіс gRPC обслуговується через unix-сокет pod-resources/kubelet.sock у кореневій теці kubelet (зазвичай /var/lib/kubelet/pod-resources/kubelet.sock). Агенти моніторингу ресурсів втулків пристроїв можна розгортати як демон або як DaemonSet. Канонічна тека pod-resources у кореневій теці kubelet (зазвичай /var/lib/kubelet/pod-resources) потребує привілейованого доступу, тому агенти моніторингу повинні працювати у привілейованому контексті безпеки. Якщо агент моніторингу пристроїв працює як DaemonSet, теку pod-resources потрібно змонтувати як Том у PodSpec агента моніторингу пристроїв.

Примітка:

Під час доступу до pod-resources/kubelet.sock з DaemonSet або будь-якого іншого застосунку, розгорнутого як контейнер на хості, який монтує сокет як том, доброю практикою є монтувати теку pod-resources, а не сам файл сокета. Це гарантує, що після перезапуску kubelet контейнер зможе повторно підключитися до цього сокета.

На типовому вузлі Linux це означає монтування /var/lib/kubelet/pod-resources/ замість /var/lib/kubelet/pod-resources/kubelet.sock.

Монтуваннями контейнера керують через inode, що посилається на сокет або теку, залежно від того, що було змонтовано. Під час перезапуску kubelet сокет видаляється і створюється новий, а тека залишається недоторканою. Тому початковий inode сокета стає непридатним для використання. inode теки продовжує працювати.

Точка доступу gRPC Get

СТАН ФУНКЦІОНАЛУ: Beta since Kubernetes v1.34

Точка доступу Get надає інформацію про ресурси запущеного Podʼа. Вона надає інформацію, подібну до описаної для точки доступу List. Точка доступу Get потребує PodName і PodNamespace запущеного Podʼа.

// GetPodResourcesRequest містить інформацію про Pod
message GetPodResourcesRequest {
    string pod_name = 1;
    string pod_namespace = 2;
}

Точка доступу Get може надавати інформацію про Pod, повʼязану з динамічними ресурсами, виділеними через API динамічного виділення ресурсів. Починаючи з Kubernetes v1.34, ця можливість стандартно увімкнена.

Інтеграція втулка пристроїв з менеджером топології

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.27

Менеджер топології (Topology Manager) — це компонент kubelet, який дозволяє координувати ресурси з урахуванням топології. Для цього API втулків пристроїв (Device Plugin API) було розширено структурою TopologyInfo.

message TopologyInfo {
    repeated NUMANode nodes = 1;
}

message NUMANode {
    int64 ID = 1;
}

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

TopologyInfo підтримує встановлення поля nodes у nil або у список вузлів NUMA. Це дозволяє втулку пристроїв оголошувати пристрій, який охоплює кілька вузлів NUMA.

Встановлення TopologyInfo у nil або надання порожнього списку вузлів NUMA для певного пристрою вказує на те, що втулок пристроїв не має переваг щодо спорідненості NUMA для цього пристрою.

Приклад структури TopologyInfo, заповненої для пристрою втулком пристроїв:

pluginapi.Device{ID: "25102017", Health: pluginapi.Healthy, Topology:&pluginapi.TopologyInfo{Nodes: []*pluginapi.NUMANode{&pluginapi.NUMANode{ID: 0,},}}}

Приклади втулків пристроїв

Примітка: Цей розділ містить посилання на проєкти сторонніх розробників, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Проєкти вказано в алфавітному порядку. Щоб додати проєкт до цього списку, ознайомтеся з посібником з контенту перед надсиланням змін. Докладніше.

Ось деякі приклади реалізації втулків пристроїв:

Що далі

12.3 - Шаблон Operator

Оператори — це програмні розширення Kubernetes, які використовують власні ресурси для керування застосунками та їхніми компонентами. Оператори дотримуються принципів Kubernetes, зокрема циклу управління.

Мотивація

Шаблон Operator має на меті відтворити ключову мету людини-оператора, яка керує сервісом або набором сервісів. Люди-оператори, які відповідають за конкретні застосунки та сервіси, мають глибокі знання про те, як система повинна поводитися, як її розгортати та як реагувати на проблеми.

Люди, які запускають робочі навантаження в Kubernetes, часто воліють використовувати автоматизацію для виконання повторюваних завдань. Шаблон Operator показує, як написати код для автоматизації завдання понад те, що надає сам Kubernetes.

Оператори в Kubernetes

Kubernetes створений для автоматизації. Із коробки ви отримуєте багато вбудованої автоматизації від ядра Kubernetes. Ви можете використовувати Kubernetes для автоматизації розгортання та запуску робочих навантажень, і ви можете автоматизувати те, як це робить Kubernetes.

Концепція шаблону Operator Kubernetes дозволяє розширити поведінку кластера без зміни коду самого Kubernetes, повʼязавши контролери з одним або кількома власними ресурсами. Оператори є клієнтами API Kubernetes, які діють як контролери для власного ресурсу.

Приклад оператора

Деякі завдання, які можна автоматизувати за допомогою оператора, включають:

  • розгортання застосунку за запитом
  • створення та відновлення резервних копій стану цього застосунку
  • оброблення оновлень коду застосунку разом із повʼязаними змінами, такими як схеми баз даних або додаткові параметри конфігурації
  • публікація Service для застосунків, які не підтримують API Kubernetes для їх виявлення
  • імітація відмови в усьому кластері або його частині для перевірки його стійкості
  • обрання лідера для розподіленого застосунку без внутрішнього процесу виборів учасників

Як може виглядати оператор докладніше? Ось приклад:

  1. Власний ресурс з назвою SampleDB, який можна налаштувати в кластері.
  2. Deployment, який забезпечує роботу Podʼа, що містить контролерну частину оператора.
  3. Образ контейнера з кодом оператора.
  4. Код контролера, який надсилає запити до панелі управління, щоб дізнатися, які ресурси SampleDB налаштовано.
  5. Ядром оператора є код, який підказує API-серверу, як привести реальний стан у відповідність до налаштованих ресурсів.
    • Якщо ви додаєте новий SampleDB, оператор налаштовує PersistentVolumeClaims для надійного сховища бази даних, StatefulSet для запуску SampleDB та Job для початкової конфігурації.
    • Якщо ви видаляєте його, оператор робить знімок, а потім переконується, що StatefulSet і томи також видалено.
  6. Оператор також керує регулярним резервним копіюванням бази даних. Для кожного ресурсу SampleDB оператор визначає, коли створити Pod, який може підʼєднатися до бази даних і зробити резервні копії. Ці Podʼи покладаються на ConfigMap та/або Secret, що містить параметри підʼєднання до бази даних та облікові дані.
  7. Оскільки оператор має на меті надійну автоматизацію ресурсу, яким він керує, у ньому буде додатковий допоміжний код. У цьому прикладі код перевіряє, чи працює база даних на старій версії, і якщо так, створює обʼєкти Job, які оновлюють її для вас.

Розгортання операторів

Найпоширеніший спосіб розгортання оператора — додавання CustomResourceDefinition та повʼязаного з ним контролера до вашого кластера. Контролер зазвичай працює за межами панелі управління, так само як і будь-який контейнеризований застосунок. Наприклад, ви можете запустити контролер у своєму кластері як Deployment.

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

Після розгортання оператора ви використовуєте його, додаючи, змінюючи або видаляючи ресурси того виду, який використовує оператор. Продовжуючи наведений вище приклад, ви налаштуєте Deployment для самого оператора, а потім:

kubectl get SampleDB                   # пошук налаштованих баз даних

kubectl edit SampleDB/example-database # ручна зміна деяких параметрів

…і все! Оператор подбає про застосування змін, а також про підтримання наявного сервісу в належному стані.

Створення власних операторів

Якщо в екосистемі немає оператора, який реалізує потрібну вам поведінку, ви можете створити власний.

Ви також можете реалізувати оператор (тобто контролер) будь-якою мовою або в будь-якому середовищі виконання, що може діяти як клієнт API Kubernetes.

Нижче наведено кілька бібліотек та інструментів, які можна використовувати для написання власного оператора cloud native.

Примітка: Цей розділ містить посилання на проєкти сторонніх розробників, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Проєкти вказано в алфавітному порядку. Щоб додати проєкт до цього списку, ознайомтеся з посібником з контенту перед надсиланням змін. Докладніше.

Що далі