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

Модуль 1.5: CRD та оператори — розширення Kubernetes

Складність: [СЕРЕДНЯ] — новинка в CKA 2025

Час на проходження: 35-45 хвилин

Передумови: Модуль 1.1 (розуміння площини управління)


Результати навчання

Розділ «Результати навчання»

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

  • Спроєктувати CustomResourceDefinition зі схемою валідації, областю дії, колонками виводу та підресурсами, які відповідають реальній операційній меті, а не існують просто заради зручності.
  • Діагностувати проблеми з виявленням CRD, групою API, версією, валідацією та видаленням, використовуючи для цього kubectl і метадані Kubernetes API.
  • Реалізувати цикл узгодження у стилі оператора, який перетворює власний ресурс на звичайні, знайомі вам об’єкти Kubernetes.
  • Оцінити, коли саме CRD та оператор кращі за вбудовані ресурси, значення Helm чи прості конфігураційні файли.
  • Діагностувати збої узгодження оператора, уважно оглядаючи статус власного ресурсу, події, поди контролера та межі RBAC.

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

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

Сценарій вправи: ваша платформна команда хоче, щоб команди застосунків могли запитувати TLS-сертифікати, цілі моніторингу та невеликі бази даних, не відкриваючи тікет для кожного допоміжного об’єкта. Команди не хочуть вивчати всі деталі Secret, Service, StatefulSet та проходження перевірки сертифіката, але кластеру все одно потрібні валідація, можливість аудиту, RBAC та повторювані операції. CustomResourceDefinition дає API-серверу новий тип ресурсу, а оператор надає цьому типу ресурсу поведінку.

Це та сама модель розширення, що стоїть за широко вживаними інструментами Kubernetes, як-от cert-manager, Prometheus Operator, Argo CD, Istio та багатьма операторами сховищ і баз даних. Ці інструменти не просять проєкт Kubernetes додати вбудований вид Certificate, ServiceMonitor чи PostgreSQL. Вони реєструють ці види через CRD, а потім запускають контролери, які стежать за новими ресурсами й узгоджують звичайні об’єкти Kubernetes, доки кластер не збігатиметься з бажаним станом.

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

Уявіть Kubernetes як громадську будівлю зі стійкою реєстрації. Вбудовані ресурси — це стандартні бланки запитів, які персонал уже вміє обробляти: Pod, Service, Deployment, ConfigMap. CRD додає до стійки новий бланк, наприклад Certificate чи Database. Оператор — це навчений співробітник, який читає цей бланк, замовляє потрібні матеріали, перевіряє хід виконання та повідомляє замовнику, коли роботу завершено.

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

За цю потужність доводиться платити. Щойно CRD встановлено, а команди починають фіксувати власні ресурси в Git, CRD стає API, від якого люди залежать. Перейменування полів, зміна типових значень, зміна області дії чи видалення версій можуть зламати розгортання так само певно, як і зміна публічного REST API. Ставтеся до проєктування CRD як до проєктування контракту: починайте з проблеми користувача, виставляйте найменший корисний намір та плануйте, як API зможе еволюціонувати, не дивуючи кожен кластер, що вже зберігає ці об’єкти.

Власні ресурси розширюють API, а не планувальник

Розділ «Власні ресурси розширюють API, а не планувальник»

CRD розширює Kubernetes API новим типом ресурсу. Після того як CRD прийнято API-сервером, новий вид бере участь у звичайних механізмах API: виявленні, валідації, зберіганні в etcd, потоках спостереження, перевірках RBAC, kubectl get, kubectl describe та видаленні. Це потужно, бо клієнтам не потрібна окрема база даних чи побічний API для відстеження платформного наміру; API кластера стає спільним контрактом.

Розширення навмисно вузьке. Встановлення CRD не вчить планувальник, як розміщувати базу даних, не створює подів і не виконує резервне копіювання. API-сервер зберігає власні ресурси та застосовує надану вами схему. Поведінка походить від контролера, який часто називають оператором, що спостерігає за власними ресурсами та створює або оновлює інші ресурси. Зупиніться та спрогнозуйте: якщо ви визначите CRD Database, а потім створите об’єкт Database із replicas: 3, що станеться до встановлення будь-якого оператора?

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

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

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

Built-in Resources: Custom Resources (via CRDs):
├── Pod ├── Certificate (cert-manager)
├── Deployment ├── Prometheus (prometheus-operator)
├── Service ├── PostgreSQL (postgres-operator)
├── ConfigMap ├── VirtualService (istio)
└── ... └── YourOwnResource

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

Terminal window
# Built-in resource
kubectl get pods
# Custom resource (after CRD is installed)
kubectl get certificates
kubectl get prometheuses
kubectl get postgresqls

Визначення CRD має кілька частин, які знову з’являються в реальних кластерах. Група API тримає ваш тип окремо від вбудованих груп і від інших проєктів-розширень. Список версій контролює, які версії обслуговуються і яка версія зберігається. Схема OpenAPI v3 каже API-серверу, якої форми мають бути дійсні власні ресурси. Область дії визначає, чи живуть екземпляри всередині просторів імен, чи на рівні кластера.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com # <plural>.<group>
spec:
group: stable.example.com # API group
versions:
- name: v1 # API version
served: true # Enable this version
storage: true # Store in etcd
schema:
openAPIV3Schema: # Validation schema
type: object
properties:
spec:
type: object
properties:
cronSpec:
type: string
image:
type: string
replicas:
type: integer
scope: Namespaced # or Cluster
names:
plural: crontabs # kubectl get crontabs
singular: crontab # kubectl get crontab
kind: CronTab # Kind in YAML
shortNames:
- ct # kubectl get ct

Угода про іменування для metadata.name не є декоративною. Kubernetes очікує, що назва CRD буде назвою ресурсу в множині, за якою йде група API, наприклад crontabs.stable.example.com. Якщо це не збігається зі spec.names.plural та spec.group, встановлення зазнає невдачі. Це робить виявлення однозначним, коли багато проєктів-розширень додають ресурси зі схожими видами.

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

Terminal window
# Apply the CRD
kubectl apply -f crontab-crd.yaml
# Verify it was created
kubectl get crd crontabs.stable.example.com
# Now you can create instances
kubectl get crontabs
# No resources found (expected - we haven't created any yet)

