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

Модуль 4.6: Custom Resource Definitions (CRDs)

Складність: [СЕРЕДНЯ] — нове в CKAD 2025, важливе концептуальне розуміння.

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

Передумови: Розуміння ресурсів Kubernetes та структури API.


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

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

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

  • Спроєктувати простірний (namespaced) CustomResourceDefinition зі схемною валідацією, прапорцями версій та передбачуваними іменами ресурсів.
  • Впровадити кастомні ресурси та використовувати команди виявлення kubectl, щоб підтвердити обслуговувані ресурси, короткі імена та поведінку валідації.
  • Діагностувати збої валідації, іменування, області видимості та узгодження оператором, відокремлюючи поведінку API-сервера від поведінки контролера.
  • Оцінити, коли CRD, ConfigMap, вбудоване API робочого навантаження чи повноцінний Оператор є правильною абстракцією для платформи на Kubernetes 1.35+.

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

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

Hypothetical scenario: ваша платформна команда встановлює оператор бази даних у спільний кластер Kubernetes. Командам застосунків кажуть створювати об’єкти Database замість того, щоб вручну писати StatefulSet’и, PersistentVolumeClaim’и, Сервіси, Secret’и та CronJob’и для резервного копіювання. Перший запит здається простим, але операційні ставки високі: якщо схема CRD приймає поля з помилками в назвах, API-сервер зберігає некоректний намір; якщо оператора немає, кастомний об’єкт існує, але жодної бази даних не з’являється; якщо хтось видаляє CRD, кожен кастомний ресурс цього типу може зникнути разом із ним.

Саме тому CRD мають значення для кандидатів на CKAD, навіть якщо іспит не вимагає від вас написати оператор виробничого рівня. Custom Resource Definitions розширюють API Kubernetes новими іменниками, тож kubectl get databases може стати настільки ж природним, як і kubectl get pods. API-сервер зберігає та валідує ці нові об’єкти, тоді як контролер, що часто постачається у вигляді Оператора, спостерігає за об’єктами й перетворює задекларований намір на реальні ресурси кластера.

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

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

CRD розширюють API Kubernetes

Розділ «CRD розширюють API Kubernetes»

CustomResourceDefinition — це об’єкт Kubernetes, який повідомляє API-серверу про інший тип об’єктів Kubernetes. Спершу це звучить як замкнене коло, але це той самий механізм розширення, який використовують багато зрілих платформних проєктів. Ви створюєте один CRD з областю видимості кластера, названий у форматі plural.group, і API-сервер починає обслуговувати нову кінцеву точку REST під шляхом /apis/<group>/<version>/<plural>, з повним набором виявлення, авторизації, допуску, валідації, підтримки спостереження та стандартної поведінки kubectl.

Уявляйте CRD як реєстраційний стіл, а не як саме робоче навантаження. Реєстрація databases.example.com повідомляє Kubernetes, як приймати об’єкти на ім’я Database, але вона не створює рушій бази даних, диск чи мережеву кінцеву точку. CRD дає кластеру нове словникове слово; кастомний ресурс використовує це словникове слово; контролер потім може перекласти цей ресурс на ресурси нижчого рівня.

Ось компактний CRD з оригінального модуля. Він навмисно малий, щоб форма була видимою, перш ніж пізніші розділи додадуть деталі валідації, команди виявлення та поведінку оператора. Зверніть увагу, що цей CRD використовує apiextensions.k8s.io/v1, оголошує example.com як свою API-групу, обслуговує одну версію на ім’я v1 та визначає імена, які користувачі вводитимуть через kubectl.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: databases.example.com # plural.group format
spec:
group: example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
engine:
type: string
size:
type: string
scope: Namespaced
names:
plural: databases
singular: database
kind: Database
shortNames:
- db

Щойно цей CRD встановлено, користувач може подати кастомний ресурс. Кастомний ресурс має групу та версію з CRD у полі apiVersion, kind із spec.names.kind, звичайні метадані Kubernetes і корисне навантаження spec, форма якого має відповідати схемі. З точки зору API-сервера, цей об’єкт не є другосортною нотаткою, запханою в ConfigMap; це об’єкт API з тими самими сховищем, спостереженням, RBAC та конвеєром допуску, що їх використовують вбудовані ресурси.

apiVersion: example.com/v1
kind: Database
metadata:
name: my-database
spec:
engine: postgres
size: large

Зробіть паузу й передбачте: ви бачите, що в кластері використовують kubectl get certificates. Чи є Certificate вбудованим ресурсом Kubernetes, кастомним ресурсом на основі CRD, чи чимось іншим? Перш ніж запускати команду, вирішіть, який доказ підтвердив би відповідь, бо завдання CKAD часто винагороджують швидке виявлення більше, ніж попереднє знання кожного додаткового проєкту.

Найшвидший доказ — це виявлення через API. kubectl api-resources перелічує як вбудовані ресурси, так і ресурси на основі CRD, включно з їхніми API-групами, ознакою належності до простору імен та будь-якими короткими іменами. kubectl get crd перелічує лише самі визначення CRD. Якщо certificates.cert-manager.io існує як CRD, а kubectl api-resources показує certificates у групі cert-manager.io, ви знаєте, що ресурс надається розширенням, а не основним API Kubernetes.

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

