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

Модуль 2.4: Конфігурація

Складність: [СЕРЕДНЯ] — концепції конфігурації

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

Передумови: Модуль 2.3 (Сховище), базові маніфести Pod’ів і Деплойментів, а також кластер Kubernetes 1.35+ або локальне пісочне середовище. Цей модуль використовує псевдонім k для kubectl; визначте його один раз командою alias k=kubectl перед запуском прикладів.

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

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

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

  1. Спроєктувати робочі навантаження Kubernetes, які відокремлюють конфігурацію застосунку від образів контейнерів за допомогою ConfigMap’ів, Secret’ів, змінних середовища, аргументів і змонтованих файлів.
  2. Порівняти ConfigMap’и та Secret’и за чутливістю, контролем доступу, поведінкою під час оновлення та операційним ризиком у середовищах Kubernetes 1.35+.
  3. Діагностувати дрейф конфігурації, коли Под продовжує використовувати застарілі значення після зміни ConfigMap’у чи Secret’у.
  4. Впровадити незмінні ConfigMap’и та Secret’и для стабільних розгортань, де випадкова мутація могла б спричинити широкий вплив на продакшн.
  5. Оцінити, чому значенню конфігурації місце в ConfigMap’і, Secret’і, аргументі команди, змінній середовища чи монтуванні тому.

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

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

Засоби керування конфігурацією — секрети, сертифікати, облікові дані, прапорці функцій (feature flags) і перемикачі часу виконання — визначають, наскільки швидко команда може змінити поведінку, коли цього вимагає інцидент, регулятор або потреба переключити прапорець функції. Коли конфігурацію впаяно в образи контейнерів, скопійовано через одноразові скрипти або розкидано по десятках маніфестів Деплойментів, відповідальні особи втрачають здатність акуратно змінити значення під тиском. Технічне виправлення може бути одним рядком у YAML; те, що перетворює цей рядок на узгоджене розгортання в продакшні, і відрізняє команди, які відновлюються за хвилини, від команд, які відновлюються за дні.

Kubernetes не робить конфігурацію безпечною за помахом чарівної палички. Він дає вам повноцінні об’єкти, головно ConfigMap’и та Secret’и, щоб ви могли відокремити артефакт застосунку від значень, які різняться між середовищами. Це відокремлення звучить просто, доки від нього не починає залежати розгортання в продакшні. Образ контейнера має відповідати на запитання «який код ми запускаємо?». Конфігурація має відповідати на інше запитання: «як цей самий код має поводитися тут, сьогодні, у цьому просторі імен, із цими привілеями?».

Цей модуль навчає конфігурації як операційної дисципліни, а не як набору дрібниць про YAML. Ви побачите, як ConfigMap’и та Secret’и зберігаються, як вони проєктуються в Под’и, як оновлюються, як захищаються і як їх використовують неправильно. Ви також попрактикуєтеся вирішувати, де саме має бути значення, бо питання KCNA часто ховають справжню перевірку всередині сценарію: у кластері є правильний тип об’єкта, але робоче навантаження споживає його у спосіб, який унеможливлює перезавантаження, витікає чутливі значення або створює несподіванки під час розгортання. Уміння побачити цю різницю між «об’єкт існує» і «об’єкт спожито правильно» — це те, що відрізняє завчену відповідь від справжнього розуміння.

Проблема конфігурації

Розділ «Проблема конфігурації»

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

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

┌─────────────────────────────────────────────────────────────┐
│ WHY SEPARATE CONFIGURATION? │
├─────────────────────────────────────────────────────────────┤
│ │
│ Without ConfigMaps/Secrets (Bad Practice): │
│ ───────────────────────────────────────────────────────── │
│ │
│ Configuration baked into image: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Dockerfile: │ │
│ │ ENV DATABASE_URL=prod-db.example.com │ │
│ │ ENV API_KEY=sk-12345... │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Problems: │
│ • Need different images for dev/staging/prod │
│ • Secrets visible in image layers │
│ • Can't change config without rebuilding image │
│ │
│ With ConfigMaps/Secrets (Best Practice): │
│ ───────────────────────────────────────────────────────── │
│ │
│ Same image everywhere: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ [my-app:v1.0] ←── same image │ │
│ │ │ │ │
│ │ ├── dev: ConfigMap with dev settings │ │
│ │ ├── staging: ConfigMap with staging settings │ │
│ │ └── prod: ConfigMap with prod settings │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Benefits: │
│ • One image, many environments │
│ • Change config without rebuilding │
│ • Secrets managed separately and securely │
│ │
└─────────────────────────────────────────────────────────────┘

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

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

Зупиніться та спрогнозуйте: якщо два середовища запускають той самий образ, але одне середовище інжектує LOG_LEVEL=debug через змінну середовища, а інше монтує файл логування з ConfigMap’у, яке середовище може перезавантажитися без перезапуску Под’а, і що має вміти робити застосунок?

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

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

ConfigMap’и: нечутлива конфігурація як дані

Розділ «ConfigMap’и: нечутлива конфігурація як дані»

ConfigMap зберігає нечутливі дані конфігурації як пари «ключ-значення». За цим простим означенням ховається корисна відмінність: значення не є секретними, але вони все одно можуть бути операційно важливими. Поганий хост бази даних, тайм-аут, прапорець функції або файл властивостей застосунку здатні покласти сервіс так само певно, як і поганий тег образу. Ставтеся до ConfigMap’ів як до контрольованих вхідних даних часу виконання, а не як до місця для чернеток.

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

