Модуль 3.1: Принципи Cloud Native
Складність: [СЕРЕДНЯ] архітектурні концепції. Час на проходження: 45–55 хвилин. Передумови: Частина 2, Оркестрація контейнерів, включно з базовим розумінням Pod’ів, Деплойментів, Сервісів і різниці між бажаним та фактичним станом.
Що ви зможете зробити
Розділ «Що ви зможете зробити»Після завершення цього модуля ви зможете використовувати наведені нижче результати як критерії перевірки під час обговорень архітектури та інспекцій робочих навантажень Kubernetes:
- Оцінити, чи відповідає застосунок принципам cloud native, простеживши його конфігурацію, стан, логування та поведінку під час збоїв.
- Порівняти монолітні та мікросервісні рішення, а потім обрати межі сервісів, які зменшують радіус ураження (blast radius) без зайвої розподіленої складності.
- Спроєктувати маніфест робочого навантаження для Kubernetes 1.35+, що застосовує практики 12 факторів із конфігурацією через середовище, репліками, пробами та логуванням у stdout.
- Діагностувати антипатерни розгортання, такі як зміни на змінюваному сервері, жорстко закодовані допоміжні сервіси, локальний стан контейнера та виключно імперативні операції.
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Головною відправною точкою тут є розбір інциденту Knight Capital 2012 у модулі про інфраструктуру як код напрямку modern DevOps, який є канонічним прикладом того, чому cloud-native цикли управління та декларативний стан розгортання важливі, коли парк машин має залишатися однорідним.
Ця історія важлива для тих, хто вивчає Kubernetes, тому що архітектуру cloud native часто зводять до поверхневої фрази: “запхай це в контейнер”. Контейнер може так само легко спакувати зламану операційну модель, як і стійку. Якщо застосунок усе ще залежить від локального диска, жорстко закодованих адрес бази даних, серверів, відредагованих вручну, або одного гігантського процесу, що падає як єдине ціле, то Kubernetes може його перезапустити, але Kubernetes не здатен чарівним чином зробити дизайн масштабованим, спостережуваним чи безпечним для змін.
Іспит KCNA очікує, що ви розпізнаватимете принципи cloud native як систему проєктування, а не як товарний ярлик. Ви поєднаєте визначення CNCF, проєктування застосунків за 12 факторами, компроміси мікросервісів, незмінну інфраструктуру, декларативні API та орієнтоване на збої мислення в одну операційну модель. Мета — практичне судження: коли ви оглядаєте робоче навантаження, ви маєте бути здатні сказати, які частини є cloud native, які частини є лише контейнеризованими, а які зміни зробили б застосунок безпечнішим для запуску на Kubernetes 1.35+.
Що насправді означає Cloud Native
Розділ «Що насправді означає Cloud Native»Cloud native описує застосунки та платформи, побудовані для сучасних динамічних середовищ, де вузли змінюються, мережі відмовляють, потужність коливається, а релізи трапляються часто. Важливе слово тут не “cloud” (хмара); важливе слово — “native” (рідний). Cloud native застосунок поводиться так, ніби рух, заміна, автоматизація та часткова відмова є нормальними умовами, так само як носій мови не перекладає кожне речення через іншу мову, перш ніж відповісти.
Визначення Cloud Native Computing Foundation називає контейнери, сервісні сітки (service meshes), мікросервіси, незмінну інфраструктуру та декларативні API, тому що ці технології підтримують саме таку операційну модель. Контейнери дають вам відтворюваний пакет середовища виконання, сервісні сітки можуть винести наскрізну мережеву поведінку за межі коду застосунку, мікросервіси дозволяють незалежні зміни, коли межі обрано вдало, незмінна інфраструктура усуває дрейф конфігурації, а декларативні API дають змогу площині управління узгоджувати бажаний стан. Жоден із цих інструментів сам по собі не є достатнім, але разом вони змінюють те, як команди проєктують програмне забезпечення та працюють із ним.
┌─────────────────────────────────────────────────────────────┐│ CLOUD NATIVE DEFINITION │├─────────────────────────────────────────────────────────────┤│ ││ CNCF Definition: ││ ───────────────────────────────────────────────────────── ││ "Cloud native technologies empower organizations to ││ build and run scalable applications in modern, dynamic ││ environments such as public, private, and hybrid clouds."││ ││ Key characteristics: ││ • Containers ││ • Service meshes ││ • Microservices ││ • Immutable infrastructure ││ • Declarative APIs ││ ││ Cloud Native ≠ "Running in the cloud" ││ ───────────────────────────────────────────────────────── ││ You can run cloud native apps on-premises ││ You can run non-cloud-native apps in the cloud ││ ││ Cloud native = designed for cloud environments ││ │└─────────────────────────────────────────────────────────────┘Уявіть cloud native як контракт між застосунком і платформою. Застосунок обіцяє бути портативним, налаштовуваним ззовні, придатним до утилізації, спостережуваним через стандартні потоки та сигнали здоров’я, а також стійким до заміни. Платформа обіцяє планувати його, перезапускати, масштабувати, з’єднувати з допоміжними сервісами, забезпечувати дотримання задекларованої політики та невпинно наближати фактичний стан до бажаного.
Корисне питання для огляду таке: “Що сталося б, якби цей Под зник упродовж звичайної робочої години?” Якщо відповідь — “Деплоймент створює інший Под, трафік перемикається, поки готовність наздоганяє, логи залишаються доступними, а тривкі дані живуть деінде”, то ви бачите поведінку cloud native. Якщо відповідь — “хтось має зайти на вузол через SSH, відновити файли з локального диска та повторно запустити скрипт налаштування з пам’яті”, то контейнер приховує традиційну операційну модель.
Ця відмінність також пояснює, чому архітектура cloud native не є автоматично мікросервісною архітектурою. Модульний моноліт, який дотримується практик 12 факторів, надає корисні перевірки здоров’я, пише логи у stdout і ставиться до своєї бази даних як до приєднаного ресурсу, може бути більш cloud native, ніж рій крихітних сервісів, що ділять одну крихку базу даних і потребують узгоджених релізів. Принцип полягає в тому, щоб спершу проєктувати під автоматизацію та стійкість, а потім розділяти сервіси тоді, коли розділення себе виправдовує.
Тож першою практичною навичкою є оцінювання. Перш ніж змінювати YAML, дослідіть контракт застосунку: де живе стан, як подається конфігурація, що відбувається під час завершення роботи, який сигнал каже, що екземпляр готовий, і як оператор може відтворити запущену версію з вихідного коду та маніфестів? Ці питання прямо відображаються на об’єкти Kubernetes, які ви вже знаєте: Деплойменти, Сервіси, ConfigMap’и, Secret’и, проби, Job’и та контролери.
Друга корисна навичка — відокремлення можливостей платформи від відповідальності застосунку. Kubernetes може розміщувати Pod’и, перезапускати контейнери, що відмовили, та оновлювати ендпоінти, але він не може знати, чи безпечно повторити запит на оформлення замовлення, чи є відповідь із рекомендаціями необов’язковою. Ці рішення належать командам застосунку та продукту, і платформа працює найкраще, коли ці рішення подаються через чіткі контракти, такі як ендпоінти здоров’я, тайм-аути, ідемпотентні обробники та осмислена поведінка під час виходу.
Огляд cloud native також виграє від мислення в подіях заміни, а не в діаграмах усталеного стану. Запитайте, що відбувається, коли образ змінюється, коли вузол спорожнюється, коли Secret ротується, коли одна залежність уповільнюється і коли той самий реліз потрібен у другому регіоні. Дизайни, які здаються прийнятними під час спокійного трафіку, часто виявляють приховане зчеплення під час цих переходів, тому що заміна змушує кожне припущення про стан, ідентичність і конфігурацію вийти назовні.
Застосунок 12 факторів у Kubernetes
Розділ «Застосунок 12 факторів у Kubernetes»Методологія 12 факторів дає архітектурі cloud native словник для опису поведінки застосунку. Вона виникла з операційного досвіду Heroku, що запускав велику кількість застосунків, але ці ідеї пасують Kubernetes, тому що обидві моделі припускають автоматизацію, відтворюваність і відокремлення коду, конфігурації, процесу виконання та приєднаних сервісів. Фактори — це не контрольний список для зазубрювання; це стрес-тести, що показують, чи може застосунок безпечно переміщатися між середовищами та чисто відновлюватися після подій платформи.
Перші шість факторів зосереджені на пакуванні та межах виконання. Одна кодова база може породжувати багато розгортань, залежності мають бути явними, конфігурація належить поза кодом, допоміжні сервіси мають бути замінними приєднаннями, етапи збирання та виконання мають бути розділені, а процеси застосунку мають бути безстановими. Kubernetes підкріплює ці ідеї, перетворюючи образ контейнера на артефакт збірки, тоді як ConfigMap’и, Secret’и, Сервіси та контролери постачають специфічну для середовища поведінку під час виконання.
┌─────────────────────────────────────────────────────────────┐│ THE 12 FACTORS │├─────────────────────────────────────────────────────────────┤│ ││ 1. CODEBASE ││ One codebase tracked in version control ││ Many deploys (dev, staging, prod) ││ ││ 2. DEPENDENCIES ││ Explicitly declare and isolate dependencies ││ Never rely on system packages ││ ││ 3. CONFIG ││ Store config in the environment ││ Not in code (ConfigMaps/Secrets!) ││ ││ 4. BACKING SERVICES ││ Treat backing services as attached resources ││ Database, cache, queue = URLs, not special cases ││ ││ 5. BUILD, RELEASE, RUN ││ Strictly separate build and run stages ││ Build → Release (build + config) → Run ││ ││ 6. PROCESSES ││ Execute app as stateless processes ││ State lives in backing services, not memory ││ │└─────────────────────────────────────────────────────────────┘Наступні шість факторів описують, як процес бере участь в автоматизованій платформі. Прив’язка до порту робить застосунок самодостатнім, конкурентність надає перевагу більшій кількості екземплярів процесу замість одного величезного хосту, придатність до утилізації потребує швидкого старту та плавного завершення роботи, паритет dev/prod зменшує несподіванки між середовищами, логи течуть як потоки подій, а адміністративні задачі виконуються як одноразові процеси. Kubernetes надає конкретні форми цим ідеям через порти контейнерів, репліки, періоди плавного завершення (termination grace period), спільні образи, збір логів контейнерів та Job’и.
┌─────────────────────────────────────────────────────────────┐│ THE 12 FACTORS (continued) │├─────────────────────────────────────────────────────────────┤│ ││ 7. PORT BINDING ││ Export services via port binding ││ App is self-contained, serves HTTP ││ ││ 8. CONCURRENCY ││ Scale out via the process model ││ Run multiple instances, not bigger instances ││ ││ 9. DISPOSABILITY ││ Maximize robustness with fast startup/shutdown ││ Can be started/stopped at any moment ││ ││ 10. DEV/PROD PARITY ││ Keep development, staging, production similar ││ Same tools, same dependencies ││ ││ 11. LOGS ││ Treat logs as event streams ││ Write to stdout, let platform handle collection ││ ││ 12. ADMIN PROCESSES ││ Run admin/management tasks as one-off processes ││ Migrations, scripts as separate Jobs ││ │└─────────────────────────────────────────────────────────────┘Безстановий не означає “ніяких даних не існує”. Це означає, що запущений вебпроцес чи воркер не використовує пам’ять, локальні файли контейнера або конкретний вузол як систему обліку записів; тривкий стан належить допоміжним сервісам із явними контрактами життєвого циклу та відновлення.
Фактор 3 і Фактор 4 часто з’являються разом під час міграцій до Kubernetes. Застарілий застосунок може під’єднуватися до localhost:5432, писати завантаження в /app/uploads і логувати в /var/log/app.log, бо його було спроєктовано для одного сервера. У Kubernetes цей дизайн стикається з плануванням Pod’ів, тому що localhost означає сам Под, локальні файли зникають разом із контейнером, а файлові логи важче зберегти збирачам рівня кластера.
Ось той самий перелік факторів, перекладений у терміни Kubernetes. Таблиця не покликана припускати механічне відображення один-до-одного для кожної промислової системи, але вона дає вам готову до іспиту мову для розпізнавання того, як робоче навантаження виражає поведінку cloud native всередині кластера. Коли ви оцінюєте дизайн, шукайте закладений принцип, а не лише назву об’єкта.
| Фактор | Реалізація в Kubernetes |
|---|---|
| Кодова база | Образи контейнерів із Git |
| Залежності | Образи контейнерів містять залежності |
| Конфігурація | ConfigMap’и та Secret’и |
| Допоміжні сервіси | Сервіси вказують на бази даних |
| Build/Release/Run | Конвеєри CI/CD |
| Процеси | Pod’и безстанові |
| Прив’язка до порту | Порти контейнерів |
| Конкурентність | Горизонтальне масштабування (репліки) |
| Придатність до утилізації | Швидкий старт контейнера |
| Паритет dev/prod | Однакові образи всюди |
| Логи | Stdout -> агрегація логів |
| Адмінпроцеси | Job’и та CronJob’и |
У командних прикладах цього модуля kubectl скорочено поширеним аліасом оболонки k. Визначте його одного разу у вашій оболонці, а потім використовуйте k get pods, k apply та подібні команди так само, як ви використовували б повну команду. Аліас не змінює поведінку Kubernetes; він лише робить повторювану практику менш гучною.
alias k=kubectlk version --clientМінімальний Деплоймент за 12 факторами не потребує екзотичних можливостей Kubernetes. Йому потрібен стабільний образ, конфігурація, керована середовищем, порт, достатня кількість реплік, щоб витримати заміну, та проби, які повідомляють Сервісу, коли Под може приймати трафік. Ця основа важливіша за додавання просунутих мережевих можливостей до того, як застосунок матиме чистий контракт виконання.
apiVersion: apps/v1kind: Deploymentmetadata: name: checkoutspec: replicas: 3 selector: matchLabels: app: checkout template: metadata: labels: app: checkout spec: containers: - name: checkout image: ghcr.io/example/checkout:1.8.0 ports: - containerPort: 8080 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: checkout-db key: url - name: LOG_FORMAT value: json readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 20 periodSeconds: 20Перш ніж це запустити: якого результату ви очікуєте від k get deploy checkout після застосування подібного маніфесту у справному кластері? Ви маєте очікувати, що Kubernetes повідомить про три бажані репліки і, після успішного старту та готовності, про три доступні репліки. Якщо образ завантажується, але готовність не проходить, Деплоймент може показувати запущені Pod’и, тоді як Сервіс усе ще тримає їх поза ендпоінтами, — а це саме те відокремлення, якого потребує робоче навантаження cloud native.
k apply -f checkout-deployment.yamlk get deploy checkoutk get pods -l app=checkoutk logs -l app=checkout --tail=20Розібраний приклад також демонструє, чому “конфігурація в середовищі” не те саме, що “вся конфігурація нешкідлива”. URL бази даних зазвичай належить Secret’у, тому що він може містити облікові дані, тоді як прапорець функції чи формат логу може належати ConfigMap’у або звичайному значенню середовища. Принцип — це винесення назовні; рішення щодо безпеки залежить від чутливості та контролю доступу.
Гіпотетичний сценарій: платформенна команда мігрує платіжний воркер до Kubernetes і залишає щоденну команду міграції всередині точки входу контейнера, бо це працювало на одній VM. Коли Деплоймент масштабувався до трьох реплік, три Pod’и спробували запустити міграцію водночас, і один процес тримав блокування достатньо довго, щоб затримати старт інших. Тривким виправленням був не більший тайм-аут; це було перенесення адміністративної задачі в окремий Kubernetes Job, щоб звичайні вебпроцеси залишалися придатними до утилізації та передбачуваними.
Зверніть увагу, скільки факторів задіяно в цій одній історії. Образ був придатний до повторного використання, але команда запуску змішувала обслуговування трафіку з адмініструванням. Процес був достатньо безстановим, щоб масштабуватися, але поведінка старту не була придатною до утилізації, тому що вона залежала від глобального блокування бази даних. Кращий дизайн Kubernetes не вимагав нової мови програмування чи сервісної сітки; він вимагав узгодження процесної моделі 12 факторів із контролерами Kubernetes — Деплоймент для обслуговування та Job для міграції.
Інший патерн міграції — виносити одну залежність за раз і спостерігати за результатом. Перенесення конфігурації в Secret має полегшити просування між середовищами, тоді як перенесення завантажень в об’єктне сховище має зробити заміну Pod’а менш ризикованою. Якщо зміна не покращує заміну, портативність, спостережуваність чи поведінку під час збоїв, вона все одно може бути корисною, але необов’язково просуває контракт cloud native.
Коли ви читаєте сценарії KCNA, перекладайте симптоми у фактори. “Команда перезібрала образ, щоб змінити хост бази даних” вказує на конфігурацію. “Користувачі втрачають кошики під час розгортання” вказує на безстанові процеси та допоміжні сервіси. “Логи зникають після перезапуску” вказує на потоки подій. “Три репліки всі запускають міграції” вказує на адміністративні процеси. Іспит зазвичай описує поведінку перед тим, як назвати принцип, тож практикуйтеся переходити від симптому до корекції дизайну.
Моноліти, мікросервіси та межі сервісів
Розділ «Моноліти, мікросервіси та межі сервісів»Мікросервіси — один із найпомітніших патернів cloud native, але також один із тих, що найлегше використати неправильно. Мікросервіс — це не просто малий процес чи репозиторій із REST API. Вдала межа сервісу дає команді незалежне володіння бізнес-можливістю, ізолює тиск збоїв і масштабування та зменшує потребу в координації для змін, які трапляються часто.
Моноліт не є автоматично поганим. Багато успішних систем починаються як моноліти, бо одну розгортувану одиницю простіше осмислювати, поки команда ще вивчає предметну область. Режим відмови — це не “існує один процес”; режим відмови в тому, що непов’язані можливості стають настільки тісно зчепленими, що кожен реліз, інцидент і рішення про масштабування мусять залучати весь застосунок.
┌─────────────────────────────────────────────────────────────┐│ MONOLITH vs MICROSERVICES │├─────────────────────────────────────────────────────────────┤│ ││ MONOLITH: ││ ───────────────────────────────────────────────────────── ││ ┌─────────────────────────────────────────────────────┐ ││ │ Single Application │ ││ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ ││ │ │ UI │ │ Orders │ │ Payment │ │ Shipping│ │ ││ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ ││ │ Shared Database │ ││ └─────────────────────────────────────────────────────┘ ││ ││ • One deployment ││ • Changes affect everything ││ • Scale entire app ││ ││ MICROSERVICES: ││ ───────────────────────────────────────────────────────── ││ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ││ │ UI │ │ Orders │ │ Payment │ │Shipping │ ││ │ Service │ │ Service │ │ Service │ │ Service │ ││ │ DB │ │ DB │ │ DB │ │ DB │ ││ └─────────┘ └─────────┘ └─────────┘ └─────────┘ ││ ↑ ↑ ↑ ↑ ││ └────────────┴─────┬──────┴────────────┘ ││ │ ││ API Gateway ││ ││ • Independent deployments ││ • Change one service only ││ • Scale individual services ││ │└─────────────────────────────────────────────────────────────┘| Характеристика | Опис |
|---|---|
| Єдина відповідальність | Кожен сервіс добре робить одну річ |
| Незалежне розгортання | Розгортання без впливу на інші |
| Децентралізовані дані | Кожен сервіс володіє своїми даними |
| Незалежність від технологій | Найкращий інструмент для кожного сервісу |
| Ізоляція збоїв | Одна відмова не валить усі |
| Володіння командою | Малі команди володіють сервісами |
Використовуйте цю таблицю як перевірку компромісів, а не як оцінювальний лист. Мікросервіси допомагають, коли можливість потребує незалежного володіння, масштабування, розгортання чи ізоляції збоїв; вони шкодять, коли розділення додає мережеві виклики, роботу з узгодженням даних, вимоги до трейсингу та узгоджені релізи, не даючи команді справжньої незалежності.
Зупиніться та подумайте: ваша компанія запускає монолітний застосунок електронної комерції, і помилка в платіжному модулі валить увесь процес, включно з кошиком покупок і каталогом товарів. Як мікросервісна архітектура змінила б радіус ураження цього збою? Платіжний потік може відмовити чи деградувати, але перегляд і операції з кошиком могли б залишатися доступними, якби сервіси, шляхи даних та користувацький досвід було спроєктовано для витримування цього часткового збою.
Практичне питання про межу — не “скільки сервісів нам мати?”. Краще питання — “яка можливість змінюється в іншому темпі, масштабується під іншим навантаженням або відмовляє з іншими бізнес-наслідками?”. Платіж, пошук, рекомендації та обробка зображень часто мають різні операційні профілі. Розділення вздовж цих ліній може бути цінним, але розділення кожного методу контролера в окремий сервіс зазвичай створює координаційний біль без значущої ізоляції.
Kubernetes підтримує обидва вибори. Cloud native моноліт може працювати як Деплоймент із репліками, пробами, зовнішньою конфігурацією та централізованими логами. Мікросервісна система може запускати багато Деплойментів і Сервісів з окремими стратегіями розгортання, запитами ресурсів, мережевими політиками та правилами автомасштабування. Архітектурна зрілість походить від узгодження форми платформи із застосунком і командою, а не від гонитви за кількістю сервісів.
Гіпотетичний сценарій: інженерна група рітейлу розділяє систему оформлення замовлень на сервіси замовлень, інвентаризації, платежів, купонів і доставки до того, як вони мають трейсинг, версіоновані контракти чи паритет із локальною розробкою. Перший святковий інцидент був спричинений не Kubernetes; його спричинила нездатність людей відповісти, який сервіс володіє несправним розрахунком знижки. Вони зберегли мікросервіси, але лише після додавання мап володіння, контрактних тестів, розподіленого трейсингу та правила, що межа сервісу потребує бізнес-власника, а не лише репозиторію.
Ця історія ілюструє, чому межі сервісів мають слідувати за межами змін. Якщо купони та платежі завжди змінюються разом, примусове їх рознесення в окремі сервіси може створити проблему розподіленої транзакції, не приносячи цінності незалежного релізу. Якщо рекомендації змінюються багато разів на тиждень, а оформлення замовлень змінюється обережно під суворішим контролем, їх відокремлення може зменшити ризик. Відповідь cloud native залежить від економічної форми змін, а не від абстрактної переваги менших розгортуваних одиниць.
За кожною межею сервісу також ховається межа даних. Сервіс, який володіє даними, мусить надати API або потік подій для використання іншими сервісами, і він мусить узяти на себе операційну вартість еволюції схеми. Якщо інші сервіси обходять цей API та читають таблиці напряму, система зберігає накладні витрати на розгортання мікросервісів, але втрачає їхню незалежність. Саме тому досвідчені команди ставляться до володіння базою даних як до частини архітектури, а не як до деталі реалізації.
Для цілей KCNA будьте готові захищати обидва вибори. Моноліт може бути правильною відповіддю, коли одній команді потрібна швидка ітерація, а робоче навантаження все одно можна реплікувати, налаштовувати ззовні та чисто спостерігати. Мікросервіси стають сильнішою відповіддю, коли окремим командам потрібні окремі повноваження на розгортання, гарячому шляху потрібне незалежне масштабування або необов’язкова функція має відмовляти без того, щоб завалити основний шлях користувача. Судження cloud native — це здатність пояснити компроміс.
Незмінні та декларативні операції
Розділ «Незмінні та декларативні операції»Незмінна інфраструктура — це дисципліна заміни запущених одиниць замість їх зміни вручну. У традиційній серверній моделі оператор може зайти на машину через SSH, встановити пакет, відредагувати конфігураційний файл, перезапустити демон і написати оновлення в тікеті по тому. Це може бути швидко під час кризи, але це також створює дрейф, бо машина тепер містить історію, якої може не бути в Git, артефакті збірки чи відтворюваному шляху розгортання.
Cloud native системи надають перевагу заміні, тому що заміна є тестованою та оборотною. Якщо образ контейнера містить версію 1.8.0 вашого застосунку, кожен Под, створений з цього образу, має стартувати з однаковим вмістом файлової системи. Коли вам потрібна версія 1.8.1, ви збираєте новий образ, оновлюєте Деплоймент і дозволяєте контролеру замінити старі Pod’и новими згідно із задекларованою стратегією розгортання.
┌─────────────────────────────────────────────────────────────┐│ IMMUTABLE INFRASTRUCTURE │├─────────────────────────────────────────────────────────────┤│ ││ MUTABLE (Traditional): ││ ───────────────────────────────────────────────────────── ││ Server → SSH in → Update packages → Modify config ││ ││ Problem: Servers diverge over time ("snowflakes") ││ "But it works on server A!" doesn't work on server B ││ ││ IMMUTABLE (Cloud Native): ││ ───────────────────────────────────────────────────────── ││ Need change? → Build new image → Deploy new container ││ → Delete old container ││ ││ ┌─────────┐ ┌─────────┐ ││ │ v1.0 │ → │ v1.1 │ (new container) ││ │ Running │ │ Running │ ││ └─────────┘ └─────────┘ ││ ↓ ││ Deleted ││ ││ Benefits: ││ • Reproducible deployments ││ • Easy rollback (just run old version) ││ • No configuration drift ││ • Better testing (same image everywhere) ││ │└─────────────────────────────────────────────────────────────┘Серйозний виробничий ризик — це парк машин, що розходиться, бо стан розгортання не забезпечується одноманітно. Одна пропущена одиниця може залишити змішані версії в дикій природі, і Kubernetes не усуває цей ризик, доки площина управління не визначає та не спостерігає бажаний стан послідовно через контролери Деплойментів, намір щодо реплік, статус розгортання та історію відкатів.
Декларативна операція — це партнер незмінності. Імперативні команди описують кроки: запусти це, масштабуй те, відкрий цей порт, пропатч те поле. Декларативна конфігурація описує бажаний кінцевий стан: цей Деплоймент має існувати, він має запускати цей образ, він має мати три репліки, і він має виставляти ці мітки та проби. Контролер далі невпинно порівнює фактичний стан із бажаним.
┌─────────────────────────────────────────────────────────────┐│ DECLARATIVE vs IMPERATIVE │├─────────────────────────────────────────────────────────────┤│ ││ IMPERATIVE: "How to do it" ││ ───────────────────────────────────────────────────────── ││ kubectl run nginx --image=nginx ││ kubectl scale deployment nginx --replicas=3 ││ kubectl expose deployment nginx --port=80 ││ ││ • Step by step commands ││ • You specify the actions ││ • No record of desired state ││ ││ DECLARATIVE: "What you want" ││ ───────────────────────────────────────────────────────── ││ apiVersion: apps/v1 ││ kind: Deployment ││ spec: ││ replicas: 3 ││ template: ││ spec: ││ containers: ││ - name: nginx ││ image: nginx ││ ││ kubectl apply -f deployment.yaml ││ ││ • Describe desired state ││ • Kubernetes figures out how ││ • Version controlled (GitOps!) ││ ││ Cloud native = Declarative ││ │└─────────────────────────────────────────────────────────────┘Імперативні команди все ще корисні для навчання, налагодження та аварійної інспекції. Антипатерн — це запуск промислового середовища переважно з послідовності команд, яку ніхто не може відтворити. Якщо єдиним записом бажаного стану є історія терміналу, інший інженер не може переглянути зміну до того, як вона трапиться, а кластер не може сказати, чи поточний стан є навмисним.
Декларативна модель стає потужною, коли вона під контролем версій. Pull request може показати, що репліки змінилися з двох на три, тег образу змінився з 1.8.0 на 1.8.1 або шлях готовності змінився з /ready на /health/ready. Рецензенти можуть осмислити очікуваний стан до того, як він досягне кластера, а історія Git стає частиною операційного аудиторського сліду.
apiVersion: v1kind: Servicemetadata: name: checkoutspec: selector: app: checkout ports: - name: http port: 80 targetPort: 8080k apply -f checkout-service.yamlk describe service checkoutk rollout status deployment/checkoutЯкий підхід ви обрали б тут і чому: однорядкове k scale deployment checkout --replicas=5 під час сплеску трафіку чи pull request, що змінює маніфест Деплойменту й дозволяє автоматизації його застосувати? Під час живого інциденту тимчасове імперативне масштабування може бути виправданим, якщо ваша команда записує його й узгоджує по тому. Для тривкої бажаної потужності маніфест має перемогти, бо він не дає наступному застосуванню тихо скасувати зміну, зроблену вручну.
Урок cloud native — це не “ніколи не набирай команду”. Урок у тому, що тривкий промисловий намір належить декларативному стану, а одноразові команди мають або інспектувати систему, або створювати тимчасові зміни, які пізніше згортаються назад у цей стан. Контролери Kubernetes — це рушії узгодження, тож ваша операційна модель має давати їм чіткий бажаний стан для узгодження.
Відкати показують цінність незмінності та декларативного стану разом. Якщо новий образ спричиняє помилки, команда може повернути Деплоймент до попереднього образу або скористатися історією розгортань, замість того щоб лагодити окремі контейнери. Стара версія не реконструюється з пам’яті; це артефакт, який уже існує. Це знижує когнітивне навантаження під час інциденту, бо шлях відновлення — це інший задекларований стан, а не низка кустарних правок машини.
Декларативний стан також покращує огляди безпеки й відповідності. Рецензент може перевірити, хто змінив образ, на який Secret посилаються, чи з’явилися привілейовані налаштування та чи були прибрані запити ресурсів. Зміни вручну можуть бути швидкими, але вони часто залишають найслабший слід доказів саме в той момент, коли організації потрібно зрозуміти, що змінилося. Версіоновані маніфести роблять промислову зміну рецензованим артефактом, а не чуткою.
Усе ще є випадки, коли імперативні команди є правильним інструментом. Ви можете використати k logs, щоб дослідити збій, k describe pod, щоб побачити події планування, або k rollout restart як частину задокументованої операційної процедури. Різниця в намірі. Команди інспекції збирають факти, тимчасові команди виграють час, а декларативні зміни визначають усталений стан, який має пережити наступний цикл узгодження.
Проєктуйте під збій замість сподівання на стабільність
Розділ «Проєктуйте під збій замість сподівання на стабільність»Традиційна інфраструктура часто ставиться до збою як до винятку, який слід запобігти. Cloud native інфраструктура ставиться до збою як до нормального вхідного параметра дизайну. Вузли спорожнюються, Pod’и витісняються, мережі гублять пакети, залежності уповільнюються, а розгортання вносять погані версії. Стійка система не уникає кожного збою; вона обмежує радіус ураження, виявляє нездорові екземпляри та відновлюється без потреби людині перебудовувати сервіс вручну.
┌─────────────────────────────────────────────────────────────┐│ DESIGN FOR FAILURE │├─────────────────────────────────────────────────────────────┤│ ││ Cloud native assumption: ││ ───────────────────────────────────────────────────────── ││ "Everything will fail. Plan for it." ││ ││ Patterns: ││ ││ 1. REDUNDANCY ││ Run multiple replicas ││ If one fails, others handle traffic ││ ││ 2. HEALTH CHECKS ││ Liveness: "Is the container alive?" ││ Readiness: "Can it receive traffic?" ││ Kubernetes restarts unhealthy containers ││ ││ 3. CIRCUIT BREAKER ││ If service B is failing, stop calling it ││ Fail fast, don't wait for timeouts ││ ││ 4. RETRY WITH BACKOFF ││ Retry failed requests ││ Wait longer between each retry ││ ││ 5. GRACEFUL DEGRADATION ││ If recommendation service fails ││ Show generic recommendations instead of error ││ │└─────────────────────────────────────────────────────────────┘Надмірність — найпомітніший патерн у Kubernetes. Деплоймент із трьома репліками дає Сервісу більше одного ендпоінта, тож збій одного Под’а не мусить перетворюватися на видиму користувачам відмову. Надмірність не безкоштовна, бо кожна репліка споживає ресурси та може збільшувати тиск на бази даних чи низхідні API, але вона часто є найпростішим способом зробити придатні до утилізації процеси практичними.
Перевірки здоров’я — це канал комунікації між застосунком і платформою. Liveness-проба відповідає, чи слід перезапустити контейнер, тоді як readiness-проба відповідає, чи має Под отримувати трафік. Плутання цих сигналів спричиняє реальні інциденти: якщо повільна залежність змушує readiness не пройти, прибрати Под з ендпоінтів може бути правильно; якщо та сама залежність змушує liveness не пройти, Kubernetes може перезапустити кожну репліку та посилити відмову.
Circuit breaker’и, тайм-аути, повтори та плавна деградація обробляють збої поза самим Под’ом. Сервіс оформлення замовлень, який викликає платіжний шлюз, не має давати кожному запиту чекати нескінченно, коли шлюз повільний. Тайм-аут обмежує очікування, circuit breaker зупиняє повторні виклики на короткий інтервал, повтор із відкочуванням (backoff) уникає бомбардування сервісу, що відновлюється, а плавна деградація дає користувачу зменшений, але контрольований досвід.
Навмисне впровадження збоїв — випадкове завершення промислових екземплярів упродовж робочих годин, хрестоматійним прикладом якого є канонічний модуль про chaos engineering — звучить безрозсудно, доки ви не зрозумієте мету. Сенс у тому, щоб змусити інженерів будувати сервіси, які переживають рутинну втрату, а не покладаються на бездоганну інфраструктуру. Kubernetes створює м’якшу версію цієї реальності щодня, бо планування, перепланування, послідовні оновлення та обслуговування вузлів є звичайними частинами життя кластера.
Навичка рівня KCNA — це розпізнавання того, який рівень володіє якою поведінкою відновлення. Kubernetes може перезапустити контейнер, що впав, припинити маршрутизацію до неготового Под’а та створити замінні репліки. Вашому застосунку все одно потрібен безпечний старт, плавне завершення, ідемпотентна обробка запитів, обмежені виклики залежностей і дані, збережені десь достатньо тривко для бізнес-вимоги.
Розгляньмо сервіс рекомендацій, який збагачує сторінку товару. Якщо виклики рекомендацій відмовляють, сторінка все одно може показати деталі товару, відгуки та загальний список “популярних товарів”. Це плавна деградація. Якщо та сама відмова не дає відображатися кожній сторінці товару, архітектура перетворила необов’язкову функцію на жорстку залежність, і Kubernetes не може вивести цей бізнес-пріоритет із маніфесту.
Перш ніж додавати політику повторів, зупиніться й передбачте, що станеться, якщо кожен клієнт повторюватиме негайно, коли залежність почне видавати тайм-аути. Сервіс, що відмовляє, отримує більше трафіку саме тоді, коли в нього менше потужності, і шторм повторів може затримати відновлення. Backoff і джитер існують тому, що cloud native системи містять цикли зворотного зв’язку, а необачні цикли зворотного зв’язку можуть погіршити інциденти.
Проєктування під збій також означає вирішення того, що не автоматизувати. Перезапуск процесу корисний, коли процес застряг, але шкідливий, коли реальна проблема — це відмова бази даних, і кожен перезапуск відкидає корисні кеші. Повтор запиту корисний, коли збій тимчасовий, але шкідливий, коли операція не ідемпотентна, а платіжний шлюз може обробити першу спробу пізніше. Cloud native системи автоматизовані, але вдала автоматизація кодує знання предметної області, а не сліпо повторює дії.
Готовність — один із найясніших прикладів цього знання предметної області. Сервіс може бути живим, але не готовим, бо він прогріває кеш, завантажує модель, чекає на залежність або спорожнюється перед завершенням роботи. Надсилання трафіку до цього екземпляра зарано створює помилки на боці користувача, тоді як перезапуск його через liveness може скинути прогрес. Добре спроєктований ендпоінт готовності повідомляє Kubernetes, коли маршрутизація безпечна, не вдаючи, ніби кожна тимчасова проблема із залежністю має вбити процес.
Плавне завершення роботи важливе з тієї самої причини. Kubernetes надсилає сигнал завершення й дає контейнеру час вийти, але застосунок мусить припинити приймати нову роботу, завершити або передати запити в процесі та чисто закрити ресурси. Сервіс, який ігнорує завершення, може втрачати повідомлення під час кожного розгортання, через що рутинна заміна відчувається небезпечною. Сервіс, який добре обробляє завершення, перетворює заміну на звичайну дію з обслуговування.
Дизайн під збій слід тестувати до першого великого інциденту. Видаліть непромисловий Под і подивіться, чи відновлюється трафік. Зламайте залежність у середовищі staging і поспостерігайте, чи відповідають готовність, логи та користувацький досвід дизайну. Уповільніть низхідний виклик і перевірте, чи захищають тайм-аути викликаючого. Ці вправи невеликі, але вони будують упевненість, що написана архітектура відображена в поведінці під час виконання.
Kubernetes дає вам конкретні механізми для кожного патерну збоїв вище. Поле replicas Деплойменту та контролер Сервісу/ендпоінтів реалізують надмірність: коли один Под не проходить готовність або зникає, решта ендпоінтів продовжує отримувати трафік. Об’єкти PodDisruptionBudget додають захист від добровільних збоїв під час спорожнення вузлів та оновлень кластера, обмежуючи, скільки Pod’ів може бути недоступними водночас. Для збоїв залежностей тайм-аути та повтори рівня застосунку належать коду чи сервісній сітці, але платформа все одно бере участь через readiness-проби, які прибирають нездорові екземпляри з балансування навантаження до того, як перезапуски через liveness посилять відмову.
Патерни та антипатерни
Розділ «Патерни та антипатерни»Патерни — це повторно використовувані рішення, які полегшують дотримання контракту cloud native під тиском. Вони не є прикрасами, що додаються після того, як застосунок запрацював; це способи зробити заміну, масштабування, спостереження та обробку збоїв звичними. Наведені нижче патерни навмисно прив’язані до симптомів, які ви можете побачити під час огляду в Kubernetes, бо питання KCNA часто описують поведінку й запитують, який принцип порушується.
| Патерн | Коли використовувати | Чому працює | Аспект масштабування |
|---|---|---|---|
| Винесена назовні конфігурація | Значення відрізняються за середовищем, регіоном, орендарем чи стадією релізу | Той самий образ може працювати в dev, staging і production з різними ConfigMap’ами чи Secret’ами | Тримайте чутливі значення в Secret’ах і ротуйте їх без перезбирання образів |
| Безстанові вебрепліки чи воркери | Запити можуть оброблятися будь-яким здоровим екземпляром | Pod’и можна замінювати, переплановувати та масштабувати горизонтально | Перенесіть сесії, завантаження та тривкі дані в допоміжні сервіси |
| Декларативні маніфести під контролем версій | Промисловий стан має бути рецензованим і відтворюваним | Pull request’и показують бажані зміни до того, як контролери їх узгодять | Використовуйте GitOps або CI/CD, щоб зменшити дрейф вручну між кластерами |
| Маршрутизація трафіку з урахуванням здоров’я | Готовність старту чи залежності може різнитися між екземплярами | Готовність тримає погані ендпоінти поза Сервісами, а liveness обробляє застряглі процеси | Налаштуйте проби, щоб не перезапускати здорові, але тимчасово неготові контейнери |
Антипатерни зазвичай з’являються тому, що старий підхід був зручним у меншому середовищі. Локальна тека завантажень проста на одній VM, жорстко закодований URL бази даних швидкий для прототипу, а правки через SSH відчуваються ефективними під час відмови. Архітектура cloud native запитує, чи працюють ці зручності й далі, коли Pod’и переміщуються, команди ростуть, релізи прискорюються, а збій очікується.
| Антипатерн | Що йде не так | Краща альтернатива |
|---|---|---|
| Контейнеризована сніжинка | Образ ідентичний, але оператори змінюють запущені контейнери чи вузли вручну | Перезбирайте образи чи оновлюйте декларативну конфігурацію, потім розгортайте заміни |
| Спільна база даних для непов’язаних сервісів | Команди не можуть розгортатися незалежно, бо зміни схеми зачіпають усіх | Дайте сервісам явне володіння даними та комунікуйте через API чи події |
| Локальний стан сесій чи завантажень | Заміна Под’а розлогінює користувачів чи втрачає файли при зміні планування | Зберігайте сесії в Redis чи іншому допоміжному сервісі, а завантаження — в об’єктному сховищі |
| Liveness-проба перевіряє кожну залежність | Низхідна відмова перезапускає інакше здорові Pod’и та розширює інцидент | Використовуйте readiness для доступності залежностей і liveness для виявлення застряглого процесу |
Аспект масштабування є організаційним так само, як технічним. Команда, яка не може безпечно експлуатувати один сервіс, не стане безпечнішою, експлуатуючи багато сервісів. Почніть із найменшої архітектури, що дотримується контракту cloud native, потім розділяйте там, де розділення зменшує реальне вузьке місце чи домен збоїв.
Патерни слід впроваджувати в порядку, який зменшує найбільший ризик за найменшу складність. Винесена назовні конфігурація часто йде рано, бо вона робить той самий образ портативним. Проби зазвичай ідуть далі, бо вони дають Kubernetes маршрутизувати трафік на основі готовності застосунку. Окремі Job’и для адміністративної роботи стають важливими, коли кількість реплік зростає. Сервісна сітка чи просунута система прогресивної доставки має з’явитися після того, як команда зрозуміє простіші контракти, які вона намагається автоматизувати.
Антипатерни часто захищають правдивими, але неповними твердженнями. Локальний диск швидкий, але він не тривкий між замінами Под’ів. Спільна база даних зручна, але вона зчіплює незалежні сервіси. Ручний гарячий фікс відновлює сервіс, але створює дрейф, якщо ніколи не стає кодом чи конфігурацією. Задача огляду не в тому, щоб присоромити ці вибори; вона в тому, щоб запитати, чи пасують вони й далі операційній моделі, якої хоче команда.
Коли ви не впевнені, опишіть збій, який ви намагаєтеся пережити. Якщо збій — це падіння одного Под’а, реплік і готовності може бути достатньо. Якщо збій — це зникнення цілого вузла, локальний стан вузла стає проблемою. Якщо збій — це уповільнення низхідного сервісу, тайм-аути та плавна деградація важать більше, ніж додаткові репліки. Правильний патерн випливає з режиму збою.
Каркас прийняття рішень
Розділ «Каркас прийняття рішень»Використовуйте цей каркас, коли оцінюєте, чи є робоче навантаження достатньо cloud native для Kubernetes. Почніть зі стану, бо стан визначає, чи може платформа вільно замінювати Pod’и. Потім дослідіть конфігурацію, метод розгортання, спостережуваність, сигналізацію здоров’я та межі сервісів. Якщо будь-яка відповідь залежить від того, що людина пам’ятає кроки, зроблені вручну, дизайн потребує більше декларативного контролю.
| Питання для рішення | Відповідь cloud native | Тривожний знак | Об’єкт чи практика Kubernetes |
|---|---|---|---|
| Де живе тривкий стан? | У допоміжному сервісі з визначеним життєвим циклом | Файли під файловою системою контейнера чи шляхами, локальними для вузла | Керована база даних, PersistentVolume, об’єктне сховище, Redis, StatefulSet за потреби |
| Як змінюється конфігурація? | Специфічні для середовища значення походять із декларативних джерел | Перезбирання образу чи правка запущеного сервера для кожного середовища | ConfigMap, Secret, значення Helm, оверлеї Kustomize |
| Як записується бажаний стан? | Маніфести живуть під контролем версій і застосовуються через автоматизацію | Промислове середовище залежить від історії команд чи ручного SSH | GitOps, CI/CD, k apply із рецензованого YAML |
| Що відбувається, коли Под помирає? | Стартує заміна, трафік уникає неготових екземплярів, дані лишаються в безпеці | Людина мусить відновити файли чи перезапустити конкретний хост | Репліки Деплойменту, readiness-проби, зовнішній стан |
| Коли слід розділити сервіс? | Інше володіння, тиск масштабування, темп релізів чи вплив збоїв | Розділення лише для того, щоб виглядати сучасним | Окремі Деплоймент і Сервіс із чітким API та володінням даними |
| Як обробляються логи та адмінзадачі? | Логи йдуть у stdout, міграції виконуються як одноразові Job’и | Логи лишаються у файлах, а міграції виконуються в кожній репліці | Логування кластера, Job, CronJob |
Ви можете перетворити цю таблицю на практичну послідовність огляду. Спершу запитайте, чи може Под зникнути без втрати бізнес-даних. По-друге, запитайте, чи може той самий образ працювати в усіх середовищах лише зі змінами конфігурації. По-третє, запитайте, чи можна відтворити поточний стан кластера з системи контролю версій. По-четверте, запитайте, чи може команда спостерігати, відкочувати та деградувати сервіс під час збою залежності.
Цей каркас також захищає вас від архітектури “інструмент перш за все”. Сервісна сітка може додати повтори та політику трафіку, але вона не може полагодити сервіс, який небезпечно повторювати. Контролер GitOps може узгоджувати YAML, але він не може зробити недокументовану ручну міграцію бази даних безпечною. Kubernetes потужний, бо він автоматизує чітко задекларований намір, тож нечіткий намір лишається проблемою дизайну.
Для нових систем обирайте модульний моноліт, коли предметна область усе ще швидко змінюється, а одна команда володіє всім продуктом. Тримайте його cloud native, виносячи конфігурацію назовні, пишучи логи у stdout, уникаючи локального тривкого стану та використовуючи Деплойменти з пробами. Обирайте мікросервіси, коли володіння командою, тиск масштабування чи ізоляція збоїв виправдовують додану мережеву, дані та операційну складність.
Для наявних систем мігруйте ризик шарами замість спроби полагодити все одразу. Ви можете спершу контейнеризувати застосунок, потім винести конфігурацію назовні, потім перенести завантаження в об’єктне сховище, потім додати readiness- та liveness-проби, потім розділити можливість із високою частотою змін. Кожен шар має зробити наступний збій легшим для розуміння, а не просто додати ще одну можливість платформи.
Використовуйте каркас як інструмент для розмови з командами застосунків. Замість того щоб запитувати, чи є вони “cloud native”, запитайте, де система зберігає тривкі дані, як налаштовується нове середовище, як вони дізнаються, що екземпляр готовий, і що вони роблять, коли залежність повільна. Конкретні питання зменшують захисну реакцію та виявляють чіткі наступні кроки. Вони також дають кращі архітектурні рішення, ніж загальна вимога модернізуватися.
Чи знали ви?
Розділ «Чи знали ви?»- 12 факторів почалися в Heroku у 2011 році — методологія виникла від операторів, які бачили, як багато застосунків відмовляють схожими способами в різних середовищах розгортання.
- Cloud native старший за Kubernetes — Kubernetes став проєктом CNCF у 2015 році, але архітектурні ідеї, що стоять за придатністю до утилізації, автоматизацією та зовнішньою конфігурацією, вже були усталеними.
- Мікросервіси — це компроміс, а не нагорода — незалежне розгортання може зменшити радіус ураження, але розподілений трейсинг, контрактне тестування та володіння даними стають обов’язковими інженерними турботами.
- Тестування збоїв стало мейнстримом на початку 2010-х — канонічний модуль про chaos engineering охоплює інструментарій, що підштовхнув команди довести: втрата екземпляра впродовж робочих годин не мусила перетворюватися на відмову для клієнта.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому вона трапляється | Як це виправити |
|---|---|---|
| Сприйняття “працює в хмарі” як “cloud native” | Інфраструктура змінилася, але застосунок усе ще припускає один стабільний хост і ремонт вручну | Оцініть конфігурацію, стан, логи, проби, розгортання та поведінку під час збоїв, перш ніж називати дизайн cloud native |
| Старт із багатьма мікросервісами до розуміння меж | Команди копіюють видиму форму великих платформ без операційної зрілості за нею | Почніть із модульного моноліту, потім розділяйте сервіси, коли володіння, масштабування чи домени збоїв це виправдовують |
| Зберігання завантажень чи сесій у файловій системі контейнера | Локальні файли прості під час розробки й здаються робочими, доки Под не переміститься | Використовуйте об’єктне сховище, Redis, базу даних чи належно керований патерн персистентних даних |
| Жорстке кодування адрес чи облікових даних бази даних у вихідному коді | Ранні прототипи цінують швидкість понад портативність між середовищами | Подавайте конфігурацію через ConfigMap’и та Secret’и, щоб той самий образ працював у різних середовищах |
| Використання liveness-проб для перевірки кожної залежності | Команди хочуть, щоб Kubernetes перезапускав будь-що, причетне до невдалого запиту | Використовуйте readiness для доступності залежностей і лишіть liveness для застряглих чи невідновних локальних збоїв процесу |
| Лагодження промислового середовища через SSH і залишання зміни там | Правки вручну відчуваються швидшими під час інциденту | Перетворіть фікс на зміну образу чи маніфесту, розгорніть її декларативно та усуньте дрейф |
| Запуск міграцій усередині кожної вебрепліки | Шлях старту здається зручним місцем для коду налаштування | Запускайте міграції як окремий Job, щоб вебпод’и лишалися придатними до утилізації та горизонтально масштабованими |
Тест
Розділ «Тест»Перевірте своє судження на цих архітектурних сценаріях, перш ніж переходити до вправи з рефакторингу.
Ваша команда перенесла застарілий моноліт у контейнер і розгорнула його на Kubernetes. Застосунок усе ще пише завантаження в `/app/uploads`, читає жорстко закодований URL бази даних і не виставляє ендпоінт готовності. Чи є він cloud native, і що б ви змінили першим?
Він контейнеризований, але ще не cloud native в операційному сенсі. Локальний шлях завантажень порушує дизайн безстанового процесу, бо заміна Под’а може втратити дані, а жорстко закодований URL бази даних порушує винесену назовні конфігурацію та приєднання допоміжного сервісу. Я б спершу переніс тривкі файли в об’єктне сховище чи інший допоміжний сервіс, подав URL бази даних через Secret і додав готовність, щоб Сервіси маршрутизували лише до екземплярів, які можуть обробляти трафік.
Сервіс оформлення замовлень має три репліки, але кожен Под зберігає сесії користувачів у пам'яті. Під час послідовного оновлення деякі користувачі втрачають кошики. Який принцип порушується, і як має змінитися дизайн?
Робоче навантаження порушує очікування безстанового процесу, що стоїть за конкурентністю та придатністю до утилізації cloud native. Kubernetes має право замінювати Pod’и під час розгортання, тож будь-які дані, що мають пережити заміну, не можуть жити лише в пам’яті одного процесу. Стан сесій має переміститися в допоміжний сервіс, такий як Redis чи база даних, а застосунок має ставитися до будь-якої репліки як до здатної обслужити наступний запит.
Стартап хоче десять мікросервісів для двотижневого запуску, бо "мікросервіси — це cloud native". Команда не має трейсингу, не має контрактних тестів і має один продуктовий загін. Як би ви оцінили цей дизайн?
Дизайн, імовірно, передчасний, бо він додає складність розподіленої системи до того, як команда має межі, володіння та операції на місці. Модульний моноліт усе ще може бути cloud native, якщо він використовує зовнішню конфігурацію, логування в stdout, репліки, проби та тривкі допоміжні сервіси. Я б розділяв лише ті можливості, які вже мають окремий тиск масштабування, темп релізів, вплив збоїв чи володіння.
Інженер запускає `k scale deployment checkout --replicas=5` під час сплеску трафіку, але маніфест у Git усе ще каже три репліки. Який ризик створила команда?
Масштабування вручну може бути виправданим як тимчасова дія під час інциденту, але воно створило дрейф між фактичним станом і задекларованим бажаним станом. Наступне автоматичне застосування може повернути Деплоймент до трьох реплік без очевидного зв’язку з рішенням під час інциденту. Тривке виправлення — оновити маніфест під контролем версій чи політику автомасштабування, щоб узгодження Kubernetes відображало рецензований намір.
Liveness-проба викликає базу даних, платіжний шлюз і сервіс рекомендацій. Коли платіжний шлюз уповільнюється, Kubernetes перезапускає кожен Под оформлення замовлень. Що не так із цим дизайном проби?
Liveness-проба перевіряє доступність залежностей замість того, чи живий локальний процес і чи відновлюється він через перезапуск. Перезапуск здорових Pod’ів оформлення замовлень під час відмови платежів прибирає потужність і може посилити інцидент. Перевірки залежностей зазвичай належать readiness чи логіці деградації рівня застосунку, тоді як liveness має виявляти застряглий процес, що потребує заміни.
Команда запускає міграції схеми з точки входу вебконтейнера. Після масштабування з однієї репліки до трьох старт стає повільним і іноді відмовляє через блокування міграцій. Яка ідея 12 факторів застосовна?
Це порушує відокремлення між звичайними процесами застосунку та адміністративними процесами. Вебрепліки мають стартувати швидко та бути придатними до утилізації, тоді як міграції — це одноразові операційні задачі з власним життєвим циклом і обробкою збоїв. У Kubernetes міграція має виконуватися як Job до чи під час процесу релізу, а Деплоймент для обслуговування має стартувати лише процес подачі трафіку.
Сторінка товару залежить від сервісу рекомендацій. Коли рекомендації відмовляють, уся сторінка повертає помилку, хоча деталі товару доступні. Який патерн збоїв cloud native покращив би досвід?
Плавна деградація дала б сторінці відобразити основні деталі товару, замінивши рекомендації загальним списком чи приховавши цей розділ. Тайм-аути та circuit breaker’и не дали б залежності рекомендацій спожити весь час і потужність запиту. Ключове — вирішити, які можливості є необов’язковими під час часткового збою, і закодувати цей бізнес-пріоритет у поведінці застосунку.
Практична вправа: рефакторинг за 12 факторами
Розділ «Практична вправа: рефакторинг за 12 факторами»У цій вправі ви оціните та зробите рефакторинг традиційного розгортання застосунку в дизайн Kubernetes у стилі cloud native. Вам не потрібен повний репозиторій застосунку, щоб виконати роботу з міркувань, але команди припускають, що у вас доступний кластер Kubernetes 1.35+ і вже визначений аліас k. Сприймайте сценарій як архітектурний огляд, перш ніж писати фінальний маніфест.
Сценарій: ви успадкували монолітний застосунок на Node.js. Наразі він зберігає завантаження користувачів у локальній теці /app/uploads, під’єднується до бази даних за адресою localhost:5432, пише логи в /var/log/app.log і запускає скрипт міграції під час кожного старту контейнера. Продакт-менеджер хоче три репліки до наступної кампанії.
Задача 1: визначте антипатерни
Розділ «Задача 1: визначте антипатерни»Перевірте поточний стан застосунку щодо принципів 12 факторів і cloud native та запишіть операційний наслідок кожного порушення, перш ніж обирати виправлення.
- Визначте порушення Фактора 4, Допоміжні сервіси.
- Визначте порушення Фактора 6, Процеси та безстановість.
- Визначте порушення Фактора 11, Логи.
- Визначте проблему адміністративного процесу, створену міграціями під час старту.
Орієнтири для розв'язання
Підключення до бази даних не має вказувати на localhost:5432, якщо база даних навмисно не запущена в тому самому Под’і, що не є звичайним патерном вебнавантаження. Завантаження під /app/uploads — це локальний стан контейнера, який може зникнути під час заміни чи перепланування. Логи мають йти в stdout і stderr, щоб Kubernetes і агенти логування кластера могли їх збирати. Міграції мають виконуватися як Job, а не всередині кожної вебрепліки.
Задача 2: спроєктуйте рішення cloud native
Розділ «Задача 2: спроєктуйте рішення cloud native»Сплануйте, як адаптувати застосунок для Kubernetes, перш ніж писати YAML, бо маніфест має виражати рішення про дизайн, а не приховувати нерозв’язане припущення застосунку.
- Переналаштуйте застосунок читати рядок підключення до бази даних зі змінної середовища, поданої Secret’ом.
- Змініть застосунок виводити логи в stdout замість файлу під
/var/log. - Перенесіть локальні завантаження файлів у зовнішній сервіс об’єктного сховища.
- Перенесіть міграції схеми в Kubernetes Job, що виконується окремо від вебреплік.
Орієнтири для розв'язання
Той самий образ контейнера має працювати в dev, staging і production, зі специфічними для середовища значеннями, поданими під час релізу. Об’єктне сховище чи інший тривкий допоміжний сервіс прибирає прив’язку завантажень до вузла. Окремий Job дає міграції явний життєвий цикл і уникає дублювання спроб міграції, коли Деплоймент для обслуговування масштабується горизонтально. Вебпроцес стає придатним до утилізації, що дозволяє Kubernetes безпечно замінювати Pod’и.
Задача 3: складіть маніфест Деплойменту
Розділ «Задача 3: складіть маніфест Деплойменту»Напишіть базовий YAML Деплойменту Kubernetes, який реалізує обслуговувальну частину вашого дизайну та лишає тривкий стан, міграції та специфічні для середовища значення поза вебреплікою.
- Визначте
Deploymentіз 3 репліками для конкурентності. - Подайте URL бази даних через посилання на
Secretу секціїenv. - Переконайтеся, що не змонтовано локальних томів для станових завантажень користувачів.
- Додайте readiness- та liveness-проби з різними цілями.
apiVersion: apps/v1kind: Deploymentmetadata: name: dojo-shopspec: replicas: 3 selector: matchLabels: app: dojo-shop template: metadata: labels: app: dojo-shop spec: containers: - name: web image: ghcr.io/example/dojo-shop:2.3.0 ports: - containerPort: 8080 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: dojo-shop-db key: url - name: UPLOAD_BUCKET value: dojo-shop-uploads readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 8080 periodSeconds: 20Орієнтири для розв'язання
Деплоймент виражає конкурентність через репліки, виносить URL бази даних назовні та уникає монтування локального шляху для тривких завантажень. Readiness-проба має відображати, чи може застосунок приймати трафік, тоді як liveness має відображати, чи застряг локальний процес і чи потребує перезапуску. У реальній системі ви також визначили б Secret, Сервіс, запити ресурсів і стратегію розгортання.
Задача 4: додайте Job міграції
Розділ «Задача 4: додайте Job міграції»Відокремте адміністративний процес від вебпроцесу, щоб звичайні обслуговувальні Pod’и лишалися придатними до утилізації, горизонтально масштабованими та безпечними для заміни під час розгортання.
- Визначте
Job, що використовує той самий образ, але запускає команду міграції. - Подайте той самий Secret бази даних у Job.
- Тримайте Job окремо від життєвого циклу Деплойменту для обслуговування.
apiVersion: batch/v1kind: Jobmetadata: name: dojo-shop-migratespec: template: spec: restartPolicy: Never containers: - name: migrate image: ghcr.io/example/dojo-shop:2.3.0 command: ["npm", "run", "migrate"] env: - name: DATABASE_URL valueFrom: secretKeyRef: name: dojo-shop-db key: urlОрієнтири для розв'язання
Job робить міграцію видимою як адміністративну задачу замість приховування її всередині кожного обслуговувального Под’а. Це допомагає операторам бачити, чи міграція успішна, навмисно її повторювати та уникати суперництва за блокування від багатьох реплік. Job усе ще використовує той самий образ, тож збирання та реліз лишаються пов’язаними, а поведінка виконання — відокремленою.
Задача 5: перевірте огляд
Розділ «Задача 5: перевірте огляд»Використайте команди інспекції, щоб підтвердити форму робочого навантаження та пов’язати кожне спостережене поле назад з одним із принципів cloud native цього модуля.
- Запустіть
k get deploy dojo-shopі підтвердьте три бажані репліки. - Запустіть
k describe deploy dojo-shopі підтвердьте конфігурацію середовища на основі Secret. - Запустіть
k logs -l app=dojo-shop --tail=20і підтвердьте, що логи видаються через потік контейнера. - Підтвердьте, що маніфест Деплойменту не містить локального тому завантажень.
Орієнтири для розв'язання
Очікуваний результат — це не просто запущений Под; це робоче навантаження, поведінка якого відповідає контракту cloud native. Застосунок має бути замінним, бо тривкий стан живе поза контейнером, налаштовуваним, бо значення середовища походять з об’єктів Kubernetes, спостережуваним, бо логи течуть через stdout і stderr, та безпечнішим для масштабування, бо міграції більше не виконуються в кожній репліці.
Критерії успіху
Розділ «Критерії успіху»- Дизайн визначає початкові порушення щодо допоміжного сервісу, безстановості, логування та адміністративного процесу.
- Деплоймент використовує три репліки без залежності від локального тривкого стану.
- Конфігурація бази даних подається з Secret замість жорсткого кодування в образі.
- Логи очікуються в stdout чи stderr, а не у файлі всередині контейнера.
- Міграція виконується як окремий Job.
- Ви можете пояснити, чому це більш cloud native, ніж просте розміщення старого застосунку в контейнері.
Наступний модуль
Розділ «Наступний модуль»Модуль 3.2: Екосистема CNCF — далі ви відобразите ці принципи cloud native на проєкти та категорії CNCF, які реалізують їх у реальних платформах.
Джерела
Розділ «Джерела»- CNCF Cloud Native Definition
- The Twelve-Factor App
- Kubernetes Deployments
- Kubernetes Services
- Kubernetes ConfigMaps
- Kubernetes Secrets
- Kubernetes Probes
- Kubernetes Jobs
- Kubernetes Logging Architecture
- Kubernetes Horizontal Pod Autoscaling
- Kubernetes StatefulSets
- CNCF Cloud Native Interactive Landscape