┌─────────────────────────────────────────────────────────────┐
│ CRD Creates New API Endpoint │
├─────────────────────────────────────────────────────────────┤
│ │
│ Before CRD: │
│ ┌─────────────────────────────────┐ │
│ │ /api/v1/pods │ │
│ │ /api/v1/services │ │
│ │ /apis/apps/v1/deployments │ │
│ └─────────────────────────────────┘ │
│ │
│ After CRD (group: example.com, plural: databases): │
│ ┌─────────────────────────────────┐ │
│ │ /api/v1/pods │ │
│ │ /api/v1/services │ │
│ │ /apis/apps/v1/deployments │ │
│ │ /apis/example.com/v1/databases │ ← NEW! │
│ └─────────────────────────────────┘ │
│ │
│ kubectl commands now work: │
│ $ kubectl get databases │
│ $ kubectl describe database my-db │
│ $ kubectl delete database my-db │
│ │
└─────────────────────────────────────────────────────────────┘

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

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

Блок names — це звернений до користувача словник. Форма множини використовується в URL-адресах та поширених командах переліку, форма однини приймається інтерфейсом командного рядка, kind з’являється в YAML, а короткі імена дають інтерактивні скорочення, коли автор їх надає. Короткі імена зручні, але їх не варто використовувати в скриптах, якщо команда не контролює CRD, бо коротке ім’я може зіткнутися з іншим ресурсом або бути відсутнім в іншому кластері.

names:
plural: databases # Used in URLs: /apis/example.com/v1/databases
singular: database # Used in CLI: kubectl get database
kind: Database # Used in YAML: kind: Database
shortNames:
- db # Shortcuts: kubectl get db

Область видимості вирішує, чи належить кожен кастомний ресурс простору імен, чи всьому кластеру. Простірний ресурс поводиться як ConfigMap чи Secret: kubectl get databases -n production і kubectl get databases -n staging можуть показувати різні об’єкти. Ресурс з областю видимості кластера поводиться більше як Node чи StorageClass: існує одна спільна колекція без простору імен, і RBAC треба проєктувати з огляду на доступ на рівні кластера.

scope: Namespaced # Resources exist in namespaces
# or
scope: Cluster # Resources are cluster-wide

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

Версії дозволяють CRD еволюціонувати, не ламаючи кожного клієнта одразу. Версію з served: true можна запитувати через API, тоді як єдина версія з storage: true — це версія, що записується в etcd. Виробничі CRD часто починаються з alpha- чи beta-версій, пізніше додають стабільну версію та продовжують обслуговувати старі версії протягом вікна міграції, поки логіка конвертації чи сумісні схеми захищають наявні ресурси.

versions:
- name: v1
served: true # API server serves this version
storage: true # Store in etcd using this version (only one can be true)

Для роботи в межах CKAD вам зазвичай потрібно читати ці прапорці, а не проєктувати багатоверсійний план конвертації. Якщо маніфест використовує apiVersion: example.com/v1beta1, а CRD обслуговує лише v1, ресурс відхиляється ще до того, як його побачить будь-який контролер. Якщо обслуговуються дві версії, але лише одна є версією зберігання, API-сервер може приймати обидві версії, зберігаючи версію зберігання внутрішньо.

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

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

Схемна валідація — це частина, яка не дає поганому наміру потрапити до кластера. За допомогою openAPIV3Schema автор CRD оголошує типи, обов’язкові поля, переліки (enum), значення за замовчуванням, числові межі, структури об’єктів та інші обмеження. API-сервер застосовує ці обмеження під час допуску, тож хибне значення може бути відхилене негайно, замість того щоб пізніше перетворитися на тихий збій оператора.

schema:
openAPIV3Schema:
type: object
required: ["spec"]
properties:
spec:
type: object
required: ["engine"]
properties:
engine:
type: string
enum: ["postgres", "mysql", "mongodb"]
size:
type: string
default: "small"

Ця схема каже, що кастомний ресурс мусить містити spec, що spec.engine є обов’язковим, що рушій має бути одним із трьох рядків і що spec.size має значення за замовчуванням, коли його пропущено. Схема не знає, як запустити PostgreSQL чи MongoDB. Вона лише захищає межу API, щоб збережений об’єкт мав передбачувану форму для користувачів, контролерів допуску, контролерів, інструментів документації та kubectl explain.

Kubernetes вимагає, щоб сучасні схеми CRD були структурними, а це означає, що схема має бути достатньо регулярною, щоб обрізання (pruning), встановлення значень за замовчуванням, валідація та серверне застосування (server-side apply) поводилися передбачувано. На практиці це підштовхує авторів CRD до явних типів об’єктів та визначень полів, а не до довільних вкладених структур. Така дисципліна допомагає користувачам, бо відхилений маніфест зазвичай називає точний шлях поля, яке не пройшло перевірку, і допомагає контролерам, бо потік спостереження містить об’єкти очікуваної форми.

На особливу увагу заслуговують невідомі поля. Якщо схема CRD не зберігає невідомі поля, а користувач подає поле, яке схема не розпізнає, Kubernetes може обрізати це поле перед збереженням об’єкта. Якщо схема надто вільна, поле може бути збережене, але контролер його проігнорує. Будь-який із результатів може здивувати того, хто навчається, тож безпечна звичка під час налагодження — застосувати маніфест, а потім зчитати збережений об’єкт за допомогою kubectl get <resource> <name> -o yaml, щоб побачити, що зберіг API-сервер.

Перш ніж запускати це, який вивід ви очікуєте, якщо engine встановлено в redis? Відповідь важлива, бо вона каже вам, який компонент примушує дотримуватися контракту. У цьому прикладі API-сервер відхиляє об’єкт під час валідації, що означає, що оператор бази даних ніколи не отримує некоректний кастомний ресурс через свій потік спостереження.