┌─────────────────────────────────────────────────────────────┐
│ CONFIGMAP │
├─────────────────────────────────────────────────────────────┤
│ │
│ ConfigMaps store: │
│ • Key-value pairs │
│ • Configuration files │
│ • Environment variables │
│ │
│ Example ConfigMap: │
│ ───────────────────────────────────────────────────────── │
│ │
│ apiVersion: v1 │
│ kind: ConfigMap │
│ metadata: │
│ name: app-config │
│ data: │
│ DATABASE_HOST: mysql.default.svc │
│ LOG_LEVEL: info │
│ MAX_CONNECTIONS: "100" │
│ app.properties: | │
│ server.port=8080 │
│ server.name=myapp │
│ │
│ Note: All values are strings (even "100") │
│ │
└─────────────────────────────────────────────────────────────┘

Той самий ConfigMap може представляти кілька форм конфігурації. Короткі значення на кшталт LOG_LEVEL природно вкладаються як окремі ключі. Довші файли на кшталт app.properties, nginx.conf або конфігурації логування також можуть вкладатися як багаторядкові значення. Ця гнучкість корисна, але вона може спокусити команди створювати роздуті ConfigMap’и «на все», які важко переглядати й ризиковано оновлювати.

У справжньому кластері ConfigMap прив’язаний до простору імен. Деплоймент у просторі імен payments-prod не читає ConfigMap із payments-dev, доки якийсь зовнішній інструмент не скопіює чи не відрендерить його туди. Ця прив’язка до простору імен підтримує модель «той самий образ, інше середовище». Кожен простір імен може містити об’єкт app-config з однаковою назвою, але різними значеннями, а шаблон робочого навантаження може залишатися узгодженим між середовищами.

Ось придатний для запуску приклад, який створює ConfigMap із літеральних значень, а потім перевіряє його. У прикладах використано k після введення псевдоніма, бо KCNA очікує, що ви розпізнаватимете стандартні операції kubectl, навіть коли в повсякденних сеансах оболонки застосовують скорочення.

Terminal window
alias k=kubectl
k create namespace config-lab
k create configmap app-config \
--namespace config-lab \
--from-literal=DATABASE_HOST=mysql.default.svc \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
k get configmap app-config --namespace config-lab -o yaml

Ця команда зручна для практики, але декларативний YAML легше переглядати в продакшні. Зміна ConfigMap’у може змінити кількість запитів, які застосунок надсилає до залежності, який ендпоінт він викликає чи яку функцію розкриває користувачам. Підпорядкуйте важливу конфігурацію тій самій дисципліні перегляду, що й інші зміни розгортання, навіть коли дані не є конфіденційними.

Коли ключ ConfigMap’у стає частиною контракту застосунку, задокументуйте його очікуваний формат поруч із робочим навантаженням або в репозиторії застосунку. Kubernetes може перевірити, що об’єкт ConfigMap існує, але не може знати, чи MAX_CONNECTIONS=banana має сенс для вашого сервісу. Політики допуску (admission policies), інструменти розгортання з підтримкою схем і валідація на старті застосунку можуть закрити цю прогалину, проте перший захист — невелика й читабельна поверхня конфігурації.

Інжектування конфігурації в Под’и

Розділ «Інжектування конфігурації в Под’и»

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

┌─────────────────────────────────────────────────────────────┐
│ USING CONFIGMAPS │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. As Environment Variables: │
│ ───────────────────────────────────────────────────────── │
│ │
│ spec: │
│ containers: │
│ - name: app │
│ envFrom: │
│ - configMapRef: │
│ name: app-config # All keys become env vars │
│ │
│ Or specific keys: │
│ env: │
│ - name: DB_HOST │
│ valueFrom: │
│ configMapKeyRef: │
│ name: app-config │
│ key: DATABASE_HOST │
│ │
│ 2. As Volume (files): │
│ ───────────────────────────────────────────────────────── │
│ │
│ volumes: │
│ - name: config-volume │
│ configMap: │
│ name: app-config │
│ │
│ volumeMounts: │
│ - name: config-volume │
│ mountPath: /etc/config │
│ │
│ Result: │
│ /etc/config/DATABASE_HOST (contains: mysql.default..) │
│ /etc/config/LOG_LEVEL (contains: info) │
│ /etc/config/app.properties (contains: server.port...) │
│ │
└─────────────────────────────────────────────────────────────┘

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

Монтування томів краще пасує, коли застосунок уже очікує файли. NGINX читає файли конфігурації, застосунки Java часто читають файли властивостей, а багато бібліотек TLS очікують файли сертифікатів на диску. Kubernetes проєктує кожен ключ як файл, і вміст файлу збігається з відповідним значенням. Така проєкція робить ConfigMap схожим на невелику керовану файлову систему, яку наповнюють API-сервер і kubelet.

apiVersion: v1
kind: Pod
metadata:
name: configured-demo
namespace: config-lab
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo DB=$DB_HOST; echo file=$(cat /etc/config/LOG_LEVEL); sleep 3600"]
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
volumeMounts:
- name: app-config-files
mountPath: /etc/config
readOnly: true
volumes:
- name: app-config-files
configMap:
name: app-config