CRD зберігаються в etcd, як і вбудовані ресурси, але API-сервер усе одно суворіший за просте сховище «ключ-значення». Він валідує об’єкти за схемою, виставляє тип через виявлення, застосовує RBAC до дієслів на кшталт get та update і дозволяє клієнтам спостерігати за змінами. Саме ці спільні механізми пояснюють, чому власні ресурси чисто працюють із інструментами GitOps, контролерами допуску, журналами аудиту та стандартними операційними процесами.

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

Перший екземпляр власного ресурсу виглядає як будь-який інший маніфест, бо CRD зробив stable.example.com/v1 та CronTab розпізнаваними для API-сервера. Об’єкт нижче фіксує бажаний стан, а не фактичну запущену реалізацію cron. Без контролера він просто лежатиме в etcd як дійсні декларативні дані — видимі та редаговані, але кластер на них не діятиме.

apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-cron-job
namespace: default
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image
replicas: 3
Terminal window
kubectl apply -f my-crontab.yaml
kubectl get crontabs
kubectl get ct # Using shortName
kubectl describe crontab my-cron-job

Стандартні операції працюють, бо власний ресурс тепер є ресурсом API. Ви можете виводити його по всіх просторах імен, редагувати, видаляти, спостерігати за ним та отримувати збережений YAML. Конкретні доступні дієслова досі залежать від RBAC, тож розробник може мати змогу створити Certificate в одному просторі імен, тоді як керувати issuer на рівні кластера можуть лише адміністратори платформи.

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

Terminal window
# Create
kubectl apply -f crontab.yaml
# List
kubectl get crontabs -A
# Describe
kubectl describe crontab my-cron-job
# Edit
kubectl edit crontab my-cron-job
# Delete
kubectl delete crontab my-cron-job
# Watch
kubectl get crontabs -w
# Get as YAML
kubectl get crontab my-cron-job -o yaml

Перш ніж запускати наступну послідовність команд у тренувальному кластері, вирішіть, який рівень, на вашу думку, відповість на кожне запитання. Чи доводить kubectl api-resources, що контролер працює, чи лише те, що API-сервер знає тип? Чи показує kubectl describe на власному ресурсі бажаний стан, спостережуваний статус, чи обидва? Такий ментальний поділ робить діагностику CRD набагато менш випадковою.

Ще одна практична деталь полягає в тому, що виявлення може ненадовго відставати під час встановлення, бо клієнти кешують інформацію про ресурси API. Якщо ви встановлюєте CRD і одразу застосовуєте власний ресурс зі скрипта, старіший кеш клієнта чи проблема з порядком можуть дати плутане повідомлення «no matches». Повторний запуск виявлення, застосування CRD перед залежними ресурсами та явні кроки встановлення — це прості способи уникнути такого шуму. На іспитах не поспішайте та доведіть, що тип існує, перш ніж гнатися за непов’язаними симптомами.

Оператори перетворюють власні ресурси на роботу

Розділ «Оператори перетворюють власні ресурси на роботу»

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

Слово «оператор» іноді вживають вільно, але релевантний для CKA патерн є точним: CRD визначають поверхню API, а контролери реалізують поведінку. Багато операторів також постачають RBAC, ServiceAccount, деплойменти, конфігурацію виборів лідера, вебхуки допуску, вебхуки конвертації та логіку оновлення статусу. Ці допоміжні частини мають значення, бо контролер із неправильними дозволами може успішно спостерігати за ресурсами, але водночас не створювати Deployment, Secret чи StatefulSet, якими він має керувати.

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

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

┌────────────────────────────────────────────────────────────────┐
│ Operator Pattern │
│ │
│ You create: │
│ ┌─────────────────────────────────────────┐ │
│ │ apiVersion: databases.example.com/v1 │ │
│ │ kind: PostgreSQL │ │
│ │ spec: │ │
│ │ version: "15" │ │
│ │ replicas: 3 │ │
│ │ storage: 100Gi │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Operator (Controller) │ │
│ │ │ │
│ │ Watches PostgreSQL resources │ │
│ │ Creates: │ │
│ │ • StatefulSet with 3 replicas │ │
│ │ • PVCs for 100Gi storage │ │
│ │ • Services for connections │ │
│ │ • Secrets for credentials │ │
│ │ • ConfigMaps for configuration │ │
│ │ │ │
│ │ Manages: │ │
│ │ • Automatic failover │ │
│ │ • Backups │ │
│ │ • Version upgrades │ │
│ └─────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘

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

Посилання-власники (owner references) часто пов’язують керовані дочірні об’єкти назад із власним ресурсом, але вони не є всією історією. Оператори можуть керувати об’єктами, які не можна безпечно прибрати збиральником сміття, об’єктами в інших просторах імен чи ресурсами в зовнішніх системах, як-от DNS-провайдери та хмарні API. Саме тому статус, події, фіналізатори та логи залишаються важливими. Вам потрібно знати, чи мав оператор намір володіти дочірнім об’єктом, чи заблоковане прибирання та чи завершилася зовнішня робота.

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

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

┌─────────────────────────────────────────────────────────────┐
│ Reconciliation Loop │
│ │
│ ┌─────────┐ │
│ │ Watch │◄─────────────────────────────────────────┐ │
│ └────┬────┘ │ │
│ │ Event: PostgreSQL resource changed │ │
│ ▼ │ │
│ ┌─────────┐ │ │
│ │ Read │ Get current state from cluster │ │
│ └────┬────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────┐ │ │
│ │ Compare │ Current state vs. Desired state │ │
│ └────┬────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────┐ │ │
│ │ Act │ Create/Update/Delete resources │ │
│ └────┬────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────►─┘ │
│ Repeat forever │
│ │
└─────────────────────────────────────────────────────────────┘

Гіпотетичний сценарій: власний ресурс Certificate існує, але Secret, названий у spec.secretName, так і не з’являється. Перше запитання не «чи зламано Kubernetes?». Воно звучить так: «який контролер має узгодити цей тип і чи може цей контролер бачити об’єкт та записати цільовий Secret?». Ви перевірили б CRD, статус та події власного ресурсу, Deployment оператора, логи оператора та прив’язку RBAC, яка дає контролеру дозвіл керувати об’єктами Secret.

Що сталося б, якби под контролера cert-manager аварійно завершився, тоді як його CRD залишилися встановленими? Ви все ще могли б створювати власні ресурси Certificate, бо API-сервер знає тип, валідує схему та зберігає прийняті об’єкти. Наявні TLS-Secret продовжували б обслуговувати трафік, бо це звичайні Secret, які вже споживаються Ingress чи навантаженнями. Нова видача та поновлення зупинилися б, доки контролер не відновиться, бо жоден активний цикл узгодження не обробляв би ці власні ресурси.

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