apiVersion: example.com/v1
kind: Database
metadata:
name: my-database
spec:
engine: redis
size: large

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

Terminal window
$ kubectl apply -f bad-db.yaml
The Database "my-database" is invalid: spec.engine: Unsupported value: "redis": supported values: "postgres", "mysql", "mongodb"

Суворі схеми також захищають від помилок у назвах. Без схеми, яка вимагає engine, користувач може подати engin: postgres, API-сервер може зберегти об’єкт, а оператор може його проігнорувати чи зафіксувати розпливчасту умову статусу. Хороший CRD зазнає невдачі рано з точною помилкою, що дружніше для команд застосунків і безпечніше для автоматизації.

Значення за замовчуванням корисні, але вони не повинні приховувати важливі рішення. Значення за замовчуванням на кшталт size: small може зробити прості приклади легшими, тоді як значення за замовчуванням для деструктивної політики збереження могло б створити несподівану втрату даних. Коли ви досліджуєте CRD, читайте значення за замовчуванням як частину поведінки API, а не як декоративний елемент документації. Кастомний ресурс, зчитаний з API, може містити поля, яких користувач не писав, бо API-сервер встановив їх за замовчуванням під час допуску.

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

Робота з кастомними ресурсами

Розділ «Робота з кастомними ресурсами»

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

Почніть з визначень. kubectl get crd перелічує об’єкти CRD з областю видимості кластера, а не кастомні ресурси, створені з них. kubectl describe crd корисний, бо показує версії, імена, прийняті імена, умови, а іноді й деталі схеми. kubectl get crd <name> -o yaml — це найглибший погляд, коли вам потрібно дослідити валідацію, підресурси статусу, стовпці виводу (printer columns) чи прапорці версій.

Terminal window
# List all CRDs
kubectl get crd
# Describe a CRD
kubectl describe crd certificates.cert-manager.io
# Get CRD YAML
kubectl get crd mycrd.example.com -o yaml

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

Terminal window
# List custom resources (once CRD exists)
kubectl get databases
kubectl get db # Using shortName
# Describe a CR
kubectl describe database my-database
# Get CR YAML
kubectl get database my-database -o yaml
# Delete a CR
kubectl delete database my-database

Найпоширеніша помилка виявлення — це сплутування імені CRD з іменем ресурсу. databases.example.com — це ім’я об’єкта CRD, тоді як databases, database та, можливо, db — це імена для кінцевої точки кастомного ресурсу. Якщо kubectl get database не спрацьовує, не вгадуйте множину; виконайте kubectl api-resources | grep example.com або дослідіть spec.names у CRD.

Terminal window
# List CRDs
kubectl get crd
# View CRD details
kubectl describe crd NAME
# Work with custom resources
kubectl get <resource>
kubectl describe <resource> NAME
kubectl delete <resource> NAME
# Get API resources (includes CRDs)
kubectl api-resources | grep example.com
# Check if CRD exists
kubectl get crd myresource.example.com

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

Terminal window
# Works for installed CRDs too, after discovery refreshes
until kubectl explain database >/dev/null 2>&1; do sleep 1; done
kubectl explain database
until kubectl explain database.spec >/dev/null 2>&1; do sleep 1; done
kubectl explain database.spec
until kubectl explain certificate.spec.secretName >/dev/null 2>&1; do sleep 1; done
kubectl explain certificate.spec.secretName

Деякі поширені CRD варто впізнавати, бо вони з’являються в багатьох кластерах. cert-manager створює ресурси, пов’язані з сертифікатами, Prometheus Operator створює ресурси моніторингу, а Gateway API надає ресурси шлюзів та маршрутів. Упізнавання допомагає рухатися швидше, але надійний метод усе одно — це виявлення через API-сервер, а не пам’ять.

Terminal window
kubectl get crd | grep cert-manager
# certificates.cert-manager.io
# clusterissuers.cert-manager.io
# issuers.cert-manager.io
# Create a Certificate
kubectl get certificates
kubectl describe certificate my-cert
Terminal window
kubectl get crd | grep monitoring
# servicemonitors.monitoring.coreos.com
# prometheusrules.monitoring.coreos.com
Terminal window
kubectl get crd | grep gateway
# gateways.gateway.networking.k8s.io
# httproutes.gateway.networking.k8s.io

Який підхід ви б обрали тут і чому: спершу прочитати YAML CRD, запустити kubectl explain чи дослідити логи оператора? Якщо питання — “які поля я можу встановити”, починайте зі схеми та explain. Якщо питання — “чому мій валідний об’єкт не створив дочірні ресурси”, переходьте до статусу, подій та логів контролера. Такий порядок не дає вам налагоджувати не той рівень.

Оператори та узгодження

Розділ «Оператори та узгодження»

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

┌─────────────────────────────────────────────────────────────┐
│ Operator Pattern │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. User Creates Custom Resource │
│ ┌─────────────────────────────────┐ │
│ │ apiVersion: example.com/v1 │ │
│ │ kind: Database │ │
│ │ spec: │ │
│ │ engine: postgres │ │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 2. Controller Watches for Database CRs │
│ ┌─────────────────────────────────┐ │
│ │ Operator Pod │ │
│ │ - Sees new Database CR │ │
│ │ - Creates StatefulSet │ │
│ │ - Creates Service │ │
│ │ - Creates Secret (password) │ │
│ │ - Updates CR status │ │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 3. Actual Resources Created │
│ ┌─────────────────────────────────┐ │
│ │ StatefulSet: my-database │ │
│ │ Service: my-database │ │
│ │ Secret: my-database-creds │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