Перш ніж запускати цей Под, спрогнозуйте два місця, де з’являється той самий ConfigMap. DB_HOST читається із середовища процесу, тоді як /etc/config/LOG_LEVEL читається зі змонтованого файлу. Якщо пізніше ви оновите ConfigMap, змінна середовища всередині запущеного процесу залишиться незмінною, тоді як змонтований файл згодом зможе відобразити нове значення.

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

Вправа: Інжектування змінних середовища У вас є ConfigMap із назвою backend-config з ключем LOG_LEVEL. Доповніть це часткове означення Под’а так, щоб інжектувати це значення як змінну середовища з назвою APP_LOG_LEVEL.

apiVersion: v1
kind: Pod
metadata:
name: backend-pod
spec:
containers:
- name: app
image: my-app:v1
# ADD YOUR CODE HERE
Розв'язання
env:
- name: APP_LOG_LEVEL
valueFrom:
configMapKeyRef:
name: backend-config
key: LOG_LEVEL

Явно використовуючи valueFrom і configMapKeyRef, ви відображаєте конкретний ключ зі свого ConfigMap’у на конкретну назву змінної середовища всередині контейнера.

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

Аргументи команди — це третій патерн споживання. Kubernetes не зчитує ConfigMap безпосередньо в args, але може наповнити змінну середовища з ConfigMap’у, а потім розгорнути цю змінну в команді чи аргументах контейнера. Цей патерн корисний для програм, які надають перевагу прапорцям на кшталт --log-level=$(LOG_LEVEL), але він усе одно поводиться як конфігурація середовища, бо значення обчислюється під час запуску контейнера.

apiVersion: v1
kind: Pod
metadata:
name: args-demo
namespace: config-lab
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c"]
args: ["echo starting with --log-level=$(LOG_LEVEL); sleep 3600"]
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL

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

Secret’и: чутливі дані з іншим ризиком

Розділ «Secret’и: чутливі дані з іншим ризиком»

Secret зберігає чутливі дані на кшталт паролів, токенів API, приватних ключів TLS, облікових даних реєстру та SSH-ключів. Secret’и схожі на ConfigMap’и, бо обидва можна споживати як змінні середовища або змонтовані файли. Відмінність не в тому, що Secret’и є чарівно безпечними; відмінність у тому, що Kubernetes і оператори кластера можуть застосовувати до об’єктів Secret суворіший контроль доступу, шифрування сховища, правила аудиту та домовленості щодо поводження.

┌─────────────────────────────────────────────────────────────┐
│ SECRETS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Secrets store: │
│ • Passwords │
│ • API keys │
│ • Certificates │
│ • SSH keys │
│ │
│ Secret Types: │
│ ───────────────────────────────────────────────────────── │
│ • Opaque: Generic secret (default) │
│ • kubernetes.io/tls: TLS certificates │
│ • kubernetes.io/dockerconfigjson: Docker registry auth │
│ • kubernetes.io/basic-auth: Basic authentication │
│ • kubernetes.io/ssh-auth: SSH credentials │
│ │
│ Example Secret: │
│ ───────────────────────────────────────────────────────── │
│ │
│ apiVersion: v1 │
│ kind: Secret │
│ metadata: │
│ name: db-credentials │
│ type: Opaque │
│ data: │
│ username: YWRtaW4= # base64 encoded "admin" │
│ password: cGFzc3dvcmQxMjM= # base64 encoded "password123"│
│ │
│ Or use stringData (plain text, encoded automatically): │
│ stringData: │
│ username: admin │
│ password: password123 │
│ │
└─────────────────────────────────────────────────────────────┘

Кодування base64 — найчастіше неправильно зрозуміла деталь у Secret’ах Kubernetes. Поле data зберігає закодовані в base64 значення, бо об’єкт API має безпечно нести довільні байти через JSON і YAML. Кодування — це не шифрування. Будь-хто, хто може прочитати об’єкт Secret, може декодувати значення, тож безпечна продакшн-конфігурація залежить від RBAC, шифрування на спокої, меж простору імен, обережного логування та обмеженого доступу людей.

Поле stringData зручне для написання, бо ви можете писати звичайні рядки й дозволити API-серверу закодувати їх у data. Ця зручність не означає, що звичайні секрети мають жити в Git. У продакшні команди зазвичай генерують маніфести Secret із захищеного процесу доставки, використовують інструменти запечатаних або зовнішніх секретів чи синхронізують їх із хмарного менеджера секретів. KCNA очікує, що ви знатимете рідний об’єкт Secret, але реальна експлуатація також вимагає дизайну життєвого циклу секретів.

┌─────────────────────────────────────────────────────────────┐
│ USING SECRETS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Same patterns as ConfigMaps: │
│ │
│ 1. As Environment Variables: │
│ ───────────────────────────────────────────────────────── │
│ │
│ env: │
│ - name: DB_PASSWORD │
│ valueFrom: │
│ secretKeyRef: │
│ name: db-credentials │
│ key: password │
│ │
│ 2. As Volume: │
│ ───────────────────────────────────────────────────────── │
│ │
│ volumes: │
│ - name: secret-volume │
│ secret: │
│ secretName: db-credentials │
│ │
│ volumeMounts: │
│ - name: secret-volume │
│ mountPath: /etc/secrets │
│ readOnly: true # Best practice for secrets │
│ │
│ Result: │
│ /etc/secrets/username (contains: admin) │
│ /etc/secrets/password (contains: password123) │
│ │
└─────────────────────────────────────────────────────────────┘

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