Terminal window
# Install cert-manager (includes CRDs)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
# Check CRDs created
kubectl get crd | grep cert-manager
# certificates.cert-manager.io
# clusterissuers.cert-manager.io
# issuers.cert-manager.io
# ...

Об’єкт Certificate нижче є корисним прикладом, бо власний ресурс короткий, тоді як фактичний робочий процес — ні. Контролеру може знадобитися створити CertificateRequest, розв’язати перевірки ACME, оновити умови статусу, обробити повтори та записати Secret лише після успішної видачі. API, видимий для того, хто навчається, залишається компактним, бо оператор ховає процедурну роботу за декларативним типом.

# Create a Certificate resource
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-tls
namespace: default
spec:
secretName: myapp-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- myapp.example.com

Оператор cert-manager спостерігає за цим Certificate і запитує сертифікат, завершує перевірку ACME, зберігає сертифікат у названому Secret та поновлює його до закінчення терміну дії. Цей перелік — не магія, прихована всередині API-сервера. Це поведінка контролера, тож збої з’являються у статусі власного ресурсу, подіях, пов’язаних ресурсах та логах контролера, а не у виводі планувальника.

Ресурс, видимий користувачеві, навмисно декларативний. Він називає Secret, який має містити сертифікат, посилається на issuer та перелічує DNS-імена. Він не каже cert-manager, скільки повторів виконати чи які тимчасові ресурси створити для перевірки. Це відокремлення і є цінністю патерну оператора: власний ресурс фіксує стабільний намір, тоді як контролер може покращувати свою реалізацію від релізу до релізу, не змінюючи кожен маніфест застосунку.

Prometheus Operator дотримується того самого патерну з ресурсами моніторингу. Власний ресурс Prometheus оголошує екземпляр Prometheus, тоді як ServiceMonitor та пов’язані ресурси описують цілі збору та правила. Оператор спостерігає за цими власними ресурсами та виробляє StatefulSet, ConfigMap, Service та згенеровану конфігурацію. Результатом є рідний для Kubernetes API моніторингу, а не купа конфігураційних файлів, відредагованих вручну.

Ресурси моніторингу також показують, чому селектори та мітки мають значення в API операторів. Створення ServiceMonitor не гарантує, що кожен екземпляр Prometheus його вибере. Власний ресурс Prometheus може визначати селектори, що включають лише певні мітки чи простори імен. Коли збір даних не вдається, відповідь може бути у відношенні між власними ресурсами, а не в подах цільового застосунку. Завжди читайте обидві сторони декларативного контракту.

Terminal window
# Check Prometheus CRDs
kubectl get crd | grep monitoring.coreos.com
# prometheuses.monitoring.coreos.com
# servicemonitors.monitoring.coreos.com
# alertmanagers.monitoring.coreos.com
# Create a Prometheus instance
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: main
namespace: monitoring
spec:
replicas: 2
serviceAccountName: prometheus
serviceMonitorSelector:
matchLabels:
team: frontend

Виявлення — це навичка, яка тримає вас зорієнтованим у незнайомому кластері. Починайте з CRD, щоб дізнатися, які власні типи існують, потім переходьте до власних ресурсів, щоб знайти екземпляри, а далі знаходьте контролер, який володіє цими типами. Простори імен часто є підказками: cert-manager зазвичай працює в cert-manager, стеки моніторингу часто працюють у monitoring, а платформні команди можуть встановлювати оператори у виділених системних просторах імен.

У реальному кластері можуть бути десятки чи сотні CRD, тож розпізнавання патернів допомагає. Назви груп часто ідентифікують продукти, як-от cert-manager.io чи monitoring.coreos.com. Назви ресурсів часто ідентифікують концепцію, видиму користувачеві, як-от certificates, issuers, servicemonitors чи prometheusrules. Поди контролерів часто містять назву продукту чи оператора. Ці підказки самі по собі не є доказом, але разом вони утворюють швидку карту того, яке розширення API належить якому контролеру.

Terminal window
# List all CRDs in cluster
kubectl get crd
# See all custom resources of a type
kubectl get certificates -A
# Check if operator is running
kubectl get pods -A | grep operator
kubectl get pods -A | grep -E "cert-manager|prometheus"

Схема, область дії, статус та життєвий цикл

Розділ «Схема, область дії, статус та життєвий цикл»

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

Приклад нижче додає обов’язкові поля, патерн у стилі cron, числові межі та типове значення. Він досі навмисно невеликий, але показує спосіб мислення: розмістіть стабільну валідацію на межі API, а потім дайте контролеру зосередитися на поведінці. Не вкладайте кожне бізнес-правило у статичну схему; правила, що залежать від зовнішніх систем, поточного стану кластера чи асинхронної роботи, належать вебхукам допуску чи статусу узгодження.

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

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

schema:
openAPIV3Schema:
type: object
required:
- spec
properties:
spec:
type: object
required:
- cronSpec
- image
properties:
cronSpec:
type: string
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
image:
type: string
replicas:
type: integer
minimum: 1
maximum: 10
default: 1
Terminal window
# This would fail validation
kubectl apply -f bad-crontab.yaml
# Error: spec.replicas: Invalid value: 15: must be <= 10

Додаткові колонки виводу покращують досвід «другого дня». Власний ресурс може мати десятки полів, але найкорисніші поля мають з’являтися у kubectl get, не змушуючи кожного оператора читати YAML. Це особливо корисно під час іспитів та інцидентів, де потрібно швидко переглянути статус. Обирайте колонки, які допомагають відповісти на операційні запитання, а не кожне поле, що існує.

Колонки виводу — це невелика функція з великим впливом на зручність. Список сертифікатів, що показує готовність та термін дії, легше експлуатувати за той, що показує лише назви. Список резервних копій, що показує розклад, останній запуск та фазу, легше сортувати за інтерфейс, де є лише YAML. Якщо користувачам постійно потрібна та сама команда jsonpath, це сигнал, що CRD має виставляти кращу колонку.

versions:
- name: v1
additionalPrinterColumns:
- name: Schedule
type: string
jsonPath: .spec.cronSpec
- name: Replicas
type: integer
jsonPath: .spec.replicas
- name: Age
type: date
jsonPath: .metadata.creationTimestamp
Terminal window
kubectl get crontabs
# NAME SCHEDULE REPLICAS AGE
# my-cron-job * * * * */5 3 5m

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