Діаграма зберігає оригінальний приклад бази даних, бо він чітко показує операційну межу. Користувач створює один кастомний ресурс. Контролер бачить цей об’єкт і створює StatefulSet, Сервіс, Secret, а часто й інші ресурси, як-от PersistentVolumeClaim’и, завдання резервного копіювання, PodDisruptionBudget’и чи правила моніторингу. Кастомний ресурс — це стабільний інтерфейс; згенеровані ресурси — це деталі реалізації, якими керує оператор.

Цей розподіл потужний, бо він дозволяє експертам предметної області закодувати операційні знання одного разу. Замість того щоб навчати кожну команду застосунків, як встановлювати кожен прапорець PostgreSQL, платформна команда публікує API Database з полями на кшталт engine, size, backupPolicy та version. Оператор може перекласти ці поля на безпечні примітиви Kubernetes, забезпечити дотримання конвенцій іменування, ротувати облікові дані та оновлювати умови статусу, які користувачі можуть досліджувати.

ПеревагаПриклад
АбстракціяСтворіть Database, оператор обробляє StatefulSet, PVC тощо
АвтоматизаціяОператор обробляє резервні копії, відмовостійкість (failover), масштабування
Доменна експертизаОператор знає, як правильно сконфігурувати Postgres
Операції Day 2Оновлення, відновлення, моніторинг вбудовані

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

Умови статусу — це публічний звіт оператора про прогрес. Зрілий контролер не змушує користувачів робити висновки про все з дочірніх ресурсів чи логів; він оновлює поля на кшталт спостереженого покоління (observed generation), готовності, причини, повідомлення та часу останнього переходу. Коли кастомний ресурс має корисні умови, найшвидшою командою для усунення несправностей часто є kubectl describe <resource> <name>, бо вона показує, чи прийняв контролер найновіший бажаний стан і на що він чекає.

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

Фіналізатори (finalizers) — це ще один механізм оператора, з яким користувачі CRD стикаються під час видалення. Контролер може додати рядок-фіналізатор до кастомного ресурсу, щоб затримати видалення, поки він прибирає зовнішні системи чи дочірні ресурси. Якщо контролера немає чи він зламаний, кастомний ресурс може застрягнути в стані завершення (terminating), бо Kubernetes чекає на видалення фіналізатора. Це проблема життєвого циклу контролера, а не проблема схемної валідації.

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

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

Додаткові стовпці виводу (additional printer columns) можуть зробити CRD схожими на вбудовані ресурси, показуючи вибрані поля у виводі kubectl get. Наприклад, CRD Database може виводити рушій, розмір, статус готовності та вік. Ці стовпці не змінюють збережений об’єкт, але вони покращують операційне сканування й можуть розкрити, чи записує контролер статус. Якщо в CRD немає стовпців виводу, використовуйте -o yaml або JSONPath, замість того щоб припускати, що ресурс не має корисного стану.

Зупиніться й подумайте: у кластері існує CRD для Database, і ви створюєте кастомний ресурс Database, але жодної реальної бази даних не надається. Чого бракує і яку команду ви б виконали перед тим, як відкрити логи оператора? Хороша перша відповідь — описати кастомний ресурс і дослідити його події та статус, бо ці поля часто показують, чи побачив контролер об’єкт узагалі.

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

Добре спроєктований оператор робить цю межу видимою, а не загадковою. Він документує, якими полями володіють користувачі, якими дочірніми ресурсами володіє він сам, які умови статусу він записує та що відбувається під час видалення. Ця документація потрібна не лише платформним інженерам. Користувачі CKAD виграють, бо ті самі підказки з’являються у kubectl describe, виводі YAML, подіях та збоях RBAC під час звичайного усунення несправностей.

Робочий процес налагодження та виявлення

Розділ «Робочий процес налагодження та виявлення»

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

Перша гілка — це виявлення. Підтвердьте, що CRD існує, що він установлений (established) і що ресурс з’являється у виявленні API. CRD мають умови статусу, і невстановлений CRD може ще не обслуговувати кінцеву точку. В автоматизованих прикладах очікування на умову Established уникає перегонів (race), коли наступна команда виконується ще до того, як API-сервер готовий приймати кастомні ресурси.

Друга гілка — це схема. Якщо kubectl apply зазнає невдачі з повідомленням про валідацію, дослідіть spec.versions[*].schema.openAPIV3Schema. Шукайте обов’язкові поля, значення enum, мінімуми, невідповідності типів та форми вкладених об’єктів. Пам’ятайте, що валідація CRD відбувається до того, як контролер побачить об’єкт, тож логи контролера не є першим доказом для відхилення схемою.

Третя гілка — це область видимості та простір імен. Простірний CRD може мати один об’єкт my-database у dev та інший у production; CRD з областю видимості кластера — не може. Якщо kubectl get databases нічого не показує, додайте -A або очікуваний простір імен, а потім порівняйте вивід kubectl api-resources, щоб побачити, чи є ресурс простірним. Ця єдина перевірка запобігає багатьом хибним припущенням про відсутні ресурси.

Остання гілка — це узгодження. Якщо кастомний ресурс прийнятий і видимий, опишіть його, дослідіть поле status, перевірте події, а потім дослідіть деплоймент оператора, под’и, RBAC та логи. Багато операторів фіксують умови на кшталт Ready, Reconciling чи Error безпосередньо в кастомному ресурсі, і ці поля статусу зазвичай сфокусованіші, ніж гортання кожного рядка логів контролера.

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