Terminal window
k create secret generic db-credentials \
--namespace config-lab \
--from-literal=username=admin \
--from-literal=password='example-password-only'
k describe secret db-credentials --namespace config-lab
k get secret db-credentials --namespace config-lab -o jsonpath='{.data.username}' | base64 --decode

Зверніть увагу на різницю між describe та get -o yaml чи jsonpath. k describe secret приховує сирі значення й показує метадані на кшталт ключів і розмірів. Пряме отримання об’єкта може розкрити закодовані дані будь-кому з відповідним дозволом. Ця відмінність — одна з причин, чому політики RBAC часто дозволяють ширший доступ до читання ConfigMap’ів, ніж до Secret’ів.

Вправа: Монтування тому Secret Вашому застосунку потрібен доступ до сертифіката TLS, що зберігається в Secret із назвою tls-certs. Оновіть цей Деплоймент так, щоб змонтувати секрет як том за шляхом /etc/tls і забезпечити доступ лише для читання.

apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
spec:
template:
spec:
containers:
- name: app
image: secure-app:v2
# ADD VOLUME MOUNTS HERE
# ADD VOLUMES HERE
Розв'язання
volumeMounts:
- name: cert-volume
mountPath: /etc/tls
readOnly: true
volumes:
- name: cert-volume
secret:
secretName: tls-certs

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

Гіпотетичний сценарій: одного разу платформенна команда ротувала пароль бази даних, оновивши Secret, і припустила, що кожне робоче навантаження одразу почне використовувати нове значення. Кілька Деплойментів споживали пароль як змінну середовища, тож ці Под’и продовжували автентифікуватися старими обліковими даними, доки не перезапустилися. Виправлення полягало не просто в «оновити Secret»; виправлення полягало в тому, щоб задокументувати патерни споживання, перезапускати уражені Под’и під час ротацій і перенести матеріал на кшталт сертифікатів у монтування томів лише для читання, де застосунок міг його перезавантажити.

Зупиніться й подумайте: Secret’и в Kubernetes закодовано в base64, а не зашифровано. Якщо хтось може виконати k get secret db-credentials -o yaml, які дозволи кластера, налаштування сховища та операційні процеси ви переглянули б, перш ніж назвати конфігурацію готовою до продакшну?

ConfigMap проти Secret: класифікація та компроміси

Розділ «ConfigMap проти Secret: класифікація та компроміси»

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

┌─────────────────────────────────────────────────────────────┐
│ CONFIGMAP vs SECRET │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ ConfigMap │ │ Secret │ │
│ ├──────────────────────┤ ├──────────────────────┤ │
│ │ • Non-sensitive │ │ • Sensitive data │ │
│ │ • Plain text │ │ • Base64 encoded │ │
│ │ • Not encrypted │ │ • Can be encrypted │ │
│ │ • No size limit │ │ • 1MB limit │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
│ When to use what: │
│ ───────────────────────────────────────────────────────── │
│ │
│ ConfigMap: Secret: │
│ • Database host • Database password │
│ • Log level • API keys │
│ • Feature flags • TLS certificates │
│ • Config files • SSH keys │
│ │
│ Important: │
│ ───────────────────────────────────────────────────────── │
│ Base64 is NOT encryption! │
│ It's just encoding. Anyone can decode it. │
│ For real security, enable encryption at rest. │
│ │
└─────────────────────────────────────────────────────────────┘

Оригінальна діаграма каже «без обмеження розміру» для ConfigMap’ів як контраст для початківців, але операційно вам слід тримати ConfigMap’и невеликими. Об’єкти Kubernetes живуть в API-сервері та сховищі-підкладці, і дуже великі об’єкти конфігурації можуть збільшувати навантаження, сповільнювати трафік спостереження (watch) та ускладнювати перегляд. Secret’и мають задокументоване обмеження розміру в один мебібайт, і до ConfigMap’ів слід ставитися зі схожою стриманістю, навіть коли точне жорстке обмеження не є головною турботою.

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

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

  1. Прапорець функції, що вмикає новий макет UI (ENABLE_NEW_UI=true)
  2. Токен API стороннього платіжного шлюзу
  3. Файл конфігурації NGINX (nginx.conf)
  4. Приватний SSH-ключ для доступу до репозиторію Git
Розв'язання
  1. ConfigMap: Прапорці функцій не є чутливими. Якщо їх розкрити, вони не становлять загрози безпеці.
  2. Secret: Токени API надають доступ до зовнішніх систем і становлять фінансовий ризик. Їх слід захищати за допомогою RBAC і шифрування на спокої.
  3. ConfigMap: Файли конфігурації визначають поведінку застосунку, але зазвичай не містять облікових даних. Вони призначені для того, щоб бути видимими й редагованими.
  4. Secret: SSH-ключі надають доступ до зовнішніх ресурсів. Їх розкриття могло б скомпрометувати вашу інфраструктуру або кодову базу.

У цій вправі є одна тонка пастка. Значення може починатися як нечутливе й стати чутливим у поєднанні з іншими значеннями. URL сервісу сам по собі може не бути конфіденційним, але URL із вбудованими обліковими даними чи ідентифікаторами орендарів належить до Secret’у, або ж його варто перепроєктувати. Переглядайте конфігурацію як фактичні дані, а не лише як назву ключа, що звучить безпечно.

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

Оновлення, дрейф і незмінна конфігурація