Поділ spec-status — одна з найважливіших звичок Kubernetes, яку слід зберегти у власних API. Користувачі записують бажаний стан під spec, а контролери записують спостережуваний стан під status. Коли ці обов’язки розмиваються, володіння полями стає заплутаним, а автоматизація починає перезаписувати намір користувача. Чиста модель статусу дозволяє користувачам ставити практичні запитання: чи бачив контролер мою останню генерацію, у якій він фазі та яка умова пояснює поточне блокування?

versions:
- name: v1
subresources:
status: {} # Enable /status subresource
scale: # Enable kubectl scale
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicas
Terminal window
# Now this works
kubectl scale crontab my-cron-job --replicas=5

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

Область дії також змінює модель безпеки. Ресурси з областю простору імен зазвичай можна делегувати за допомогою Role та RoleBinding, що дозволяє командам керувати власними екземплярами, не торкаючись інших просторів імен. Ресурси з областю кластера потребують ClusterRole та імен на рівні кластера, тож вони мають представляти концепції, які справді перетинають межі просторів імен. Якщо ви не впевнені, запитайте, де мають бути володіння та радіус ураження (blast radius). Відповідь часто підказує правильну область ще до написання будь-якого YAML.

# Namespaced (default)
scope: Namespaced
# Resources exist within a namespace
# kubectl get crontabs -n myapp
# Cluster-scoped
scope: Cluster
# Resources are cluster-wide (like Nodes, PVs)
# kubectl get clusterissuers (cert-manager example)
Область діїКоли використовуватиПриклади
NamespacedРесурс належить команді/застосункуCertificate, Database, Application
ClusterРесурс спільний/глобальнийClusterIssuer, StorageProfile

Видалення CRD — це подія життєвого циклу, а не нешкідлива команда прибирання. Kubernetes видаляє екземпляри власних ресурсів цього CRD, бо їхній тип більше не існує. Об’єкти, створені оператором, як-от Secret чи Deployment, можуть бути видалені або ні, залежно від посилань-власників, фіналізаторів та поведінки контролера. Зупиніться та подумайте: якщо CRD видалено до того, як оператор прибрав свої фіналізатори, які докази ви б шукали, щоб підтвердити, які ресурси вціліли?

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

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

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

Огляд та діагностика, релевантні для іспиту

Розділ «Огляд та діагностика, релевантні для іспиту»

Найнадійніша послідовність діагностики CRD починається з виявлення. Запитайте, чи існує тип, до якої групи та версії він належить, чи має він область простору імен та які короткі назви доступні. kubectl api-resources відповідає на багато з цих запитань, не змушуючи вас читати повний CRD. Коли маніфест зазнає невдачі з «no matches for kind», порівняйте його apiVersion та kind із виводом виявлення, перш ніж гнатися за логами контролера.

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

Terminal window
# List all CRDs
kubectl get crd
# Get details about a CRD
kubectl describe crd certificates.cert-manager.io
# See the full CRD definition
kubectl get crd certificates.cert-manager.io -o yaml

Огляд власного ресурсу йде далі. Успішний kubectl get доводить, що об’єкт існує, але kubectl describe та kubectl get -o yaml показують поля, що мають значення під час діагностики. Шукайте status.conditions, події, спостережувану генерацію, фіналізатори, посилання-власники та чи перебуває об’єкт у тому просторі імен, на який ви очікуєте. Багато зрілих операторів використовують умови статусу як свій основний інтерфейс підтримки.

Події корисні, але вони не є тривкою базою даних усього, що сталося. Вони можуть закінчуватися, а кластери з великим обсягом можуть робити їх шумними. Умови статусу зазвичай стабільніші, бо вони живуть на об’єкті та оновлюються контролером. Логи потім дають детальний погляд контролера, особливо коли зовнішня система відхиляє запит. Використовуйте всі три джерела разом: події для свіжих підказок, статус для поточного стану та логи для міркувань контролера.

Terminal window
# List custom resources
kubectl get <resource-name> -A
# Get specific resource
kubectl get certificate my-cert -o yaml
# Edit custom resource
kubectl edit certificate my-cert
# Delete custom resource
kubectl delete certificate my-cert

Виявлення API також допомагає уникнути вгадування назв у множині. Вид може бути Certificate, але ресурс може бути certificates; вид може бути PrometheusRule, але назва ресурсу може бути prometheusrules. Група API ідентифікує проєкт-розширення, що нею володіє, що допомагає знайти правильну документацію, Deployment контролера та правила RBAC, коли поведінки немає.

Назви в множині та короткі назви можуть бути оманливо важливими під тиском часу. Якщо команда зазнає невдачі, бо ви вгадали неправильну назву ресурсу, помилка може виглядати як відсутній CRD, навіть якщо API здоровий. kubectl api-resources швидший і безпечніший за вгадування. Він показує назву ресурсу, короткі назви, групу API, статус простору імен та вид в одному місці — це саме та інформація, яка вам потрібна, щоб правильно сформувати наступну команду.

Terminal window
# List all resource types (including custom)
kubectl api-resources
# Filter by group
kubectl api-resources --api-group=cert-manager.io
# Show if namespaced
kubectl api-resources --namespaced=true

Поширена пастка на іспиті — зупинитися після того, як CRD існує. CRD лише доводить, що API-сервер може зберігати об’єкт. Якщо поведінки немає, оглядайте Deployment контролера, його логи, його статус виборів лідера, якщо застосовно, та дозволи на його ServiceAccount. Контролер може тихо відмовляти з точки зору того, хто навчається, бо власний ресурс досі виглядає здоровим, доки ви не оглянете його статус чи події.

Гіпотетичний сценарій: команда застосовує ServiceMonitor, але Prometheus так і не починає збирати дані із сервісу. Хороший шлях діагностики — перевірити CRD servicemonitors.monitoring.coreos.com, вивести ServiceMonitor у правильному просторі імен, перевірити мітки відносно селектора ресурсу Prometheus, оглянути логи Prometheus Operator та підтвердити, що RBAC дозволяє оператору читати простір імен. Перехід одразу до перезапусків подів пропускає декларативний контракт, за яким оператор насправді спостерігає.

Той самий процес працює для збоїв валідації. Якщо API-сервер відхиляє об’єкт, прочитайте шлях поля в помилці та порівняйте його зі схемою CRD. Якщо об’єкт прийнято, але оператор повідомляє Ready=False, прочитайте умови статусу та події. Якщо статус ніколи не змінюється, перевірте, чи має контролер дозвіл update на підресурсі status цього ресурсу. Кожен симптом вказує на іншу межу.