Керовані поля (managed fields) можуть допомогти, коли задіяні серверне застосування чи кілька систем автоматизації. Вони показують, який менеджер полів останнім заявив про володіння конкретними полями, що може пояснити, чому оновлення конфліктує чи чому поле постійно повертається до попереднього значення. Вам не потрібно запам’ятовувати повний формат керованих полів для CKAD, але знання про їхнє існування допомагає вам відрізнити проблему валідації від проблеми володіння полем чи перезапису автоматизацією.

Застарілий кеш виявлення (discovery cache staleness) може створити заплутані моменти під час швидкого створення та видалення CRD. kubectl кешує дані виявлення локально, і API-серверу теж потрібна коротка мить, щоб установити нову кінцеву точку. Очікування на умову CRD та повторний запуск команд виявлення зазвичай вирішує перегони. У скриптах використовуйте kubectl wait --for condition=established, щоб наступна команда не залежала від везіння з таймінгом.

Помилки RBAC слід читати буквально. Користувач може мати дозвіл створювати кастомні ресурси в просторі імен, але не мати дозволу перелічувати CRD, або оператор може мати дозвіл спостерігати за кастомним ресурсом, але не створювати дочірні ресурси, які йому потрібні. Це різні суб’єкти та дієслова. kubectl auth can-i часто є правильною наступною командою, коли повідомлення про помилку каже forbidden, а не invalid.

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

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

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

Сценарій вправи: товариш по команді дає вам маніфест KafkaTopic і каже, що оператор має створити топік автоматично. kubectl apply відхиляє spec.partitions: 0 з помилкою про мінімальне значення. Правильне виправлення — це не перезапуск оператора Kafka; це або подати значення, прийнятне для схеми CRD, або попросити власника CRD змінити схему, якщо 0 має означати “оператор вирішує сам”.

Практичний приклад: CRD Website

Розділ «Практичний приклад: CRD Website»

Цей практичний приклад зберігає CRD Website з оригінального модуля, бо він достатньо малий, щоб дослідити його за один раз. Сенс не в тому, щоб побудувати оператор для вебсайтів; контролера в цій вправі немає. Сенс у тому, щоб побачити, як API-сервер приймає новий тип ресурсу, зберігає кастомні ресурси, розкриває ресурс через виявлення, підтримує kubectl explain та забезпечує дотримання межі між збереженим наміром та автоматизованою дією.

Спершу створіть CRD та зачекайте, доки кінцева точка API не буде встановлена. Команда очікування не є декоративною. Без неї швидкий термінал може подати перший кастомний ресурс, перш ніж виявлення наздожене, видаючи заплутану помилку “no matches for kind”, навіть якщо CRD щойно успішно застосовано.

Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: websites.example.com
spec:
group: example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
domain:
type: string
replicas:
type: integer
scope: Namespaced
names:
plural: websites
singular: website
kind: Website
shortNames:
- ws
EOF
# Verify CRD created and API endpoint is established
kubectl wait --for condition=established --timeout=60s crd/websites.example.com
kubectl get crd websites.example.com

Тепер створіть два кастомні ресурси. Вони будуть збережені API-сервером, бо CRD існує, а поля spec відповідають типам схеми. Жодного Деплойменту, Сервісу чи Інгресу не з’явиться, бо цей приклад навмисно не має контролера, що спостерігає за об’єктами Website. Ця відсутність корисна: вона змушує вас побачити, що CRD сам по собі робить, а чого — ні.

Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: example.com/v1
kind: Website
metadata:
name: my-blog
spec:
domain: blog.example.com
replicas: 3
---
apiVersion: example.com/v1
kind: Website
metadata:
name: my-shop
spec:
domain: shop.example.com
replicas: 5
EOF
# List using different names
kubectl get websites
kubectl get website
kubectl get ws

Дослідіть один об’єкт і оновіть його патчем. Команда патчу використовує --type=merge, бо CRD не підтримують стратегічне злиття патчів (strategic merge patch) так само, як це роблять вбудовані типи Kubernetes. Стратегічне злиття покладається на метадані вбудованих типів, яких кастомні ресурси не мають, тож явний merge-патч є безпечнішою звичкою під час автоматизації оновлень кастомних ресурсів.

Terminal window
# Describe
kubectl describe website my-blog
# Get YAML
kubectl get ws my-blog -o yaml
# Edit (using patch for non-interactive automation)
# Rationale: CRDs do not support strategic merge patch (the default), so we must explicitly use --type=merge
kubectl patch website my-blog --type=merge -p '{"spec":{"replicas":2}}'

Нарешті, підтвердьте виявлення та видимість схеми. kubectl api-resources показує групу нового ресурсу, ознаку належності до простору імен, kind та короткі імена. Найбезпечніша звичка виявлення — зачекати, доки CRD стане встановленим, а потім повторювати kubectl explain, доки виявлення не зможе розв’язати кастомний ресурс. kubectl explain читає опубліковану схему й допомагає вам навігувати полями кастомного об’єкта, не залишаючи термінал.

Terminal window
# Check API resources
kubectl api-resources | grep example.com
# Use explain
until kubectl explain website >/dev/null 2>&1; do sleep 1; done
kubectl explain website

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

Terminal window
kubectl delete website my-blog my-shop
kubectl delete crd websites.example.com

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

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

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

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