Розділ «Оновлення, дрейф і незмінна конфігурація»

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

Перше запитання — звідки Под отримує своє значення. Якщо значення надійшло з env або envFrom, Под має перезапуститися, перш ніж процес побачить нове значення. Якщо значення надійшло зі спроєктованого тому, змонтований файл може оновитися після того, як kubelet його оновить, але застосунок має перечитати файл. Якщо значення надійшло з аргументу команди, воно поводиться як конфігурація запуску й потребує перезапуску.

Terminal window
k get configmap app-config --namespace config-lab -o yaml
k get pod configured-demo --namespace config-lab -o jsonpath='{.spec.containers[0].env}'
k exec configured-demo --namespace config-lab -- sh -c 'echo "$DB_HOST"; cat /etc/config/LOG_LEVEL'

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

┌─────────────────────────────────────────────────────────────┐
│ IMMUTABLE CONFIGMAPS/SECRETS │
├─────────────────────────────────────────────────────────────┤
│ │
│ apiVersion: v1 │
│ kind: ConfigMap │
│ metadata: │
│ name: app-config │
│ immutable: true ← Cannot be changed after creation │
│ data: │
│ key: value │
│ │
│ Benefits: │
│ • Protects against accidental changes │
│ • Improves performance (no watches needed) │
│ • Reduces API server load │
│ │
│ To update: Delete and recreate with new name │
│ │
└─────────────────────────────────────────────────────────────┘

Незмінні ConfigMap’и та Secret’и змінюють операційну модель. Після створення з immutable: true об’єкт не можна оновити на місці. Щоб змінити дані, ви створюєте новий об’єкт, зазвичай із версійованою назвою, і оновлюєте робоче навантаження так, щоб воно посилалося на нього. Це звучить менш зручно, але робить розгортання явними й запобігає тому, щоб одне редагування потайки змінило багато запущених Под’ів.

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v2
namespace: config-lab
immutable: true
data:
DATABASE_HOST: mysql.default.svc
LOG_LEVEL: info
MAX_CONNECTIONS: "100"

Незмінні об’єкти особливо корисні в масштабі. Kubelet не потребує спостерігати за змінами незмінного ConfigMap’у чи Secret’у, що зменшує навантаження на API-сервер у великих кластерах. Вони також пасують до робочих процесів GitOps, бо нова назва створює видиму межу розгортання. Ви можете переглянути app-config-v2, розгорнути його, спостерігати за розгортанням і тримати app-config-v1 доступним, доки нові Под’и не стабілізуються.

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

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

Змінні середовища проти монтувань томів

Розділ «Змінні середовища проти монтувань томів»

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

АспектЗмінні середовищаМонтування томів
ОновленняПотребує перезапуску Под’аМожливе гаряче перезавантаження
ДоступСередовище процесуФайлова система
Сценарій використанняПрості пари «ключ-значення»Файли конфігурації
РозмірОбмеженийБільші файли допустимі

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

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

Є також корисний середній шлях: монтувати лише конкретні ключі за конкретними шляхами. Це уникає проєкції цілого ConfigMap’у чи Secret’у, коли контейнеру потрібен лише один файл. Це також дозволяє обирати назви файлів, що відповідають очікуванням застосунку. Будьте обережні з subPath, бо оновлення ConfigMap’ів і Secret’ів не поширюються через монтування subPath так само, як через звичайні спроєктовані томи.

apiVersion: v1
kind: Pod
metadata:
name: selected-key-demo
namespace: config-lab
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "cat /etc/app/app.properties; sleep 3600"]
volumeMounts:
- name: config-volume
mountPath: /etc/app
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: app.properties
path: app.properties

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

Важлива звичка — уникати конфігурації за принципом «один розмір для всіх». Деплоймент може споживати кілька ConfigMap’ів і Secret’ів через різні механізми. Це нормально, коли значення мають різну форму, чутливість і потреби в оновленні. Чітке іменування й невеликі об’єкти роблять це керованим, тоді як один гігантський об’єкт із кожним значенням створює непотрібний зв’язок між незв’язаними змінами.

Приклад операційного перегляду: випуск зміни конфігурації

Розділ «Приклад операційного перегляду: випуск зміни конфігурації»

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

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

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

Третій крок перегляду — радіус ураження (blast radius). ConfigMap, спільний для всіх реплік обробки замовлень, може змінити кожен Под, що посилається на нього, тоді як версійований ConfigMap, на який посилається нова ревізія Деплойменту, змінює лише Под’и в розгортанні. Для стейджингу латання змінного ConfigMap’у може бути прийнятним, бо простір імен невеликий, а відкат швидкий. Для продакшну версійований незмінний ConfigMap дає команді чіткішу подію розгортання, сильнішу історію переглядів і безпечніший шлях відкату.

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

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

Шостий крок перегляду — відкат. Якщо команда латає змінний ConfigMap на місці, відкат означає повторне латання й перезапуск будь-яких Под’ів, що захопили старі змінні середовища. Якщо команда використовує незмінний об’єкт app-config-v3, відкат означає оновлення посилання робочого навантаження назад на app-config-v2 і спостереження за розгортанням. Обидва методи можуть працювати, але другий дає релізному інструментарію видиміший перехід стану.

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

