ІнфраструктураSenior

Kubernetes

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

Ви оголошуєте
Стан, а не команду
Контролери
Зводять постійно
Більшість інцидентів
Ліміти й проби

Цикл керування, а не виконавець команд

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

Kubernetes — це цикл керування. Ви подаєте обʼєкт, що описує, як має бути, сервер API його зберігає, а контролер крутиться завжди: прочитати бажаний стан, прочитати дійсний стан, подіяти на різницю — і прочитати знову. Саме тому видалення Pod руками не дає нічого тривкого: Deployment досі каже «три репліки», і контролер робить третій. Саме тому застосувати той самий маніфест двічі нешкідливо: ви повторили факт, а не видали другу вказівку. І саме тому корисне питання під час інциденту — у що кластер вірить як у належне і що йому заважає, а відповідь дають статус обʼєкта та його події. Це ж і є аргумент тримати маніфести в репозиторії: ручна зміна — це перевизначення, яке якийсь контролер скасує.

Саме тому видалення Pod руками не дає нічого тривкого: Deployment досі каже «три репліки», тож контролер помічає дві й робить третю. Саме тому застосувати той самий маніфест двічі нешкідливо: ви повторили факт, а не видали другу вказівку. І саме тому цікаве питання під час інциденту — ніколи не «чи виконалася команда», а «у що кластер зараз вірить як у належне і що йому заважає». Відповідь — у статусі обʼєкта та його подіях, і це перше місце, куди варто дивитися, і останнє, куди дивляться.

Обʼєкти, які вам справді потрібні

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

  • Pod

    Один чи кілька контейнерів, що ділять мережеву адресу й можуть ділити том. Найменше, що планується, — і те, що ніколи не створюють напряму.

  • Deployment

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

  • Service

    Стала назва й адреса перед змінним набором Pod. Pod приходять і зникають з новими адресами; Service — ні.

  • Ingress

    Маршрутизує зовнішній HTTP-трафік до Service за хостом і шляхом і завершує TLS. Одна точка входу замість однієї на сервіс.

  • ConfigMap і Secret

    Конфігурація й облікові дані, подані до Pod як змінні чи файли. Secret закодовано в base64, а не зашифровано, — вмикайте шифрування у спокої.

  • StatefulSet

    Для Pod, яким потрібні стала ідентичність і власне сховище. Потрібен базам — і привід спитати, чи хочете ви їх тут узагалі.

Service вартий ще одного речення, бо саме тут живе плутанина з іменами. Усередині кластера Service дає вам DNS-імʼя, що розвʼязується у сталу віртуальну адресу, а трафік до неї розподіляється між тими Pod, які зараз збігаються з його селектором. Ця непрямість і є причиною, чому Pod можна замінити будь-якої миті, нічого більше не переналаштовуючи, — і вона ж є причиною, чому «Pod працює, але до нього ніхто не достукається» майже завжди означає селектор, що не збігається з мітками Pod.

Звідки беруться інциденти

Два налаштування спричиняють більше проблем у проді, ніж усе решта разом, і обидва виглядають як шаблонний текст. Перше — ресурси. Запит (request) — це те, що планувальник резервує, вирішуючи, на який вузол Pod влазить; ліміт — це стеля, що діє під час роботи. Задайте запити замалими — і вузол перепідписаний, тож Pod приземляються там, де немає куди рости. Задайте зависокими — і платите за потужність, якою ніхто не користується. Не задайте зовсім — і планувальник вгадує, а так один сервіс і починає морити сусідів на тому самому вузлі.

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

ПробаНа що відповідаєЩо стається, коли падає
ReadinessЧи може цей Pod прийняти трафік прямо зараз?Його виводять із Service і лишають працювати. Безпечна — і та, за якою варто тягтися першою.
LivenessЧи цей процес заклинило без надії на відновлення?Контейнер убивають і перезапускають. Потужна — і звична причина аварії, завданої самому собі.
StartupЧи закінчив стартувати застосунок, що довго вантажиться?Інші проби відкладають, доки вона не пройде: так повільний старт перестає бути циклом перезапусків.

Як це виявляється в реальній поставці

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

Якщо ви все ж його тримаєте, практика, що окупається найшвидше, — тримати маніфести в репозиторії й давати чомусь їх застосовувати, а не давати людям виконувати команди проти кластера. Тоді кластер має історію, придатну до перегляду, відкат стає ревертом, а різниця між тим, що має працювати, і тим, що працює, стає видимою, а не переказаною. Шаблонізація — Helm, Kustomize чи будь-хто з їхніх нащадків — заслуговує на місце тієї миті, коли ті самі маніфести існують для кожного середовища, і перестає заслуговувати тієї миті, коли ніхто не може сказати, що ці шаблони породжують.

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

Де це вироджується

  • Узяти його на три сервіси на двох машинах: експлуатаційна ціна є, вигоди немає.
  • Проба liveness із залежністю на шляху: повільна база перетворюється на бурю перезапусків у всьому кластері.
  • Відсутність запитів ресурсів: планувальник вгадує, і один сервіс морить сусідів.
  • Пошук стектрейсу для перезапуску OOMKilled, якого ніколи не було, бо вбивало ядро.
  • Зміни через kubectl edit на живому кластері, які наступне застосування тихо викидає.
  • Секрети, закомічені як маніфести: закодовані base64 й помилково взяті за зашифровані.
  • Service, чий селектор не збігається з мітками Pod: здоровий Pod не дістає нічого.
  • Власна база в кластері, де ніхто не володіє бекапами, перемиканням при збої й сховищем.
  • Шаблони поверх шаблонів, аж поки ніхто не може сказати, що саме застосовується.

Коли це застосовувати

Застосовуйте, коли

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

Уникайте, коли

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

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.