ПатернКоли його використовуватиЧому це працює
Схема-перш-за-все CRDКористувачі чи автоматизація подають кастомні ресурси напрямуВалідація виловлює поганий намір до узгодження й дає kubectl explain корисну структуру.
Простірний ресурс орендаряКоманди застосунків володіють окремими екземплярами в окремих просторах іменRBAC простору імен, квоти та межі життєвого циклу узгоджуються з моделлю володіння ресурсом.
Статус, яким володіє контролерОператору потрібно звітувати про готовність, помилки чи спостережений станСтатус відокремлює бажаний стан у spec від спостереженого стану, прискорюючи усунення несправностей.
Стабільні імена з опціональними короткимиЛюди використовують ресурс інтерактивно, але скрипти мають бути портативнимиПовні імена залишаються передбачуваними, а короткі імена покращують інтерактивну ергономіку.

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

АнтипатернЩо йде не такКраща альтернатива
CRD без контролера, коли очікується діяКористувачі створюють об’єкти й чекають на дочірні ресурси, що ніколи не з’являються.Встановіть чи побудуйте контролер, або чітко задокументуйте, що CRD призначений лише для зберігання.
Вільна схема з довільними полямиПомилки в назвах та хибні типи приймаються, а потім провалюються пізніше в логіці контролера.Визначте структурну схему OpenAPI v3 з обов’язковими полями, enum’ами, межами та значеннями за замовчуванням.
Ресурс з областю кластера для наміру орендаряІзоляція простору імен та делегований RBAC стають незручними чи небезпечними.Використовуйте простірну область, якщо об’єкт справді не представляє спільну інфраструктуру кластера.
Видалення CRD як прибиранняУсі кастомні ресурси цього типу можуть бути видалені по всьому кластеру.Видаляйте екземпляри свідомо, робіть резервні копії за потреби та обмежуйте видалення CRD через RBAC.

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

Фреймворк прийняття рішень

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

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

Need to model new Kubernetes-facing desired state?
|
+-- No --> Use a built-in resource, ConfigMap, Secret, or application config.
|
+-- Yes
|
+-- Does Kubernetes already provide the API shape?
| |
| +-- Yes --> Use the built-in API and avoid duplicate abstractions.
| |
| +-- No
|
+-- Should the API server validate and store the object?
| |
| +-- No --> Use a simpler configuration or external service API.
| |
| +-- Yes
|
+-- Does something need to reconcile the object continuously?
|
+-- No --> CRD can be storage/discovery only, but document that clearly.
|
+-- Yes --> CRD plus controller/operator is the right pattern.

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

ВибірНайкраще підходить, колиУникайте, коли
Вбудоване API KubernetesДеплойменти, Сервіси, Job’и, маршрутизація на кшталт Інгресу чи стандартні потреби робочого навантаження вже моделюють проблемуВам потрібні доменні поля та статус, які вбудовані типи не можуть виразити чисто.
ConfigMap чи SecretРобочому навантаженню потрібна лише конфігурація ключ-значення чи облікові даніКористувачам потрібні валідація, виявлення, статус чи керування життєвим циклом, кероване контролером.
CRD без контролераКластеру потрібні типізоване зберігання, валідація та виявлення для кастомного наміруКористувачі очікують, що об’єкт автоматично створить чи відремонтує реальну інфраструктуру.
CRD плюс ОператорБажаний стан має узгоджуватися до ресурсів нижчого рівня з часомКоманда не може підтримувати логіку контролера, RBAC, оновлення та спостережуваність.

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

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

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

  • CRD самі по собі є ресурсами Kubernetes. API-група apiextensions.k8s.io/v1 визначає, як реєструвати кастомні типи ресурсів, тож механізмом розширення керують через той самий стиль API, який ті, хто навчається, вже використовують для інших об’єктів кластера.
  • Видалення CRD може видалити всі його кастомні ресурси. kubectl delete crd databases.example.com — це не просто видалення документа схеми; воно прибирає визначення API й може видалити кожен збережений об’єкт Database, що обслуговувався цим визначенням.
  • CRD може обслуговувати кілька версій, зберігаючи лише одну. Це дозволяє авторам API підтримувати вікна міграції на кшталт v1alpha1, v1beta1 та v1, тримаючи одне представлення зберігання в etcd.
  • Багато широковживаних проєктів Kubernetes є розширеннями API. cert-manager, Prometheus Operator, реалізації Gateway API, Argo CD та проєкти сервісної сітки зазвичай покладаються на CRD, щоб розкрити специфічні для домену ресурси.
ПомилкаЧому вона трапляєтьсяЯк її виправити
Сплутування CRD з кастомним ресурсомІмена виглядають спорідненими, і обома маніпулюють через kubectl.Ставтеся до CRD як до визначення API, а до кастомного ресурсу — як до одного екземпляра цього API.
Видалення CRD як рутинне прибиранняКоманда виглядає як видалення одного об’єкта, але вона видаляє тип ресурсу.Спершу видаляйте кастомні ресурси, робіть резервні копії важливих екземплярів та обмежуйте видалення CRD через RBAC.
Запит за хибним іменемІм’я CRD, множина, однина, kind та коротке ім’я — це різні точки входу.Використовуйте kubectl api-resources та spec.names, щоб обрати правильну форму команди.
Очікування, що CRD виконуватиме роботу сам по собіAPI-сервер зберігає об’єкти, але не запускає доменну автоматизацію.Встановіть чи налагодьте контролер/оператор, що спостерігає за типом кастомного ресурсу.
Ігнорування області видимості простору іменПростірні ресурси зникають із типового перегляду, коли вони живуть деінде.Перевірте стовпець належності до простору імен у kubectl api-resources та запитуйте з -n чи -A.
Налагодження логів контролера до валідаціїВідхилення схемою на API-сервері відбувається до того, як контролер отримує об’єкт.Прочитайте помилку валідації, дослідіть openAPIV3Schema та виправте маніфест чи схему.
Використання вільних схем для важливих APIЦе здається швидшим на ранній розробці, але користувачі можуть подати невалідний намір.Додайте обов’язкові поля, типи, enum’и, значення за замовчуванням та межі, щойно API стає спільним.
Питання 1: Ваша команда встановлює CRD `Database` та створює кастомний ресурс зі `spec.engine: postgres`. Жодного StatefulSet, Сервісу чи PVC не з'являється, але `kubectl get database my-database` спрацьовує успішно. Що слід перевірити далі?

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

