Модуль 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.
# Built-in resourcekubectl get pods
# Custom resource (after CRD is installed)kubectl get certificateskubectl get prometheuseskubectl get postgresqlsВизначення CRD має кілька частин, які знову з’являються в реальних кластерах. Група API тримає ваш тип окремо від вбудованих груп і від інших проєктів-розширень. Список версій контролює, які версії обслуговуються і яка версія зберігається. Схема OpenAPI v3 каже API-серверу, якої форми мають бути дійсні власні ресурси. Область дії визначає, чи живуть екземпляри всередині просторів імен, чи на рівні кластера.
apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: 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, якщо вони не призначені лише для навчання, бо колізії та незрозуміле володіння ускладнюють подальші операції. Група має підказувати майбутньому інженеру, звідки взявся тип і яка команда володіє його життєвим циклом.
# Apply the CRDkubectl apply -f crontab-crd.yaml
# Verify it was createdkubectl get crd crontabs.stable.example.com
# Now you can create instanceskubectl 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/v1kind: CronTabmetadata: name: my-cron-job namespace: defaultspec: cronSpec: "* * * * */5" image: my-awesome-cron-image replicas: 3kubectl apply -f my-crontab.yamlkubectl get crontabskubectl get ct # Using shortNamekubectl describe crontab my-cron-jobСтандартні операції працюють, бо власний ресурс тепер є ресурсом API. Ви можете виводити його по всіх просторах імен, редагувати, видаляти, спостерігати за ним та отримувати збережений YAML. Конкретні доступні дієслова досі залежать від RBAC, тож розробник може мати змогу створити Certificate в одному просторі імен, тоді як керувати issuer на рівні кластера можуть лише адміністратори платформи.
Важлива звичка — читати ресурс і через погляд користувача, і через погляд API. Погляд користувача запитує, чи чітко об’єкт виражає бажаний результат. Погляд API запитує, яка група, версія, область дії, схема, статус та дозволи керують цим об’єктом. Коли кластер містить багато операторів, другий погляд утримує вас від того, щоб сприймати кожен незнайомий вид як загадковий. Це досі механіка Kubernetes API, лише розширена контрактом, специфічним для проєкту.
# Createkubectl apply -f crontab.yaml
# Listkubectl get crontabs -A
# Describekubectl describe crontab my-cron-job
# Editkubectl edit crontab my-cron-job
# Deletekubectl delete crontab my-cron-job
# Watchkubectl get crontabs -w
# Get as YAMLkubectl 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 та схеми. Коли дочірні об’єкти існують, але повертаються до попереднього стану після ручних правок, оглядайте, який власний ресурс володіє бажаним станом. Ці три запитання охоплюють велику частку інцидентів з операторами, не вимагаючи глибокого знання конкретного продукту.
# Install cert-manager (includes CRDs)kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
# Check CRDs createdkubectl 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 resourceapiVersion: cert-manager.io/v1kind: Certificatemetadata: name: myapp-tls namespace: defaultspec: 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 може визначати селектори, що включають лише певні мітки чи простори імен. Коли збір даних не вдається, відповідь може бути у відношенні між власними ресурсами, а не в подах цільового застосунку. Завжди читайте обидві сторони декларативного контракту.
# Check Prometheus CRDskubectl get crd | grep monitoring.coreos.com# prometheuses.monitoring.coreos.com# servicemonitors.monitoring.coreos.com# alertmanagers.monitoring.coreos.com# Create a Prometheus instanceapiVersion: monitoring.coreos.com/v1kind: Prometheusmetadata: name: main namespace: monitoringspec: 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 належить якому контролеру.
# List all CRDs in clusterkubectl get crd
# See all custom resources of a typekubectl get certificates -A
# Check if operator is runningkubectl get pods -A | grep operatorkubectl 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# This would fail validationkubectl 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.creationTimestampkubectl 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# Now this workskubectl 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-scopedscope: 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. Якщо генерації збігаються, але умова хибна, оператор обробив запит і каже вам, чому не може завершити.
# List all CRDskubectl get crd
# Get details about a CRDkubectl describe crd certificates.cert-manager.io
# See the full CRD definitionkubectl get crd certificates.cert-manager.io -o yamlОгляд власного ресурсу йде далі. Успішний kubectl get доводить, що об’єкт існує, але kubectl describe та kubectl get -o yaml показують поля, що мають значення під час діагностики. Шукайте status.conditions, події, спостережувану генерацію, фіналізатори, посилання-власники та чи перебуває об’єкт у тому просторі імен, на який ви очікуєте. Багато зрілих операторів використовують умови статусу як свій основний інтерфейс підтримки.
Події корисні, але вони не є тривкою базою даних усього, що сталося. Вони можуть закінчуватися, а кластери з великим обсягом можуть робити їх шумними. Умови статусу зазвичай стабільніші, бо вони живуть на об’єкті та оновлюються контролером. Логи потім дають детальний погляд контролера, особливо коли зовнішня система відхиляє запит. Використовуйте всі три джерела разом: події для свіжих підказок, статус для поточного стану та логи для міркувань контролера.
# List custom resourceskubectl get <resource-name> -A
# Get specific resourcekubectl get certificate my-cert -o yaml
# Edit custom resourcekubectl edit certificate my-cert
# Delete custom resourcekubectl delete certificate my-certВиявлення API також допомагає уникнути вгадування назв у множині. Вид може бути Certificate, але ресурс може бути certificates; вид може бути PrometheusRule, але назва ресурсу може бути prometheusrules. Група API ідентифікує проєкт-розширення, що нею володіє, що допомагає знайти правильну документацію, Deployment контролера та правила RBAC, коли поведінки немає.
Назви в множині та короткі назви можуть бути оманливо важливими під тиском часу. Якщо команда зазнає невдачі, бо ви вгадали неправильну назву ресурсу, помилка може виглядати як відсутній CRD, навіть якщо API здоровий. kubectl api-resources швидший і безпечніший за вгадування. Він показує назву ресурсу, короткі назви, групу API, статус простору імен та вид в одному місці — це саме та інформація, яка вам потрібна, щоб правильно сформувати наступну команду.
# List all resource types (including custom)kubectl api-resources
# Filter by groupkubectl api-resources --api-group=cert-manager.io
# Show if namespacedkubectl 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 чи BackupPolicy | CRD описує бажаний результат, тоді як оператор обробляє деталі | Тримайте spec невеликим і обережно версіонуйте в міру зростання використання |
| Умови статусу | Користувачам потрібно діагностувати асинхронну роботу | Умови виставляють спостережуваний стан, не змінюючи бажаний | Стандартизуйте назви умов, щоб дашборди та runbook залишалися послідовними |
| Володіння на рівні простору імен | Команди застосунків володіють екземплярами незалежно | RBAC та квоти можуть слідувати межам простору імен | Надайте шаблони чи issuer з областю кластера для спільних типових значень |
| Колонки виводу | Операторам потрібне швидке сортування через kubectl get | Важливі поля з’являються у списках без читання повного YAML | Уникайте виставлення нестабільних внутрішніх даних, що змінюються кожен цикл узгодження |
Антипатерни зазвичай походять зі сприйняття CRD або як надто магічних, або як надто тривіальних. Вони не магічні, бо контролер усе одно має узгодити поведінку. Вони не тривіальні, бо щойно CRD широко вживається, зміна схеми, версій та поведінки видалення стає супроводом API. Недбалий CRD може створити довготривалий операційний борг, бо кожен GitOps-репозиторій, скрипт та м’язова пам’ять користувача від нього залежать.
Ще один антипатерн — ховати всі помилки в логах контролера. Логи необхідні супровідникам, але користувачі API не мають потребувати доступу cluster-admin, щоб дізнатися, чому їхній об’єкт не готовий. Умови статусу, події та зрозумілі повідомлення валідації наближають збій до людини, яка може виправити маніфест. Саме тому зрілі оператори активно інвестують у дизайн статусу: це перетворює асинхронну роботу на підтримувану розмову через API.
| Антипатерн | Що йде не так | Краща альтернатива |
|---|---|---|
| CRD без контролера для поведінки | Користувачі створюють дійсні об’єкти, що ніколи не впливають на навантаження | Або встановіть оператор, або задокументуйте CRD як суто дані |
| Перевантажені поля spec | CRD стає копією кожного базового об’єкта Kubernetes | Виставляйте намір і дайте контролеру обирати деталі реалізації |
| Відсутній підресурс status | Контролери та користувачі змагаються за ті самі поля об’єкта | Увімкніть status і розмістіть спостережуваний стан під .status |
| Небезпечне видалення CRD | Усі власні ресурси цього типу несподівано зникають | Зробіть резервні копії маніфестів, видаляйте екземпляри обдумано та розумійте фіналізатори |
Рамка прийняття рішень
Розділ «Рамка прийняття рішень»Вибір CRD — це рішення дизайну API. Використовуйте його, коли ресурс представляє тривкий концепт у вашій платформі, користувачам потрібні рідні для Kubernetes робочі процеси, а контролер може надійно перетворювати бажаний стан на спостережуваний. Уникайте його, коли простіший вбудований ресурс, значення Helm-чарта, ConfigMap чи угода в документації вирішують проблему без введення нової поверхні API та життєвого циклу.
Запитання «чи могли б ми побудувати це як CRD?» менш корисне за «кому вигідно, щоб це стало API?». Якщо одна команда застосовує один маніфест двічі на рік, CRD може бути церемонією. Якщо багато команд щотижня повторюють ризиковану послідовність, CRD плюс оператор можуть прибрати рутину та зменшити помилки. Що сильніші операційні знання за робочим процесом, то сильніший аргумент на користь оператора, а не статичного шаблону.
Need a new Kubernetes-facing abstraction? | vIs the desired state durable and reusable across teams? |-- no --> Prefer built-in resources, Helm values, or a ConfigMap. | yes vDoes something need to reconcile or validate asynchronous work? |-- no --> A CRD may be data-only, but document that clearly. | yes vCan you operate the controller, RBAC, upgrades, and status model? |-- no --> Use an existing operator or narrow the problem first. | yes vDesign 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 щодо спостереження та узгодження, хоча вони пакують каркас по-різному.
- Фіналізатори — це звичайні рядки в метаданих, але вони можуть блокувати видалення, доки контролер не прибере їх після завершення прибирання.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому вона трапляється | Як її виправити |
|---|---|---|
| Створення власного ресурсу до CRD | API-сервер ще не має зареєстрованого виду чи запису виявлення | Спочатку встановіть 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 має типове значення.
cat > website-crd.yaml << 'EOF'apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: name: websites.stable.example.comspec: 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: - wsEOF
kubectl apply -f website-crd.yamlkubectl get crd websites.stable.example.comkubectl api-resources | grep websiteНотатки до розв'язання
CRD має з’явитися як websites.stable.example.com, а kubectl api-resources має показати ресурс websites із короткою назвою ws. Якщо виявлення його не показує, огляньте помилку встановлення CRD, перш ніж створювати екземпляри. API-сервер має знати тип, перш ніж зможе прийняти об’єкт Website.
Завдання 2: Створіть та огляньте власні ресурси
Розділ «Завдання 2: Створіть та огляньте власні ресурси»Створіть один екземпляр Website, потім використайте форми множини, однини та короткої назви, щоб оглянути його. Це завдання навмисно про поведінку API, а не про хостинг вебсайтів. Об’єкт записує бажаний стан і стає видимим для стандартних інструментів Kubernetes.
cat > my-website.yaml << 'EOF'apiVersion: stable.example.com/v1kind: Websitemetadata: name: company-site namespace: defaultspec: url: https://example.com replicas: 3EOF
kubectl apply -f my-website.yaml# List websiteskubectl get websiteskubectl get ws # Short name
# Describekubectl describe website company-site
# Get as YAMLkubectl get website company-site -o yaml
# Editkubectl edit website company-sitecat > blog.yaml << 'EOF'apiVersion: stable.example.com/v1kind: Websitemetadata: name: blogspec: url: https://blog.example.com replicas: 2EOF
kubectl apply -f blog.yamlkubectl get wsНотатки до розв'язання
І company-site, і blog мають з’явитися з власними колонками виводу, якщо ваш кластер прийняв CRD точно так, як його написано. Якщо вивід не показує колонки, перевірте, що additionalPrinterColumns вкладено під обслуговувану версію. Якщо kubectl get ws зазнає невдачі, переконайтеся, що shortNames було прийнято в CRD.
Завдання 3: Дослідіть встановлені оператори та CRD
Розділ «Завдання 3: Дослідіть встановлені оператори та CRD»Використайте команди виявлення, щоб ідентифікувати будь-які API, керовані операторами, що вже присутні у вашому кластері. Невеликий тренувальний кластер може не мати встановлених cert-manager чи Prometheus, тож відсутній результат не є автоматично невдачею. Сенс у тому, щоб попрактикувати перехід від назв CRD до екземплярів ресурсів, а потім до ймовірних подів контролера.
# Check for cert-managerkubectl get crd | grep cert-manager
# Check for prometheus operatorkubectl get crd | grep monitoring.coreos.com
# List all CRDskubectl get crd# List all CRDskubectl get crd
# Get details on a specific CRDkubectl get crd <crd-name> -o yaml | head -50
# List instances of a CRDkubectl get <resource-name> -A
# Describe a CRDkubectl 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, щоб ви могли оглянути, як моделюється статус, навіть коли жоден контролер його не оновлює.
# Create CRDcat << 'EOF' | kubectl apply -f -apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: name: apps.example.comspec: 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: integerEOF
# Verify CRD existskubectl get crd apps.example.com
# Create an instancecat << 'EOF' | kubectl apply -f -apiVersion: example.com/v1kind: Appmetadata: name: my-appspec: image: nginx:1.25 replicas: 3EOF
# Query using short namekubectl get ap
# Cleanupkubectl delete app my-appkubectl delete crd apps.example.comcat << 'EOF' | kubectl apply -f -apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: name: databases.stable.example.comspec: 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/v1kind: Databasemetadata: name: invalid-dbspec: engine: oracle # Not in enum! version: "14"EOF
# Create valid resourcecat << 'EOF' | kubectl apply -f -apiVersion: stable.example.com/v1kind: Databasemetadata: name: prod-dbspec: engine: postgres version: "14"EOF
# Cleanupkubectl delete database prod-dbkubectl delete crd databases.stable.example.comcat << 'EOF' | kubectl apply -f -apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: name: tasks.work.example.comspec: 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: stringEOF
# Create taskcat << 'EOF' | kubectl apply -f -apiVersion: work.example.com/v1kind: Taskmetadata: name: build-jobspec: command: "make build"EOF
# View the taskkubectl get task build-job -o yaml
# Cleanupkubectl delete task build-jobkubectl 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.
# Try to create a resource for non-existent CRDcat << 'EOF' | kubectl apply -f -apiVersion: nonexistent.example.com/v1kind: Widgetmetadata: name: testspec: size: largeEOF
# Error: no matches for kind "Widget"
# Diagnosekubectl get crd | grep widget # Nothingkubectl 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 і запитайте його через коротку назву.
# YOUR TASK: Create the CRD and a sample Backup resourceРозв'язання
cat << 'EOF' | kubectl apply -f -apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: name: backups.backup.example.comspec: 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: 7EOF
cat << 'EOF' | kubectl apply -f -apiVersion: backup.example.com/v1kind: Backupmetadata: name: daily-db-backupspec: source: /data/postgres destination: s3://backups/postgres schedule: "0 2 * * *" retention: 14EOF
kubectl get bkkubectl delete backup daily-db-backupkubectl delete crd backups.backup.example.comЗавдання 6: Запустіть базовий навчальний оператор
Розділ «Завдання 6: Запустіть базовий навчальний оператор»Це фінальне завдання використовує цикл Bash як навчальний контролер для CRD Website. Це не робочий оператор, але він робить узгодження видимим: цикл виводить власні ресурси, читає їхній бажаний стан та застосовує Deployment, названий за кожним вебсайтом. Після того як ви видалите керований Deployment, цикл відтворює його з власного ресурсу.
# 1. First, create the Website CRDcat << 'EOF' | kubectl apply -f -apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: name: websites.stable.example.comspec: 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 loopcat << 'EOF' > website-operator.sh#!/bin/bashecho "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 5doneEOFchmod +x website-operator.sh
# 3. Run the operator in the background./website-operator.sh &OPERATOR_PID=$!
# 4. Create a Custom Resourcecat << 'EOF' | kubectl apply -f -apiVersion: stable.example.com/v1kind: Websitemetadata: name: my-portfoliospec: image: "nginx:alpine" replicas: 2EOF
# 5. Observe the reconciliation (Wait a few seconds for the loop)sleep 6kubectl get deploymentskubectl get pods
# 6. Test the Reconciliation Loop# The operator should fight back if we delete the managed deploymentecho "Deleting the managed deployment to simulate a failure..."kubectl delete deployment my-portfolio-site
# 7. Check again in 5-10 secondssleep 6kubectl get deployments# The operator recreated it! This is the reconciliation loop in action.
# 8. Clean upkill $OPERATOR_PIDkubectl delete website my-portfoliokubectl delete deployment my-portfolio-sitekubectl delete crd websites.stable.example.comrm website-operator.shНотатки до розв'язання
Коли скрипт працює, Deployment має з’явитися після того, як власний ресурс Website існує. Якщо ви видалите Deployment, доки скрипт продовжує працювати, наступний цикл має його відтворити. Якщо цього не сталося, огляньте, чи повертає kubectl get websites власний ресурс, чи має скрипт дозвіл створювати деплойменти та чи збігається назва Deployment з угодою про іменування у скрипті.
Критерії успіху
Розділ «Критерії успіху»- Спроєктувати CRD із дійсною групою, іменами, областю дії, версією, схемою та колонками виводу.
- Створити власні ресурси та запитати їх формами множини, однини та короткої назви.
- Діагностувати збої виявлення та валідації CRD за допомогою
kubectl get crd,kubectl api-resourcesта помилок застосування. - Реалізувати цикл узгодження у стилі оператора, який відтворює Deployment із власного ресурсу.
- Оцінити, чи має власний ресурс мати область простору імен, чи область кластера.
- Діагностувати збої узгодження оператора, перевіряючи статус власного ресурсу, події, поди контролера, логи та RBAC.
Прибирання
Розділ «Прибирання»Запустіть це прибирання, якщо ви виконали попередні завдання та хочете видалити тренувальні ресурси. Деякі об’єкти можуть бути вже відсутні, якщо ви дотрималися кожної вбудованої команди прибирання, тож --ignore-not-found робить фінальне очищення нешкідливим.
kubectl delete website company-site blog my-portfolio --ignore-not-foundkubectl delete deployment my-portfolio-site --ignore-not-foundkubectl delete crd websites.stable.example.com --ignore-not-foundkubectl delete crd apps.example.com --ignore-not-foundkubectl delete crd databases.stable.example.com --ignore-not-foundkubectl delete crd tasks.work.example.com --ignore-not-foundkubectl delete crd backups.backup.example.com --ignore-not-foundrm -f website-crd.yaml my-website.yaml blog.yaml website-operator.shДжерела
Розділ «Джерела»- Документація Kubernetes: Custom Resources
- Документація Kubernetes: Extend the Kubernetes API with CustomResourceDefinitions
- Документація Kubernetes: API concepts
- Документація Kubernetes: Controllers
- Документація Kubernetes: Finalizers
- Документація Kubernetes: API access control
- Kubebuilder book: CronJob tutorial
- Kubebuilder book: Status subresource
- Документація Operator SDK
- Документація controller-runtime
- Документація cert-manager
- Документація Prometheus Operator
Наступний модуль
Розділ «Наступний модуль»Модуль 1.6: RBAC — рольовий контроль доступу для захисту вашого кластера.