Збої RBAC заслуговують особливої уваги, бо вони можуть виглядати як вади оператора. Контролер може мати дозвіл виводити власні ресурси, але не створювати дочірній Secret, оновлювати підресурс status чи спостерігати за ресурсами в іншому просторі імен. Помилка, що виникає, зазвичай з’являється в логах контролера, а іноді в умовах статусу. Коли власний ресурс залишається в очікуванні без очевидної проблеми зі схемою, перевірте ServiceAccount, Role, ClusterRole та прив’язки, що належать оператору.

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

Патерни та антипатерни

Розділ «Патерни та антипатерни»

Хороший дизайн CRD починається зі стабільного контракту, видимого користувачеві, та ховає нестабільні деталі реалізації за контролером. Той, хто навчається, має могти прочитати власний ресурс і зрозуміти намір, не знаючи кожного згенерованого дочірнього об’єкта. Коли CRD віддзеркалює кожне поле в Deployment чи StatefulSet, він перестає спрощувати операції і стає другим, менш знайомим API для тієї самої проблеми.

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

Сильний патерн — проєктувати від runbook у зворотному порядку. Якщо черговому інженеру потрібно відповісти, чи заплановано резервну копію, чи вона востаннє завершилася, чи зараз вона відмовляє, чи заблокована обліковими даними, ці відповіді мають з’являтися в полях, умовах статусу, колонках виводу чи пов’язаних подіях. CRD — це не лише формат вхідних даних для розробника; це також операційна поверхня. Така перспектива породжує API, які легше діагностувати після того, як початковий автор пішов далі.

ПатернКоли використовуватиЧому це працюєМіркування щодо масштабування
CRD, орієнтований на намірКомандам потрібен простий ресурс, як-от Certificate, Database чи BackupPolicyCRD описує бажаний результат, тоді як оператор обробляє деталіТримайте spec невеликим і обережно версіонуйте в міру зростання використання
Умови статусуКористувачам потрібно діагностувати асинхронну роботуУмови виставляють спостережуваний стан, не змінюючи бажанийСтандартизуйте назви умов, щоб дашборди та runbook залишалися послідовними
Володіння на рівні простору іменКоманди застосунків володіють екземплярами незалежноRBAC та квоти можуть слідувати межам простору іменНадайте шаблони чи issuer з областю кластера для спільних типових значень
Колонки виводуОператорам потрібне швидке сортування через kubectl getВажливі поля з’являються у списках без читання повного YAMLУникайте виставлення нестабільних внутрішніх даних, що змінюються кожен цикл узгодження

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

Ще один антипатерн — ховати всі помилки в логах контролера. Логи необхідні супровідникам, але користувачі API не мають потребувати доступу cluster-admin, щоб дізнатися, чому їхній об’єкт не готовий. Умови статусу, події та зрозумілі повідомлення валідації наближають збій до людини, яка може виправити маніфест. Саме тому зрілі оператори активно інвестують у дизайн статусу: це перетворює асинхронну роботу на підтримувану розмову через API.

АнтипатернЩо йде не такКраща альтернатива
CRD без контролера для поведінкиКористувачі створюють дійсні об’єкти, що ніколи не впливають на навантаженняАбо встановіть оператор, або задокументуйте CRD як суто дані
Перевантажені поля specCRD стає копією кожного базового об’єкта KubernetesВиставляйте намір і дайте контролеру обирати деталі реалізації
Відсутній підресурс statusКонтролери та користувачі змагаються за ті самі поля об’єктаУвімкніть status і розмістіть спостережуваний стан під .status
Небезпечне видалення CRDУсі власні ресурси цього типу несподівано зникаютьЗробіть резервні копії маніфестів, видаляйте екземпляри обдумано та розумійте фіналізатори

Рамка прийняття рішень

Розділ «Рамка прийняття рішень»

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

Запитання «чи могли б ми побудувати це як CRD?» менш корисне за «кому вигідно, щоб це стало API?». Якщо одна команда застосовує один маніфест двічі на рік, CRD може бути церемонією. Якщо багато команд щотижня повторюють ризиковану послідовність, CRD плюс оператор можуть прибрати рутину та зменшити помилки. Що сильніші операційні знання за робочим процесом, то сильніший аргумент на користь оператора, а не статичного шаблону.

Need a new Kubernetes-facing abstraction?
|
v
Is the desired state durable and reusable across teams?
|-- no --> Prefer built-in resources, Helm values, or a ConfigMap.
|
yes
v
Does something need to reconcile or validate asynchronous work?
|-- no --> A CRD may be data-only, but document that clearly.
|
yes
v
Can you operate the controller, RBAC, upgrades, and status model?
|-- no --> Use an existing operator or narrow the problem first.
|
yes
v
Design a CRD with schema, scope, status, printer columns, and versioning.

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

Використовуйте такий фінальний тест перед введенням нового власного API: чи можете ви пояснити бажаний стан одним-двома реченнями, чи можете ви валідувати найбезпечніші частини під час допуску, чи можете ви виставити хід виконання через статус і чи можете ви відновитися, якщо контролер недоступний? Якщо відповідь «так», у вас, імовірно, є справжнє розширення Kubernetes. Якщо відповідь «ні», звузьте API чи оберіть простіший механізм, доки володіння не стане зрозумілішим.

ВибірВикористовуйте, колиУникайте, коли
Вбудований ресурсKubernetes уже моделює цю річ напрямуКористувачам потрібен предметний концепт, що охоплює кілька ресурсів
Значення HelmШаблонізації під час встановлення достатньоБажаний стан має бути під спостереженням та узгодженням після встановлення
ConfigMapСпоживачам потрібна лише статична конфігураціяВам потрібні валідація, статус, RBAC за типом ресурсу чи спостереження
CRD плюс операторПлатформі потрібен тривкий API та активне узгодженняЖодна команда не може володіти операціями контролера та сумісністю версій
  • Kubernetes додав CRD як стабільну заміну старішому механізму розширення ThirdPartyResource, і apiextensions.k8s.io/v1 уже роками є нормальним робочим API.
  • CRD може обслуговувати кілька версій, зберігаючи одну версію, що дозволяє авторам API мігрувати клієнтів поступово, замість того щоб ламати кожен маніфест одразу.
  • Operator SDK, Kubebuilder та controller-runtime — усі будуються на тих самих ідеях Kubernetes щодо спостереження та узгодження, хоча вони пакують каркас по-різному.
  • Фіналізатори — це звичайні рядки в метаданих, але вони можуть блокувати видалення, доки контролер не прибере їх після завершення прибирання.