Коли ви переглядаєте справжній pull request, шукайте цей ланцюжок у маніфесті. ConfigMap має сказати вам, яка нечутлива поведінка змінюється. Посилання на Secret має сказати вам, що чутливий матеріал відокремлено й захищено. Специфікація Под’а має показати, чи надходить значення як змінна середовища, аргумент чи файл. План розгортання має пояснити, як запущені Под’и переходять від старого значення до нового.

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

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

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

Патерни конфігурації стають цінними, коли кілька команд спільно використовують кластер. Без домовленостей кожне робоче навантаження вигадує власні назви ключів, метод ротації секретів і поведінку перезапуску. Результат — не лише безладний YAML; це повільніше реагування на інциденти, бо відповідальні особи мусять заново з’ясовувати, як кожен сервіс отримує свої значення, перш ніж зможуть безпечно щось змінити.

ПатернКоли застосовуватиЧому працюєМіркування щодо масштабування
Один образ, конфігурація на рівні простору іменТой самий сервіс працює в dev, стейджингу та продакшніОбраз лишається переносним, тоді як кожен простір імен постачає власні значення ConfigMap і SecretТримайте назви об’єктів узгодженими між просторами імен, щоб маніфести лишалися легкими для просування
Явні посилання на ключі для важливих значеньКонтейнеру потрібно лише кілька ключів або внутрішні назвиРецензенти бачать саме те, які ключі стають вхідними даними часу виконанняУникайте envFrom для великих спільних об’єктів, бо випадкові ключі стають станом процесу
Проєкція файлів для файлів конфігурації та сертифікатівЗастосунки очікують структуровані файли чи матеріал TLSKubernetes подає кожен ключ як файл без перезбирання образуНавчіть застосунок перезавантажувати файли чи свідомо перезапускати Под’и під час змін
Версійовані незмінні об’єктиЗміни конфігурації слід випускати як кодНові назви об’єктів створюють явні межі розгортання й запобігають випадковій мутаціїДодайте практики очищення, щоб старі версійовані об’єкти не накопичувалися назавжди

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

Антипатерни зазвичай починаються як скорочення. Розробник кладе пароль у ConfigMap, бо демо має швидко запрацювати. Команда використовує envFrom, бо це економить рядки. Хтось латає спільний ConfigMap під час інциденту, не перевіривши, які Под’и його споживають. Кожен вибір може виглядати нешкідливим окремо, але кластери підсилюють скорочення, бо багато робочих навантажень можуть залежати від тієї самої форми об’єкта.

АнтипатернЩо йде не такКраща альтернатива
Секрети, що зберігаються в ConfigMap’ахЧутливі значення стають видимими для ролей, що можуть читати звичайну конфігураціюЗберігайте облікові дані в Secret’ах, обмежуйте RBAC та вмикайте шифрування на спокої
Один гігантський ConfigMap на простір іменНезв’язані зміни стають пов’язаними, а перегляд стає галасливимРозділяйте конфігурацію за застосунком чи операційним життєвим циклом
Припущення, що оновлення Secret означає оновлення застосункуЗапущені Под’и можуть зберігати старі змінні середовища чи кешовані файлиДокументуйте поведінку перезапуску чи перезавантаження для кожного споживача Secret’у
Монтування поверх заповнених каталогів образуСпроєктовані файли можуть приховати файли, вбудовані в образМонтуйте у виділений шлях або навмисно використовуйте конкретні шляхи ключів
Змінна спільна конфігурація для широких парківОдне латання може вплинути на багато Под’ів із малою видимістю розгортанняВикористовуйте незмінні версійовані об’єкти для стабільних значень у масштабі парку

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

Структура прийняття рішень

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

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

ЗапитанняОберіть цеОбґрунтування
Чи надасть розкриття доступ, розкриє приватний матеріал або послабить автентифікацію?SecretЧутливість зумовлює суворіший RBAC, шифрування сховища й правила поводження
Чи є значення нечутливою поведінкою застосунку, як-от прапорець, хост, тайм-аут чи файл конфігурації?ConfigMapДані належать поза образ, але не потребують поводження як із Secret’ом
Чи читає застосунок значення лише під час запуску?Змінна середовища або аргумент командиЗначення лише для запуску однаково потребують перезапуску, тож просте інжектування прийнятне
Чи очікує застосунок файл або підтримує перезавантаження з диска?Монтування томуПроєкція файлів відповідає контракту застосунку й може підтримувати робочі процеси перезавантаження
Чи мають зміни бути свідомими розгортаннями, а не редагуваннями на місці?Незмінний ConfigMap або Secret із версійованою назвоюНезмінність захищає стабільність і створює чітку історію розгортань
Чи потребує значення зовнішньої ротації або централізованого аудиту поза межами Kubernetes?Зовнішній робочий процес секретів плюс проєкція Secret’у KubernetesРідні об’єкти Secret — це точка доставки, а не повноцінний корпоративний менеджер секретів

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

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

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

  • Secret’и Kubernetes можна позначити незмінними, і ця функція є стабільною з Kubernetes 1.21, що робить її доступною в кластерах Kubernetes 1.35+ без церемонії з перемикачами функцій (feature gates).
  • Один об’єкт Secret обмежено одним мебібайтом даних, що утримує API-сервер і сховище-підкладку від перетворення на систему доставки файлів загального призначення.
  • stringData на практиці є зручністю лише для запису: ви подаєте звичайний текст через API, а Kubernetes зберігає отримані значення в закодованому полі data.
  • Спроєктовані томи ConfigMap і Secret оновлюються згодом через поведінку оновлення kubelet, але змінні середовища й аргументи команди фіксовані на весь час життя процесу контейнера.