Питання 2: Колега випадково виконує `kubectl delete crd databases.example.com`, і кілька просторів імен втрачають свої об'єкти `Database`. Чому це сталося і який засіб контролю зменшив би ризик?

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

Питання 3: Ви отримуєте `The KafkaTopic "orders-topic" is invalid: spec.partitions: Invalid value: 0: spec.partitions in body should be greater than or equal to 1`. Власник оператора каже, що нуль означає автоматичне визначення розміру. Де невідповідність?

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

Питання 4: Розробник виконує `kubectl get databases` у просторі імен `staging` й не бачить об'єктів, тоді як ви знаєте, що `my-database` існує в `production`. Що слід пояснити?

Якщо CRD використовує scope: Namespaced, кожен кастомний ресурс Database живе всередині одного простору імен. Ресурс може існувати в production і бути невидимим зі staging, якщо команда не використовує -n production чи -A. CRD не зламаний; область запиту хибна. Якщо ресурс має бути спільним для всього кластера, це проєктне рішення CRD з іншими компромісами RBAC та володіння.

Питання 5: Ви виконуєте `kubectl get database` й отримуєте помилку в стилі "ресурс не знайдено", але CRD `databases.example.com` існує. Які команди виявлення допоможуть вам не вгадувати?

Виконайте kubectl api-resources | grep example.com, щоб побачити фактичні імена ресурсів, групу, kind, короткі імена та ознаку належності до простору імен. Потім дослідіть kubectl get crd databases.example.com -o yaml, якщо вам потрібен точний блок spec.names. Ім’я об’єкта CRD не завжди є тією формою команди, яку вам слід використовувати. Вгадування множини марнує час і може приховати просту невідповідність іменування.

Питання 6: Вам потрібно оновити `spec.replicas` на кастомному ресурсі зі скрипта автоматизації. Чому `kubectl patch --type=merge` безпечніший за покладання на типову поведінку патчу?

Кастомні ресурси не підтримують стратегічне злиття патчів так, як це роблять вбудовані типи Kubernetes, бо стратегічне злиття покладається на метадані вбудованих типів. Явний merge-патч робить тип патчу зрозумілим та портативним для CRD. Серверне застосування теж може бути доречним, коли ви свідомо керуєте володінням полями, але некваліфікований типовий патч може здивувати тих, хто навчається й переходить між вбудованими та кастомними ресурсами.

Питання 7: Ви переглядаєте пропозицію змоделювати статичні прапорці функцій (feature flags) одного застосунку як новий CRD з областю кластера плюс оператор. Як ви оцінили б цей дизайн?

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

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

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

Завдання 1: Створити та дослідити CRD Website

Розділ «Завдання 1: Створити та дослідити CRD Website»
  • Застосуйте CRD websites.example.com та зачекайте, доки він не буде встановлений.
  • Підтвердьте, що kubectl api-resources перелічує websites у групі example.com.
  • Використайте kubectl explain website.spec, щоб дослідити опубліковану схему.
Розв'язок
Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: websites.example.com
spec:
group: example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
domain:
type: string
replicas:
type: integer
scope: Namespaced
names:
plural: websites
singular: website
kind: Website
shortNames:
- ws
EOF
kubectl wait --for condition=established --timeout=60s crd/websites.example.com
kubectl api-resources | grep example.com
until kubectl explain website.spec >/dev/null 2>&1; do sleep 1; done
kubectl explain website.spec

Завдання 2: Створити, перелічити та оновити патчем ресурси Website

Розділ «Завдання 2: Створити, перелічити та оновити патчем ресурси Website»
  • Створіть кастомні ресурси Website my-blog та my-shop.
  • Перелічіть ресурси, використовуючи форми команд з множиною, одниною та коротким іменем.
  • Пропатчте my-blog так, щоб spec.replicas стало 2, а потім дослідіть YAML.
Розв'язок
Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: example.com/v1
kind: Website
metadata:
name: my-blog
spec:
domain: blog.example.com
replicas: 3
---
apiVersion: example.com/v1
kind: Website
metadata:
name: my-shop
spec:
domain: shop.example.com
replicas: 5
EOF
kubectl get websites
kubectl get website
kubectl get ws
kubectl patch website my-blog --type=merge -p '{"spec":{"replicas":2}}'
kubectl get ws my-blog -o yaml

Завдання 3: Попрактикуватися у швидкому виявленні CRD

Розділ «Завдання 3: Попрактикуватися у швидкому виявленні CRD»
  • Перелічіть кожен CRD, установлений у кластері, та порахуйте їх.
  • Опишіть certificates.cert-manager.io, якщо cert-manager установлено, інакше опишіть перший доступний CRD.
  • Перелічіть визначення CRD з їхніми групами, kind’ами та областями видимості, а потім відфільтруйте ресурси API за однією відомою групою розширення.