ПомилкаЧому вона трапляєтьсяЯк її виправити
Створення власного ресурсу до CRDAPI-сервер ще не має зареєстрованого виду чи запису виявленняСпочатку встановіть CRD, потім повторно застосуйте власний ресурс після оновлення виявлення
Використання неправильної групи чи версії APIМаніфести скопійовано з іншого релізу оператора чи старої документаціїПеревірте kubectl api-resources та kubectl get crd <name> -o yaml на обслуговувані версії
Очікування, що CRD створить навантаження сам по собіCRD зберігає бажаний стан, але жодна логіка узгодження не працюєВстановіть та перевірте контролер оператора, потім огляньте статус власного ресурсу
Видалення CRD до видалення екземплярівТип і його власні ресурси видаляються разомЕкспортуйте чи зробіть резервну копію екземплярів, видаляйте їх обдумано та розумійте фіналізатори перед видаленням CRD
Припущення, що кожен власний ресурс має область простору іменДеякі платформні ресурси мають область кластера та ігнорують -nПеревірте kubectl api-resources --namespaced=false чи прочитайте spec.scope на CRD
Ігнорування умов статусуОператори часто повідомляють корисні збої на власному ресурсі, а не лише в логахВикористайте kubectl describe та огляньте .status.conditions, події та спостережувану генерацію
Надання оператору широких дозволів cluster-adminЦе швидше під час встановлення, але ризиковано під час інцидентів та аудитівВикористайте задокументований RBAC, потім звузьте дозволи до ресурсів та підресурсів, які він узгоджує
Недбала зміна схем CRDНаявні маніфести, конвеєри GitOps та збережені об’єкти залежать від контракту APIВерсіонуйте API, надайте настанови щодо конвертації чи міграції та тестуйте оновлення перед розгортанням
1. Ваша команда застосовує власний ресурс `Database`, його прийнято, але жодного StatefulSet не з'являється. Що ви перевіряєте першим і чому?

Почніть із доведення, який рівень працює. Прийнятий власний ресурс означає, що CRD існує, а API-сервер може зберегти об’єкт, тож наступні перевірки — це Deployment оператора, його логи, дозволи його ServiceAccount та статус чи події власного ресурсу. Сам CRD не створює StatefulSet, тож повторне редагування власного ресурсу навряд чи допоможе, доки контролер не спостерігає та не узгоджує його. Якщо статус порожній, перевірте також, чи має контролер дозвіл оновлювати підресурс status.

2. Маніфест зазнає невдачі з `no matches for kind "Certificate" in version "cert-manager.io/v1"`. Як би ви діагностували бік API, перш ніж досліджувати логи cert-manager?

Спершу виконайте kubectl get crd | grep cert-manager та kubectl api-resources --api-group=cert-manager.io, щоб підтвердити, що CRD встановлено та обслуговує очікувану версію. Якщо CRD відсутній, встановіть чи відремонтуйте cert-manager перед застосуванням ресурсу. Якщо група існує, але версія відрізняється, оновіть маніфест до обслуговуваної версії чи оновіть CRD згідно з документацією вендора. Логи контролера тут вторинні, бо API-сервер відхилив об’єкт, перш ніж будь-який контролер міг його узгодити.

3. Платформний інженер пропонує CRD `BackupPolicy` на рівні всього кластера, але встановлює `scope: Namespaced`. Які операційні проблеми ви маєте підняти?

Область простору імен змусила б кожен простір імен дублювати те, що має бути спільною політикою, збільшуючи дрейф і ускладнюючи інтерпретацію результатів аудиту. Вона також змінює RBAC та поведінку команд, бо користувачам знадобилися б дозволи, специфічні для простору імен, та команди kubectl. Якщо політика справді керує всім кластером, scope: Cluster є зрозумілішим API, з ClusterRole та глобально унікальними іменами. Якщо командам потрібні локальні перевизначення, використайте окремий ресурс із областю простору імен чи поля, що посилаються на політику з областю кластера.

4. Ви видаляєте `certificates.cert-manager.io`, тоді як існує багато ресурсів `Certificate`. Що вціліє, що буде втрачено та який шлях відновлення є реалістичним?

Видалення CRD прибирає власні ресурси цього типу, бо тип API зник. Secret, уже записані cert-manager, можуть продовжувати існувати та обслуговувати наявний TLS-трафік, але бажані об’єкти сертифікатів та намір поновлення зникли. Відновлення зазвичай означає перевстановлення CRD та повторне створення маніфестів Certificate з Git чи резервної копії. Саме тому видалення CRD слід трактувати як видалення API, а не як звичайне прибирання.

5. Власний ресурс `CronTab` відхилено, бо `spec.replicas` дорівнює `15`, але команда наполягає, що оператор міг би обробити це значення. Де слід зробити виправлення?

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

6. Prometheus не збирає дані із сервісу після створення `ServiceMonitor`. Які підказки, специфічні для CRD та оператора, допомагають діагностувати проблему?

Підтвердьте, що CRD servicemonitors.monitoring.coreos.com існує, а ServiceMonitor перебуває в просторі імен, на який очікує ресурс Prometheus. Потім порівняйте мітки та селектори між ServiceMonitor та власним ресурсом Prometheus, бо оператор може вибирати лише монітори зі збіжними мітками. Огляньте логи Prometheus Operator та статус пов’язаних ресурсів на помилки узгодження. Цей шлях перевіряє декларативний зв’язок, перш ніж припустити, що проблема в подах навантаження.

7. Deployment, керований оператором, вручну масштабовано з двох реплік до п'яти, але згодом він повертається до двох. Що це каже вам про узгодження?

Це показує, що оператор трактує власний ресурс як джерело істини та усуває дрейф у дочірніх ресурсах. Ручні зміни згенерованих об’єктів можуть здаватися робочими на короткий час, але наступний цикл узгодження порівнює фактичний стан із бажаним та повертає дочірній об’єкт до оголошеного значення. Тривке виправлення — відредагувати поле власного ресурсу, що керує кількістю реплік, якщо CRD його виставляє. Це очікувана поведінка, а не контролер Deployment, що сам по собі бореться з користувачем.

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

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

Завдання 1: Створіть CRD Website