ПомилкаЧому стаєтьсяЯк виправити
Зберігання секретів у ConfigMap’ахКоманди бачать обидва об’єкти як сховища «ключ-значення» й ігнорують відмінність у контролі доступуКладіть облікові дані в Secret’и, обмежуйте RBAC для Secret’ів і вмикайте шифрування на спокої для продакшн-кластерів
Думка, що base64 — це шифруванняЗакодовані значення Secret виглядають нечитабельними у виводі YAMLСтавтеся до base64 лише як до транспортного кодування й захищайте сам об’єкт Secret
Жорстке прописування конфігурації в образахПерше середовище працює, тож скорочення ховається до просуванняЗбирайте один образ та інжектуйте специфічні для середовища значення через ConfigMap’и та Secret’и
Оновлення ConfigMap’у з очікуванням, що змінні середовища змінятьсяОб’єкт API змінився, але середовище процесу було створене під час старту контейнераПерезапускайте Под’и або використовуйте змонтований файл плюс поведінку перезавантаження застосунку
Монтування ConfigMap’у поверх наявного каталогу застосункуСпроєктований том приховує файли, уже присутні за шляхом монтуванняМонтуйте у виділений каталог конфігурації або відображайте конкретні ключі на конкретні шляхи
Використання одного широкого envFrom для багатьох ключівЦе економить YAML, але кожен ключ стає середовищем процесуВикористовуйте явні записи env для важливих значень і тримайте спільні об’єкти невеликими
Лишання змінної спільної конфігурації для великих парківОдне редагування може здивувати багато Под’ів і створити навантаження спостереження APIВикористовуйте незмінні версійовані ConfigMap’и чи Secret’и для стабільної конфігурації в масштабі парку
Припущення, що рідні Secret’и — це повноцінна програма керування секретамиKubernetes доставляє значення, але сам не визначає володіння ротацією чи зовнішній аудитПоєднуйте Secret’и з RBAC, шифруванням, логами аудиту, процедурами ротації та зовнішніми менеджерами за потреби
Ваша команда оновлює ключ ConfigMap'у з `LOG_LEVEL=debug` на `LOG_LEVEL=info`, але запущений Под усе ще друкує логи рівня debug. Деплоймент інжектує ключ як змінну середовища. Що слід перевірити і яку дію вжити?

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

Платіжний сервіс зберігає токен API в ConfigMap'і, бо токен — це просто рядок. Під час аудиту оператори з доступом до читання ConfigMap'ів можуть його переглянути. Яку зміну дизайну слід зробити?

Перенесіть токен у Secret, бо розкриття надало б доступ до зовнішньої фінансової системи. Те, що Secret’и за замовчуванням використовують кодування base64, не робить їх зашифрованими, але дозволяє кластеру застосовувати інші практики RBAC, шифрування на спокої, аудиту й поводження. Перегляньте, які ролі можуть читати Secret’и, і уникайте фіксації звичайних значень токенів у Git. Це порівнює ConfigMap’и та Secret’и за чутливістю й операційним ризиком.

Застосунок із підтримкою TLS очікує файли сертифікатів у `/etc/tls` і може перезавантажувати їх з диска. Команда наразі передає текст сертифіката через змінні середовища. Який метод споживання кращий і чому?

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

Платформенна команда підтримує сотні Под'ів, які всі посилаються на той самий рідко змінюваний файл конфігурації. Випадкові редагування спричиняли збої. Яка функція Kubernetes допомагає і яку модель розгортання слід використовувати?

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

Розробник монтує ConfigMap у `/etc/app`, і застосунок раптом не може знайти файли, вбудовані в образ за цим каталогом. Що сталося?

Спроєктований том, змонтований у /etc/app, приховав файли, що вже були присутні в образі контейнера за тим самим шляхом. Kubernetes не видалив файли образу, але монтування змінило те, що процес бачить за цим каталогом. Змонтуйте ConfigMap у виділений каталог або спроєктуйте конкретні ключі на конкретні шляхи, що не перекривають наявні ресурси застосунку. Це проблема дизайну тому, а не даних ConfigMap’у.

Ви запускаєте той самий образ контейнера в dev, стейджингу та продакшні, але кожне середовище потребує іншого хоста бази даних. Як спроєктувати конфігурацію Kubernetes?

Створіть ConfigMap’и на рівні простору імен з однаковим очікуваним ключем, як-от DATABASE_HOST, і нехай кожне середовище постачає власне значення. Деплоймент може посилатися на ту саму назву об’єкта й ключ у кожному просторі імен, тоді як образ лишається незмінним. Це дотримується моделі «зібрати один раз, конфігурувати на середовище» і тримає відмінності середовищ поза образом контейнера. Якщо також потрібні облікові дані, кладіть їх у Secret’и на рівні простору імен.

Secret було ротовано в кластері, але деякі Под'и все ще автентифікуються старим паролем, тоді як нові Под'и використовують новий. Яка ймовірна причина?

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

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

Terminal window
alias k=kubectl
k create namespace config-lab

Завдання 1: Створення об’єктів конфігурації

Розділ «Завдання 1: Створення об’єктів конфігурації»

Створіть ConfigMap для налаштувань застосунку та Secret для облікових даних. Значення навмисно є простими прикладами, а не продакшн-обліковими даними. Зверніть увагу, що ConfigMap містить налаштування поведінки, тоді як Secret містить матеріал автентифікації.