Розв'язок
Terminal window
# List all CRDs
kubectl get crd
# Count CRDs
kubectl get crd --no-headers | wc -l
Terminal window
# If cert-manager or similar is installed
kubectl describe crd certificates.cert-manager.io 2>/dev/null || echo "cert-manager not installed"
# Otherwise use any CRD
kubectl get crd -o name | head -1 | xargs kubectl describe
Terminal window
# List all API resources
kubectl api-resources
# Filter for a specific built-in API group
kubectl api-resources --api-group=networking.k8s.io
# Show only CRD definitions with CRD-specific metadata
kubectl get crd -o custom-columns=NAME:.metadata.name,GROUP:.spec.group,KIND:.spec.names.kind,SCOPE:.spec.scope
# Show resources from the extension group created earlier in the lab
kubectl api-resources --api-group=example.com

Завдання 4: Створити малі тренувальні CRD

Розділ «Завдання 4: Створити малі тренувальні CRD»
  • Створіть backups.drill.example.com, перевірте, що CRD існує, а потім видаліть його.
  • Створіть tasks.drill.example.com, створіть один Task, опишіть його, а потім приберіть.
  • Створіть configs.drill.example.com, використайте kubectl explain, а потім видаліть CRD.
Розв'язок
Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: backups.drill.example.com
spec:
group: drill.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
schedule:
type: string
retention:
type: integer
scope: Namespaced
names:
plural: backups
singular: backup
kind: Backup
shortNames:
- bk
EOF
kubectl get crd backups.drill.example.com
kubectl delete crd backups.drill.example.com
Terminal window
# First create CRD
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: tasks.drill.example.com
spec:
group: drill.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
priority:
type: string
scope: Namespaced
names:
plural: tasks
singular: task
kind: Task
EOF
# Verify CRD is established
kubectl wait --for condition=established --timeout=60s crd/tasks.drill.example.com
# Create CR
cat << 'EOF' | kubectl apply -f -
apiVersion: drill.example.com/v1
kind: Task
metadata:
name: important-task
spec:
priority: high
EOF
# Query
kubectl get tasks
kubectl describe task important-task
kubectl get task important-task -o yaml
# Cleanup
kubectl delete task important-task
kubectl delete crd tasks.drill.example.com
Terminal window
# Create a simple CRD
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: configs.drill.example.com
spec:
group: drill.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
key:
type: string
value:
type: string
scope: Namespaced
names:
plural: configs
singular: config
kind: Config
EOF
# Verify CRD is established
kubectl wait --for condition=established --timeout=60s crd/configs.drill.example.com
# Use explain
until kubectl explain config >/dev/null 2>&1; do sleep 1; done
kubectl explain config
until kubectl explain config.spec >/dev/null 2>&1; do sleep 1; done
kubectl explain config.spec
# Cleanup
kubectl delete crd configs.drill.example.com

Завдання 5: Спровокувати та виправити валідацію

Розділ «Завдання 5: Спровокувати та виправити валідацію»
  • Створіть CRD Cache, який вимагає, щоб spec.memoryLimit був цілим числом щонайменше 128.
  • Застосуйте невалідний Cache з "64" як рядком та спостерігайте за помилкою валідації API-сервера.
  • Застосуйте валідний Cache, підтвердьте, що він існує, та видаліть CRD.
Розв'язок
Terminal window
# 1. Create a CRD with validation
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: caches.drill.example.com
spec:
group: drill.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
required: ["spec"]
properties:
spec:
type: object
required: ["memoryLimit"]
properties:
memoryLimit:
type: integer
minimum: 128
scope: Namespaced
names:
plural: caches
singular: cache
kind: Cache
EOF
# Verify CRD is established
kubectl wait --for condition=established --timeout=60s crd/caches.drill.example.com
# 2. Try to apply an invalid CR (memoryLimit is a string instead of an integer)
cat << 'EOF' | kubectl apply -f -
apiVersion: drill.example.com/v1
kind: Cache
metadata:
name: bad-cache
spec:
memoryLimit: "64"
EOF
# Notice the validation error from the API server!
# error: ValidationError(Cache.spec.memoryLimit): invalid type for drill.example.com/v1.Cache.spec.memoryLimit: got "string", expected "integer"
# 3. Fix the CR by providing a valid integer >= 128
cat << 'EOF' | kubectl apply -f -
apiVersion: drill.example.com/v1
kind: Cache
metadata:
name: good-cache
spec:
memoryLimit: 256
EOF
# 4. Verify it was created successfully
kubectl get cache good-cache
# 5. Cleanup
kubectl delete crd caches.drill.example.com
  • Ви можете пояснити різницю між CRD та кастомним ресурсом, не використовуючи слово “оператор” як скорочення.
  • Ви можете використати kubectl get crd, kubectl api-resources та kubectl explain, щоб виявити незнайоме API на основі CRD.
  • Ви можете визначити, чи належить збій до валідації API-сервера, області видимості простору імен, іменування ресурсу чи узгодження контролера.
  • Ви можете створити та прибрати тренувальні CRD, не залишаючи кастомних ресурсів.
  • Ви можете описати, коли CRD непотрібний, бо ConfigMap, Secret чи вбудований ресурс Kubernetes є простішим.

Перевірка засвоєння

Розділ «Перевірка засвоєння»

Найбезпечніша звичка виявлення — зачекати, доки CRD стане встановленим, а потім повторювати kubectl explain, доки виявлення не зможе розв’язати кастомний ресурс.

Перш ніж рухатися далі, поясніть, чому умова Established у CRD та оновлення виявлення на стороні клієнта є пов’язаними, але окремими перевірками готовності. У вашій відповіді має бути згадано, що прийняв API-сервер, що kubectl explain усе ще потребує виявити та як повторна спроба уникає оманливого збою одразу після створення CRD.

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