Розділ «Завдання 1: Створіть CRD Website»

Створіть ресурс Website, що записує URL та кількість реплік. Цей перший CRD не запускає вебсайт сам по собі; він дає API-серверу новий тип і дозволяє вам попрактикувати виявлення та валідацію. Зверніть увагу, що схема вимагає spec.url, тоді як spec.replicas має типове значення.

Terminal window
cat > website-crd.yaml << 'EOF'
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: websites.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required:
- url
properties:
url:
type: string
replicas:
type: integer
default: 1
additionalPrinterColumns:
- name: URL
type: string
jsonPath: .spec.url
- name: Replicas
type: integer
jsonPath: .spec.replicas
- name: Age
type: date
jsonPath: .metadata.creationTimestamp
scope: Namespaced
names:
plural: websites
singular: website
kind: Website
shortNames:
- ws
EOF
kubectl apply -f website-crd.yaml
Terminal window
kubectl get crd websites.stable.example.com
kubectl api-resources | grep website
Нотатки до розв'язання

CRD має з’явитися як websites.stable.example.com, а kubectl api-resources має показати ресурс websites із короткою назвою ws. Якщо виявлення його не показує, огляньте помилку встановлення CRD, перш ніж створювати екземпляри. API-сервер має знати тип, перш ніж зможе прийняти об’єкт Website.

Завдання 2: Створіть та огляньте власні ресурси

Розділ «Завдання 2: Створіть та огляньте власні ресурси»

Створіть один екземпляр Website, потім використайте форми множини, однини та короткої назви, щоб оглянути його. Це завдання навмисно про поведінку API, а не про хостинг вебсайтів. Об’єкт записує бажаний стан і стає видимим для стандартних інструментів Kubernetes.

Terminal window
cat > my-website.yaml << 'EOF'
apiVersion: stable.example.com/v1
kind: Website
metadata:
name: company-site
namespace: default
spec:
url: https://example.com
replicas: 3
EOF
kubectl apply -f my-website.yaml
Terminal window
# List websites
kubectl get websites
kubectl get ws # Short name
# Describe
kubectl describe website company-site
# Get as YAML
kubectl get website company-site -o yaml
# Edit
kubectl edit website company-site
Terminal window
cat > blog.yaml << 'EOF'
apiVersion: stable.example.com/v1
kind: Website
metadata:
name: blog
spec:
url: https://blog.example.com
replicas: 2
EOF
kubectl apply -f blog.yaml
kubectl get ws
Нотатки до розв'язання

І company-site, і blog мають з’явитися з власними колонками виводу, якщо ваш кластер прийняв CRD точно так, як його написано. Якщо вивід не показує колонки, перевірте, що additionalPrinterColumns вкладено під обслуговувану версію. Якщо kubectl get ws зазнає невдачі, переконайтеся, що shortNames було прийнято в CRD.

Завдання 3: Дослідіть встановлені оператори та CRD

Розділ «Завдання 3: Дослідіть встановлені оператори та CRD»

Використайте команди виявлення, щоб ідентифікувати будь-які API, керовані операторами, що вже присутні у вашому кластері. Невеликий тренувальний кластер може не мати встановлених cert-manager чи Prometheus, тож відсутній результат не є автоматично невдачею. Сенс у тому, щоб попрактикувати перехід від назв CRD до екземплярів ресурсів, а потім до ймовірних подів контролера.

Terminal window
# Check for cert-manager
kubectl get crd | grep cert-manager
# Check for prometheus operator
kubectl get crd | grep monitoring.coreos.com
# List all CRDs
kubectl get crd
Terminal window
# List all CRDs
kubectl get crd
# Get details on a specific CRD
kubectl get crd <crd-name> -o yaml | head -50
# List instances of a CRD
kubectl get <resource-name> -A
# Describe a CRD
kubectl describe crd <crd-name>
Нотатки до розв'язання

Для встановленого оператора ви маєте могти пов’язати щонайменше три підказки: групу CRD, один чи кілька власних ресурсів та под чи Deployment контролера. Наприклад, ресурси cert-manager використовують групу cert-manager.io, тоді як ресурси Prometheus Operator використовують monitoring.coreos.com. Якщо поширених операторів не встановлено, використайте свій CRD Website для того самого процесу виявлення.

Завдання 4: Побудуйте приклади валідації та статусу

Розділ «Завдання 4: Побудуйте приклади валідації та статусу»

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

Terminal window
# Create CRD
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: apps.example.com
spec:
group: example.com
names:
kind: App
listKind: AppList
plural: apps
singular: app
shortNames:
- ap
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
image:
type: string
replicas:
type: integer
EOF
# Verify CRD exists
kubectl get crd apps.example.com
# Create an instance
cat << 'EOF' | kubectl apply -f -
apiVersion: example.com/v1
kind: App
metadata:
name: my-app
spec:
image: nginx:1.25
replicas: 3
EOF
# Query using short name
kubectl get ap
# Cleanup
kubectl delete app my-app
kubectl delete crd apps.example.com
Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: databases.stable.example.com
spec:
group: stable.example.com
names:
kind: Database
plural: databases
singular: database
shortNames:
- db
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
required:
- spec
properties:
spec:
type: object
required:
- engine
- version
properties:
engine:
type: string
enum:
- postgres
- mysql
- mongodb
version:
type: string
storage:
type: string
default: "10Gi"
EOF
# Try to create invalid resource (should fail)
cat << 'EOF' | kubectl apply -f -
apiVersion: stable.example.com/v1
kind: Database
metadata:
name: invalid-db
spec:
engine: oracle # Not in enum!
version: "14"
EOF
# Create valid resource
cat << 'EOF' | kubectl apply -f -
apiVersion: stable.example.com/v1
kind: Database
metadata:
name: prod-db
spec:
engine: postgres
version: "14"
EOF
# Cleanup
kubectl delete database prod-db
kubectl delete crd databases.stable.example.com
Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: tasks.work.example.com
spec:
group: work.example.com
names:
kind: Task
plural: tasks
scope: Namespaced
versions:
- name: v1
served: true
storage: true
subresources:
status: {}
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
command:
type: string
status:
type: object
properties:
phase:
type: string
completedAt:
type: string
EOF
# Create task
cat << 'EOF' | kubectl apply -f -
apiVersion: work.example.com/v1
kind: Task
metadata:
name: build-job
spec:
command: "make build"
EOF
# View the task
kubectl get task build-job -o yaml
# Cleanup
kubectl delete task build-job
kubectl delete crd tasks.work.example.com
Нотатки до розв'язання