Terminal window
k create configmap app-config \
--namespace config-lab \
--from-literal=DATABASE_HOST=mysql.default.svc \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
k create secret generic db-credentials \
--namespace config-lab \
--from-literal=username=admin \
--from-literal=password='example-password-only'
Нотатки до розв'язання Завдання 1

ConfigMap має з’явитися з трьома ключами під data, а Secret має з’явитися із закодованими значеннями під data. Використовуйте k describe secret db-credentials --namespace config-lab, щоб підтвердити ключі без друку значень. Використовуйте k get configmap app-config --namespace config-lab -o yaml, щоб підтвердити нечутливі налаштування безпосередньо.

Завдання 2: Споживання ConfigMap’у як змінної середовища та тому

Розділ «Завдання 2: Споживання ConfigMap’у як змінної середовища та тому»

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

apiVersion: v1
kind: Pod
metadata:
name: config-consumer
namespace: config-lab
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "while true; do echo env=$DB_HOST file=$(cat /etc/config/LOG_LEVEL); sleep 10; done"]
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
volumeMounts:
- name: config-files
mountPath: /etc/config
readOnly: true
volumes:
- name: config-files
configMap:
name: app-config
Terminal window
k apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: config-consumer
namespace: config-lab
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "while true; do echo env=$DB_HOST file=$(cat /etc/config/LOG_LEVEL); sleep 10; done"]
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
volumeMounts:
- name: config-files
mountPath: /etc/config
readOnly: true
volumes:
- name: config-files
configMap:
name: app-config
EOF
k logs config-consumer --namespace config-lab --tail=5
Нотатки до розв'язання Завдання 2

Логи мають показати хост бази даних зі змінної середовища й рівень логування зі змонтованого файлу. Якщо Под усе ще в стані pending, використовуйте k describe pod config-consumer --namespace config-lab, щоб перевірити завантаження образів і події планування. Важливе спостереження полягає в тому, що Под має два окремі шляхи від того самого ConfigMap’у в контейнер.

Завдання 3: Оновлення ConfigMap’у та спостереження за дрейфом

Розділ «Завдання 3: Оновлення ConfigMap’у та спостереження за дрейфом»

Залатайте ConfigMap і спостерігайте за логами Под’а. Значення файлу може оновитися після того, як kubelet оновить спроєктований том, тоді як змінна середовища лишається тією самою, доки Под не перезапуститься.

Terminal window
k patch configmap app-config \
--namespace config-lab \
--type merge \
-p '{"data":{"DATABASE_HOST":"mysql-new.default.svc","LOG_LEVEL":"warn","MAX_CONNECTIONS":"100"}}'
k logs config-consumer --namespace config-lab --tail=10
Нотатки до розв'язання Завдання 3

Слід очікувати, що значення середовища лишиться mysql.default.svc у запущеному процесі. Значення файлу згодом може стати warn, але точний час залежить від поведінки оновлення kubelet. Це патерн дрейфу, який ви маєте діагностувати в продакшні: об’єкт API, спроєктований файл і середовище процесу можуть розходитися з вагомих причин.

Завдання 4: Монтування Secret’у як файлів лише для читання

Розділ «Завдання 4: Монтування Secret’у як файлів лише для читання»

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

Terminal window
k apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: secret-consumer
namespace: config-lab
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "ls -l /etc/secrets; sleep 3600"]
volumeMounts:
- name: secret-files
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-files
secret:
secretName: db-credentials
EOF
k logs secret-consumer --namespace config-lab
Нотатки до розв'язання Завдання 4

Логи мають перелічити файли з назвами username і password під /etc/secrets. Под не повинен потребувати друку значень Secret у логах для цієї перевірки. У реальних системах уникайте логування вмісту секретів навіть під час усунення несправностей, бо логи часто мають ширше зберігання й доступ, ніж сам об’єкт Secret.

Завдання 5: Створення незмінного версійованого ConfigMap’у

Розділ «Завдання 5: Створення незмінного версійованого ConfigMap’у»

Створіть незмінний ConfigMap із версійованою назвою та проінспектуйте його. Потім спробуйте залатати його, щоб побачити режим збою безпосередньо.

Terminal window
k apply -f - <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v2
namespace: config-lab
immutable: true
data:
DATABASE_HOST: mysql.default.svc
LOG_LEVEL: info
MAX_CONNECTIONS: "100"
EOF
k patch configmap app-config-v2 \
--namespace config-lab \
--type merge \
-p '{"data":{"LOG_LEVEL":"debug"}}'
Нотатки до розв'язання Завдання 5

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

Завдання 6: Очищення

Розділ «Завдання 6: Очищення»

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

Terminal window
k delete namespace config-lab
Нотатки до розв'язання Завдання 6

Видалення простору імен прибирає ConfigMap’и, Secret’и та Под’и, створені лабораторною роботою. Якщо видалення триває деякий час, використовуйте k get namespace config-lab, щоб спостерігати за поступом. Уникайте повторного використання лабораторних просторів імен для пізніших модулів, якщо ви навмисно не хочете залишкового стану.

Критерії успіху:

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

Далі перейдіть до Частини 3: Хмарна архітектура, щоб пов’язати ці механіки робочих навантажень із принципами хмарного дизайну та екосистемою CNCF.