Недійсний Database має бути відхилено до зберігання, бо oracle немає в enum. Дійсний об’єкт має бути збережено та виведено. Об’єкт Task має показувати поле status, лише якщо щось його записує, але CRD тепер виставляє підресурс status, який контролер міг би оновлювати окремо від spec, керованого користувачем.

Завдання 5: Діагностуйте відсутній CRD та спроєктуйте CRD Backup

Розділ «Завдання 5: Діагностуйте відсутній CRD та спроєктуйте CRD Backup»

Застосуйте ресурс для типу, що не існує, поспостерігайте за збоєм, а потім спроєктуйте CRD Backup з обов’язковими полями. Це віддзеркалює найпоширеніший перший збій під час встановлення застосунків, керованих операторами, із неповних маніфестів: власний ресурс застосовується до CRD.

Terminal window
# Try to create a resource for non-existent CRD
cat << 'EOF' | kubectl apply -f -
apiVersion: nonexistent.example.com/v1
kind: Widget
metadata:
name: test
spec:
size: large
EOF
# Error: no matches for kind "Widget"
# Diagnose
kubectl get crd | grep widget # Nothing
kubectl api-resources | grep -i widget # Nothing
# Solution: CRD must be created before resources
# Create the CRD first, then the resource

Спроєктуйте та реалізуйте CRD для ресурсу Backup із групою backup.example.com, обов’язковими полями source, destination та schedule, необов’язковим цілим полем retention із типовим значенням сім днів та валідацією розкладу як рядкового поля. Потім створіть зразковий ресурс Backup і запитайте його через коротку назву.

Terminal window
# YOUR TASK: Create the CRD and a sample Backup resource
Розв'язання
Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: backups.backup.example.com
spec:
group: backup.example.com
names:
kind: Backup
plural: backups
shortNames:
- bk
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
required:
- spec
properties:
spec:
type: object
required:
- source
- destination
- schedule
properties:
source:
type: string
destination:
type: string
schedule:
type: string
retention:
type: integer
default: 7
EOF
cat << 'EOF' | kubectl apply -f -
apiVersion: backup.example.com/v1
kind: Backup
metadata:
name: daily-db-backup
spec:
source: /data/postgres
destination: s3://backups/postgres
schedule: "0 2 * * *"
retention: 14
EOF
kubectl get bk
kubectl delete backup daily-db-backup
kubectl delete crd backups.backup.example.com

Завдання 6: Запустіть базовий навчальний оператор

Розділ «Завдання 6: Запустіть базовий навчальний оператор»

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

Terminal window
# 1. First, create the Website CRD
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: websites.stable.example.com
spec:
group: stable.example.com
scope: Namespaced
names:
plural: websites
singular: website
kind: Website
shortNames: [ws]
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
image: {type: string, default: "nginx:alpine"}
replicas: {type: integer, default: 1}
EOF
# 2. Create the Operator Script
# This represents the controller's reconciliation loop
cat << 'EOF' > website-operator.sh
#!/bin/bash
echo "Starting Website Operator..."
while true; do
# Find all Website custom resources
for ws in $(kubectl get websites -o jsonpath='{.items[*].metadata.name}' 2>/dev/null); do
image=$(kubectl get website $ws -o jsonpath='{.spec.image}')
replicas=$(kubectl get website $ws -o jsonpath='{.spec.replicas}')
# Reconcile: Ensure a Deployment exists with the desired state
kubectl create deployment $ws-site --image=$image --replicas=$replicas --dry-run=client -o yaml | kubectl apply -f - >/dev/null 2>&1
echo "Reconciled Website: $ws -> Image: $image, Replicas: $replicas"
done
sleep 5
done
EOF
chmod +x website-operator.sh
# 3. Run the operator in the background
./website-operator.sh &
OPERATOR_PID=$!
# 4. Create a Custom Resource
cat << 'EOF' | kubectl apply -f -
apiVersion: stable.example.com/v1
kind: Website
metadata:
name: my-portfolio
spec:
image: "nginx:alpine"
replicas: 2
EOF
# 5. Observe the reconciliation (Wait a few seconds for the loop)
sleep 6
kubectl get deployments
kubectl get pods
# 6. Test the Reconciliation Loop
# The operator should fight back if we delete the managed deployment
echo "Deleting the managed deployment to simulate a failure..."
kubectl delete deployment my-portfolio-site
# 7. Check again in 5-10 seconds
sleep 6
kubectl get deployments
# The operator recreated it! This is the reconciliation loop in action.
# 8. Clean up
kill $OPERATOR_PID
kubectl delete website my-portfolio
kubectl delete deployment my-portfolio-site
kubectl delete crd websites.stable.example.com
rm website-operator.sh
Нотатки до розв'язання

Коли скрипт працює, Deployment має з’явитися після того, як власний ресурс Website існує. Якщо ви видалите Deployment, доки скрипт продовжує працювати, наступний цикл має його відтворити. Якщо цього не сталося, огляньте, чи повертає kubectl get websites власний ресурс, чи має скрипт дозвіл створювати деплойменти та чи збігається назва Deployment з угодою про іменування у скрипті.

  • Спроєктувати CRD із дійсною групою, іменами, областю дії, версією, схемою та колонками виводу.
  • Створити власні ресурси та запитати їх формами множини, однини та короткої назви.
  • Діагностувати збої виявлення та валідації CRD за допомогою kubectl get crd, kubectl api-resources та помилок застосування.
  • Реалізувати цикл узгодження у стилі оператора, який відтворює Deployment із власного ресурсу.
  • Оцінити, чи має власний ресурс мати область простору імен, чи область кластера.
  • Діагностувати збої узгодження оператора, перевіряючи статус власного ресурсу, події, поди контролера, логи та RBAC.

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

Terminal window
kubectl delete website company-site blog my-portfolio --ignore-not-found
kubectl delete deployment my-portfolio-site --ignore-not-found
kubectl delete crd websites.stable.example.com --ignore-not-found
kubectl delete crd apps.example.com --ignore-not-found
kubectl delete crd databases.stable.example.com --ignore-not-found
kubectl delete crd tasks.work.example.com --ignore-not-found
kubectl delete crd backups.backup.example.com --ignore-not-found
rm -f website-crd.yaml my-website.yaml blog.yaml website-operator.sh

Модуль 1.6: RBAC — рольовий контроль доступу для захисту вашого кластера.