Цей розділ документації Kubernetes містить посібники. Посібник показує, як досягти мети, яка більша за одне завдання. Зазвичай посібник має кілька розділів, кожен з яких має послідовність кроків. Перед тим, як пройти кожен посібник, ви можете додати сторінку глосарія до закладок для подальших посилань.
Основи
Основи Kubernetes — це поглиблений інтерактивний посібник, який допомагає зрозуміти систему Kubernetes та спробувати деякі основні функції Kubernetes.
Якщо ви хочете написати посібник, див. Типи сторінок для отримання інформації про типи сторінок посібника.
1 - Привіт Minikube
Цей посібник покаже вам, як запустити простий кластер Kubernetes використовуючи minikube. Посібник надає образ контейнера, який використовує NGINX для відклику на всі запити.
Цілі
Розгортання тестового застосунку в minikube.
Запуск застосунку.
Перегляд логів застосунку.
Перш ніж ви розпочнете
Цей застосунок передбачає, що у вас вже є встановлений minikube. Дивіться Крок 1 в minikube start для інструкцій щодо встановлення.
Примітка:
Виконайте лише інструкції з Кроку 1, Встановлення. Решту розглянуто на цій сторінці.
Вам також потрібно встановити kubectl. Дивіться Встановлення інструментів для інструкцій щодо встановлення.
Створення кластера minikube
minikube start
Відкрийте інформаційну панель
Відкрийте інформаційну панель Kubernetes. Це можна зробити двома різними способами:
# Запустіть новий термінал та залиште його працювати.minikube dashboard
Тепер поверніться до термінала, де ви запустили minikube start.
Примітка:
Команда dashboard вмикає додаток інформаційної панелі та відкриває проксі для типового системного вебоглядача. Ви можете створювати ресурси Kubernetes на інформаційній панелі, такі як Deployment та Service.
Щоб дізнатися, як уникнути прямого запуску вебоглядача з термінала та отримати URL-адресу для вебінтерфейсу, дивіться вкладку «Копіювання та вставлення URL-адреси».
Стандартно, інформаційна панель доступна лише з внутрішньої віртуальної мережі Kubernetes. Команда dashboard створює тимчасовий проксі, щоб інформаційна панель була доступна за межами віртуальної мережі Kubernetes.
Щоб зупинити проксі, натисніть Ctrl+C, щоб вийти з процесу. Після виходу з команди інформаційна панель залишається запущеною в кластері Kubernetes. Ви можете знову запустити команду dashboard, щоб створити інший проксі для доступу до інформаційної панелі.
Якщо ви не хочете, щоб minikube відкривав вебоглядач для вас, запустіть підкоманду dashboard з прапорцем --url. minikube виводить URL-адресу, яку ви можете відкрити у вибраному вами вебоглядачі.
Відкрийте новий термінал та виконайте команду:
# Запустіть новий термінал та залиште його працювати.minikube dashboard --url
Тепер поверніться до термінала, де ви запустили minikube start.
Створення Deployment
Pod в Kubernetes — це група з одного або більше контейнерів, які повʼязуються один з одним для керування та використання мережевих ресурсів. Pod в цьому посібнику містить тільки один контейнер. Kubernetes Deployment перевіряє життєздатність вашого Podʼа та, якщо він виходить з ладу, перезапускає його. Deployment є рекомендованим способом створення та масштабування Podʼів.
Скористайтесь командою kubectl create для створення Deployment, що буде керувати Podʼом. Pod виконує контейнер, який міститься в образі Docker.
# Запустіть тестовий образ контейнера, який містить вебсерверkubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.39 -- /agnhost netexec --http-port=8080
Перевірте, чи створено Deployment.
kubectl get deployments
Ви маєте отримати вивід, подібний до такого:
NAME READY UP-TO-DATE AVAILABLE AGE
hello-node 1/1 1 1 1m
(Зачекайте деякий час, поки Pod стане доступним. Якщо ви бачите "0/1", спробуйте ще раз через кілька секунд.)
Перевірте, чи створено Pod.
kubectl get pods
Ви маєте отримати вивід, подібний до такого:
NAME READY STATUS RESTARTS AGE
hello-node--5f76cf6ccf-br9b5 1/1 Running 0 1m
Перегляд подій кластера:
kubectl get events
Перегляд конфігурації kubectl:
kubectl config view
Перегляд логів застосунку з контейнера в Podʼі (замініть назву Podʼа на ту, яку ви отримали з kubectl get pods).
Примітка:
Замініть hello-node-5f76cf6ccf-br9b5 у команді kubectl logs на назву Podʼа, яку ви отримали з виводу kubectl get pods.
kubectl logs hello-node-5f76cf6ccf-br9b5
Вивід має бути подібним до такого:
I0911 09:19:26.677397 1 log.go:195] Started HTTP server on port 8080
I0911 09:19:26.677586 1 log.go:195] Started UDP server on port 8081
Примітка:
Для ознайомлення з додатковими командами kubectl дивіться Команди kubectl.
Створення Service
Стандартно, Pod доступний лише за його внутрішньою IP-адресою в межах Kubernetes-кластера. Щоб зробити контейнер hello-node доступним назовні віртуальної мережі Kubernetes, вам потрібно подати Pod як Service Kubernetes.
Попередження:
Контейнер agnhost має точку доступу /shell, яка корисна для налагодження, але небезпечний для публічного доступу з інтернету. Не запускайте його в кластері, що має вихід до інтернету, або на операційному кластері.
Скористайтесь командою kubectl expose для експонування Podʼа:
Прапорець --type=LoadBalancer вказує, що ви хочете надати доступ до вашого Serviceʼу назовні кластера.
Код застосунку всередині тестового образу контейнера тільки прослуховує порт 8080. Якщо ви використовуєте інший порт в kubectl expose, клієнти не зможуть отримати доступ до вашого застосунку.
Перевірте, чи створено Service:
kubectl get services
Ви маєте отримати вивід, подібний до такого:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-node LoadBalancer 10.108.144.78 <pending> 8080:30369/TCP 21s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23m
Хмарні провайдери, які підтримують балансувальники навантаження, зовнішні IP-адреси можуть надаватись для доступу до Serviceʼу. В minikube LoadBalancer створює Service, доступний через команду minikube service.
Виконайте наступну команду:
minikube service hello-node
Це відкриє вікно вебоглядача, що показує відповідь застосунку.
Увімкнення надбудов
Інструменти minikube містять набір вбудованих надбудов, які можна увімкнути, вимкнути та відкрити в локальному оточені Kubernetes.
NAME CPU(cores) MEMORY(bytes)
hello-node-ccf4b9788-4jn97 1m 6Mi
Якщо ви бачите наступне повідомлення, почекайте та спробуйте ще раз:
error: Metrics API not available
Вимкніть metrics-server:
metrics-server was successfully disabled
Видалення кластера minikube
Тепер ви можете видалити ресурси, які ви створили в кластері:
kubectl delete service hello-node
kubectl delete deployment hello-node
Зупиніть кластер minikube:
minikube stop
Необовʼязково, ви можете видалити кластер minikube:
minikube delete
Якщо ви бажаєте використовувати minikube знову для продовження вивчення Kubernetes, ви можете не видаляти його.
Підсумки
Ця сторінка містить базові аспекти використання minikube для розгортання простого кластера Kubernetes та запуску тестового застосунку. Тепер ви готові до розгортання власних застосунків.
Цей посібник надає інструкції з основ системи оркестрування кластерів Kubernetes. Кожний модуль містить деяку вступну інформацію про основні функції та концепції Kubernetes, а також практичний посібник, за яким ви можете вчитися.
За допомогою цих посібників ви дізнаєтесь:
як розгорнути контейнеризований застосунок в кластері.
як масштабувати Deployment.
як розгорнути нову версію контейнеризованого застосунку.
як налагодити контейнеризований застосунок.
Чим Kubernetes може бути корисний для вас?
З сучасними вебсервісами користувачі очікують доступності застосунків 24/7, а розробники прагнуть розгортати нові версії цих застосунків кілька разів на день. Контейнеризація допомагає упаковувати програмне забезпечення для досягнення цих цілей, дозволяючи випускати оновлення застосунків без перерви в роботі. Kubernetes допомагає забезпечити те, що контейнеризовані застосунки працюватимуть там і тоді, де це потрібно, та надає їм необхідні ресурси та інструменти для ефективної роботи. Kubernetes — це готова до використання, відкрита платформа, розроблена на основі здобутого Google досвіду в оркеструванні контейнерів, поєднаного з найкращими ідеями та практиками спільноти.
Дізнайтесь про кластери Kubernetes та створіть простий кластер за допомогою Minikube.
2.1.1 - Використання Minikube для створення кластера
Дізнайтесь, що таке Kubernetes кластер.
Дізнайтесь, що таке Minikube.
Запустіть кластер Kubernetes.
Мета
Дізнатись, що таке кластер Kubernetes.
Дізнатись, що таке Minikube.
Запустити Kubernetes кластер на вашому компʼютері.
Kubernetes кластери
Kubernetes організовує високодоступний кластер компʼютерів, які взаємодіють, працюючи як єдиний блок. Абстракції в Kubernetes дозволяють розгортати контейнеризовані застосунки в кластері, не привʼязуючи їх специфічно до окремих машин. Щоб скористатися цією новою моделлю розгортання, застосунки повинні бути упаковані таким чином, що відділяє їх від конкретних хостів: вони повинні бути контейнеризовані. Контейнеризовані застосунки є більш гнучкими та доступними, ніж у минулих моделях розгортання, коли застосунки встановлювалися безпосередньо на конкретні машини як пакунки, глибоко інтегровані в хост. Kubernetes автоматизує розподіл та планування контейнерів застосунків у кластері більш ефективним способом. Kubernetes є платформою з відкритим кодом і готовою до операційного використання.
Кластер Kubernetes складається з двох типів ресурсів:
Панелі управління (Control Plane), що координує роботу кластера
Вузлів (Nodes) — робочіх машин, на яких запущені застосунки
Зміст:
Кластер Kubernetes
Minikube
Kubernetes — це платформа з відкритим кодом промислового класу, яка оркеструє розташування (планування) та виконання контейнерів застосунків в межах та поміж компʼютерними кластерами.
Схема кластера
Панель управління (Control Plane) відповідає за керування кластером. Вона координує всі процеси у вашому кластері, такі як запуск застосунків, підтримка їх бажаного стану, масштабування застосунків та розгортання оновлень.
Вузол (Node) — це ВМ або фізичний компʼютер, що виступає у ролі робочої машини в кластері Kubernetes. Кожен вузол має kubelet — агента для управління вузлом та обміну даними з панеллю управління Kubernetes. Також на вузлі мають бути встановлені інструменти для виконання операцій з контейнерами, такі як containerd або CRI-O. Кластер Kubernetes, що обслуговує операційний трафік має складатися як мінімум із трьох вузлів. Якщо один вузол вийде з ладу обидва члени etcd та панель управління будуть втрачені, а надмірність буде порушена. Ви можете зменшити ризик додаванням більше одного вузла з панеллю управління.
Панелі управління керують кластером, а вузли призначені для запуску застосунків.
Коли ви розгортаєте застосунки у Kubernetes, ви наказуєте панелі управління запустити контейнери застосунку. Панель управління розподіляє (планує) контейнери для запуску на вузлах кластера. Компоненти на рівні вузла, такі, як kubelet, спілкуються з панеллю управління за допомогою API Kubernetes, який надається панеллю управління. Кінцеві користувачі також можуть використовувати API Kubernetes для взаємодії з кластером.
Кластер Kubernetes можна розгорнути як на фізичних, так й на віртуальних машинах. Щоб розпочати розробку під Kubernetes, ви можете скористатися Minikube — спрощеною реалізацією Kubernetes. Minikube створює на вашому локальному компʼютері ВМ, на якій розгортає простий кластер з одного вузла. Існують версії Minikube для операційних систем Linux, macOS та Windows. Minikube CLI надає основні операції для роботи з вашим кластером, такі як start, stop, status та delete.
Тепер ви знаєте, що таке Kubernetes. Тож відвідаємо сторінку Привіт Minikube та спробуємо його на вашому компʼютері.
2.2 - Розгортання застосунку
2.2.1 - Використання kubectl для створення Deploymentʼа
Дізнайтесь, що таке Deployment застосунків.
Розгорніть свій перший застосунок у Kubernetes за допомогою kubectl.
Мета
Дізнатися, що таке Deployment застосунків.
Розгорнути свій перший застосунок у Kubernetes за допомогою kubectl.
Процеси Kubernetes Deployment
Примітка:
Цей навчальний посібник використовує контейнер, який вимагає архітектури AMD64. Якщо ви використовуєте minikube на компʼютері з іншою архітектурою процесора, ви можете спробувати використовувати minikube з драйвером, який може емулювати AMD64. Наприклад, драйвер Docker Desktop може це зробити.
Після створення працюючого кластера Kubernetes ви можете розгортати свої контейнеризовані застосунки на його основі. Для цього створіть розгортання (Deployment) Kubernetes. Deployment вказує Kubernetes, як створювати та оновлювати екземпляри вашого застосунку. Після створення Deployment панель управління Kubernetes розподіляє екземпляри застосунків, що включені в цей Deployment, для запуску на окремих вузлах в кластері.
Коли екземпляри застосунків створені, контролер розгортання Kubernetes безперервно відстежує їх стан. Якщо вузол, на якому запущено екземпляр, вимикається або видаляється, контролер розгортання замінює екземпляр новим на іншому вузлі в кластері. Це забезпечує механізм самовідновлення для розвʼязання проблем з відмовою або обслуговуванням вузлів.
У світі до-оркестрування часто використовувалися скрипти встановлення для запуску застосунків, але вони не дозволяли відновлення після відмови вузла. Шляхом як створення екземплярів застосунків, так і утримання їх в робочому стані на різних вузлах, розгортання Kubernetes забезпечує фундаментально відмінний підхід до управління застосунками.
Зміст:
Deployment'и
Kubectl
Deployment відповідає за створення та оновлення Podʼів для вашого застосунку
Розгортання вашого першого застосунку в Kubernetes
Ви можете створювати та управляти розгортанням (Deployment) за допомогою інтерфейсу командного рядка Kubernetes, kubectl. Kubectl використовує API Kubernetes для взаємодії з кластером. У цьому модулі ви вивчите найпоширеніші команди kubectl, які потрібні для створення розгортань та запуску застосунків в кластері Kubernetes.
Коли ви створюєте Deployment, вам необхідно вказати образ контейнера вашого застосунку та скільки його реплік ви бажаєте запустити. Згодом цю інформацію можна змінити, оновивши Deployment. В навчальних модулях 5 і 6 йдеться про те, як масштабувати і оновлювати Deployment'и.
Для того, щоб розгортати застосунки в Kubernetes, їх потрібно упакувати в один із підтримуваних форматів контейнерів
Для вашого першого розгортання ви будете використовувати застосунок hello-node, який упакований в Docker-контейнер і використовує NGINX для відгуку на всі запити. (Якщо ви ще не пробували створити застосунок hello-node та розгортати його за допомогою контейнера, ви можете це зробити, слідуючи інструкціям з посібника Привіт Minikube).
Вам також потрібно мати `kubectl`. Якщо ви ще не встановили його, відвідайте встановлення інструментів.
Тепер ви знаєте, що таке Deployment. Тож розгорнемо ваш перший застосунок!
Основи kubectl
Загальний формат команди kubectl: kubectl дія ресурс
Тут виконується вказана дія (наприклад, create, describe або delete) для вказаного ресурсу (наприклад, node або deployment). Ви можете використовувати --help після команд для отримання додаткової інформації про можливі параметри (наприклад, kubectl get nodes --help).
Перевірте, що kubectl налаштовано для роботи з вашим кластером, виконавши команду kubectl version.
Перевірте, що kubectl встановлено і ви можете бачити як версію клієнта, так і версію сервера.
Щоб переглянути вузли в кластері, виконайте команду kubectl get nodes.
Ви побачите доступні вузли. Пізніше Kubernetes вибере, де розгорнути наш застосунок на основі ресурсів, доступних на вузлах.
Розгортання застосунку
Розгорнімо наш перший застосунок на Kubernetes за допомогою команди kubectl create deployment. Ми повинні вказати назву розгортання та розташування образу застосунку (вкажіть повну URL-адресу репозиторію образів, розміщених за межами Docker Hub).
Відмінно! Ви щойно розгорнули свій перший застосунок, створивши Deployment. Це призвело до виконання для вас кількох дій:
пошук відповідного вузла, де може бути запущений екземпляр застосунку (у нас доступний лише 1 вузол)
планування запуску застосунку на цьому вузлі
налаштування кластера для перепланування екземпляра на новому вузлі за необхідності
Щоб переглянути ваші розгортання, використовуйте команду kubectl get deployments:
kubectl get deployments
Ми бачимо, що є 1 розгортання, яке запускає один екземпляр вашого застосунку. Екземпляр працює в контейнері на вашому вузлі.
Перегляд застосунку
Podʼи, які працюють всередині Kubernetes, запущені в ізольованій приватній мережі. Типово вони видимі з інших Podʼів та служб всередині того ж самого кластера Kubernetes, але не за межами цієї мережі. Коли ми використовуємо kubectl, ми взаємодіємо через точку доступу API для спілкування з нашим застосунком.
Розглянемо інші варіанти того, як вивести наш застосунок за межі кластера Kubernetes пізніше, в Модулі 4. Також, як у базовому посібнику, тут ми не пояснюємо докладно, що таке Podʼи, це буде розглянуто в подальших темах.
Команда kubectl proxy може створити проксі, який буде пересилати дані в мережу, що охоплює весь кластер. Роботу проксі можна завершити, натиснувши control-C, і він не виводитиме жодного результату під час роботи.
Вам потрібно відкрити друге вікно термінала для запуску проксі.
kubectl proxy
Тепер у нас є зʼєднання між вашим хостом (терміналом) та кластером Kubernetes. Проксі дозволяє безпосередній доступ до API з цих терміналів.
Ви можете побачити всі ці API, розміщені через проксі-endpoint. Наприклад, ми можемо запитати версію напряму через API, використовуючи команду curl:
curl http://localhost:8001/version
Примітка: Якщо порт 8001 недоступний, переконайтеся, що команда kubectl proxy, яку ви запустили вище, працює в другому терміналі.
Сервер API автоматично створює точку доступу для кожного Podʼа на основі імені Podʼа, яка також доступна через проксі.
Спочатку нам потрібно отримати імʼя Podʼа, і ми збережемо його в змінну оточення POD_NAME:
export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}') echo Name of the Pod: $POD_NAME
Ви можете отримати доступ до Podʼа через проксі-API, запустивши:
Дізнайтесь, як розвʼязувати проблеми з розгорнутими застосунками,
використовуючи kubectl get, kubectl describe, kubectl logs та kubectl exec.
Мета
Дізнатися, що таке Podʼи Kubernetes.
Дізнатися, що таке вузли Kubernetes.
Діагностика розгорнутих застосунків.
Podʼи Kubernetes
Коли ви створили Deployment у модулі 2, Kubernetes створив Pod, щоб розмістити ваш застосунок. Pod — це абстракція в Kubernetes, що являє собою групу з одного або декількох контейнерів застосунку (таких як Docker) та ресурсів, спільних для цих контейнерів. До цих ресурсів належать:
Спільні сховища даних, або Volumes
Мережа, адже кожен Pod у кластері має унікальну IP-адресу
Інформація про запуск кожного контейнера, така як версія образу контейнера або використання певних портів
Pod моделює специфічний для даного застосунку "логічний хост" та може містити різні, але доволі щільно звʼязані один з одним контейнери. Наприклад, в одному Podʼі може бути контейнер з вашим Node.js застосунком та інший контейнер, що передає дані для публікації Node.js вебсерверу. Контейнери в межах Podʼа мають спільну IP-адресу та порти, завжди є сполученими, плануються для запуску разом та запускаються у спільному контексті на одному вузлі.
Pod є неподільною одиницею платформи Kubernetes. Коли ви створюєте Deployment у Kubernetes, цей Deployment створює Podʼи вже з контейнерами всередині, на відміну від створення контейнерів окремо. Кожен Pod привʼязаний до вузла, до якого його було розподілено, і лишається на ньому до припинення роботи (згідно з політикою перезапуску) або видалення. У разі відмови вузла ідентичні Podʼи розподіляються по інших доступних вузлах кластера.
Зміст:
Podʼи
Вузли
Основні команди kubectl
Pod — це група з одного або декількох контейнерів (таких як Docker), що має спільне сховище даних (volumes), унікальну IP-адресу і містить інформацію про те, як їх запустити.
Узагальнена схема Podʼів
Вузли
Pod завжди запускається на вузлі. Вузол — це робоча машина в Kubernetes, віртуальна або фізична, залежно від кластера. Функціонування кожного вузла контролюється панеллю управління. Вузол може мати декілька Podʼів, а панель управління автоматично призначає Podʼи вузлам в кластері. Панель управління Kubernetes автоматично розподіляє Podʼи по вузлах кластера з урахуванням ресурсів, наявних на кожному вузлі.
На кожному вузлі Kubernetes запущені як мінімум:
Kubelet — процес, що забезпечує обмін даними між панеллю управління Kubernetes та робочим вузлом; kubelet керує Podʼами та контейнерами, запущеним на машині.
Оточення для запуску контейнерів (таке як Docker) забезпечує завантаження образу контейнера з реєстру, розпакування контейнера та запуск застосунку.
Контейнери повинні бути разом в одному Podʼі, лише якщо вони щільно звʼязані і мають спільні ресурси, такі як диск.
Узагальнена схема вузлів
Виправлення проблем за допомогою kubectl
У модулі 2 ви використовували інтерфейс командного рядка kubectl. Ви будете продовжувати його використовувати в модулі 3 для отримання інформації про розгорнуті застосунки та їхні середовища. За допомогою наступних підкоманд kubectl можна виконати найбільш поширені операції:
kubectl get — показати перелік ресурсів
kubectl describe — виведення детальної інформації про ресурс
kubectl logs — виведення логів контейнера в Podʼі
kubectl exec — виконання команди в контейнері в Podʼі
Ви можете використовувати ці команди, щоб переглядати інформацію про те, коли були розгорнуті застосунки, який їхній поточний статус, де вони запущені та які їхні конфігурації.
Тепер, коли ми більше знаємо про компоненти нашого кластера та командний рядок, дослідімо наш застосунок.
Вузол — це робоча машина в Kubernetes і може бути віртуальною машиною або фізичною машиною залежно від кластера. На одному вузлі може працювати кілька Podʼів.
Перевірка конфігурації застосунку
Перевірмо, чи працює застосунок, який ми розгорнули в попередньому сценарії. Ми використаємо команду kubectl get і подивимося на наявні Podʼи:
kubectl get pods
Якщо Podʼи не запущені, зачекайте кілька секунд і знову спробуйте вивести список Podʼів. Ви можете продовжити, якщо ви бачите, що працює один Pod.
Далі, щоб переглянути, які контейнери є всередині цього Podʼа і які образи використовуються для створення цих контейнерів, ми використовуємо команду kubectl describe pods:
kubectl describe pods
Тут ми бачимо деталі про контейнер Podʼа: IP-адресу, порти та список подій, повʼязаних з життєвим циклом Podʼа.
Вивід підкоманди describe є розлогим і охоплює деякі концепції, які ми ще не пояснили, але не хвилюйтеся, вони стануть зрозумілими до кінця цього курсу.
Примітка: підкоманду describe можна використовувати для отримання детальної інформації про більшість примітивів Kubernetes, включаючи вузли, Podʼи та Deployment. Вивід команди describe призначений для сприйняття людиною, а не для сценаріїв.
Показати застосунок у терміналі
Пригадайте, що Podʼи працюють в ізольованій, приватній мережі — тому нам потрібно налаштувати проксі-доступ до них для налагодження та взаємодії з ними. Для цього ми використовуємо команду kubectl proxy для запуску проксі в іншому терміналі. Відкрийте нове вікно термінала і введіть у цьому новому терміналі:
kubectl proxy
Тепер ми знову отримаємо імʼя Podʼа та будемо звертатись до цього Podʼа безпосередньо через проксі. Щоб отримати імʼя Podʼа та зберегти його в змінну середовища POD_NAME:
export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')" echo Name of the Pod: $POD_NAME
Щоб переглянути вивід нашого застосунку, виконайте запит curl:
Все, що застосунок зазвичай виводить на стандартний вивід, стає логами контейнера всередині Podʼа. Ми можемо отримати ці логи, використовуючи команду kubectl logs:
kubectl logs "$POD_NAME"
Примітка: Нам не потрібно вказувати імʼя контейнера, оскільки у нас є лише один контейнер всередині Podʼа.
Виконання команди в контейнері
Ми можемо виконувати команди безпосередньо в контейнері після того, як Pod буде запущено та він працюватиме. Для цього ми використовуємо підкоманду exec і вказуємо імʼя Podʼа як параметр. Перегляньмо змінні середовища:
kubectl exec "$POD_NAME" -- env
Знову варто зазначити, що можна пропустити імʼя самого контейнера, оскільки у нас є лише один контейнер в Podʼі.
Далі розпочнім сеанс bash в контейнері Podʼа:
kubectl exec -ti $POD_NAME -- bash
Тепер у нас є відкрита консоль в контейнері, де запущений наш застосунок NodeJS. Вихідний код застосунку знаходиться у файлі server.js:
cat server.js
Ви можете перевірити, чи застосунок запущено, виконавши команду curl:
curl http://localhost:8080
Примітка: тут ми використовували localhost, оскільки ми виконали команду всередині Podʼа NodeJS. Якщо ви не можете підʼєднатися до localhost:8080, перевірте, чи ви виконали команду kubectl exec і запускаєте команду зсередини Podʼа
Щоб закрити підключення до контейнера, введіть exit.
2.4.1 - Використання Service для доступу до застосунку
Дізнайтесь про Service у Kubernetes.
Ознайомтесь з тим, як мітки та селектори повʼязані з Service.
Відкрийте доступ до застосунку по за межами Kubernetes кластера.
Мета
Дізнатись про Service у Kubernetes.
Ознайомитись з тим, як мітки та селектори повʼязані з Service.
Відкрити доступ до застосунку по за межами Kubernetes кластера.
Огляд Сервісів в Kubernetes
Існування Podʼів в Kubernetes є обмеженими за часом, також вони мають свій життєвий цикл. Коли робочий вузол припиняє існування, також втрачаються Podʼи, які виконуються на цьому вузлі. ReplicaSet може динамічно приводити кластер до бажаного стану шляхом створення нових Podʼів, щоб ваш застосунок продовжував працювати. Наприклад, розгляньмо обробник зображень, що має 3 копії. Ці копії (репліки) можуть бути взаємозамінні; система форонтенду не повинна перейматися репліками бекенду або навіть тим, чи Pod втрачено та створено наново. Проте кожен Pod в кластері Kubernetes має унікальну IP-адресу, навіть Podʼи на одному вузлі, тому потрібно мати спосіб автоматичного узгодження змін серед Podʼів, щоб ваші застосунки продовжували працювати.
Service в Kubernetes є абстракцією, яка визначає логічний набір Podʼів та політику доступу до них. Serviceʼи забезпечують слабку звʼязаність між залежними Podʼами. Service визначається за допомогою YAML або JSON, так само як і всі обʼєкти Kubernetes. Набір Podʼів, на які призначений Service, зазвичай визначається селектором міток (label selector) (див. нижче, чому selector іноді не включають у специфікацію Service).
Хоча кожний Pod має унікальну IP-адресу, ці IP не доступні за межі кластера без використання Service. Serviceʼи уможливлюють надходження трафіку до ваших застосунків. Serviceʼи можуть бути оприлюднені у різний спосіб, за допомогою type у spec Service:
ClusterIP (типове значення) — відкриває доступ до внутрішнього IP Service в кластері. Цей тип робить Service доступним лише зсередини кластера.
NodePort — відкриває доступ до Service на тому ж порті кожного обраного вузла в кластері за допомогою NAT. Робить Service доступним ззовні кластера за допомогою <NodeIP>:<NodePort>. Є надмножиною відносно ClusterIP.
LoadBalancer — створює зовнішній балансувальник навантаження в поточному хмарному середовищі (якщо підтримується) та призначає фіксовану зовнішню IP-адресу для Service. Є надмножиною відносно NodePort.
ExternalName — звʼязує Service з вмістом поля externalName (наприклад, foo.bar.example.com), повертаючи запис CNAME із його значенням. Не встановлюється жодного проксі. Цей тип вимагає v1.7 або вище kube-dns або CoreDNS версії 0.0.8 або вище.
Крім того, слід зауважити, що існують випадки використання Service, при яких не визначається selector у специфікації. Service, створений без selector, також не створить відповідний обʼєкт Endpoints. Це дозволяє користувачам вручну відкривати доступ Service на конкретні точки доступу. Ще одна можливість, чому може бути відсутній селектор — використання виключно type: ExternalName.
Зміст
Відкриття Podʼів для зовнішнього трафіка
Балансування навантаження трафіку між Podʼами
Використання міток
Service в Kubernetes – це рівень абстракції, який визначає логічний набір Podʼів і дозволяє експонування зовнішнього трафіку, балансування навантаження та виявлення служб для цих Podʼів.
Services та мітки (Labels)
Service маршрутизує трафік набору Podʼів. Service є абстракцією, яка дозволяє Podʼам зникати та реплікуватися в Kubernetes, не впливаючи на ваш застосунок. Виявлення та маршрутизація серед залежних Podʼів (таких як компоненти frontend та backend у застосунку) обробляються Serviceʼами Kubernetes.
Service визначають набір Podʼів за допомогою міток та селекторів, примітиву гуртування, який дозволяє логічно взаємодіяти з обʼєктами в Kubernetes. Мітки — це пари ключ/значення, призначені обʼєктам та можуть бути використані різними способами:
Призначення обʼєктів до оточення розробки, тестування та експлуатації
Вбудовування теґів версій
Класифікація обʼєкта за допомогою теґів
Мітки можна прикріплювати до обʼєктів при їх створенні або пізніше. Їх можна змінювати в будь-який час. Давайте зараз використаємо Service для надання доступу до нашого застосунку та застосуємо деякі мітки.
Крок 1: Створення нового Service
Перевіримо, що наш застосунок працює. Ми використовуватимемо команду kubectl get та переглядатимемо наявні Podʼи:
kubectl get pods
Якщо Podʼи не запущені, це означає, що обʼєкти з попередніх кроків були видалені. У цьому випадку поверніться та створіть Deployment з посібника Використання kubectl для створення Deployment. Зачекайте кілька секунд та перегляньте список Podʼів знову. Ви можете продовжити, як тільки побачите, що один Pod працює.
Далі виведемо список наявних Serviceʼів у нашому кластері:
kubectl get services
У нас є Service з назвою kubernetes, яка створюється стандартно, коли minikube запускає кластер. Щоб створити новий Service та зробити зовнішній трафік доступним для нього, ми використовуватимемо команду expose з параметром NodePort.
Тепер у нас є робочий Service з назвою kubernetes-bootcamp. Тут ми бачимо, що Service отримав унікальний IP в кластері, внутрішній порт та зовнішній IP (IP вузла).
Щоб дізнатися, який порт був відкритий ззовні (для Serviceʼу з type: NodePort), ми запустимо команду describe service:
kubectl describe services/kubernetes-bootcamp
Створимо змінну середовища з назвою NODE_PORT, яка матиме значення порту вузла:
export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')" echo "NODE_PORT=$NODE_PORT"
Тепер ми можемо перевірити, що застосунок отримав доступ за межі кластера, використовуючи curl, IP-адресу вузла та зовнішній порт:
curl http://"$(minikube ip):$NODE_PORT"
Примітка:
Якщо ви використовуєте minikube з Docker Desktop як драйвер контейнера, потрібно використовувати minikube tunnel. Це тому, що контейнери всередині Docker Desktop ізольовані від вашого компʼютера.
В окремому вікні термінала виконайте: minikube service kubernetes-bootcamp --url
Вивід виглядає так:
http://127.0.0.1:51082 ! Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
Потім використовуйте вказаний URL для доступу до застосунку: curl 127.0.0.1:51082
І, ми отримуємо відповідь від сервера. Service — працює.
Крок 2: Використання міток
Deployment автоматично створив мітку для нашого Podʼа. За допомогою команди describe deployment ви можете побачити імʼя (ключ) цієї мітки:
kubectl describe deployment
Використаємо цю мітку для отримання списку наших Podʼів. Ми використовуватимемо команду kubectl get pods з -l як параметр, за яким слідують значення мітки:
kubectl get pods -l app=kubernetes-bootcamp
Ви можете зробити те саме, щоб вивести список поточних служб:
kubectl get services -l app=kubernetes-bootcamp
Отримайте назву Podʼа та збережіть її в змінній середовища POD_NAME:
export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')" echo "Name of the Pod: $POD_NAME"
Для застосування нової мітки ми використовуємо команду label за якою слідує тип обʼєкта, назва обʼєкта та нова мітка:
kubectl label pods "$POD_NAME" version=v1
Це додасть нову мітку до нашого Podʼа (ми закріпили версію програми для Podʼа), і ми можемо перевірити це за допомогою команди describe pod:
kubectl describe pods "$POD_NAME"
Тут ми бачимо, що мітка тепер прикріплена до нашого Podʼа. І тепер ми можемо отримати список Podʼів, використовуючи нову мітку:
kubectl get pods -l version=v1
І, ми бачимо Pod.
Крок 3: Видалення Service
Для видалення Service ви можете використовувати команду delete service. Тут також можна використовувати мітки:
kubectl delete service -l app=kubernetes-bootcamp
Переконайтеся, що Service видалено:
kubectl get services
Це підтверджує, що наш Service видалено. Щоб переконатися, що маршрут більше не відкритий, ви можете перевірити раніше відкриті IP та порт за допомогою curl:
curl http://"$(minikube ip):$NODE_PORT"
Це доводить, що застосунок більше не доступний ззовні кластера. Ви можете переконатись, що застосунок все ще працює за допомогою curl зсередини Podʼа:
Тут ми бачимо, що застосунок працює. Це тому, що Deployment управляє застосунком. Для припинення роботи застосунку вам слід видалити також й Deployment.
2.5.1 - Запуск кількох екземплярів вашого застосунку
Масштабування застосунку вручну за допомогою kubectl.
Мета
Масштабування застосунку за допомогою kubectl.
Масштабування Застосунку
Раніше ми створили Deployment, а потім робили його загальнодоступним за допомогою Service. Deployment створив лише один Pod для запуску нашого pfcnjceyre. Зі збільшенням трафіку, нам потрібно масштабувати застосунок, щоб відповідати зростаючим вимогам користувачів.
Масштабування досягається зміною кількості реплік в Deployment.
Зміст:
Масштабування Deployment
Ви можете створити Deployment з початку з декількома екземплярами, використовуючи параметр --replicas для команди створення Deployment kubectl
Примітка:
Якщо ви спробуєте це після попереднього розділу, то, можливо, ви видалили Service, який створили, або створили Service з type: NodePort. У цьому розділі припускається, що для Deployment kubernetes-bootcamp створено Service з type: LoadBalancer.
Якщо ви не видалили Service, створений в попередньому розділі, спочатку видаліть цей Service, а потім запустіть наступну команду для створення нового Service з параметром type встановленим на LoadBalancer:
Масштабування Deployment гарантує створення нових Podʼів і їх призначення на Вузли з вільними ресурсами. Масштабування збільшить кількість Podʼів до нового бажаного стану. Kubernetes також підтримує автоматичне масштабування Podʼів, але це виходить за рамки цього посібника. Також можливе масштабування до нуля, і це призведе до завершення всіх Podʼів вказаного Deployment.
Запуск кількох екземплярів застосунку вимагає засобів для розподілу трафіку між ними. У Service є вбудований балансер навантаження, який розподілить мережевий трафік на всі Podʼи, що виставлені Deployment. Service будуть постійно відстежувати робочі Podʼи, використовуючи точки доступу, щоб гарантувати, що трафік направляється лише на доступні Podʼи.
Масштабування досягається зміною кількості реплік в Deployment.
Якщо у вас є кілька екземплярів застосунку, ви зможете робити оновлення без перерв. Ми розглянемо це в наступному розділі посібника. Тепер перейдемо до термінала та масштабуємо наш застосунок.
Масштабування Deployment
Щоб переглянути свій Deployment, використовуйте команду get deployments:
kubectl get deployments
Вивід повинен бути схожий на:
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 11m
Маємо 1 Pod. Якщо цього немає, виконайте команду ще раз. Тут маємо:
NAME — назви Deployment в кластері.
READY — показує співвідношення поточних/бажаних реплік
UP-TO-DATE — показує кількість реплік, які були оновлені для досягнення бажаного стану.
AVAILABLE — показує, скільки реплік застосунку доступні користувачам.
AGE — показує час, протягом якого застосунок працює.
Щоб переглянути ReplicaSet, створений Deployment, виконайте:
kubectl get rs
Зверніть увагу, що назва ReplicaSet завжди форматується як [DEPLOYMENT-NAME]-[ВИПАДКОВИЙ-РЯДОК]. Випадковий рядок генерується випадковим чином та використовує pod-template-hash як основу для створеня.
Два важливі стовпці цього виводу:
DESIRED — показує бажану кількість реплік застосунку, яку ви визначаєте під час створення Deployment. Це бажаний стан.
CURRENT — показує, скільки реплік в цей час працюють.
Далі масштабуймо Deployment до 4 реплік. Ми використаємо команду kubectl scale, за якою слідує тип Deployment, назва та бажана кількість екземплярів:
Щоб знову переглянути свої Deployment, використовуйте get deployments:
kubectl get deployments
Зміни були застосовані, і у нас є 4 екземпляри застосунку. Далі перевірмо, чи змінилася кількість Podʼів:
kubectl get pods -o wide
Тепер є 4 Podʼа з різними IP-адресами. Зміна була зареєстрована в журналі подій Deployment. Щоб перевірити це, використовуйте команду describe:
kubectl describe deployments/kubernetes-bootcamp
Ви також можете побачити, що у виводі цієї команди тепер є 4 репліки.
Балансування навантаження
Перевіримо, чи Service балансує трафік. Щоб дізнатися зовнішній IP та порт, ми можемо використовувати команду describe, як ми дізнались в попередній частині посібника:
kubectl describe services/kubernetes-bootcamp
Створіть змінну середовища з іменем NODE_PORT, яка має значення як порт Вузла:
export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
echo NODE_PORT=$NODE_PORT
Далі ми запустимо curl з зовнішньою IP-адресою та портом. Виконайте команду кілька разів:
curl http://"$(minikube ip):$NODE_PORT"
Ми потрапляємо на різні Podʼи при кожному запиті. Це демонструє, що балансування навантаження працює.
Вивід має бути схожим на:
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-644c5687f4-wp67j | v=1
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-644c5687f4-hs9dj | v=1
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-644c5687f4-4hjvf | v=1
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-644c5687f4-wp67j | v=1
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-644c5687f4-4hjvf | v=1
Примітка:
Якщо ви використовуєте minikube з Docker Desktop як драйвер контейнера, потрібен тунель minikube. Це через те, що контейнери всередині Docker Desktop ізольовані від вашого компʼютера-хосту.
В окремому вікні термінала виконайте: minikube service kubernetes-bootcamp --url
Вивід виглядає так:
http://127.0.0.1:51082 ! Оскільки ви використовуєте драйвер Docker на darwin, термінал повинен бути відкритий для його запуску.
Потім використовуйте наданий URL для доступу до застосунку: curl 127.0.0.1:51082
Зменшення розгортання
Щоб зменшити Deployment до 2 реплік, знову запустіть команду scale:
Виконання поетапного оновлення застосунку (rolling update) за допомогою kubectl.
Мета
Виконати розгортання з оновленням за допомогою kubectl.
Оновлення застосунку
Користувачі очікують, що застосунки будуть доступні цілодобово, і очікується, що розробники розгортатимуть нові версії кілька разів на день. У Kubernetes це робиться за допомогою розгортань з поступовим оновленням. Поступове оновлення (rolling update) дозволяє виконати оновлення Deployment без перерви в роботі застосунку. Це досягається поетапною заміною поточних Podʼів новими. Нові Podʼи призначаються вузлам з вільними ресурсами, а Kubernetes чекає, доки ці нові Podʼи не почнуть працювати, перш ніж вилучити старі Podʼи.
У попередньому розділі ми масштабували наш застосунок для запуску кількох екземплярів. Це є вимогою для виконання оновлень без впливу на доступність застосунку. Стандартно максимальна кількість Podʼів, які можуть бути недоступні під час оновлення, та максимальна кількість нових Podʼів, які можуть бути створені, дорівнює одному. Обидві опції можуть бути налаштовані як у вигляді точної кількості, так і у відсотках (від усіх Podʼів).
У Kubernetes оновлення мають версії, і будь-яке оновлення Deployment може бути повернуте до попередньої (стабільної) версії.
Зміст:
Оновлення застосунку
Поступові оновлення дозволяють виконувати оновлення Deployment без зупинки роботи застосунку за допомогою поетапного оновлення екземплярів Podʼів.
Аналогічно масштабуванню застосунку, якщо Deployment є публічно доступним, Service буде балансувати трафік лише на доступні Podʼи під час оновлення. Доступний Pod — це екземпляр, який доступний користувачам застосунку.
Поступові оновлення дозволяють виконувати наступні дії:
Просування застосунку з одного середовища в інше (за допомогою оновлень образів контейнерів)
Відкат до попередніх версій
Неперервна інтеграція та постійна доставка застосунків без перерви в роботі
Якщо Deployment публічно доступний, Service буде балансувати трафік лише на доступні Podʼи під час оновлення.
У наступному інтерактивному практикумі ми оновимо наш застосунок до нової версії та виконаємо відкат.
Оновлення версії застосунку
Для виведення списку Deploymentʼів виконайте команду get deployments:
kubectl get deployments
Для виведення списку запущених Podʼів виконайте команду get pods:
kubectl get pods
Для перегляду поточної версії образу застосунку виконайте команду describe pods та шукайте поле Image:
kubectl describe pods
Для оновлення образу застосунку до версії 2 використовуйте команду set image, за якою йде назва Deployment та нова версія образу:
kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=docker.io/jocatalin/kubernetes-bootcamp:v2
Команда повідомляє Deployment про використання іншого образу для вашого застосунку та запускає поступове оновлення. Перевірте стан нових Podʼів та перегляньте той, що відключається, використовуючи команду get pods:
kubectl get pods
Перевірка оновлення
Спочатку перевірте, чи Service працює, бо ви могли його видалити його на попередньому кроці, виконайте kubectl describe services/kubernetes-bootcamp. Якщо його немає, створіть його знов:
Команда rollout undo повертає розгортання до попередньо відомого стану (v2 образу). Оновлення мають версії, і ви можете повернутися до будь-якого раніше відомого стану розгортання.
Використайте команду get pods, щоб знову вивести список Podʼів:
kubectl get pods
Щоб перевірити образ, розгорнутий на цих Podʼах, використайте команду describe pods:
kubectl describe pods
Розгортання знову використовує стабільну версію застосунку (v2). Відкат був успішним.
3.1.1 - Зовнішня конфігурація за допомогою MicroProfile, ConfigMaps та Secrets
У цьому посібнику ви дізнаєтеся, як і чому варто зовнішньо налаштовувати конфігурацію вашого мікросервісу. Зокрема, ви дізнаєтеся, як використовувати Kubernetes ConfigMaps і Secrets для встановлення змінних середовища та їх подальшого використання за допомогою MicroProfile Config.
Перш ніж ви розпочнете
Створення Kubernetes ConfigMaps та Secrets
Існує кілька способів встановлення змінних середовища для Docker-контейнера в Kubernetes, зокрема: Dockerfile, kubernetes.yml, Kubernetes ConfigMap та Kubernetes Secret. У цьому посібнику ви дізнаєтеся, як використовувати останні два для встановлення змінних середовища, значення яких будуть впроваджені у ваші мікросервіси. Однією з переваг використання ConfigMap та Secret є те, що вони можуть повторно використовуватися у кількох контейнерах, включаючи можливість призначення різним змінним середовища для різних контейнерів.
ConfigMap — це обʼєкти API, які зберігають неконфіденційні пари "ключ-значення". У інтерактивному посібнику ви дізнаєтеся, як використовувати ConfigMap для зберігання імені програми. Більше інформації про ConfigMap ви можете знайти у документації.
Хоча Secret також використовуються для зберігання пар "ключ-значення", вони відрізняються від ConfigMap тим, що призначені для конфіденційної/чутливої інформації та зберігаються за допомогою кодування Base64. Це робить Secret відповідним вибором для зберігання таких речей, як облікові дані, ключі та токени, що ви й зробите в інтерактивному завданні. Більше інформації про Secret ви можете знайти у документації.
Зовнішня конфігурація з коду
Зовнішня конфігурація застосунків корисна, оскільки конфігурація зазвичай змінюється залежно від вашого середовища. Для цього ми будемо використовувати Java Contexts and Dependency Injection (CDI) та MicroProfile Config. MicroProfile Config — це функція MicroProfile, набору відкритих Java-технологій для розробки та розгортання хмаро-орієнтованих мікросервісів.
CDI надає стандартну можливість впровадження залежностей, що дозволяє створювати застосунок з працюючих разом, слабо звʼязаних частин. MicroProfile Config надає застосункам та мікросервісам стандартний спосіб отримання конфігураційних властивостей з різних джерел, включаючи застосунок, середовище виконання та оточення. Відповідно до визначеного пріоритету джерела, властивості автоматично комбінуються в єдиний набір властивостей, до якого застосунок може отримати доступ через API. Разом, CDI та MicroProfile будуть використані в інтерактивному посібнику для отримання зовнішньо наданих властивостей з Kubernetes ConfigMap та Secret і їх додавання у ваш код застосунку.
Багато відкритих фреймворків та середовищ виконання реалізують і підтримують MicroProfile Config. Протягом інтерактивного уроку ви будете використовувати Open Liberty, гнучке відкрите середовище виконання Java для створення та запуску хмаро-орієнтованих застосунків та мікросервісів. Однак замість нього можна використовувати будь-яке сумісне з MicroProfile середовище.
Цілі
Створити Kubernetes ConfigMap та Secret
Встановити конфігурацію мікросервісу за допомогою MicroProfile Config
Приклад: Зовнішня конфігурація за допомогою MicroProfile, ConfigMap та Secret
3.2 - Оновлення конфігурації за допомогою ConfigMap
Ця сторінка містить покроковий приклад оновлення конфігурації всередині Pod за допомогою ConfigMap і базується на завданні Налаштування Pod для використання ConfigMap. Наприкінці цього посібника ви зрозумієте, як змінити конфігурацію для запущеного застосунку. Цей посібник використовує образи alpine та nginx як приклади.
Перш ніж ви розпочнете
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Вам потрібно мати інструмент командного рядка curl для виконання HTTP-запитів з термінала або командного рядка. Якщо у вас немає curl, ви можете його встановити. Ознайомтеся з документацією для вашої операційної системи.
Цілі
Оновлення конфігурації через ConfigMap, змонтованого як том
Оновлення змінних середовища Pod за допомогою ConfigMap
Оновлення конфігурації через ConfigMap в багатоконтейнерному Pod
Оновлення конфігурації через ConfigMap у Pod, що містить контейнер Sidecar
Оновлення конфігурації через ConfigMap, змонтовану як том
Використовуйте команду kubectl create configmap для створення ConfigMap з літеральних значень:
kubectl create configmap sport --from-literal=sport=football
Нижче наведено приклад маніфесту Deployment з ConfigMap sport, змонтованим як том у єдиний контейнер Pod.
apiVersion:apps/v1kind:Deploymentmetadata:name:configmap-volumelabels:app.kubernetes.io/name:configmap-volumespec:replicas:3selector:matchLabels:app.kubernetes.io/name:configmap-volumetemplate:metadata:labels:app.kubernetes.io/name:configmap-volumespec:containers:- name:alpineimage:alpine:3command:- /bin/sh- -c- while true; do echo "$(date) My preferred sport is $(cat /etc/config/sport)";sleep 10; done;ports:- containerPort:80volumeMounts:- name:config-volumemountPath:/etc/configvolumes:- name:config-volumeconfigMap:name:sport
Перевірте Podʼи цього Deployment, щоб переконатися, що вони готові (відповідно до селектору):
kubectl get pods --selector=app.kubernetes.io/name=configmap-volume
Ви повинні побачити подібний вивід:
NAME READY STATUS RESTARTS AGE
configmap-volume-6b976dfdcf-qxvbm 1/1 Running 0 72s
configmap-volume-6b976dfdcf-skpvm 1/1 Running 0 72s
configmap-volume-6b976dfdcf-tbc6r 1/1 Running 0 72s
На кожному вузлі, де працює один із цих Pod, kubelet отримує дані для цього ConfigMap і перетворює їх на файли у локальному томі. Потім kubelet монтує цей том у контейнер, як зазначено у шаблоні Pod. Код, що виконується у цьому контейнері, завантажує інформацію з файлу та використовує її для друку звіту на stdout. Ви можете перевірити цей звіт, переглянувши логи одного з Pod у цьому Deployment:
# Виберіть один Pod, що належить до Deployment, і перегляньте його логиkubectl logs deployments/configmap-volume
Ви повинні побачити подібний вивід:
Found 3 pods, using pod/configmap-volume-76d9c5678f-x5rgj
Thu Jan 4 14:06:46 UTC 2024 My preferred sport is football
Thu Jan 4 14:06:56 UTC 2024 My preferred sport is football
Thu Jan 4 14:07:06 UTC 2024 My preferred sport is football
Thu Jan 4 14:07:16 UTC 2024 My preferred sport is football
Thu Jan 4 14:07:26 UTC 2024 My preferred sport is football
Відредагуйте ConfigMap:
kubectl edit configmap sport
В редакторі, що з’явиться, змініть значення ключа sport з football на cricket. Збережіть зміни. Інструмент kubectl відповідним чином оновлює ConfigMap (якщо виникне помилка, спробуйте ще раз).
Ось приклад того, як може виглядати маніфест після редагування:
apiVersion:v1data:sport:cricketkind:ConfigMap# Наявні метадані можна залишити без змін.# Значення, які ви побачите, не будуть точно відповідати цим.metadata:creationTimestamp:"2024-01-04T14:05:06Z"name:sportnamespace:defaultresourceVersion:"1743935"uid:024ee001-fe72-487e-872e-34d6464a8a23
Ви повинні побачити наступний вивід:
configmap/sport edited
Відстежуйте (слідкуйте за останніми записами в) логах одного з Pod, що належить до цього Deployment:
Через кілька секунд ви повинні побачити зміну виводу логів:
Thu Jan 4 14:11:36 UTC 2024 My preferred sport is football
Thu Jan 4 14:11:46 UTC 2024 My preferred sport is football
Thu Jan 4 14:11:56 UTC 2024 My preferred sport is football
Thu Jan 4 14:12:06 UTC 2024 My preferred sport is cricket
Thu Jan 4 14:12:16 UTC 2024 My preferred sport is cricket
Коли у вас є ConfigMap, змонтований у працюючий Pod, використовуючи або том configMap, або projected том, і ви оновлюєте цей ConfigMap, працюючий Pod майже миттєво бачить це оновлення. Однак ваш застосунок бачить зміни лише в тому випадку, якщо він запрограмований опитувати зміни або стежити за оновленнями файлів. Застосунок, що завантажує свою конфігурацію лише під час запуску, не помітить змін.
Примітка:
Загальна затримка з моменту оновлення ConfigMap до моменту, коли нові ключі проєцюються у Pod, може бути такою ж довгою, як і період синхронізації kubelet. Також перевірте Автоматичне оновлення змонтованих ConfigMap.
Оновлення змінних середовища Pod за допомогою ConfigMap
Використовуйте команду kubectl create configmap для створення ConfigMap з літеральних значень:
apiVersion:apps/v1kind:Deploymentmetadata:name:configmap-env-varlabels:app.kubernetes.io/name:configmap-env-varspec:replicas:3selector:matchLabels:app.kubernetes.io/name:configmap-env-vartemplate:metadata:labels:app.kubernetes.io/name:configmap-env-varspec:containers:- name:alpineimage:alpine:3env:- name:FRUITSvalueFrom:configMapKeyRef:key:fruitsname:fruitscommand:- /bin/sh- -c- while true; do echo "$(date) The basket is full of $FRUITS";sleep 10; done;ports:- containerPort:80
Перевірте Pod цього Deployment, щоб переконатися, що вони готові (збігаються з селектором):
kubectl get pods --selector=app.kubernetes.io/name=configmap-env-var
Ви повинні побачити подібний вивід:
NAME READY STATUS RESTARTS AGE
configmap-env-var-59cfc64f7d-74d7z 1/1 Running 0 46s
configmap-env-var-59cfc64f7d-c4wmj 1/1 Running 0 46s
configmap-env-var-59cfc64f7d-dpr98 1/1 Running 0 46s
Ключ-значення у ConfigMap налаштовано як змінну середовища в контейнері Pod. Перевірте це, переглянувши логи одного Pod, що належить до Deployment.
kubectl logs deployment/configmap-env-var
Ви повинні побачити подібний вивід:
Found 3 pods, using pod/configmap-env-var-7c994f7769-l74nq
Thu Jan 4 16:07:06 UTC 2024 The basket is full of apples
Thu Jan 4 16:07:16 UTC 2024 The basket is full of apples
Thu Jan 4 16:07:26 UTC 2024 The basket is full of apples
Відредагуйте ConfigMap:
kubectl edit configmap fruits
В редакторі, що зʼявиться, змініть значення ключа fruits з apples на mangoes. Збережіть зміни. Інструмент kubectl відповідним чином оновлює ConfigMap (якщо виникне помилка, спробуйте ще раз).
Ось приклад того, як може виглядати маніфест після редагування:
apiVersion:v1data:fruits:mangoeskind:ConfigMap# Наявні метадані можна залишити без змін.# Значення, які ви побачите, не будуть точно відповідати цим.metadata:creationTimestamp:"2024-01-04T16:04:19Z"name:fruitsnamespace:defaultresourceVersion:"1749472"
Ви повинні побачити наступний вивід:
configmap/fruits edited
Відстежуйте логи Deployment та спостерігайте за виводом протягом кількох секунд:
# Як пояснюється у тексті, вивід НЕ змінюєтьсяkubectl logs deployments/configmap-env-var --follow
Зверніть увагу, що вивід залишається незмінним, навіть якщо ви редагували ConfigMap:
Thu Jan 4 16:12:56 UTC 2024 The basket is full of apples
Thu Jan 4 16:13:06 UTC 2024 The basket is full of apples
Thu Jan 4 16:13:16 UTC 2024 The basket is full of apples
Thu Jan 4 16:13:26 UTC 2024 The basket is full of apples
Примітка:
Хоча значення ключа всередині ConfigMap змінилося, змінна середовища в Pod все ще показує попереднє значення. Це тому, що змінні середовища для процесу, що виконується всередині Pod, не оновлюються, коли змінюються вихідні дані; якщо ви хочете примусово оновити, вам потрібно змусити Kubernetes замінити наявні Podʼи. Нові Podʼи будуть працювати з оновленою інформацією.
Ви можете ініціювати таку заміну. Виконайте розгортання для Deployment, використовуючи kubectl rollout:
# Запустіть розгортанняkubectl rollout restart deployment configmap-env-var
# Дочекайтеся завершення розгортанняkubectl rollout status deployment configmap-env-var --watch=true
Далі перевірте Deployment:
kubectl get deployment configmap-env-var
Ви повинні побачити подібний вивід:
NAME READY UP-TO-DATE AVAILABLE AGE
configmap-env-var 3/3 3 3 12m
Перевірте Podʼи:
kubectl get pods --selector=app.kubernetes.io/name=configmap-env-var
Розгортання змушує Kubernetes створити новий ReplicaSet для Deployment; це означає, що наявні Podʼи з часом завершуються, а нові створюються. Через кілька секунд ви повинні побачити подібний вивід:
NAME READY STATUS RESTARTS AGE
configmap-env-var-6d94d89bf5-2ph2l 1/1 Running 0 13s
configmap-env-var-6d94d89bf5-74twx 1/1 Running 0 8s
configmap-env-var-6d94d89bf5-d5vx8 1/1 Running 0 11s
Примітка:
Будь ласка, дочекайтеся повного завершення старих Podʼів, перш ніж продовжувати наступні кроки.
Перегляньте логи для одного з Podʼів у цьому Deployment:
# Виберіть один Pod, що належить до Deployment, і перегляньте його логиkubectl logs deployment/configmap-env-var
Ви повинні побачити подібний вивід:
Found 3 pods, using pod/configmap-env-var-6d9ff89fb6-bzcf6
Thu Jan 4 16:30:35 UTC 2024 The basket is full of mangoes
Thu Jan 4 16:30:45 UTC 2024 The basket is full of mangoes
Thu Jan 4 16:30:55 UTC 2024 The basket is full of mangoes
Це демонструє сценарій оновлення змінних середовища у Podʼі, що отримані з ConfigMap. Зміни до значень ConfigMap застосовуються до Pod під час наступного розгортання. Якщо Pod створюються з іншої причини, наприклад, при масштабуванні Deployment, тоді нові Pod також використовують останні значення конфігурації; якщо ви не ініціюєте розгортання, то ви можете виявити, що ваш застосунок працює зі змішаними старими та новими значеннями змінних середовища.
Оновлення конфігурації через ConfigMap у багатоконтейнерному Поді
Використовуйте команду kubectl create configmap, щоб створити ConfigMap з літеральних значень:
kubectl create configmap color --from-literal=color=red
Нижче подано приклад маніфесту для Deployment, що керує набором Podʼів, кожен з двома контейнерами. Два контейнери спільно використовують том emptyDir, який вони використовують для звʼязку. Перший контейнер працює як вебсервер (nginx). Шлях монтування для спільного тому в контейнері вебсервера — /usr/share/nginx/html. Другий допоміжний контейнер базується на alpine, і для цього контейнера том emptyDir монтується у /pod-data. Допоміжний контейнер записує файл у HTML, чий вміст залежить від ConfigMap. Контейнер вебсервера обслуговує HTML за допомогою HTTP.
apiVersion:apps/v1kind:Deploymentmetadata:name:configmap-two-containerslabels:app.kubernetes.io/name:configmap-two-containersspec:replicas:3selector:matchLabels:app.kubernetes.io/name:configmap-two-containerstemplate:metadata:labels:app.kubernetes.io/name:configmap-two-containersspec:volumes:- name:shared-dataemptyDir:{}- name:config-volumeconfigMap:name:colorcontainers:- name:nginximage:nginxvolumeMounts:- name:shared-datamountPath:/usr/share/nginx/html- name:alpineimage:alpine:3volumeMounts:- name:shared-datamountPath:/pod-data- name:config-volumemountPath:/etc/configcommand:- /bin/sh- -c- while true; do echo "$(date) My preferred color is $(cat /etc/config/color)" > /pod-data/index.html;sleep 10; done;
kubectl port-forward service/configmap-service 8080:8080 & # це залишається запущеним у фоновому режимі
Отримайте доступ до Service.
curl http://localhost:8080
Ви повинні побачити вивід, схожий на:
Fri Jan 5 08:08:22 UTC 2024 My preferred color is red
Відредагуйте ConfigMap:
kubectl edit configmap color
У відкритому редакторі, змініть значення ключа color з red на blue. Збережіть свої зміни. Інструмент kubectl оновлює ConfigMap відповідно (якщо ви побачите помилку, спробуйте ще раз).
Ось приклад того, як може виглядати цей маніфест після редагування:
apiVersion:v1data:color:bluekind:ConfigMap# Ви можете залишити наявні метадані такими, які вони є.# Значення, які ви побачите, не збігатимуться з цими.metadata:creationTimestamp:"2024-01-05T08:12:05Z"name:colornamespace:configmapresourceVersion:"1801272"uid:80d33e4a-cbb4-4bc9-ba8c-544c68e425d6
Затримайтесь на URL сервісу протягом кількох секунд.
# Скасуйте це, коли будете задоволені (Ctrl-C)while true; do curl --connect-timeout 7.5 http://localhost:8080; sleep 10; done
Ви повинні побачити, що виведення змінюється наступним чином:
Fri Jan 5 08:14:00 UTC 2024 My preferred color is red
Fri Jan 5 08:14:02 UTC 2024 My preferred color is red
Fri Jan 5 08:14:20 UTC 2024 My preferred color is red
Fri Jan 5 08:14:22 UTC 2024 My preferred color is red
Fri Jan 5 08:14:32 UTC 2024 My preferred color is blue
Fri Jan 5 08:14:43 UTC 2024 My preferred color is blue
Fri Jan 5 08:15:00 UTC 2024 My preferred color is blue
Оновлення конфігурації через ConfigMap у Поді з контейнером sidecar
Вищевказаний сценарій можна відтворити за допомогою контейнера sidecar як допоміжного контейнера для запису HTML-файлу. Оскільки контейнер sidecar концептуально є контейнером ініціалізації, гарантується, що він запускається перед головним контейнером вебсервера. Це забезпечує, що файл HTML завжди доступний, коли вебсервер буде готовий його обслуговувати. Будь ласка, дивіться Увімкнення контейнерів sidecar, щоб скористатися цією функцією.
Якщо ви продовжуєте з попереднього сценарію, ви можете використати знову ConfigMap з імʼям color для цього сценарію. Якщо ви виконуєте цей сценарій самостійно, використовуйте команду kubectl create configmap, щоб створити ConfigMap з літеральних значень:
kubectl create configmap color --from-literal=color=blue
Нижче наведено приклад маніфесту для Deployment, що керує набором Podʼів, кожен з головним контейнером і контейнером sidecar. Обидва контейнери використовують спільний том emptyDir для звʼязку. Головний контейнер працює як вебсервер (NGINX). Шлях монтування для спільного тому в контейнері вебсервера — /usr/share/nginx/html. Другий контейнер є контейнером sidecar, який базується на Alpine Linux і діє як допоміжний контейнер. Для цього контейнера том emptyDir монтується у /pod-data. Контейнер sidecar записує файл у HTML, чий вміст залежить від ConfigMap. Контейнер вебсервера обслуговує HTML через HTTP.
apiVersion:apps/v1kind:Deploymentmetadata:name:configmap-sidecar-containerlabels:app.kubernetes.io/name:configmap-sidecar-containerspec:replicas:3selector:matchLabels:app.kubernetes.io/name:configmap-sidecar-containertemplate:metadata:labels:app.kubernetes.io/name:configmap-sidecar-containerspec:volumes:- name:shared-dataemptyDir:{}- name:config-volumeconfigMap:name:colorcontainers:- name:nginximage:nginxvolumeMounts:- name:shared-datamountPath:/usr/share/nginx/htmlinitContainers:- name:alpineimage:alpine:3restartPolicy:AlwaysvolumeMounts:- name:shared-datamountPath:/pod-data- name:config-volumemountPath:/etc/configcommand:- /bin/sh- -c- while true; do echo "$(date) My preferred color is $(cat /etc/config/color)" > /pod-data/index.html;sleep 10; done;
kubectl port-forward service/configmap-sidecar-service 8081:8081 & # це залишається запущеним у фоновому режимі
Отримайте доступ до Service.
curl http://localhost:8081
Ви повинні побачити вивід, схожий на:
Sat Feb 17 13:09:05 UTC 2024 My preferred color is blue
Відредагуйте ConfigMap:
kubectl edit configmap color
У відкритому редакторі змініть значення ключа color з blue на green. Збережіть свої зміни. Інструмент kubectl оновлює ConfigMap відповідно (якщо ви побачите помилку, спробуйте ще раз).
Ось приклад того, як може виглядати цей маніфест після редагування:
apiVersion:v1data:color:greenkind:ConfigMap# You can leave the existing metadata as they are.# The values you'll see won't exactly match these.metadata:creationTimestamp:"2024-02-17T12:20:30Z"name:colornamespace:defaultresourceVersion:"1054"uid:e40bb34c-58df-4280-8bea-6ed16edccfaa
Затримайтесь на URL сервісу протягом кількох секунд.
# Скасуйте це, коли будете задоволені (Ctrl-C)while true; do curl --connect-timeout 7.5 http://localhost:8081; sleep 10; done
Ви повинні побачити, що вивід змінюється наступним чином:
Sat Feb 17 13:12:35 UTC 2024 My preferred color is blue
Sat Feb 17 13:12:45 UTC 2024 My preferred color is blue
Sat Feb 17 13:12:55 UTC 2024 My preferred color is blue
Sat Feb 17 13:13:05 UTC 2024 My preferred color is blue
Sat Feb 17 13:13:15 UTC 2024 My preferred color is green
Sat Feb 17 13:13:25 UTC 2024 My preferred color is green
Sat Feb 17 13:13:35 UTC 2024 My preferred color is green
Оновлення конфігурації через незмінний ConfigMap, який монтується як том
Примітка:
Незмінні ConfigMaps особливо корисні для конфігурації, яка є постійною і не очікується, що зміниться з часом. Позначення ConfigMap як незмінного дозволяє поліпшити продуктивність, де kubelet не відстежує зміни.
Якщо вам дійсно потрібно внести зміни, вам слід запланувати одне з наступного:
змінити імʼя ConfigMap та перейти до запуску Podʼів, які посилаються на нове імʼя
замінити всі вузли в вашому кластері, які раніше запускали Pod, що використовував старе значення
перезавантажити kubelet на будь-якому вузлі, де kubelet раніше завантажував старий ConfigMap
apiVersion:apps/v1kind:Deploymentmetadata:name:immutable-configmap-volumelabels:app.kubernetes.io/name:immutable-configmap-volumespec:replicas:3selector:matchLabels:app.kubernetes.io/name:immutable-configmap-volumetemplate:metadata:labels:app.kubernetes.io/name:immutable-configmap-volumespec:containers:- name:alpineimage:alpine:3command:- /bin/sh- -c- while true; do echo "$(date) The name of the company is $(cat /etc/config/company_name)";sleep 10; done;ports:- containerPort:80volumeMounts:- name:config-volumemountPath:/etc/configvolumes:- name:config-volumeconfigMap:name:company-name-20150801
Перевірте Podʼи для цього Deployment, щоб переконатися, що вони готові (збігаються з селектором):
kubectl get pods --selector=app.kubernetes.io/name=immutable-configmap-volume
Ви повинні побачити вивід схожий на:
ІNAME READY STATUS RESTARTS AGE
immutable-configmap-volume-78b6fbff95-5gsfh 1/1 Running 0 62s
immutable-configmap-volume-78b6fbff95-7vcj4 1/1 Running 0 62s
immutable-configmap-volume-78b6fbff95-vdslm 1/1 Running 0 62s
Контейнер Podʼа посилається на дані, визначені в ConfigMap, і використовує їх для виводу звіту в stdout. Ви можете перевірити цей звіт, переглянувши логи для одного з Podʼів у цьому Deployment:
# Виберіть один Pod, який належить до Deployment, і перегляньте його логиkubectl logs deployments/immutable-configmap-volume
Ви повинні побачити вивід, подібний до:
Found 3 pods, using pod/immutable-configmap-volume-78b6fbff95-5gsfh
Wed Mar 20 03:52:34 UTC 2024 The name of the company is ACME, Inc.
Wed Mar 20 03:52:44 UTC 2024 The name of the company is ACME, Inc.
Wed Mar 20 03:52:54 UTC 2024 The name of the company is ACME, Inc.
Примітка:
Щойно ConfigMap позначений як незмінний, неможливо скасувати цю зміну ані змінити вміст поля даних або binaryData. Для зміни поведінки Podʼів, які використовують цю конфігурацію, ви створите новий незмінний ConfigMap і редагуватимете Deployment для визначення трохи іншого шаблону Pod, з посиланням на новий ConfigMap.
Створіть новий незмінний ConfigMap, використовуючи наведений нижче маніфест:
NAME READY STATUS RESTARTS AGE
immutable-configmap-volume-5fdb88fcc8-29v8n 1/1 Running 0 43s
immutable-configmap-volume-5fdb88fcc8-52ddd 1/1 Running 0 44s
immutable-configmap-volume-5fdb88fcc8-n5jx4 1/1 Running 0 45s
Перевірте логи для одного з Podʼів у цьому Deployment:
# Виберіть один Pod, який належить до Deployment, і перегляньте його логиkubectl logs deployments/immutable-configmap-volume
Ви повинні побачити вивід, подібний до:
Found 3 pods, using pod/immutable-configmap-volume-5fdb88fcc8-n5jx4
Wed Mar 20 04:24:17 UTC 2024 The name of the company is Fiktivesunternehmen GmbH
Wed Mar 20 04:24:27 UTC 2024 The name of the company is Fiktivesunternehmen GmbH
Wed Mar 20 04:24:37 UTC 2024 The name of the company is Fiktivesunternehmen GmbH
Як тільки всі розгортання мігрують на використання нового незмінного ConfigMap, потрібно видалити старий ConfigMap:
kubectl delete configmap company-name-20150801
Підсумок
Зміни у ConfigMap, змонтованому як том у Pod, стають доступними безперешкодно після наступної синхронізації kubelet.
Зміни у ConfigMap, який налаштовує змінні середовища для Pod, стають доступними після наступного розгортання для цього Pod.
Після того як ConfigMap позначено як незмінний, неможливо скасувати цю зміну (ви не можете зробити незмінний ConfigMap змінним), і ви також не можете внести жодну зміну до вмісту поля data або binaryData. Ви можете видалити та створити ConfigMap заново, або можете створити новий відмінний ConfigMap. Коли ви видаляєте ConfigMap, запущені контейнери та їхні Podʼи зберігають точку монтування до будь-якого тому, який посилається на цей наявнийConfigMap.
Очищення
Завершіть команди kubectl port-forward, якщо вони виконуються.
Видаліть ресурси, створені під час навчання:
kubectl delete deployment configmap-volume configmap-env-var configmap-two-containers configmap-sidecar-container immutable-configmap-volume
kubectl delete service configmap-service configmap-sidecar-service
kubectl delete configmap sport fruits color company-name-20240312
kubectl delete configmap company-name-20150801 # У випадку, якщо він не був оброблений під час виконання завдання
Створити ConfigMap з конфігураційними значеннями Redis
Створити Pod з Redis, який монтує та використовує створений ConfigMap
Перевірити, що конфігурація була правильно застосована.
Перш ніж ви розпочнете
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Перегляньте вміст маніфесту Podʼа Redis і зверніть увагу на наступне:
Том config створено за допомогою spec.volumes[1]
Пара key і path у spec.volumes[1].configMap.items[0] експонує ключ redis-config з example-redis-config ConfigMap як файл з назвою redis.conf у томі config.
Том config потім монтується в /redis-master за допомогою spec.containers[0].volumeMounts[1].
Це має загальний ефект експозиції даних з data.redis-config з example-redis-config ConfigMap як /redis-master/redis.conf всередині Pod.
Ще раз перевірте Pod Redis за допомогою redis-cli через kubectl exec, щоб побачити, чи конфігурація була застосована:
kubectl exec -it redis -- redis-cli
Перевірте maxmemory:
127.0.0.1:6379> CONFIG GET maxmemory
Він залишається з типовим значенням 0:
1)"maxmemory"2)"0"
Аналогічно, maxmemory-policy залишається з типовими налаштуваннями noeviction:
127.0.0.1:6379> CONFIG GET maxmemory-policy
Повертає:
1)"maxmemory-policy"2)"noeviction"
Конфігураційні значення не змінилися, оскільки Pod необхідно перезапустити, щоб отримати оновлені значення з асоційованих ConfigMap. Видалимо та заново створимо Pod:
kubectl delete pod redis
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/pods/config/redis-pod.yaml
Цей розділ актуальний для людей, які впроваджують нову вбудовану функцію sidecar-контейнерів для своїх навантажень.
Sidecar-контейнери — це не нова концепція, про що було згадано в блог-пості ще у 2015 році. Kubernetes дозволяв запускати декілька контейнерів у Pod, щоб реалізувати цю концепцію. Однак запуск sidecar-контейнера як звичайного контейнера має багато обмежень, які вирішуються за допомогою нової підтримки вбудованих sidecar-контейнерів.
СТАН ФУНКЦІОНАЛУ:Kubernetes v1.29 [beta] (стандартно увімкнено: true)
Цілі
Зрозуміти необхідність використання sidecar-контейнерів.
Вміти розвʼязувати проблеми з sidecar-контейнерами.
Зрозуміти варіанти універсального "впровадження" sidecar-контейнерів у будь-яке навантаження.
Перш ніж ви розпочнете
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Версія вашого Kubernetes сервера має бути не старішою ніж 1.29.
Для перевірки версії введіть kubectl version.
Огляд sidecar-контейнерів
Sidecar-контейнери — це додаткові контейнери, які працюють разом з основним контейнером застосунку в межах одного Podʼа. Ці контейнери використовуються для покращення або розширення функціональності основного app-контейнера, надаючи додаткові послуги або функціональність, такі як логування, моніторинг, безпека або синхронізація даних, без прямого втручання в код основного застосунку. Детальніше можна прочитати на сторінці концепції Sidecar-контейнерів.
Концепція sidecar-контейнерів не є новою, і є багато її реалізацій. Як і sidecar-контейнери, які ви, як особа, що визначає Pod, хочете запустити, ви також можете побачити, що деякі надбудови змінюють Podʼи, до того, як Podʼи почнуть працювати, додаючи додаткові sidecar-контейнери. Механізми інʼєкцій цих додаткових sidecar-контейнерів часто використовують мутуючі вебхуки. Наприклад, надбудова service mesh може впроваджувати sidecar-контейнер, який налаштовує взаємний TLS і шифрування під час передачі між різними Podʼами.
Хоча концепція sidecar-контейнерів не є новою, вбудована реалізація цієї функції в Kubernetes, однак, нова. І як і з будь-якою новою функцією, впровадження цієї функції може викликати певні труднощі.
Цей навчальний посібник досліджує труднощі та рішення, з якими можуть зіткнутися кінцеві користувачі, а також автори sidecar-контейнерів.
Переваги вбудованих sidecar-контейнерів
Використання нативної підтримки sidecar-контейнерів у Kubernetes має кілька переваг:
Ви можете налаштувати нативний sidecar-контейнер так, щоб він запускався перед init-контейнерами.
Вбудовані sidecar-контейнери можна налаштувати так, щоб вони гарантовано завершувалися останніми. Sidecar-контейнери завершують роботу за допомогою сигналу SIGTERM, коли всі звичайні контейнери завершили роботу та завершилися. Якщо sidecar-контейнер не завершує роботу коректно, сигнал SIGKILL буде використано для його завершення.
Для Jobs, коли restartPolicy: OnFailure або restartPolicy: Never, нативні sidecar-контейнери не блокують завершення Pod. Для старих sidecar-контейнерів потрібно було приділяти особливу увагу вирішенню цієї ситуації.
Також для Jobs, вбудовані sidecar-контейнери будуть продовжувати перезапускатися після завершення, навіть якщо звичайні контейнери не будуть цього робити при restartPolicy: Never у Pod.
Функціональна можливістьSidecarContainers перебуває у стані бета-версії, починаючи з версії Kubernetes 1.29, і є стандартно увімкненою. Деякі кластери можуть мати цю функцію вимкненою або мати встановлене програмне забезпечення, яке не сумісне з цією функцією.
Коли це трапляється, Pod може бути відхилено або sidecar-контейнери можуть блокувати запуск Pod, роблячи Pod непридатним для використання. Цей стан легко виявити, оскільки Pod просто застряє на стадії ініціалізації. Однак рідко буває зрозуміло, що спричинило проблему.
Ось міркування та кроки з усунення несправностей, які можна виконати під час впровадження sidecar-контейнерів для свого навантаження.
Переконайтеся, що функціональну можливість увімкнено
Перш за все, переконайтеся, що як API-сервер, так і вузли мають версію Kubernetes v1.29 або пізнішу. Функція не працюватиме на кластерах, де вузли працюють на більш ранніх версіях, де вона не увімкнена.
Примітка
Функцію можна увімкнути на вузлах з версією 1.28. Поведінка завершення роботи вбудованих sidecar-контейнерів відрізнялася у версії 1.28, і не рекомендується налаштовувати поведінку sidecar-контейнера для цієї версії. Однак, якщо єдиною турботою є порядок запуску, вищенаведену заяву можна змінити на вузли, що працюють на версії 1.28 з увімкненою функціональною можливістю.
Ви повинні переконатися, що функціональна можливість увімкнено як для API-сервера (серверів) у межах панелі управління, так і для всіх вузлів.
Одним зі способів перевірити увімкнення функціональної можливості є виконання команди:
Для API-сервера
kubectl get --raw /metrics | grep kubernetes_feature_enabled | grep SidecarContainers
Для окремого вузла
kubectl get --raw /api/v1/nodes/<node-name>/proxy/metrics | grep kubernetes_feature_enabled | grep SidecarContainers
Якщо ви бачите щось на кшталт: kubernetes_feature_enabled{name="SidecarContainers",stage="BETA"} 1, це означає, що функцію увімкнено.
Перевірка сторонніх інструментів і мутуючих вебхуків
Якщо у вас виникли проблеми під час перевірки функції, це може бути ознакою того, що один зі сторонніх інструментів або мутуючих вебхуків не працюють.
Коли функціональну можливість SidecarContainers увімкнено, Podʼи отримують нове поле в їхньому API. Деякі інструменти або мутуючі вебхуки могли бути створені на основі попередньої версії Kubernetes API.
Якщо інструменти передають невідомі поля як є, використовуючи різні стратегії виправлення для змінни об’єкта Pod, це не буде проблемою. Однак є інструменти, які видалять невідомі поля; якщо у вас є такі, їх потрібно буде перекомпілювати з v1.28+ версією клієнтського коду Kubernetes API.
Спосіб перевірки цього полягає у використанні команди kubectl describe pod для вашого Podʼа, який пройшов через мутуючий admission webhook. Якщо будь-які інструменти вилучили нове поле (restartPolicy: Always), ви не побачите його у виводі команди.
Якщо ви зіткнулися з такою проблемою, рекомендується повідомити автора інструментів або вебхуків, щоб вони використовували одну зі стратегій накладання патчів для модифікації обʼєктів замість їх повного оновлення.
Примітка
Мутуючий webhook може оновлювати Podʼи на основі певних умов. Таким чином, sidecar-контейнери можуть працювати для одних Podʼів, а для інших — ні.
Автоматичне встромляння sidecar-контейнерів
Якщо ви використовуєте програмне забезпечення, яке автоматично встромляє sidecar-контейнери, існує кілька можливих стратегій, які ви можете застосувати, щоб забезпечити можливість використання нативних sidecar-контейнерів. Усі ці стратегії загалом є варіантами вибору того, чи буде Pod, до якого встромляється sidecar, розміщений на вузлі, який підтримує цю функцію.
Позначення Podʼів, що розміщуються на вузлах із підтримкою sidecars. Ви можете використовувати мітки вузлів і node affinity, щоб позначити вузли, які підтримують sidecar-контейнери, і забезпечити розміщення Pods на цих вузлах.
Перевірка сумісності вузлів під час додавання контейнера. Під час впровадження sidecar-контейнерів можна використовувати такі стратегії перевірки сумісності вузлів:
Запитати версію вузла та припустити, що функціональну можливість увімкнено для версії 1.29+.
Запитати метрики Prometheus вузла та перевірити стан увімкнення функції.
Можуть існувати інші власні способи визначення сумісності вузлів.
Розробка універсального інжектора sidecar-контейнерів. Ідея універсального sidecar-контейнера полягає в тому, щоб додати sidecar-контейнер як звичайний контейнер, а також як нативний sidecar-контейнер, і мати логіку на рівні виконання, яка визначить, який варіант працюватиме. Універсальний інжектор sidecar є ресурсозатратним, оскільки він враховуватиме запити двічі, але його можна розглядати як робоче рішення для особливих випадків.
Один зі способів полягає в тому, щоб під час запуску нативного sidecar-контейнера визначити версію вузла та завершити роботу негайно, якщо версія не підтримує функцію sidecar.
Розгляньте дизайн із виявленням функції під час виконання:
Визначте empty dir, щоб контейнери могли взаємодіяти один з одним.
Впровадьте init-контейнер, який назвемо NativeSidecar, з restartPolicy=Always.
NativeSidecar має записати файл в empty dir під час першого запуску та завершити роботу з кодом виходу 0.
NativeSidecar під час повторного запуску (коли нативні sidecar підтримуються) перевіряє, чи файл уже існує в empty dir, і змінює його, вказуючи, що вбудовані sidecar-контейнери підтримуються і працюють.
Впровадьте звичайний контейнер, який назвемо OldWaySidecar.
OldWaySidecar під час запуску перевіряє наявність файлу в empty dir.
Якщо файл вказує, що NativeSidecar не працює, він припускає, що функція sidecar не підтримується, і працює як sidecar.
Якщо файл вказує, що NativeSidecar працює, він або не робить нічого та спить назавжди (у випадку, коли Pod має restartPolicy=Always), або завершить роботу негайно з кодом виходу 0 (у випадку, коли Pod має restartPolicy!=Always).
Безпека є важливою темою для більшості організацій та людей, які використовують кластери Kubernetes. Ви можете знайти базовий список перевірок безпеки в іншому місці документації Kubernetes.
Щоб дізнатися, як розгорнути та керувати аспектами безпеки Kubernetes, ви можете виконати практичні завдання в цьому розділі.
4.1 - Застосування стандартів безпеки Podʼів на рівні кластера
Примітка
Цей навчальний посібник застосовується лише для нових кластерів.
Безпека Pod покладається на контролер допуску, що виконує перевірки відповідно до Стандартів безпеки Podʼів в Kubernetes при створенні нових Podʼів. Це функція, має загальну доступність з випуску v1.25. Цей навчальний посібник показує, як застосувати стандарт безпеки baseline на рівні кластера, що застосовує стандартну конфігурацію для всіх просторів імен у кластері.
Цей навчальний посібник показує, як ви можете налаштувати кластер Kubernetes, який ви повністю контролюєте. Якщо ви вивчаєте, як налаштувати Pod Security Admission для кластера, що надається постачальником послуг, де ви не можете налаштувати панель управління, прочитайте Застосування стандартів безпеки Podʼів на рівні простору імен.
Kubernetes control plane is running at https://127.0.0.1:61350
CoreDNS is running at https://127.0.0.1:61350/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Отримайте список просторів імен у кластері:
kubectl get ns
Вивід подібний до цього:
NAME STATUS AGE
default Active 9m30s
kube-node-lease Active 9m32s
kube-public Active 9m32s
kube-system Active 9m32s
local-path-storage Active 9m26s
Використовуйте --dry-run=server, щоб зрозуміти, що відбувається при застосуванні різних стандартів безпеки Podʼів:
З попередніх результатів ви можете помітити, що застосування стандарту безпеки privileged не показує жодних попереджень для жодного простору імен. Однак для стандартів baseline і restricted є попередження, зокрема для простору імен kube-system.
Встановлення режимів, версій та стандартів
У цьому розділі ви застосовуєте наступні Стандарти безпеки Pod до версії latest:
Стандарт baseline у режимі enforce.
Стандарт restricted у режимах warn та audit.
Стандарт безпеки Pod baseline надає зручний проміжний рівень, який дозволяє тримати список винятків коротким та запобігає відомим підвищенням привілеїв.
Додатково, для запобігання витоку підсистеми kube-system, ви виключите простір імен з застосуванням Стандартів безпеки Pod.
При впровадженні перевірки безпеки Pod у власному середовищі оберіть такі пункти:
Враховуючи рівень ризику, застосований до кластера, строгий Стандарт безпеки Pod, наприклад restricted, може бути кращим вибором.
Виключення простору імен kube-system дозволяє підсистемам працювати з підвищеними привілеями у цьому просторі імен. Для реального використання проєкт Kubernetes наполегливо рекомендує вам застосовувати строгі політики RBAC, які обмежують доступ до kube-system, слідуючи принципу найменших привілеїв. Для впровадження вищезазначених стандартів виконайте наступне:
Створіть файл конфігурації, який може бути використаний Контролером допуску Стандартів безпеки Pod для застосування цих Стандартів безпеки Pod:
Creating cluster "psa-with-cluster-pss" ...
✓ Ensuring node image (kindest/node:v1.31.0) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-psa-with-cluster-pss"
You can now use your cluster with:
kubectl cluster-info --context kind-psa-with-cluster-pss
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
Kubernetes control plane is running at https://127.0.0.1:63855
CoreDNS is running at https://127.0.0.1:63855/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Pod запускається звичайно, але вивід містить попередження:
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/nginx created
Очищення
Тепер ви можете видалити кластери, які ви створили:
kind delete cluster --name psa-with-cluster-pss
kind delete cluster --name psa-wo-cluster-pss
Що далі
Виконайте
скрипт оболонки для виконання всіх попередніх кроків одночасно:
Створіть конфігурацію на рівні кластера на основі Стандартів безпеки Pod.
Створіть файл для того, щоб API-сервер міг використовувати цю конфігурацію.
Створіть кластер, який створює API-сервер з цією конфігурацією.
Встановіть контекст kubectl для цього нового кластера.
Створіть мінімальний файл yaml для Podʼів.
Застосуйте цей файл для створення Podʼів в новому кластері.
4.2 - Застосування Стандартів безпеки Pod на рівні простору імен
Примітка
Цей посібник застосовується лише для нових кластерів.
Pod Security Admission — це контролер допуску, який застосовує Стандарти безпеки Pod при створенні Podʼів. Це функція, яка є загально доступною з v1.25. У цьому посібнику ви будете застосовувати Стандарт безпеки Pod baseline, по одному простору імен за раз.
Creating cluster "psa-ns-level" ...
✓ Ensuring node image (kindest/node:v1.31.0) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-psa-ns-level"
You can now use your cluster with:
kubectl cluster-info --context kind-psa-ns-level
Not sure what to do next? 😅 Check out https://kind.sigs.k8s.io/docs/user/quick-start/
Встановіть контекст kubectl для нового кластера:
kubectl cluster-info --context kind-psa-ns-level
Вивід буде подібний до цього:
Kubernetes control plane is running at https://127.0.0.1:50996
CoreDNS is running at https://127.0.0.1:50996/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Створення простору імен
Створіть новий простір імен з назвою example:
kubectl create ns example
Вивід буде подібний до цього:
namespace/example created
Увімкнення перевірки Стандартів безпеки Pod для цього простору імен
Увімкніть Стандарти безпеки Pod на цьому просторі імен за допомогою підтримуваних міток, вбудованих в Pod Security Admission. На цьому кроці ви налаштуєте перевірку, щоб система попереджувала про Podʼи, які не відповідають останньої версії стандарту безпеки підсистеми baseline.
kubectl label --overwrite ns example \
pod-security.kubernetes.io/warn=baseline \
pod-security.kubernetes.io/warn-version=latest
Ви можете налаштувати кілька перевірок стандартів безпеки Podʼів для будь-якого простору імен за допомогою міток. Наступна команда буде застосовувати стандарт безпеки Pod baseline, але warn та audit для стандартів безпеки Pod restricted згідно з останньою версією (стандартне значення)
Створіть Pod з базовим стандартом в просторі імен example:
kubectl apply -n example -f https://k8s.io/examples/security/example-baseline-pod.yaml
Pod дійсно запускається; вивід містить попередження. Наприклад:
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/nginx created
Створіть Pod з базовим стандартом у просторі імен default:
Виконання стандартів безпеки Podʼів та налаштування попереджень було застосовано лише до простору імен example. Ви можете створити такий самий Pod в просторі імен default без будь-яких попереджень.
Очищення
Тепер видаліть кластер, який було створено:
kind delete cluster --name psa-ns-level
Що далі
Виконайте скрипт для виконання всіх попередніх кроків одночасно.
Створіть кластер kind.
Створіть новий простір імен.
Застосуйте стандарт безпеки підсистеми baseline в режимі enforce, при цьому застосовуючи стандарт безпеки підсистеми restricted також у режимі warn та audit.
Створіть новий Pod з застосованими стандартами безпеки Podʼів.
4.3 - Обмежте доступ контейнера до ресурсів за допомогою AppArmor
СТАН ФУНКЦІОНАЛУ:Kubernetes v1.31 [stable] (стандартно увімкнено: true)
Ця сторінка показує, як завантажити профілі AppArmor на ваших вузлах та забезпечити їх виконання в Podʼах. Щоб дізнатися більше про те, як Kubernetes може обмежувати Podʼи за допомогою AppArmor, див. Обмеження безпеки ядра Linux для Podʼів та контейнерів.
Цілі
Ознайомитись з прикладом того, як завантажити профіль на Вузол
Дізнайтеся, як забезпечити виконання профілю в Podʼі
Дізнайтеся, як перевірити, що профіль завантажено
Побачте, що відбувається, коли умови профілю порушуються
Побачте, що відбувається, якщо профіль не може бути завантажено
Перш ніж ви розпочнете
AppArmor — це необовʼязковий модуль ядра та функція Kubernetes, тому переконайтеся, що він підтримується на ваших вузлах перед продовженням:
Модуль ядра AppArmor увімкнено — для того, щоб ядро Linux застосовувало профіль AppArmor, модуль ядра AppArmor повинен бути встановлений та увімкнений. Декілька дистрибутивів стандартно вмикають модуль, такі як Ubuntu і SUSE, і багато інших надають опціональну підтримку. Щоб перевірити, чи модуль увімкнено, перевірте файл /sys/module/apparmor/parameters/enabled:
cat /sys/module/apparmor/parameters/enabled
Y
Kubelet перевіряє, чи AppArmor увімкнено на хості, перед тим як допустити Pod з явно налаштованим AppArmor.
Середовище виконання контейнерів підтримує AppArmor — всі загальні середовища виконання контейнерів, що підтримуються Kubernetes, мають підтримку AppArmor, включаючи containerd та CRI-O. Будь ласка, зверніться до відповідної документації середовища та перевірте, що кластер відповідає вимогам до використання AppArmor.
Профіль завантажено — AppArmor застосовується до Podʼа, вказуючи профіль AppArmor, з яким кожен контейнер повинен працювати. Якщо будь-які вказані профілі не завантажені в ядро, Kubelet відхилить Pod. Ви можете переглянути завантажені профілі на вузлі, перевіривши файл /sys/kernel/security/apparmor/profiles. Наприклад:
До версії Kubernetes v1.30, AppArmor вказувався через анотації. Використовуйте потрбну версію документації для перегляду документації з цим застарілим API.
Профілі AppArmor можна вказати на рівні Podʼа або на рівні контейнера. Профіль AppArmor контейнера перевагу перед профілем Podʼа.
Щоб перевірити, що профіль був застосований, ви можете перевірити, що кореневий процес контейнера працює з правильним профілем, переглянувши його атрибут proc:
Профіль повинен бути завантажений на всі вузли, оскільки ви не знаєте, де буде заплановано Pod. Для цього прикладу ви можете використовувати SSH для встановлення профілів, але існують інші методи, які обговорюються в Налаштуваннях вузлів з профілями.
# Цей приклад передбачає, що імена вузлів відповідають іменам хостів і доступні через SSH.NODES=($(kubectl get nodes -o name))for NODE in ${NODES[*]}; do ssh $NODE'sudo apparmor_parser -q <<EOF
#include <tunables/global>
profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
#include <abstractions/base>
file,
# Заборонити всі записи файлів.
deny /** w,
}
EOF'done
Потім запустіть простий Pod "Hello AppArmor" з профілем deny-write:
Хоча Pod було створено успішно, подальший огляд покаже, що він застряг в стані очікування:
kubectl describe pod hello-apparmor-2
Name: hello-apparmor-2
Namespace: default
Node: gke-test-default-pool-239f5d02-x1kf/10.128.0.27
Start Time: Tue, 30 Aug 2016 17:58:56 -0700
Labels: <none>
Annotations: container.apparmor.security.beta.kubernetes.io/hello=localhost/k8s-apparmor-example-allow-write
Status: Pending
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 10s default-scheduler Successfully assigned default/hello-apparmor to gke-test-default-pool-239f5d02-x1kf
Normal Pulled 8s kubelet Successfully pulled image "busybox:1.28" in 370.157088ms (370.172701ms including waiting)
Normal Pulling 7s (x2 over 9s) kubelet Pulling image "busybox:1.28"
Warning Failed 7s (x2 over 8s) kubelet Error: failed to get container spec opts: failed to generate apparmor spec opts: apparmor profile not found k8s-apparmor-example-allow-write
Normal Pulled 7s kubelet Successfully pulled image "busybox:1.28" in 90.980331ms (91.005869ms including waiting)
Event надає повідомлення про помилку з причиною, конкретне формулювання залежить від виконавчого середовища:
Warning Failed 7s (x2 over 8s) kubelet Error: failed to get container spec opts: failed to generate apparmor spec opts: apparmor profile not found
Адміністрування
Налаштування вузлів з профілями
Kubernetes 1.31 не надає вбудованих механізмів для завантаження профілів AppArmor на Вузли. Профілі можна завантажити через власну інфраструктуру або інструменти, такі як Оператор профілів безпеки Kubernetes.
Планувальник не знає, які профілі завантажені на який Вузол, тому повний набір профілів повинні бути завантажені на кожен Вузол. Альтернативним підходом є додавання мітки Вузла для кожного профілю (або клас профілів) на Вузлі та використання селектора вузла, щоб забезпечити виконання Podʼа на
Вузлі з потрібним профілем.
Створення профілів
Встановлення правильних профілів AppArmor може бути складною справою. На щастя, існують деякі інструменти, що допомагають у цьому:
aa-genprof та aa-logprof генерують правила профілю, моніторячи діяльність програми та логи та дозволяючи дії, які вона виконує. Додаткові інструкції надаються у документації AppArmor.
bane — генератор профілів AppArmor для Docker, який використовує спрощену мову профілю.
Для дослідження проблем з AppArmor ви можете перевірити системні логи, щоб побачити, що саме було заборонено. AppArmor логує вичерпні повідомлення в dmesg, а помилки зазвичай можна знайти в системних логах або за допомогою journalctl. Більше інформації надається в розділі AppArmor failures.
Вказівки щодо обмеження AppArmor
Увага:
До версії Kubernetes v1.30, AppArmor вказувався через анотації. Використовуйте відповідну версію документації для перегляду з цим застарілим API.
Профіль AppArmor у контексті безпеки
Ви можете вказати appArmorProfile як у контексті безпеки контейнера, так і в контексті безпеки Podʼа. Якщо профіль встановлено на ні Podʼа, він буде використовуватися як стандартний профіль всіх контейнерів у Podʼі (включаючи контейнери ініціалізації, допоміжний та тимчасовий контейнери). Якщо вказані профілі Podʼа та контейнера, буде використано профіль контейнера.
Профіль AppArmor має 2 поля:
type(обовʼязково) — вказує, який тип профілю AppArmor буде застосований. Дійсні варіанти:
Localhost
профіль, завантажений на вузол (вказаний як localhostProfile).
RuntimeDefault
типовий профіль виконавчого середовища контейнера.
Unconfined
без застосування AppArmor.
localhostProfile — Назва профілю, завантаженого на вузол, який слід використовувати. Профіль повинен бути попередньо налаштований на вузлі, щоб працювати. Цей параметр потрібно вказувати лише в разі, якщо type — Localhost.
4.4 - Обмеження системних викликів контейнера за допомогою seccomp
СТАН ФУНКЦІОНАЛУ:Kubernetes v1.19 [stable]
Seccomp походить від secure computing mode (безпечний режим обчислення) і є функцією ядра Linux з версії 2.6.12. Його можна використовувати для ізоляції привілеїв процесу, обмежуючи виклики, які він може здійснювати з простору користувача в ядро. Kubernetes дозволяє автоматично застосовувати профілі seccomp, завантажені на вузол, до ваших Podʼів та контейнерів.
Визначення необхідних привілеїв для ваших робочих навантажень може бути важким завданням. У цьому посібнику ви дізнаєтеся, як завантажувати профілі seccomp в локальний кластер Kubernetes, як застосовувати їх до Podʼа та як ви можете почати створювати профілі, які надають лише необхідні привілеї процесам вашого контейнера.
Цілі
Навчитися завантажувати профілі seccomp на вузол
Навчитися застосовувати профіль seccomp до контейнера
Спостерігати за аудитом системних викликів, здійснюваних процесом контейнера
Спостерігати поведінку при відсутності вказаного профілю
Спостерігати порушення профілю seccomp
Навчитися створювати деталізовані профілі seccomp
Навчитися застосовувати стандартний профіль seccomp для контейнера
Перш ніж ви розпочнете
Для завершення всіх кроків у цьому посібнику вам потрібно встановити kind та kubectl.
Команди, які використовуються в посібнику, передбачають, що ви використовуєте Docker як ваше середовище для виконання контейнерів. (Кластер, який створює kind, може використовувати інше контейнерне середовище також). Ви також можете використовувати Podman, але у цьому випадку вам доведеться дотримуватися відповідних інструкцій, щоб успішно виконати завдання.
У цьому посібнику показано деякі приклади, які є бета-версією (починаючи з v1.25) і інші, які використовують лише загальнодоступну функціональність seccomp. Вам слід переконатися, що ваш кластер правильно налаштований для версії, яку ви використовуєте.
У посібнику також використовується інструмент curl для завантаження прикладів на ваш компʼютер. Ви можете адаптувати кроки, щоб використовувати інший інструмент, якщо вам це зручно.
Примітка:
Неможливо застосувати профіль seccomp до контейнера, який працює з параметром privileged: true, встановленим в securityContext контейнера. Привілейовані контейнери завжди виконуються як Unconfined.
Завантаження прикладів профілів seccomp
Вміст цих профілів буде досліджено пізніше, але наразі продовжте та завантажте їх у каталог з назвою profiles/, щоб їх можна було завантажити в кластер.
Ви повинні побачити три профілі, перераховані в кінці останнього кроку:
audit.json fine-grained.json violation.json
Створення локального кластера Kubernetes за допомогою kind
Для спрощення можна використовувати kind, щоб створити одновузловий кластер з завантаженими профілями seccomp. Kind запускає Kubernetes в Docker, тому кожен вузол кластера є контейнером. Це дозволяє монтувати файли в файлову систему кожного контейнера, схоже на завантаження файлів на вузол.
Ви можете встановити конкретну версію Kubernetes, встановивши образ контейнера вузла. Дивіться Вузли в документації kind щодо конфігурації для отримання більш детальної інформації з цього питання. Цей посібник передбачає, що ви використовуєте Kubernetes v1.31.
Як тільки у вас буде конфігурація kind, створіть кластер kind з цією конфігурацією:
kind create cluster --config=kind.yaml
Після створення нового кластера Kubernetes ідентифікуйте контейнер Docker, що працює як одновузловий кластер:
docker ps
Ви повинні побачити вивід, який вказує на те, що контейнер працює з назвою kind-control-plane, подібний до:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6a96207fed4b kindest/node:v1.18.2 "/usr/local/bin/entr…" 27 seconds ago Up 24 seconds 127.0.0.1:42223->6443/tcp kind-control-plane
Якщо спостерігати файлову систему цього контейнера, ви побачите, що тека profiles/ була успішно завантажена в стандартний шлях seccomp з kubelet. Використовуйте docker exec, щоб виконати команду в Podʼі:
# Змініть 6a96207fed4b на ідентифікатор контейнера, який ви знайдете в результаті "docker ps"docker exec -it 6a96207fed4b ls /var/lib/kubelet/seccomp/profiles
audit.json fine-grained.json violation.json
Ви перевірили, що ці профілі seccomp доступні для kubelet, що працює всередині kind.
Створення Pod, що використовує стандартний профіль seccomp середовища виконання контейнерів
Більшість контейнерних середовищ надають типовий перелік системних викликів, які дозволені або заборонені. Ви можете використовувати ці стандартні налаштування для вашого робочого навантаження, встановивши тип seccomp у контексті безпеки Pod або контейнера на RuntimeDefault.
Примітка:
Якщо у вас увімкнуто параметр конфігураціїseccompDefault, то Podʼи використовують профіль seccomp RuntimeDefault, якщо не вказано жодного іншого профілю seccomp. В іншому випадку, використовується Unconfined.
Ось маніфест Podʼа, який запитує профіль seccomp RuntimeDefault для всіх своїх контейнерів:
apiVersion:v1kind:Podmetadata:name:default-podlabels:app:default-podspec:securityContext:seccompProfile:type:RuntimeDefaultcontainers:- name:test-containerimage:hashicorp/http-echo:1.0args:- "-text=just made some more syscalls!"securityContext:allowPrivilegeEscalation:false
apiVersion:v1kind:Podmetadata:name:audit-podlabels:app:audit-podspec:securityContext:seccompProfile:type:LocalhostlocalhostProfile:profiles/audit.jsoncontainers:- name:test-containerimage:hashicorp/http-echo:1.0args:- "-text=just made some syscalls!"securityContext:allowPrivilegeEscalation:false
Примітка:
Старі версії Kubernetes дозволяли налаштовувати поведінку seccomp за допомогою анотацій. Kubernetes 1.31 підтримує лише використання полів у межах .spec.securityContext для налаштування seccomp, і цей посібник пояснює саме цей підхід.
Цей профіль не обмежує жодні системні виклики, тому Pod повинен успішно запуститися.
kubectl get pod audit-pod
NAME READY STATUS RESTARTS AGE
audit-pod 1/1 Running 0 30s
Щоб мати можливість взаємодіяти з цим точкою доступу, створіть Service NodePort, який дозволяє доступ до точки доступу зсередини контейнера панелі управління kind.
kubectl expose pod audit-pod --type NodePort --port 5678
Перевірте, який порт було призначено Service на вузлі.
kubectl get service audit-pod
Вивід буде схожим на:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
audit-pod NodePort 10.111.36.142 <none> 5678:32373/TCP 72s
Тепер ви можете використовувати curl, щоб отримати доступ до цієї точки доступу зсередини контейнера панелі управління kind на порту, який було відкрито цим Service. Використовуйте docker exec, щоб виконати команду curl всередині контейнера панелі управління:
# Змініть 6a96207fed4b на ідентифікатор контейнера панелі управління та 32373 на номер порту, який ви побачили у "docker ps"docker exec -it 6a96207fed4b curl localhost:32373
just made some syscalls!
Ви можете бачити, що процес працює, але які саме системні виклики він робить? Оскільки цей Pod працює у локальному кластері, ви повинні бачити їх у /var/log/syslog у вашій локальній системі. Відкрийте нове вікно термінала та використовуйте команду tail, щоб переглянути виклики від http-echo:
# Шлях до логу на вашому компʼютері може відрізнятися від "/var/log/syslog"tail -f /var/log/syslog | grep 'http-echo'
Ви вже повинні бачити деякі логи системних викликів, зроблених http-echo, і якщо ви знову запустите curl всередині контейнера панелі управління, ви побачите більше записів у лозі.
Ви можете почати розуміти системні виклики, необхідні для процесу http-echo, переглядаючи запис syscall= на кожному рядку. Хоча ці виклики навряд чи охоплюють усі системні виклики, які він використовує, це може слугувати основою для профілю seccomp для цього контейнера.
Видаліть Service та Pod перед переходом до наступного розділу:
kubectl delete service audit-pod --wait
kubectl delete pod audit-pod --wait --now
Створення Podʼа з профілем seccomp, що спричиняє порушення правил
Для демонстрації застосуйте до Podʼа профіль, який не дозволяє жодних системних викликів.
apiVersion:v1kind:Podmetadata:name:violation-podlabels:app:violation-podspec:securityContext:seccompProfile:type:LocalhostlocalhostProfile:profiles/violation.jsoncontainers:- name:test-containerimage:hashicorp/http-echo:1.0args:- "-text=just made some syscalls!"securityContext:allowPrivilegeEscalation:false
Pod буде створено, але виникне проблема. Якщо ви перевірите стан Podʼа, ви побачите, що його не вдалося запустити.
kubectl get pod violation-pod
NAME READY STATUS RESTARTS AGE
violation-pod 0/1 CrashLoopBackOff 1 6s
Як видно з попереднього прикладу, процес http-echo потребує досить багато системних викликів. У цьому випадку seccomp було налаштовано на помилку при будь-якому системному виклику, встановивши "defaultAction": "SCMP_ACT_ERRNO". Це надзвичайно безпечно, але робить неможливим виконання будь-яких значущих дій. Насправді ви хочете надати робочим навантаженням тільки ті привілеї, які їм потрібні.
Видаліть Pod перед переходом до наступного розділу:
kubectl delete pod violation-pod --wait --now
Створення Podʼа з профілем seccomp, що дозволяє лише необхідні системні виклики
Якщо ви подивитеся на профіль fine-grained.json, ви помітите деякі з системних викликів, які спостерігалися в syslog у першому прикладі, де профіль встановлював "defaultAction": "SCMP_ACT_LOG". Тепер профіль встановлює "defaultAction": "SCMP_ACT_ERRNO", але явно дозволяє набір системних викликів у блоці "action": "SCMP_ACT_ALLOW". Ідеально, якщо контейнер буде успішно працювати, і ви не побачите жодних повідомлень у syslog.
apiVersion:v1kind:Podmetadata:name:fine-podlabels:app:fine-podspec:securityContext:seccompProfile:type:LocalhostlocalhostProfile:profiles/fine-grained.jsoncontainers:- name:test-containerimage:hashicorp/http-echo:1.0args:- "-text=just made some syscalls!"securityContext:allowPrivilegeEscalation:false
NAME READY STATUS RESTARTS AGE
fine-pod 1/1 Running 0 30s
Відкрийте нове вікно термінала і використовуйте tail для моніторингу записів лога, які згадують виклики від http-echo:
# Шлях до лога на вашому компʼютері може відрізнятися від "/var/log/syslog"tail -f /var/log/syslog | grep 'http-echo'
Далі, опублікуйте Pod за допомогою Service NodePort:
kubectl expose pod fine-pod --type NodePort --port 5678
Перевірте, який порт було призначено для цього сервісу на вузлі:
kubectl get service fine-pod
Вихідні дані можуть виглядати приблизно так:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
fine-pod NodePort 10.111.36.142 <none> 5678:32373/TCP 72s
Використовуйте curl для доступу до цієї точки доступу з контейнера панелі управління kind:
# Змініть 6a96207fed4b на ID контейнера панелі управління і 32373 на номер порту, який ви побачили у "docker ps"docker exec -it 6a96207fed4b curl localhost:32373
just made some syscalls!
Ви не повинні побачити жодних записів у syslog. Це тому, що профіль дозволив усі необхідні системні виклики та вказав, що повинна статися помилка, якщо буде викликано виклик, який не входить до списку. Це ідеальна ситуація з погляду безпеки, але потребує певних зусиль для аналізу програми. Було б добре, якби існував простий спосіб наблизитися до такої безпеки без стількох зусиль.
Видаліть Service і Pod перед переходом до наступного розділу:
kubectl delete service fine-pod --wait
kubectl delete pod fine-pod --wait --now
Увімкнення використання RuntimeDefault як стандартного профілю seccomp для всіх робочих навантажень
СТАН ФУНКЦІОНАЛУ:Kubernetes v1.27 [stable]
Для використання стандартного профілю seccomp, необхідно запустити kubelet з параметром командного рядка --seccomp-default увімкненим для кожного вузла, де ви хочете його використовувати.
Якщо ця функція увімкнена, kubelet буде використовувати як типовий профіль seccomp RuntimeDefault, який визначений середовищем виконання контейнерів, замість використання режиму Unconfined (seccomp вимкнений). Типові профілі прагнуть забезпечити сильний набір налаштувань безпеки, зберігаючи функціональність робочих навантажень. Можливо, типові профілі відрізняються між середовищами виконання контейнерів та їх версіями випуску, наприклад, порівнюючи профілів CRI-O та containerd.
Примітка:
Увімкнення цієї функції не змінює поле API securityContext.seccompProfile у Kubernetes і не додає застарілі анотації робочих навантажень. Це дає користувачам можливість повернутися до попереднього стану в будь-який час без фактичної зміни конфігурації робочих навантажень. Такі інструменти, як crictl inspect, можуть бути використані для перевірки, який профіль seccomp використовується контейнером.
Деякі робочі навантаження можуть вимагати меншої кількості обмежень системних викликів, ніж інші. Це означає, що вони можуть зазнати невдачі під час виконання навіть із профілем RuntimeDefault. Для помʼякшення таких невдач можна:
Запустити робоче навантаження явно як Unconfined.
Вимкнути функцію SeccompDefault для вузлів, забезпечуючи, щоб робочі навантаження виконувалися на вузлах, де ця функція вимкнена.
Створити власний профіль seccomp для робочого навантаження.
Якщо ви вводите цю функцію до кластера подібного до операційного, проєкт Kubernetes рекомендує увімкнути цю функцію на підмножині ваших вузлів та перевірити виконання робочих навантажень перед повсюдним впровадженням змін.
Детальнішу інформацію про можливу стратегію оновлення та відкату ви можете знайти в повʼязаній пропозиції щодо поліпшення Kubernetes (KEP): Увімкнення seccomp стандартно.
Kubernetes 1.31 дозволяє налаштувати профіль seccomp, який застосовується, коли специфікація для Podʼа не визначає конкретний профіль seccomp. Однак, вам все одно потрібно увімкнути це налаштування як типове для кожного вузла, де ви хочете його використовувати.
Якщо ви працюєте у кластері Kubernetes 1.31 і хочете увімкнути цю функцію, ви можете запустити kubelet з параметром командного рядка --seccomp-default або увімкнути її через файл конфігурації kubelet. Щоб увімкнути цю функцію у kind, переконайтеся, що kind забезпечує мінімально необхідну версію Kubernetes і вмикає функцію SeccompDefaultу конфігурації kind:
kubectl run --rm -it --restart=Never --image=alpine alpine -- sh
тепер він повинен мати прикріплений типовий профіль seccomp. Це можна перевірити, використовуючи docker exec для запуску crictl inspect для контейнера на вузлі kind:
Використовуйте хмарного провайдера, такого як Google Kubernetes Engine або Amazon Web Services, щоб створити кластер Kubernetes. У цьому підручнику створюється зовнішній балансувальник навантаження, який вимагає хмарного провайдера.
Налаштуйте kubectl для спілкування з вашим API-сервером Kubernetes. Для інструкцій дивіться документацію вашого хмарного провайдера.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service LoadBalancer 10.3.245.137 104.198.205.71 8080/TCP 54s
Примітка:
Сервіс типу type=LoadBalancer підтримується зовнішніми хмарними провайдерами, що не розглядається в цьому прикладі, будь ласка, зверніться до цієї сторінки для деталей.
Примітка:
Якщо зовнішня IP-адреса показується як <pending>, зачекайте хвилину та введіть ту саму команду знову.
Занотуйте зовнішню IP-адресу (LoadBalancer Ingress), відкриту вашим сервісом. У цьому прикладі, зовнішня IP-адреса — 104.198.205.71. Також занотуйте значення Port і NodePort. У цьому прикладі, Port — 8080, а NodePort — 32377.
У попередньому виводі ви можете побачити, що сервіс має кілька точок доступу: 10.0.0.6:8080, 10.0.1.6:8080, 10.0.1.7:8080 + ще 2. Це внутрішні адреси Podʼів, які запускають застосунок Hello World. Щоб переконатися, що це адреси Podʼів, введіть цю команду:
Використовуйте зовнішню IP-адресу (LoadBalancer Ingress) для доступу до застосунку Hello World:
curl http://<external-ip>:<port>
де <external-ip> — це зовнішня IP-адреса (LoadBalancer Ingress) вашого сервісу, а <port> — значення Port в описі вашого сервісу. Якщо ви використовуєте minikube, ввівши minikube service my-service автоматично відкриє застосунок Hello World в оглядачі.
Відповідь на успішний запит — привітальне повідомлення:
5.2 - Приклад: Розгортання PHP застосунку гостьової книги з Redis
Цей посібник показує, як створити та розгорнути простий (але не готовий до промислового використання) багатошаровий вебзастосунок, використовуючи Kubernetes та Docker. Цей приклад складається з наступних компонентів:
Одно-екземплярний Redis для зберігання записів у гостьовій книзі
Кілька веб-фронтендів
Цілі
Запустити Redis-лідер.
Запустити двох Redis-фолловерів.
Запустити фронтенд гостьової книги.
Опублікувати та переглянути Frontend Service.
Виконати очищення.
Перш ніж ви розпочнете
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Перевірте список Podʼів, щоб переконатися, що Pod Redis запущений:
kubectl get pods
Відповідь повинна бути схожою на цю:
NAME READY STATUS RESTARTS AGE
redis-leader-fb76b4755-xjr2n 1/1 Running 0 13s
Виконайте наступну команду, щоб переглянути лог з Podʼа Redis лідера:
kubectl logs -f deployment/redis-leader
Створення Service Redis-лідера
Застосунок гостьової книги потребує звʼязку з Redis для запису своїх даних. Вам потрібно застосувати Service, щоб спрямовувати трафік до Pod Redis. Service визначає політику доступу до Podʼів.
Перевірте список Serviceʼів, щоб переконатися, що Service Redis запущений:
kubectl get service
Відповідь повинна бути схожою на цю:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 1m
redis-leader ClusterIP 10.103.78.24 <none> 6379/TCP 16s
Примітка:
Цей файл маніфесту створює Service з іменем redis-leader з набором міток, які відповідають раніше визначеним міткам, тому Service спрямовує мережевий трафік до Pod Redis.
Налаштування Redis-фолловерів
Хоча Redis-лідер є одним Pod, ви можете зробити його високо доступним і задовольняти потреби в трафіку, додавши кілька фолловерів Redis або реплік.
Перевірте, що дві репліки Redis-фолловерів запущені, виконавши запит списку Podʼів:
kubectl get pods
Відповідь повинна бути схожою на цю:
NAME READY STATUS RESTARTS AGE
redis-follower-dddfbdcc9-82sfr 1/1 Running 0 37s
redis-follower-dddfbdcc9-qrt5k 1/1 Running 0 38s
redis-leader-fb76b4755-xjr2n 1/1 Running 0 11m
Створення Service Redis-фолловера
Застосунок гостьової книги потребує зʼязку з фолловерами Redis для читання даних. Щоб зробити фолловерів Redis доступними, потрібно налаштувати інший Service.
# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbookapiVersion:v1kind:Servicemetadata:name:redis-followerlabels:app:redisrole:followertier:backendspec:ports:# порт на якому повинен працювати цей Service- port:6379selector:app:redisrole:followertier:backend
Застосуйте сервіс Redis з файлу redis-follower-service.yaml:
Перевірте список Serviceʼів, щоб переконатися, що Service Redis запущений:
kubectl get service
Відповідь повинна бути схожою на цю:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d19h
redis-follower ClusterIP 10.110.162.42 <none> 6379/TCP 9s
redis-leader ClusterIP 10.103.78.24 <none> 6379/TCP 6m10s
Примітка:
Цей файл маніфесту створює Service з іменем redis-follower з набором міток, які відповідають раніше визначеним міткам, тому сервіс спрямовує мережевий трафік до Pod Redis.
Налаштування та експонування фронтенда гостьової книги
Тепер, коли ви запустили сховище Redis для своєї гостьової книги, запустіть вебсервери фронтенду. Як і фолловери Redis, фронтенд розгортається за допомогою контролера Deployment Kubernetes.
Застосунок гостьової книги використовує PHP фронтенд. Він налаштований для звʼязку з Serviceʼами фолловера або лідера Redis, залежно від того, чи є запит читанням або записом. Фронтенд відкриває інтерфейс JSON та забезпечує UX на основі jQuery-Ajax.
Перевірте список Podʼів, щоб переконатися, що три репліки фронтенду запущені:
kubectl get pods -l app=guestbook -l tier=frontend
Відповідь повинна бути схожою на цю:
NAME READY STATUS RESTARTS AGE
frontend-85595f5bf9-5tqhb 1/1 Running 0 47s
frontend-85595f5bf9-qbzwm 1/1 Running 0 47s
frontend-85595f5bf9-zchwc 1/1 Running 0 47s
Створення Service для Фронтенду
Serviceʼи Redis, які ви створили, доступні тільки всередині кластера Kubernetes, оскільки типовий тип для Service — ClusterIP. ClusterIP надає одну IP-адресу для набору Podʼів, на які вказує Service. Ця IP-адреса доступна тільки всередині кластера.
Якщо ви хочете, щоб гості могли отримати доступ до вашої гостьової книги, ви повинні налаштувати Service фронтенду таким чином, щоб він був видимий зовні, щоб клієнт міг запитувати Service ззовні кластера Kubernetes. Проте користувачі Kubernetes можуть скористатися командою kubectl port-forward, щоб отримати доступ до Service, навіть якщо він використовує ClusterIP.
Примітка:
Деякі хмарні провайдери, такі як Google Compute Engine або Google Kubernetes Engine, підтримують зовнішні балансувальники навантаження. Якщо ваш хмарний провайдер підтримує балансувальники навантаження і ви хочете його використовувати, розкоментуйте type: LoadBalancer.
# SOURCE: https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbookapiVersion:v1kind:Servicemetadata:name:frontendlabels:app:guestbooktier:frontendspec:# якщо ваш кластер підтримує це, розкоментуйте наступне, щоб автоматично створити# зовнішню IP-адресу з балансуванням навантаження для служби frontend.# type: LoadBalancer#type: LoadBalancerports:# порт на якому має працювати цей Service- port:80selector:app:guestbooktier:frontend
Застосуйте Service фронтенду з файлу frontend-service.yaml:
Виконайте запит для отримання списку Service, щоб переконатися, що Service фронтенду запущений:
kubectl get services
Відповідь повинна бути схожою на цю:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend ClusterIP 10.97.28.230 <none> 80/TCP 19s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d19h
redis-follower ClusterIP 10.110.162.42 <none> 6379/TCP 5m48s
redis-leader ClusterIP 10.103.78.24 <none> 6379/TCP 11m
Перегляд Service Фронтенду через kubectl port-forward
Виконайте наступну команду, щоб перенаправити порт 8080 на вашій локальній машині на порт 80 на сервісі.
kubectl port-forward svc/frontend 8080:80
Відповідь повинна бути схожою на цю:
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Завантажте сторінку http://localhost:8080 у вашому оглядачі, щоб переглянути вашу гостьову книгу.
Перегляд Service Фронтенду через LoadBalancer
Якщо ви розгорнули маніфест frontend-service.yaml з типом LoadBalancer, вам потрібно знайти IP-адресу, щоб переглянути вашу гостьову книгу.
Виконайте наступну команду, щоб отримати IP-адресу для Service фронтенду.
kubectl get service frontend
Відповідь повинна бути схожою на цю:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend LoadBalancer 10.51.242.136 109.197.92.229 80:32372/TCP 1m
Скопіюйте зовнішню IP-адресу та завантажте сторінку у вашому оглядачі, щоб переглянути вашу гостьову книгу.
Примітка:
Спробуйте додати кілька записів у гостьову книгу, ввівши повідомлення та натиснувши "Submit". Повідомлення, яке ви ввели, зʼявиться на фронтенді. Це повідомлення свідчить про те, що дані успішно додані до Redis через Serviceʼи, які ви створили раніше.
Масштабування Веб-Фронтенду
Ви можете збільшувати або зменшувати кількість реплік за потребою, оскільки ваші сервери визначені як Service, що використовує контролер Deployment.
Виконайте наступну команду, щоб збільшити кількість Podʼів фронтенду:
kubectl scale deployment frontend --replicas=5
Виконайте запит для отримання списку Podʼів, щоб перевірити кількість запущених Podʼів фронтенду:
Цей підручник надає вступ до управління застосунками за допомогою StatefulSets. Він демонструє, як створювати, видаляти, масштабувати та оновлювати Podʼи StatefulSets.
Перш ніж ви розпочнете
Перш ніж розпочати цей підручник, вам слід ознайомитися з наступними концепціями Kubernetes:
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Вам слід налаштувати kubectl для використання контексту, який використовує простір імен default. Якщо ви використовуєте наявний кластер, переконайтеся, що можна використовувати
простір імен цього кластера для практики. Ідеальною буде практика в кластері, де не запущені реальні робочі навантаження.
Також корисно прочитати сторінку з концепціями про StatefulSets.
Примітка:
Цей підручник передбачає, що ваш кластер налаштований на динамічне забезпечення
PersistentVolumes. Вам також потрібно мати типовий StorageClass. Якщо ваш кластер не налаштований для динамічного забезпечення сховища, вам доведеться вручну забезпечити два томи по 1 GiB перед початком цього уроку та налаштувати ваш кластер так, щоб ці PersistentVolumes відповідали шаблонам PersistentVolumeClaim, які визначає StatefulSet.
Цілі
StatefulSets призначені для використання з застосунками, які зберігаються свій стан, та розподіленими системами. Однак, адміністрування стану застосунків та розподілених систем у Kubernetes є широкою та складною темою. Щоб продемонструвати базові можливості StatefulSet і не змішувати першу тему з другою, ви розгорнете простий вебзастосунок за допомогою StatefulSet.
Після цього уроку ви будете знайомі з наступним.
Як створити StatefulSet
Як StatefulSet керує своїми Podʼами
Як видалити StatefulSet
Як масштабувати StatefulSet
Як оновлювати Podʼи StatefulSet
Створення StatefulSet
Почніть зі створення StatefulSet (і Service, на який він спирається) за допомогою
наведеного нижче прикладу. Він схожий на приклад, наведений у концепції StatefulSets. Він створює headless Service, nginx, щоб опублікувати IP-адреси Podʼів у StatefulSet, web.
Вам знадобляться принаймні два термінали. У першому терміналі використовуйте kubectl get для спостереження (watch) за створенням Podʼів StatefulSet.
# використовуйте цей термінал для виконання команд із зазначенням --watch# завершіть цей watch, коли вам буде запропоновано розпочати новий watchkubectl get pods --watch -l app=nginx
У другому терміналі використовуйте kubectl apply для створення headless Service та StatefulSet:
service/nginx created
statefulset.apps/web created
Наведена вище команда створює два Podʼи, кожен з яких запускає вебсервер NGINX. Отримайте інформацію про nginx Service…
kubectl get service nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 12s
…потім отримайте інформацію про web StatefulSet, щоб переконатися, що обидва були створені успішно:
kubectl get statefulset web
NAME READY AGE
web 2/2 37s
Упорядковане створення Podʼів
Типово StatefulSet створює свої Podʼи в строгому порядку.
Для StatefulSet з n репліками, коли Podʼи розгортаються, вони створюються послідовно, впорядковані від {0..n-1}. Перегляньте вивід команди kubectl get у першому терміналі. Зрештою вивід буде виглядати як у наведеному нижче прикладі.
# Не починайте новий watch;# це вже повинно працюватиkubectl get pods --watch -l app=nginx
Щоб налаштувати цілочисельний порядковий номер, призначений кожному Pod у StatefulSet, дивіться Початковий порядковий номер.
Podʼи в StatefulSet
Podʼи в StatefulSet мають унікальний порядковий індекс та стабільну мережеву ідентичність.
Перевірка порядкового індексу Podʼів
Отримайте Podʼи StatefulSet:
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 1m
web-1 1/1 Running 0 1m
Як зазначено в концепції StatefulSets, Podʼи в StatefulSet мають фіксовану, унікальну ідентичність. Ця ідентичність базується на унікальному порядковому індексі, який призначається кожному Podʼу контролером StatefulSet controller. Імена Podʼів приймають форму <імʼя statefulset>-<порядковий індекс>. Оскільки StatefulSet web має дві репліки, він створює два Podʼи, web-0 та web-1.
Використання стабільних мережевих ідентичностей
Кожен Pod має стабільне імʼя хосту на основі свого порядкового індексу. Використовуйте kubectl exec для виконання команди hostname в кожному Podʼі:
for i in 0 1; do kubectl exec"web-$i" -- sh -c 'hostname'; done
web-0
web-1
Використайте kubectl run для запуску контейнера, який надає команду nslookup з пакунка dnsutils. Використовуючи nslookup з іменами хостів Podʼів, ви можете переглянути їх внутрішні адреси DNS:
kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
що запускає нову оболонку. У цій новій оболонці запустіть:
# Виконайте це в оболонці контейнера dns-testnslookup web-0.nginx
CNAME headless сервісу вказує на SRV записи (один для кожного Podʼа, який виконується та готовий). SRV записи вказують на записи A, які містять IP-адреси Podʼів.
У одному терміналі спостерігайте за Podʼами StatefulSet:
# Розпочніть новий watch# Завершіть цей watch, коли бачите, що видалення завершеноkubectl get pod --watch -l app=nginx
У другому терміналі використовуйте kubectl delete для видалення всіх Podʼів у StatefulSet:
kubectl delete pod -l app=nginx
Pod "web-0" видалено
Pod "web-1" видалено
Чекайте, поки StatefulSet перезапустить їх, і обидва Podʼи перейдуть до стану Running та Ready:
# Це вже має працюватиkubectl get pod --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 0/1 ContainerCreating 0 0s
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 34s
Використовуйте kubectl exec та kubectl run, щоб переглянути імена хостів Podʼів та внутрішні
DNS-записи. Спочатку перегляньте імена хостів Podʼів:
for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1
потім, запустіть:
kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
що запускає новиe оболонку. У цій новій оболонці запустіть:
# Виконайте це в оболонці контейнера dns-testnslookup web-0.nginx
Порядкові номери, імена хостів Podʼів, SRV записи та імена записів A не змінилися, але IP-адреси, повʼязані з Podʼами, можуть змінюватися. У кластері, який використовується для цього навчального посібника, вони змінюються. Тому важливо не налаштовувати інші застосунки на підʼєднання до Podʼів у StatefulSet за IP-адресою конкретного Podʼа (можна підключатися до Podʼів, за їх іменем хосту).
Виявлення конкретних Podʼів у StatefulSet
Якщо вам потрібно знайти та підʼєднатись до активних учасників StatefulSet, вам слід запитувати CNAME headless Service (nginx.default.svc.cluster.local). SRV записи, повʼязані з CNAME, будуть містити лише Podʼ у StatefulSet, які виконуються та готові.
Якщо ваш застосунок вже реалізував логіку підʼєднання, яка перевіряє працездатність та готовність, ви можете використовувати SRV записи Podʼів (web-0.nginx.default.svc.cluster.local, web-1.nginx.default.svc.cluster.local), оскільки вони стабільні, і ваш застосунок зможе виявляти адреси Podʼів, коли вони переходять до стану Running та Ready.
Якщо ваш застосунок хоче знайти будь-який здоровий Pod у StatefulSet і, отже, не потрібно відстежувати кожний конкретний Pod, ви також можете підʼєднатись до IP-адреси type: ClusterIP сервісу, Podʼів в цьому StatefulSet. Ви можете використовувати той самий Service, який відстежує StatefulSet (вказаний у serviceName StatefulSet) або окремий Service, який вибирає відповідний набір Podʼів.
Запис у стійке сховище
Отримайте PersistentVolumeClaims для web-0 та web-1:
kubectl get pvc -l app=nginx
Вивід схожий на:
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
www-web-0 Bound pvc-15c268c7-b507-11e6-932f-42010a800002 1Gi RWO 48s
www-web-1 Bound pvc-15c79307-b507-11e6-932f-42010a800002 1Gi RWO 48s
Оскільки кластер, використаний у цьому посібнику, налаштований на динамічну підготовку PersistentVolumes, PersistentVolumes були створені та привʼязані автоматично.
NGINX вебсервер типово обслуговує індексний файл із /usr/share/nginx/html/index.html. Поле volumeMounts в spec StatefulSet забезпечує, що тека /usr/share/nginx/html є зберігається у PersistentVolume.
Запишіть імена хостів Podʼів у їх файли index.html та перевірте, що вебсервери NGINX обслуговують імена хостів:
for i in 0 1; do kubectl exec"web-$i" -- sh -c 'echo "$(hostname)" > /usr/share/nginx/html/index.html'; donefor i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
Примітка:
Якщо замість цього ви бачите відповіді 403 Forbidden для вищезазначеної команди curl, вам потрібно виправити дозволи для теки, змонтованого volumeMounts (через помилку під час використання томів hostPath),
запустивши:
for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done
перш ніж повторити команду curl вище.
У одному терміналі спостерігайте за Podʼами StatefulSet:
# Завершіть цей watch, коли ви дійдете до кінця розділу.# На початку "Scaling a StatefulSet" ви розпочнете новий watch.kubectl get pod --watch -l app=nginx
У другому терміналі видаліть всі Podʼи StatefulSet:
kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted
Дослідіть вивід команди kubectl get у першому терміналі та зачекайте, поки всі Podʼи перейдуть до стану Running та Ready.
# Це вже має працюватиkubectl get pod --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 0/1 ContainerCreating 0 0s
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 34s
Перевірте, чи продовжують вебсервери обслуговувати свої імена хостів:
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
Навіть якщо web-0 та web-1 переплановані, вони продовжують обслуговувати свої імена хостів, оскільки PersistentVolumes, повʼязані з їхніми PersistentVolumeClaims, знову монтується до їхніх volumeMounts. Незалежно від того, на якому вузлі заплановані web-0 та web-1, їхні PersistentVolumes будуть підключені до відповідних точок монтування.
Масштабування StatefulSet
Масштабування StatefulSet передбачає збільшення або зменшення кількості реплік (горизонтальне масштабування). Це досягається шляхом оновлення поля replicas. Ви можете використовувати або kubectl scale, або kubectl patch, щоб масштабувати StatefulSet.
Збільшення масштабу
Збільшення масштабу означає додавання додаткових реплік. Забезпечивши те, що ваш застосунок може розподіляти роботу між StatefulSet, новий більший набір Podʼів може виконувати більше цієї роботи.
У одному вікні термінала спостерігайте за Podʼами у StatefulSet:
# Якщо у вас вже запущений watch, ви можете продовжити його використовувати.# В іншому випадку запустіть один.# Закінчіть цей watch, коли для StatefulSet буде 5 справних Podʼівkubectl get pods --watch -l app=nginx
У іншому вікні термінала використовуйте kubectl scale, щоб змінити кількість реплік на 5:
kubectl scale sts web --replicas=5
statefulset.apps/web scaled
Дослідіть вивід команди kubectl get у першому терміналі та зачекайте, доки три додаткові Podʼи перейдуть у стан Running та Ready.
# Це вже має працюватиkubectl get pod --watch -l app=nginx
Контролер StatefulSet змінив кількість реплік. Як і при створенні StatefulSet, контролер StatefulSet створював кожен Pod послідовно з урахуванням його порядкового індексу, і чекав, доки попередній Pod перейде у стан Running та Ready перед запуском наступного Podʼа.
Зменшення масштабу
Зменшення масштабу означає зменшення кількості реплік. Наприклад, ви можете це зробити через те, що рівень трафіку до служби зменшився, і на поточному масштабі є ресурси, що простоюють.
У одному вікні термінала спостерігайте за Podʼами у StatefulSet:
# Закінчіть цей watch, коли лишиться лише 3 Podʼи для StatefulSetkubectl get pod --watch -l app=nginx
У іншому вікні термінала використовуйте kubectl patch, щоб зменшити кількість реплік StatefulSet до трьох:
kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched
Зачекайте, доки web-4 і web-3 перейдуть у стан Terminating.
# Це вже має працюватиkubectl get pods --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 3h
web-1 1/1 Running 0 3h
web-2 1/1 Running 0 55s
web-3 1/1 Running 0 36s
web-4 0/1 ContainerCreating 0 18s
NAME READY STATUS RESTARTS AGE
web-4 1/1 Running 0 19s
web-4 1/1 Terminating 0 24s
web-4 1/1 Terminating 0 24s
web-3 1/1 Terminating 0 42s
web-3 1/1 Terminating 0 42s
Послідовне завершення Podʼів
Панель управління видаляє кожний Pod по одному, у зворотньому порядку щодо його порядкового індексу, і чекає, доки кожен Pod повністю не зупиниться перед видаленням наступного.
Ще існують пʼять PersistentVolumeClaims та пʼять PersistentVolumes. Під час дослідження стабільного сховища Podʼа, ви бачили, що PersistentVolumes, змонтовані у Podʼи StatefulSet, не видаляються, коли Podʼи StatefulSet видаляються. Це також вірно, коли видалення Podʼів викликано зменшенням масштабу StatefulSet.
Оновлення StatefulSets
Контролер StatefulSet підтримує автоматизоване оновлення. Стратегія, яка використовується, визначається полем spec.updateStrategy обʼєкта API StatefulSet. Ця функція може бути використана для оновлення образів контейнерів, запитів ресурсів та/або лімітів, міток та анотацій Podʼів у StatefulSet.
Існують дві стратегії оновлення: RollingUpdate (типово) та OnDelete.
RollingUpdate
Стратегія оновлення RollingUpdate оновлює всі Podʼи в StatefulSet у зворотньому порядку щодо їх порядкового індексу, з дотриманням гарантій StatefulSet.
Ви можете розділити оновлення StatefulSet, який використовує стратегію RollingUpdate, на партиції, вказавши .spec.updateStrategy.rollingUpdate.partition. Ви будете практикувати це пізніше у цьому посібнику.
Спочатку спробуйте просте поетапне оновлення.
В одному вікні термінала відредагуйте StatefulSet web, щоб знову змінити образ контейнера:
kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.24"}]'
statefulset.apps/web patched
В іншому терміналі спостерігайте за Podʼами у StatefulSet:
# Закінчіть це спостереження, коли відбудеться розгортання## Якщо ви не впевнені, залиште його ще на одну хвилинуkubectl get pod -l app=nginx --watch
Podʼи в StatefulSet оновлюються у зворотньому порядку щодо їх порядкового індексу. Контролер StatefulSet завершує кожен Pod і чекає, доки він не перейде в стан Running та Ready, перед тим як оновити наступний Pod. Зверніть увагу, що, навіть якщо контролер StatefulSet не продовжить оновлювати наступний Pod, поки його порядковий наступник не буде Running та Ready, він відновить будь-який Pod, який зазнав помилки під час оновлення до попередньої версії.
Podʼи, які вже отримали оновлення, будуть відновлені до оновленої версії, а Podʼи, які ще не отримали оновлення, будуть відновлені до попередньої версії. Таким чином, контролер намагається продовжувати забезпечувати працездатність застосунку та зберігати оновлення в однорідному стані при наявності тимчасових збоїв.
Отримайте Podʼи, щоб переглянути їх образи контейнерів:
for p in 01 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
Всі Podʼи в StatefulSet зараз використовують попередній образ контейнера.
Примітка:
Ви також можете використовувати kubectl rollout status sts/<name> для перегляду стану поетапного оновлення StatefulSet
Підготовка оновлення
Ви можете розбити оновлення StatefulSet, який використовує стратегію RollingUpdate, на розділи, вказавши .spec.updateStrategy.rollingUpdate.partition.
Для отримання додаткового контексту ви можете прочитати Поточні оновлення частинами на сторінці концепції StatefulSet.
Ви можете підготувати оновлення StatefulSet, використовуючи поле partition всередині .spec.updateStrategy.rollingUpdate. Для цього оновлення ви залишите наявні Podʼи в StatefulSet без змін, поки змінюєте шаблон Podʼа для StatefulSet. Потім ви (або, це поза цим навчальним посібником, якась зовнішня автоматизація) можете запустити це підготовлене оновлення.
Спочатку виправте StatefulSet web, щоб додати розділ до поля updateStrategy:
# Значення "partition" визначає, до яких порядкових номерів застосовується зміна# Переконайтеся, що використовуєте число, більше, ніж останній порядковий номер для# StatefulSetkubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset.apps/web patched
Знову виправте StatefulSet, щоб змінити образ контейнера, який використовує цей StatefulSet:
kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.21"}]'
statefulset.apps/web patched
Видаліть Pod у StatefulSet:
kubectl delete pod web-2
pod "web-2" deleted
Зачекайте, поки замінений Pod web-2 буде запущений і готовий:
# Закінчіть перегляд, коли побачите, що web-2 справнийkubectl get pod -l app=nginx --watch
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m
web-1 1/1 Running 0 4m
web-2 0/1 ContainerCreating 0 11s
web-2 1/1 Running 0 18s
Отримайте образ контейнера Podʼа:
kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.24
Зверніть увагу, що, навіть якщо стратегія оновлення — RollingUpdate, StatefulSet відновив Pod з початковим образом контейнера. Це тому, що порядковий номер Podʼа менше, ніж partition, вказаний у updateStrategy.
Ви можете виконати канаркове оновлення (для тестування зміненого шаблону) шляхом зменшення partition, який ви вказали вище.
Виправте StatefulSet, щоб зменшити розділ:
# Значення "partition" повинно відповідати найвищому існуючому порядковому номеру для# StatefulSetkubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/web patched
Панель управління спровокує заміну для web-2 (реалізовану через належне видалення, а потім створення нового Podʼа, як тільки видалення завершиться). Зачекайте, поки новий Pod web-2 буде запущений і готовий.
# Це вже має бути запущеноkubectl get pod -l app=nginx --watch
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m
web-1 1/1 Running 0 4m
web-2 0/1 ContainerCreating 0 11s
web-2 1/1 Running 0 18s
Отримайте інформацію про контейнер Podʼа:
kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.21
Коли ви змінили partition, контролер StatefulSet автоматично оновив Pod web-2, оскільки порядковий номер Podʼа був більшим або рівним partition.
Видаліть Pod web-1:
kubectl delete pod web-1
pod "web-1" deleted
Зачекайте, поки Pod web-1 буде запущений і готовий.
# Це вже має бути запущеноkubectl get pod -l app=nginx --watch
kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.24
web-1 був відновлений до своєї початкової конфігурації, тому що порядковий номер Podʼа був меншим за partition. Коли вказано partition, всі Podʼи з порядковим номером, який більше або дорівнює partition, будуть оновлені, коли буде оновлено .spec.template StatefulSet. Якщо Pod з порядковим номером, який менший за partition, буде видалений або інакше припинений, він буде відновлено до початкової конфігурації.
Поступові оновлення
Ви можете виконати поступове оновлення (наприклад, лінійне, геометричне або експоненціальне оновлення) за допомогою розділеного оновлення з аналогічним методом до того, як ви впровадили канаркове оновлення. Щоб виконати поступове оновлення, встановіть partition на порядковий номер, на якому ви хочете, щоб контролер призупинив оновлення.
Розділ в цей момент встановлено на 2. Встановіть розділ на 0:
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset.apps/web patched
Зачекайте, поки всі Podʼи в StatefulSet стануть запущеними і готовими.
# Це вже має бути запущеноkubectl get pod -l app=nginx --watch
Переміщуючи partition на 0, ви дозволили StatefulSet продовжити процес оновлення.
OnDelete
Ви обираєте цю стратегію оновлення для StatefulSet, встановивши .spec.template.updateStrategy.type на OnDelete.
Виправте StatefulSet web, щоб використовувати стратегію оновлення OnDelete:
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
statefulset.apps/web patched
Коли ви обираєте цю стратегію оновлення, контролер StatefulSet не автоматично оновлює Podʼи, коли вноситься зміна до поля .spec.template StatefulSet. Вам потрібно керувати оновленням самостійно — або вручну, або за допомогою окремої автоматизації.
Видалення StatefulSet
StatefulSet підтримує як не каскадне, так і каскадне видалення. При не каскадному видаленні Podʼи StatefulSet не видаляються при видаленні самого StatefulSet. При каскадному видаленні видаляються як StatefulSet, так і його Podʼи.
В одному вікні термінала спостерігайте за Podʼами у StatefulSet.
# Завершіть цей перегляд, коли не буде Podʼів для StatefulSetkubectl get pods --watch -l app=nginx
Використовуйте kubectl delete, щоб видалити StatefulSet. Переконайтеся, що ви передали параметр --cascade=orphan до команди. Цей параметр повідомляє Kubernetes видаляти лише StatefulSet, і не видаляти жодного з його Podʼів.
kubectl delete statefulset web --cascade=orphan
statefulset.apps "web" deleted
Отримайте Podʼи, щоб перевірити їх статус:
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 6m
web-1 1/1 Running 0 7m
web-2 1/1 Running 0 5m
Навіть якщо web був видалений, всі Podʼи все ще запущені і готові. Видаліть web-0:
kubectl delete pod web-0
pod "web-0" deleted
Отримайте Podʼи StatefulSet:
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
web-1 1/1 Running 0 10m
web-2 1/1 Running 0 7m
Оскільки StatefulSet web було видалено, web-0 не було перезапущено.
В одному терміналі спостерігайте за Podʼами StatefulSet.
# Залиште цей перегляд запущеним до наступного разу, коли ви почнете переглядkubectl get pods --watch -l app=nginx
У другому терміналі створіть знову StatefulSet. Зверніть увагу, якщо ви не видалили nginx Service (що ви не повинні були робити), ви побачите помилку, яка вказує, що Service вже існує.
statefulset.apps/web created
service/nginx unchanged
Ігноруйте помилку. Вона лише вказує на те, що була спроба створити headless Service nginx, незважаючи на те, що цей Service вже існує.
Дослідіть вихідні дані команди kubectl get, яка працює у першому терміналі.
# Це вже має бути запущеноkubectl get pods --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-1 1/1 Running 0 16m
web-2 1/1 Running 0 2m
NAME READY STATUS RESTARTS AGE
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 18s
web-2 1/1 Terminating 0 3m
web-2 0/1 Terminating 0 3m
web-2 0/1 Terminating 0 3m
web-2 0/1 Terminating 0 3m
Коли StatefulSet web був відновлений, він спочатку перезапустив web-0. Оскільки web-1 вже був запущений і готовий, коли web-0 перейшов до стану Running і Ready, він став резервним для цього Podʼа. Оскільки ви відновили StatefulSet з replicas рівним 2, як тільки web-0 був відновлений, і як тільки web-1 вже був визначений як Running і Ready, web-2 був закінчений.
Тепер ще раз розгляньте вміст файлу index.html, який обслуговують вебсервери Podʼів:
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
Навіть якщо ви видалили як StatefulSet, так і Pod web-0, він все ще обслуговує імʼя хоста, яке спочатку було введено в його файл index.html. Це тому, що StatefulSet ніколи не видаляє PersistentVolumes, повʼязані з Podʼом. Коли ви відновили StatefulSet і перезапустили web-0, його оригінальний PersistentVolume знову змонтувався.
Каскадне видалення
В одному вікні термінала спостерігайте за Podʼами у StatefulSet.
# Залиште це запущеним до наступного розділу сторінкиkubectl get pods --watch -l app=nginx
В іншому терміналі знову видаліть StatefulSet. Цього разу не використовуйте параметр --cascade=orphan.
kubectl delete statefulset web
statefulset.apps "web" deleted
Дослідіть вихідні дані команди kubectl get, яка працює у першому терміналі, і зачекайте, поки всі Podʼи перейдуть у стан Terminating.
# Це вже має бути запущеноkubectl get pods --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 11m
web-1 1/1 Running 0 27m
NAME READY STATUS RESTARTS AGE
web-0 1/1 Terminating 0 12m
web-1 1/1 Terminating 0 29m
web-0 0/1 Terminating 0 12m
web-0 0/1 Terminating 0 12m
web-0 0/1 Terminating 0 12m
web-1 0/1 Terminating 0 29m
web-1 0/1 Terminating 0 29m
web-1 0/1 Terminating 0 29m
Як ви бачили в розділі Зменшення мастабу, Podʼи видаляються по одному, з урахуванням зворотного порядку їх порядкових індексів. Перед видаленням Podʼа контролер StatefulSet чекає, поки Pod-наступник буде повністю видалено.
Примітка:
Хоча каскадне видалення видаляє StatefulSet разом з його Podʼами, каскад не видаляє headless Service, повʼязаний з StatefulSet. Ви повинні видалити Service nginx вручну.
service/nginx created
statefulset.apps/web created
Коли всі Podʼи StatefulSet перейдуть у стан Running і Ready, отримайте вміст їх файлів index.html:
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1
Навіть якщо ви повністю видалили StatefulSet і всі його Podʼи, Podʼи перестворюються з монтуванням їх PersistentVolumes, і web-0 й web-1 продовжують обслуговувати свої імена хостів.
Нарешті, видаліть Service nginx…
kubectl delete service nginx
service "nginx" deleted
…і StatefulSet web:
kubectl delete statefulset web
statefulset "web" deleted
Політика управління Podʼами
Для деяких розподілених систем порядок, гарантований StatefulSet, є непотрібним або небажаним. Ці системи потребують лише унікальності та ідентичності.
Ви можете вказати політику управління Podʼами, щоб уникнути цього строгого порядку; або OrderedReady (типово), або Parallel.
Політика управління Podʼами OrderedReady
Управління Podʼами OrderedReady є стандартним значенням для StatefulSet. Воно каже контролеру StatefulSet дотримуватися гарантій порядку, показаних вище.
Використовуйте цей параметр, коли ваш застосунок вимагає або очікує, що зміни, такі як запуск нової версії вашого застосунку, відбуваються у строгому порядку за порядковим номером (номером Pod), який надає StatefulSet. Іншими словами, якщо у вас є Podʼи app-0, app-1 та app-2, Kubernetes оновить app-0 першим і перевірить його. Якщо перевірка пройшла успішно, Kubernetes оновить app-1, а потім app-2.
Якщо ви додали ще два Podʼи, Kubernetes налаштує app-3 та почекає, доки він не стане справним, перед тим як розгорнути app-4.
Оскільки це стандартне налаштування, ви вже практикували його використання.
Політика управління Podʼами Parallel
Альтернатива, управління Podʼами Parallel, каже контролеру StatefulSet запускати або видаляти всі Podʼи паралельно, і не чекати, поки Podʼи не стануть Running і Ready або повністю видаленими перед запуском або видаленням іншого Podʼа.
Опція управління Podʼами Parallel впливає лише на поведінку при масштабуванні. Оновлення не піддаються впливу; Kubernetes все ще впроваджує зміни по черзі. Для цього навчального посібника застосунок дуже простий: вебсервер, який повідомляє вам його імʼя хосту (тому що це StatefulSet, імʼя хосту для кожного Podʼа є різним і передбачуваним).
StatefulSet запустив три нові Podʼи, і він не чекав, поки перший стане Running і Ready перед запуском другого і третього Podʼів.
Цей підхід є корисним, якщо ваше робоче навантаження зберігає стан або Podʼи мають можливість ідентифікувати один одного за передбачуваною назвою, особливо якщо вам іноді потрібно швидко надати більше потужності. Якщо цей простий вебсервіс для навчального посібника раптово отримав додаткові 1,000,000 запитів на хвилину, то ви хотіли б запустити додаткові Podʼи — але ви також не хотіли б чекати, поки кожен новий Pod буде запущений. Запуск додаткових Podʼів паралельно скорочує час між запитом на додаткову потужність і її доступністю для використання.
Очищення
У вас маєть бути два термінали, готові для запуску команд kubectl для проведення очищення.
kubectl delete sts web
# sts - скорочення для statefulset
Ви можете відстежувати виконання команди kubectl get, щоб переглянути видалення цих ресурсів.
# завершіть перегляд, коли побачите необхіднеkubectl get pod -l app=nginx --watch
Вам також потрібно видалити носій постійне зберігання для PersistentVolumes, що використовувалось в цьому посібнику. Виконайте необхідні дії згідно з вашим середовищем, конфігурацією зберігання та методом надання послуг, щоб забезпечити повторне використання всього сховища.
6.2 - Приклад: Розгортання WordPress та MySQL з постійними томами
У цьому посібнику ви дізнаєтеся, як розгорнути сайт WordPress та базу даних MySQL за допомогою Minikube. Обидва застосунки використовують PersistentVolumes та PersistentVolumeClaims для зберігання даних.
Постійні томи (PersistentVolume (PV)) — це частина системи зберігання в кластері, яку адміністратор вручну надав або яку Kubernetes автоматично надав за допомогою StorageClass. Запити на постійні томи (PersistentVolumeClaim (PVC)) — це запит на зберігання, який користувач може отримати через PV. PersistentVolumes та PersistentVolumeClaims незалежні від життєвого циклу Podʼів і зберігають дані під час перезапуску, перепланування та навіть видалення Podʼів.
Попередження:
Це розгортання не підходить для використання в операційній діяльності, оскільки використовує Podʼи з одним екземпляром WordPress та MySQL. Розгляньте використання WordPress Helm Chart, щоб розгорнути WordPress для промислової експлуатації.
Примітка:
Файли, наведені в цьому посібнику, використовують API-інтерфейси Deployment GA та є специфічними для версії Kubernetes 1.9 і пізніших. Якщо ви хочете скористатися цим посібником з ранньою версією Kubernetes, оновіть, будь ласка, відповідно версію API або звертайтеся до раніших версій цього посібника.
Цілі
Створення PersistentVolumeClaims and PersistentVolumes
Створення kustomization.yaml з
генератором Secret
конфігураціями ресурсів MySQL
конфігураціями ресурсів WordPress
Застосування теки kustomization за допомогою kubectl apply -k ./
Очищення
Перш ніж ви розпочнете
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Створення запитів на постійні томи та постійних томів
Для зберігання даних MySQL та Wordpress кожному потрібен постійний том (PersistentVolume). Їх запити на постійні томи (PersistentVolumeClaim) будуть створені на етапі розгортання.
У багатьох середовищах кластера встановлено типовий StorageClass. Якщо StorageClass не вказано у запиті на постійний том (PersistentVolumeClaim), то використовується типовий StorageClass кластера.
Коли створюється запит на постійний том (PersistentVolumeClaim), постійний том (PersistentVolume) динамічно надається на основі конфігурації StorageClass.
Попередження:
У локальних кластерах типовий StorageClass використовує провізор hostPath. Томи hostPath підходять лише для розробки та тестування. З томами hostPath, ваші дані зберігаються в /tmp на вузлі, на який заплановано Pod, і не переміщуються між вузлами. Якщо Pod перестає існувати та переплановується на інший вузол в кластері або вузол перезавантажується, дані втрачаються.
Примітка:
Якщо ви запускаєте кластер, який потребує використання провізора hostPath, прапорець --enable-hostpath-provisioner повинен бути встановлений в компоненті controller-manager.
Примітка:
Якщо у вас є кластер Kubernetes, що працює на Google Kubernetes Engine, будь ласка, дотримуйтесь рекомендацій цього посібника.
Створення файлу kustomization.yaml
Додавання генератора Secret
Secret — це обʼєкт, який зберігає чутливі дані, такі як паролі або ключі. Починаючи з версії 1.14, kubectl підтримує керування обʼєктами Kubernetes за допомогою файлу kustomization. Ви можете створити Secret за допомогою генераторів у файлі kustomization.yaml.
Додайте генератор Secret у файл kustomization.yaml за допомогою наступної команди. Вам потрібно буде замінити YOUR_PASSWORD на пароль, який ви хочете використовувати.
Додавання конфігурації ресурсів для MySQL та WordPress
Наступний маніфест описує Deployment одного екземпляра MySQL. Контейнер MySQL монтує PersistentVolume у /var/lib/mysql. Змінна оточення MYSQL_ROOT_PASSWORD встановлює пароль бази даних з Secret.
Наступний маніфест описує Deployment одного екземпляра WordPress. Контейнер WordPress монтує PersistentVolume у /var/www/html для файлів даних вебсайту. Змінна оточення WORDPRESS_DB_HOST встановлює імʼя служби MySQL, визначеної вище, і WordPress буде звертатися до бази даних через службу. Змінна оточення WORDPRESS_DB_PASSWORD встановлює пароль бази даних згенерованого kustomize Secret.
Файл kustomization.yaml містить всі ресурси для розгортання сайту WordPress та бази даних MySQL. Ви можете застосувати цю теку командою
kubectl apply -k ./
Тепер перевірте, що всі обʼєкти існують.
Перевірте, що Secret існує, виконавши наступну команду:
kubectl get secrets
Відповідь має бути подібною до цієї:
NAME TYPE DATA AGE
mysql-pass-c57bb4t7mf Opaque 1 9s
Перевірте, що PersistentVolume був динамічно наданий.
kubectl get pvc
Примітка:
Процес надання PV може зайняти кілька хвилин.
Відповідь має бути подібною до цієї:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pv-claim Bound pvc-8cbd7b2e-4044-11e9-b2bb-42010a800002 20Gi RWO standard 77s
wp-pv-claim Bound pvc-8cd0df54-4044-11e9-b2bb-42010a800002 20Gi RWO standard 77s
Перевірте, що Pod працює, виконавши наступну команду:
kubectl get pods
Примітка:
Статус Pod може бути RUNNING через кілька хвилин.
Відповідь має бути подібною до цієї:
NAME READY STATUS RESTARTS AGE
wordpress-mysql-1894417608-x5dzt 1/1 Running 0 40s
Перевірте, що Service працює, виконавши наступну команду:
kubectl get services wordpress
Відповідь має бути подібною до цієї:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
wordpress LoadBalancer 10.0.0.89 <pending> 80:32406/TCP 4m
Примітка:
Minikube може використовувати лише NodePort для викладання служб. Зовнішня IP-адреса завжди знаходиться в стані очікування.
Виконайте наступну команду, щоб отримати IP-адресу для Service WordPress:
minikube service wordpress --url
Відповідь має бути подібною до цієї:
http://1.2.3.4:32406
Скопіюйте IP-адресу та завантажте сторінку у своєму оглядачі, щоб переглянути ваш сайт.
Ви повинні побачити сторінку налаштування WordPress, схожу на знімок екрана нижче.
Попередження:
Не залишайте вашу установку WordPress на цій сторінці. Якщо інший користувач знайде її, вони можуть налаштувати вебсайт на вашому екземплярі та використовувати його для обслуговування шкідливого вмісту.
Або встановіть WordPress, створивши імʼя користувача та пароль, або видаліть свій екземпляр.
Очищення
Виконайте наступну команду, щоб видалити ваш Secret, Deployments, Services та PersistentVolumeClaims:
6.3 - Приклад: Розгортання Cassandra з використанням StatefulSet
Цей підручник покаже вам, як запустити Apache Cassandra у Kubernetes. Cassandra, база даних, потребує постійного сховища для забезпечення стійкості даних (стан застосунку). У цьому прикладі використовується власний постачальник насіння Cassandra, що дозволяє базі даних виявляти нові екземпляри Cassandra, коли вони приєднуються до кластера Cassandra.
StatefulSet полегшує розгортання стійких застосунків у вашому кластері Kubernetes. Для отримання додаткової інформації про використані у цьому підручнику функції дивіться StatefulSet.
Примітка:
Cassandra та Kubernetes використовують термін вузол для позначення члена кластера. У цьому підручнику Podʼи, що належать StatefulSet, є вузлами Cassandra та є членами кластера Cassandra (званого кільцем (ring)). Коли ці Podʼи працюють у вашому кластері Kubernetes, панель управління Kubernetes розміщує ці Podʼи на Вузлах Kubernetes.
Коли вузол Cassandra стартує, він використовує список насіння для відкриття виявлення інших вузлів у кільці. У цьому підручнику розгортається власний постачальник насіння Cassandra, який дозволяє базі даних виявляти нові Podʼи Cassandra в міру їх появи у вашому кластері Kubernetes.
Цілі
Створення та перевірка Cassandra headless Service.
Використання StatefulSet для створення кільця Cassandra.
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Для роботи з цим посібником ви повинні вже мати базові знання про Podʼи, Сервіси, та StatefulSets.
Додаткові інструкції щодо налаштування Minikube
Увага:
Minikube стандартно використовує 2048 МБ пам’яті та 2 ЦП. Запуск Minikube зі стандартною конфігурацією ресурсів призводить до помилок недостатності ресурсів під час виконання настанов цього посібника. Щоб уникнути цих помилок, запустіть Minikube з наступними налаштуваннями:
minikube start --memory 5120 --cpus=4
Створення headless Service для Cassandra
У Kubernetes Service описує набір Podʼів, які виконують одну й ту ж задачу.
Наступний Service використовується для DNS-пошуку між Podʼами Cassandra та клієнтами у вашому кластері:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cassandra ClusterIP None <none> 9042/TCP 45s
Якщо ви не бачите Service з назвою cassandra, це означає, що його створення не вдалося. Прочитайте Налагодження Service, щоб отримати довідку з усунення загальних проблем.
Використання StatefulSet для створення кільця Cassandra
Маніфест StatefulSet, наведений нижче, створює кільце Cassandra, що складається з трьох Podʼів.
Примітка:
У цьому прикладі використовується стандартний провізор для Minikube. Будь ласка, оновіть наступний StatefulSet для хмари, з якою ви працюєте.
apiVersion:apps/v1kind:StatefulSetmetadata:name:cassandralabels:app:cassandraspec:serviceName:cassandrareplicas:3selector:matchLabels:app:cassandratemplate:metadata:labels:app:cassandraspec:terminationGracePeriodSeconds:500containers:- name:cassandraimage:gcr.io/google-samples/cassandra:v13imagePullPolicy:Alwaysports:- containerPort:7000name:intra-node- containerPort:7001name:tls-intra-node- containerPort:7199name:jmx- containerPort:9042name:cqlresources:limits:cpu:"500m"memory:1Girequests:cpu:"500m"memory:1GisecurityContext:capabilities:add:- IPC_LOCKlifecycle:preStop:exec:command:- /bin/sh- -c- nodetool drainenv:- name:MAX_HEAP_SIZEvalue:512M- name:HEAP_NEWSIZEvalue:100M- name:CASSANDRA_SEEDSvalue:"cassandra-0.cassandra.default.svc.cluster.local"- name:CASSANDRA_CLUSTER_NAMEvalue:"K8Demo"- name:CASSANDRA_DCvalue:"DC1-K8Demo"- name:CASSANDRA_RACKvalue:"Rack1-K8Demo"- name:POD_IPvalueFrom:fieldRef:fieldPath:status.podIPreadinessProbe:exec:command:- /bin/bash- -c- /ready-probe.shinitialDelaySeconds:15timeoutSeconds:5# Ці точки монтування томів є постійними. Вони подібни до вбудованих заявок,# але не зовсіс, тому що імена повинні точно збігатись з одним з томів# томів Podʼів stateful.volumeMounts:- name:cassandra-datamountPath:/cassandra_data# Це перетворюється у заявки на томи контролером# та монтується в шлях, зазначений вище.# не використовуйте це в операційній діяльності, допоки тип ssd є GCEPersistentDisk чи інший ssd pdvolumeClaimTemplates:- metadata:name:cassandra-dataspec:accessModes:["ReadWriteOnce"]storageClassName:fastresources:requests:storage:1Gi---kind:StorageClassapiVersion:storage.k8s.io/v1metadata:name:fastprovisioner:k8s.io/minikube-hostpathparameters:type:pd-ssd
Створіть StatefulSet Cassandra з файлу cassandra-statefulset.yaml:
# Використовуйте це, якщо ви можете застосувати cassandra-statefulset.yaml без змінkubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml
# Використовуйте це, якщо ви змінили cassandra-statefulset.yaml локальноkubectl apply -f cassandra-statefulset.yaml
Перевірка StatefulSet Cassandra
Отримайте StatefulSet Cassandra:
kubectl get statefulset cassandra
Відповідь буде схожа на:
NAME DESIRED CURRENT AGE
cassandra 3 0 13s
Ресурс StatefulSet розгортає Podʼи послідовно.
Отримайте Podʼи, щоб побачити статус створення в зазначеному порядку:
kubectl get pods -l="app=cassandra"
Відповідь буде схожа на:
NAME READY STATUS RESTARTS AGE
cassandra-0 1/1 Running 0 1m
cassandra-1 0/1 ContainerCreating 0 8s
На створення всіх трьох Podʼів може піти декілька хвилин. Після їх розгортання ця ж команда повертає вихідні дані, схожі на:
NAME READY STATUS RESTARTS AGE
cassandra-0 1/1 Running 0 10m
cassandra-1 1/1 Running 0 9m
cassandra-2 1/1 Running 0 8m
Виконайте інструмент nodetool Cassandra всередині першого Podʼа, щоб переглянути стан кільця.
kubectl exec -it cassandra-0 -- nodetool status
Відповідь буде схожою на:
Datacenter: DC1-K8Demo
======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 172.17.0.5 83.57 KiB 32 74.0% e2dd09e6-d9d3-477e-96c5-45094c08db0f Rack1-K8Demo
UN 172.17.0.4 101.04 KiB 32 58.8% f89d6835-3a42-4419-92b3-0e62cae1479c Rack1-K8Demo
UN 172.17.0.6 84.74 KiB 32 67.1% a6a1e8c2-3dc5-4417-b1a0-26507af2aaad Rack1-K8Demo
Зміна StatefulSet Cassandra
Використовуйте kubectl edit, щоб змінити розмір StatefulSet Cassandra.
Виконайте наступну команду:
kubectl edit statefulset cassandra
Ця команда відкриває редактор у вашому терміналі. Рядок, який вам потрібно змінити, — це поле replicas. Наведений нижче приклад — це уривок з файлу StatefulSet:
# Будь ласка, відредагуйте об’єкт нижче. Рядки, що починаються з '#', будуть проігноровані,# і пустий файл призведе до відмови редагування. Якщо під час збереження файлу виникне помилка,# цей файл буде знову відкритий із відповідними несправностями.#apiVersion:apps/v1kind:StatefulSetmetadata:creationTimestamp:2016-08-13T18:40:58Zgeneration:1labels:app:cassandraname:cassandranamespace:defaultresourceVersion:"323"uid:7a219483-6185-11e6-a910-42010a8a0fc0spec:replicas:3
Змініть кількість реплік на 4 і збережіть маніфест.
Тепер StatefulSet масштабується для роботи з 4 Podʼами.
Отримайте StatefulSet Cassandra, щоб перевірити зміни:
kubectl get statefulset cassandra
Відповідь буде схожа на:
NAME DESIRED CURRENT AGE
cassandra 4 4 36m
Очищення
Видалення або зменшення масштабу StatefulSet не призводить до видалення томів, повʼязаних із StatefulSet. Це налаштування захищає вас, оскільки ваші дані цінніші, ніж автоматичне очищення всіх повʼязаних ресурсів StatefulSet.
Попередження:
Залежно від класу сховища та політики вилучення, видалення PersistentVolumeClaims може призвести до вилучення також повʼязаних томів. Ніколи не припускайте, що ви зможете отримати доступ до даних, якщо їх томи будуть видалені.
Виконайте наступні команди (послідовно обʼєднані в одну команду) для видалення всього в StatefulSet Cassandra:
Цей образ включає стандартну установку Cassandra з репозиторію Apache Debian. За допомогою змінних середовища ви можете змінити значення, які вставляються в cassandra.yaml.
Вам необхідно мати кластер із щонайменше чотирма вузлами, і кожен вузол повинен мати щонайменше 2 ЦП та 4 ГБ памʼяті. У цьому посібнику ви будете закривати (cordon) та очищувати для обслуговування (drain) вузли кластера. Це означає, що кластер припинить роботу та виселить всі Podʼи зі своїх вузлів, і вузли тимчасово стануть не придатними до розміщення Podʼів. Вам слід використовувати окремий кластер для цього посібника, або ви повинні забезпечити, що порушення, яке ви викличете, не нашкодить іншим мешканцям кластера.
У цьому посібнику передбачається, що ви налаштували свій кластер для динамічного надання постійних томів (PersistentVolumes). Якщо ваш кластер не налаштований на це, вам доведеться вручну створити три томи обсягом 20 ГБ перед початком виконання кроків цього посібника.
Цілі
Після проходження цього посібника ви будете знати, як:
Розгортати ансамбль Apache Zookeeper використовуючи StatefulSet.
Як надійно налаштовувати ансамбль.
Як поширювати розгортання серверів Zookeeper в ансамблі.
Як використовувати PodDisruptionBudgets для забезпечення високої доступності послуг під час запланованого обслуговування.
ZooKeeper
Apache ZooKeeper — це розподілена, відкрита координаційна служба для розподілених застосунків. ZooKeeper дозволяє читати, записувати та спостерігати за оновленнями даних. Дані організовані у вигляді ієрархії файлової системи та реплікуються на всі сервери ZooKeeper в ансамблі (набір серверів ZooKeeper). Всі операції з даними є атомарними та послідовно консистентними. ZooKeeper забезпечує це, використовуючи протокол консенсусу Zab для реплікації машини стану на всіх серверах в ансамблі.
Ансамбль використовує протокол Zab для вибору лідера, ансамбль не може записувати дані, поки цей вибір лідера не завершиться. Після його завершення ансамбль використовує Zab, щоб забезпечити реплікацію всіх записів до кворуму, перш ніж він підтверджує їх та робить їх видимими для клієнтів. Без зваженого кворуму, кворум — буде більшістю складових ансамблю, які містять поточного лідера. Наприклад, якщо в ансамблі є три сервери, компонент, який містить лідера і ще один сервер, становитимуть кворум. Якщо ансамбль не може досягти кворуму, він не може записувати дані.
Сервери ZooKeeper зберігають свою повну машину стану в памʼяті та записують кожну зміну до довгострокового WAL (Write Ahead Log) на носії інформації. Коли сервер аварійно завершує роботу, він може відновити свій попередній стан, відтворюючи WAL. Щоб запобігти безмежному зростанню WAL, сервери ZooKeeper періодично роблять знімки їхнього стану в памʼяті на носій інформації. Ці знімки можуть бути завантажені безпосередньо в памʼять, а всі записи WAL, які передували знімку, можуть бути видалені.
Контролер StatefulSet створює три Podʼи, і кожен Pod містить контейнер з сервером ZooKeeper.
Забезпечення виборів лідера
Оскільки в анонімній мережі немає алгоритму завершення для вибору лідера, Zab вимагає явної конфігурації членства для виконання виборів лідера. Кожен сервер в ансамблі повинен мати унікальний ідентифікатор, всі сервери повинні знати глобальний набір ідентифікаторів, і кожен ідентифікатор повинен бути повʼязаний з мережевою адресою.
Використовуйте kubectl exec, щоб отримати імена хостів Podʼів у StatefulSet zk.
for i in 01 2; do kubectl exec zk-$i -- hostname; done
Контролер StatefulSet надає кожному Podʼу унікальне імʼя хосту на основі його порядкового індексу. Імена хостів мають форму <імʼя statefulset>-<порядковий індекс>. Оскільки поле replicas StatefulSet zk встановлено на 3, контролер набору створює три Podʼа з іменами хостів zk-0, zk-1 і zk-2.
zk-0
zk-1
zk-2
Сервери в ансамблі ZooKeeper використовують натуральні числа як унікальні ідентифікатори, і зберігають ідентифікатор кожного сервера у файлі, який називається myid, в теці даних сервера.
Щоб переглянути вміст файлу myid для кожного сервера, скористайтеся такою командою.
for i in 01 2; doecho"myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done
Оскільки ідентифікатори є натуральними числами, а порядкові індекси є не відʼємними цілими числами, можна згенерувати ідентифікатор, додавши 1 до порядкового номера.
myid zk-0
1
myid zk-1
2
myid zk-2
3
Щоб отримати повне доменне імʼя (FQDN) кожного Podʼа у StatefulSet zk, використовуйте таку команду.
for i in 01 2; do kubectl exec zk-$i -- hostname -f; done
Service zk-hs створює домен для всіх Podʼів, zk-hs.default.svc.cluster.local.
Записи A в DNS Kubernetes перетворюють FQDN в IP-адреси Podʼів. Якщо Kubernetes переплановує Podʼи, він оновлює записи A із новими IP-адресами Podʼів, але імена записів A не змінюються.
ZooKeeper зберігає свою конфігурацію застосунку в файлі з іменем zoo.cfg. Використовуйте kubectl exec, щоб переглянути вміст файлу zoo.cfg у Поді zk-0.
У властивостях server.1, server.2 та server.3 внизу файлу, 1, 2 та 3 відповідають ідентифікаторам у файлах myid серверів ZooKeeper. Вони встановлені на FQDN для Podʼів у StatefulSet zk.
Протоколи консенсусу вимагають, щоб ідентифікатори кожного учасника були унікальними. Два учасники у протоколі Zab не повинні претендувати на той самий унікальний ідентифікатор. Це необхідно для того, щоб процеси в системі могли погодитися щодо того, які процеси затвердили які дані. Якщо запускаються два Podʼа з тим самим порядковим номером, два сервери ZooKeeper ідентифікуватимуть себе як один і той самий сервер.
Записи A для кожного Pod вводяться, коли Pod стає готовим. Тому, FQDN серверів ZooKeeper посилається на єдину точку доступу, і ця точка доступу буде унікальним сервером ZooKeeper, який претендує на ідентифікацію, налаштовану в його файлі myid.
Коли сервери використовують протокол Zab, щоб спробувати затвердити значення, вони або досягатимуть консенсусу і затверджуватимуть значення (якщо вибори лідера пройшли успішно і принаймні два Podʼа працюють та готові), або вони не зможуть цього зробити (якщо будь-яка з умов не виконується). Ні один стан не призведе до того, що один сервер підтверджує запис від імені іншого.
Перевірка адекватності ансамблю
Найбільш базове тестування на адекватність — це запис даних на один сервер ZooKeeper і читання даних з іншого.
Наведена нижче команда виконує скрипт zkCli.sh, щоб записати world в шлях /hello у Pod zk-0 в ансамблі.
kubectl exec zk-0 -- zkCli.sh create /hello world
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
Created /hello
Щоб отримати дані з Podʼа zk-1, використовуйте таку команду.
kubectl exec zk-1 -- zkCli.sh get /hello
Дані, які ви створили на zk-0, доступні на всіх серверах в ансамблі.
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
Забезпечення стійкості зберігання
Як зазначено в розділі Основи ZooKeeper, ZooKeeper фіксує всі записи в стійкому журналі (WAL) та періодично записує знімки стану памʼяті на носії. Використання WAL для забезпечення стійкості є поширеною технікою для застосунків, які використовують протоколи консенсусу для досягнення реплікованої машини станів.
Використовуйте команду kubectl delete, щоб видалити обʼєкт StatefulSetzk.
kubectl delete statefulset zk
statefulset.apps "zk" deleted
Спостерігайте за завершенням роботи Podʼів у StatefulSet.
kubectl get pods -w -l app=zk
Коли zk-0 повністю завершить роботу, використовуйте CTRL-C, щоб завершити виконання kubectl.
Коли Pod у zkStatefulSet (пере)планується, у нього завжди монтується той самий PersistentVolume у теку даних сервера ZooKeeper. Навіть коли Podʼи переплануються, всі записи, зроблені у логах WAL серверів ZooKeeper, та всі їх знімки залишаються стійкими.
Забезпечення однорідної конфігурації
Як вказано в розділах Забезпечення виборів лідера та Досягнення консенсусу, сервери в ансамблі ZooKeeper потребують однорідної конфігурації для вибору лідера та формування кворуму. Також потрібна однорідна конфігурація протоколу Zab для коректної роботи протоколу мережею. У нашому прикладі ми досягаємо однорідної конфігурації, вбудувавши конфігурацію безпосередньо у маніфест.
Команда, яка використовується для запуску серверів ZooKeeper, передає конфігурацію як параметр командного рядка. Ви також можете використовувати змінні середовища для передачі конфігурації в ансамбль.
Налаштування системи логування
Один з файлів, що генерується сценарієм zkGenConfig.sh, керує логуванням ZooKeeper. ZooKeeper використовує Log4j, і типово він використовує інструмент постійного додавання, який покладається на час та розмір для конфігурації логування.
Використайте команду нижче, щоб отримати конфігурацію логування з одного з контейнерів у StatefulSet zk.
Це найпростіший спосіб безпечного логування всередині контейнера. Тому що програми записують логи у стандартний вивід, Kubernetes буде відповідальним за ротацію логів для вас. Kubernetes також реалізує розумну політику зберігання, яка гарантує, що логи застосунків, записані у потоки стандартного виводу та помилок, не виснажують локальні носії інформації.
Використайте kubectl logs, щоб отримати останні 20 рядків логів з одного з контейнерів.
kubectl logs zk-0 --tail 20
Ви можете переглядати логи застосунків, записані у стандартний вивід або стандартну помилку, використовуючи kubectl logs та з Kubernetes Dashboard.
2016-12-06 19:34:16,236 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52740
2016-12-06 19:34:16,237 [myid:1] - INFO [Thread-1136:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52740 (no session established for client)
2016-12-06 19:34:26,155 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52749
2016-12-06 19:34:26,155 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52749
2016-12-06 19:34:26,156 [myid:1] - INFO [Thread-1137:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52749 (no session established for client)
2016-12-06 19:34:26,222 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52750
2016-12-06 19:34:26,222 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52750
2016-12-06 19:34:26,226 [myid:1] - INFO [Thread-1138:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52750 (no session established for client)
2016-12-06 19:34:36,151 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO [Thread-1139:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52760 (no session established for client)
2016-12-06 19:34:36,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO [Thread-1140:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52761 (no session established for client)
2016-12-06 19:34:46,149 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO [Thread-1141:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52767 (no session established for client)
2016-12-06 19:34:46,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO [Thread-1142:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52768 (no session established for client)
Kubernetes інтегрується з багатьма рішеннями для логування. Ви можете вибрати рішення для логування, яке найкраще підходить для вашого кластера та застосунків. Для логування та агрегації на рівні кластера розгляньте розгортання sidecar контейнера, для ротації та надсилання ваших логів.
Налаштування не привілейованого користувача
Найкращі практики дозволити застосунку працювати як привілейований користувач всередині контейнера — це предмет для обговорення. Якщо ваша організація вимагає, щоб застосунки працювали як непривілейований користувач, ви можете використовувати SecurityContext для контролю над користувачем, під яким запускається точка входу.
У template Podʼів StatefulSetzk є SecurityContext.
securityContext:runAsUser:1000fsGroup:1000
У контейнерах Podʼів, UID 1000 відповідає користувачеві zookeeper, а GID 1000 відповідає групі zookeeper.
Отримайте інформацію про процес ZooKeeper з Поду zk-0.
kubectl exec zk-0 -- ps -elf
Оскільки поле runAsUser обʼєкта securityContext встановлене на 1000, замість того, щоб запускатися як root, процес ZooKeeper запускається як користувач zookeeper.
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S zookeep+ 1 0 0 80 0 - 1127 - 20:46 ? 00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
0 S zookeep+ 27 1 0 80 0 - 1155556 - 20:46 ? 00:00:19 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg
Типово, коли PersistentVolumes Поду монтується в теку даних сервера ZooKeeper, він доступний тільки для користувача root. Ця конфігурація заважає процесу ZooKeeper записувати свій лог та зберігати свої знімки.
Використовуйте команду нижче, щоб отримати права доступу до файлу теки даних ZooKeeper на Поді zk-0.
kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data
Оскільки поле fsGroup обʼєкта securityContext встановлене на 1000, власність PersistentVolumes Podʼів встановлюється на групу zookeeper, і процес ZooKeeper може читати та записувати свої дані.
drwxr-sr-x 3 zookeeper zookeeper 4096 Dec 5 20:45 /var/lib/zookeeper/data
Управління процесом ZooKeeper
В документації ZooKeeper зазначено, що "Вам захочеться мати наглядовий процес, який керує кожним з ваших процесів сервера ZooKeeper (JVM)." Використання watchdog (наглядового процесу) для перезапуску процесів, що зазнали збою, у розподіленій системі є загальним шаблоном. При розгортанні застосунку в Kubernetes, замість використання зовнішньої утиліти як наглядового процесу, ви повинні використовувати Kubernetes як watchdog для вашого застосунку.
Оновлення ансамблю
StatefulSetzk налаштований на використання стратегії оновлення RollingUpdate.
Ви можете використовувати kubectl patch, щоб оновити кількість cpus, виділених серверам.
Використовуйте kubectl rollout status, щоб спостерігати за статусом оновлення.
kubectl rollout status sts/zk
waiting for statefulset rolling update to complete 0 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision zk-5db4499664...
Це припиняє роботу Podʼів, по одному, у зворотному порядку, і перестворює їх з новою конфігурацією. Це забезпечує підтримку кворуму під час постійного оновлення.
Використовуйте команду kubectl rollout history, щоб переглянути історію або попередні конфігурації.
kubectl rollout history sts/zk
Вивід схожий на такий:
statefulsets "zk"
REVISION
1
2
Використовуйте команду kubectl rollout undo, щоб скасувати модифікацію.
kubectl rollout undo sts/zk
Вивід схожий на такий:
statefulset.apps/zk повернувся до попереднього стану
Обробка відмови процесу
Політики перезапуску контролюють, як Kubernetes обробляє відмови процесів для вхідної точки контейнера в Pod. Для Podʼів у StatefulSet єдине припустиме значення RestartPolicy — це Always, і це є стандартним значенням. Для StatefulSet застосунків ви ніколи не повинні змінювати стандартні значення.
Використовуйте наступну команду, щоб переглянути дерево процесів сервера ZooKeeper, який працює в поді zk-0.
kubectl exec zk-0 -- ps -ef
Команда, яка використовується як точка входу контейнера, має PID 1, а процес ZooKeeper, нащадок точки входу, має PID 27.
У іншому терміналі спостерігайте за Podʼами у StatefulSetzk за допомогою наступної команди.
kubectl get pod -w -l app=zk
У іншому терміналі завершіть процес ZooKeeper у поді zk-0 за допомогою наступної команди.
kubectl exec zk-0 -- pkill java
Завершення процесу ZooKeeper призвело до завершення його батьківського процесу. Оскільки RestartPolicy контейнера — завжди, він перезапустив батьківський процес.
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Running 0 21m
zk-1 1/1 Running 0 20m
zk-2 1/1 Running 0 19m
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Error 0 29m
zk-0 0/1 Running 1 29m
zk-0 1/1 Running 1 29m
Якщо ваш застосунок використовує сценарій (наприклад, zkServer.sh) для запуску процесу, який реалізує бізнес-логіку застосунку, сценарій повинен завершуватися разом з дочірнім процесом. Це забезпечує перезапуск контейнера застосунку, коли процес, що реалізує бізнес-логіку застосунку, зазнає збою.
Тестування на доступність
Налаштування вашого застосунку на перезапуск збійних процесів це ще не все, щоб забезпечити справність розподіленої системи. Існують сценарії, коли процеси системи можуть бути живими, але недоступними або інакше несправними. Вам слід використовувати проби на доступність, щоб повідомити Kubernetes про те, що процеси вашого застосунку є несправними, і його слід перезапустити.
Шаблон Pod для StatefulSetzk визначає пробу на доступність.
Коли проба на доступність для процесу ZooKeeper невдала, Kubernetes автоматично перезапустить процес за вас, забезпечуючи тим самим перезапуск несправних процесів у наборі.
kubectl get pod -w -l app=zk
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Running 0 1h
zk-1 1/1 Running 0 1h
zk-2 1/1 Running 0 1h
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Running 0 1h
zk-0 0/1 Running 1 1h
zk-0 1/1 Running 1 1h
Тестування готовності
Готовність не те саме, що і доступність. Якщо процес живий, він запланований і справний. Якщо процес готовий, він може обробляти вхідні дані. Доступність є необхідною, але не достатньою, умовою для готовності. Є випадки, особливо під час ініціалізації та завершення, коли процес може бути доступним, але не готовим.
Якщо ви вказуєте пробу на готовність, Kubernetes буде переконуватися, що процеси вашого застосунку не отримуватимуть мережевого трафіку до тих пір, поки їхні перевірки готовності не пройдуть.
Для сервера ZooKeeper, доступність означає готовність. Тому проба готовності з маніфесту zookeeper.yaml ідентична пробі на доступність.
Навіть якщо проби на доступність та готовність ідентичні, важливо вказати обидві. Це забезпечує, що мережевий трафік отримуватимуть лише справні сервери в ансамблі ZooKeeper.
Толерантність до відмов вузлів
Для успішного збереження змін даних ZooKeeper потрібен кворум серверів. Для ансамблю з трьох серверів, два сервери повинні бути справними, щоб записи вдалися. У системах на основі кворуму учасники розгорнені в областях відмов для забезпечення доступності. Щоб уникнути перебоїв через втрату окремої машини, найкращі практики виключають спільне розташування кількох екземплярів застосунку на одній машині.
Станадартно Kubernetes може розміщувати Podʼи в StatefulSet на тому ж вузлі. Для створеного вами ансамблю з трьох серверів, якщо два сервери знаходяться на тому ж вузлі, і цей вузол виходить з ладу, клієнти вашої служби ZooKeeper зазнають перебою, поки хоча б один з Podʼів не буде перепланований.
Ви завжди повинні надавати додаткові можливості для перепланування процесів критичних систем у разі відмови вузла. Якщо ви це зробите, перерва буде тривати лише до тих пір, поки планувальник Kubernetes перепланує один з серверів ZooKeeper. Однак, якщо ви хочете, щоб ваша служба терпіла відмови вузлів без перебою роботи, вам слід встановити podAntiAffinity.
Використовуйте наведену нижче команду, щоб отримати вузли для Podʼів у StatefulSetzk.
for i in 01 2; do kubectl get pod zk-$i --template={{.spec.nodeName}}; echo""; done
Всі Podʼи у StatefulSetzk розгорнуті на різних вузлах.
Поле requiredDuringSchedulingIgnoredDuringExecution говорить планувальнику Kubernetes, що він ніколи не повинен розміщувати два Podʼи з міткою app як zk у домені, визначеному за допомогою topologyKey. Ключ topologyKeykubernetes.io/hostname вказує, що домен — це окремий вузол. Використовуючи різні правила, мітки та селектори, ви можете розширити цей метод, щоб розподілити свій ансамбль на фізичні, мережеві та домени відмов.
Виживання під час обслуговування
У цьому розділі ви здійсните блокування та виведення вузлів для обслуговування. Якщо ви використовуєте цей посібник на спільному кластері, переконайтеся, що це не позначиться негативно на інших мешканцях.
Попередній розділ показав, як розподілити ваші Podʼи по вузлах, щоб вижити в разі непередбачуваних відмов вузлів, але вам також потрібно планувати тимчасові відмови вузлів, які виникають через заплановане обслуговування.
Використайте цю команду, щоб отримати вузли у вашому кластері.
kubectl get nodes
У цьому посібнику передбачається наявність кластера з щонайменше чотирма вузлами. Якщо в кластері є більше чотирьох вузлів, використовуйте kubectl cordon, щоб заборонити доступ до всіх вузлів, окрім чотирьох. Обмеження до чотирьох вузлів гарантуватиме, що Kubernetes врахує обмеження подібності та PodDisruptionBudget при плануванні Podʼів zookeeper у наступній симуляції обслуговування.
kubectl cordon <імʼя-вузла>
Використайте цю команду, щоб отримати zk-pdbPodDisruptionBudget.
kubectl get pdb zk-pdb
Поле max-unavailable показує Kubernetes, що в будь-який момент може бути недоступний найбільше один Pod з StatefulSetzk.
NAME MIN-AVAILABLE MAX-UNAVAILABLE ALLOWED-DISRUPTIONS AGE
zk-pdb N/A 1 1
У одному терміналі використовуйте цю команду, щоб переглядати Podʼи у StatefulSetzk.
kubectl get pods -w -l app=zk
У іншому терміналі використовуйте цю команду, щоб отримати вузли, на яких наразі заплановані Podʼи.
for i in 01 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo""; done
Продовжуйте слідкувати за Podʼами StatefulSet у першому терміналі і виведіть вузол, на якому запланований zk-1.
kubectl drain $(kubectl get pod zk-1 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
Вихідна інформація подібна до наступної:
"kubernetes-node-ixsl" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-ixsl, kube-proxy-kubernetes-node-ixsl; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-voc74
pod "zk-1" deleted
node "kubernetes-node-ixsl" drained
Pod zk-1 не може бути перепланований, оскільки StatefulSetzk містить правило PodAntiAffinity, яке запобігає спільному розташуванню Podʼів, і так як доступні тільки два вузла, Pod залишиться в стані очікування.
Продовжуйте слідкувати за Podʼами StatefulSet, і виведіть з використання вузол, на якому запланований zk-2.
kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
Вихідна інформація подібна до наступної:
node "kubernetes-node-i4c4" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
WARNING: Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog; Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4
There are pending pods when an error occurred: Cannot evict pod as it would violate the pod's disruption budget.
pod/zk-2
Використовуйте CTRL-C для припинення роботи kubectl.
Ви не можете вивести з роботи третій вузол, оскільки zk-2 буде порушувати zk-budget. Однак, вузол залишатиметься заблокованим (cordoned).
Використовуйте zkCli.sh для отримання значень введених впродовж перевірки адекватності zk-0.
kubectl exec zk-0 zkCli.sh get /hello
Service все ще доступний, оскільки його PodDisruptionBudget не порушено.
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Dec 07 00:08:59 UTC 2016
mZxid = 0x200000002
mtime = Wed Dec 07 00:08:59 UTC 2016
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
Спробуйте вивести вузол з обслуговування на якмоу запланований zk-2.
kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
Вивід буде подібний до наступного:
node "kubernetes-node-i4c4" already cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
pod "heapster-v1.2.0-2604621511-wht1r" deleted
pod "zk-2" deleted
node "kubernetes-node-i4c4" drained
На цей раз kubectl drain успішно виконується.
Розблокуйте другий вузол, щоб дозволити перепланування zk-2.
kubectl uncordon kubernetes-node-ixsl
Вивід буде подібний до наступного:
node "kubernetes-node-ixsl" uncordoned
Ви можете використовувати kubectl drain разом з PodDisruptionBudgets, щоб забезпечити доступність ваших служб під час обслуговування. Якщо drain використовується для блокування вузлів та видалення Podʼів до виключення вузла з експлуатації для обслуговування, служби, які виражають бюджет відмов, мають поважати цей бюджет . Ви завжди повинні виділяти додаткову потужність для критичних служб, щоб їх Podʼи могли негайно бути переплановані.
Очищення
Використайте kubectl uncordon, щоб розблокувати всі вузли у вашому кластері.
Ви повинні видалити носії постійного носія для PersistentVolumes, використаних у цьому посібнику. Дотримуйтеся необхідних кроків, залежно від вашого середовища, конфігурації зберігання та методу надання послуг, щоб переконатися, що всі збережені дані вилучено.
7 - Керування кластером
7.1 - Запуск автономного Kublet
Цей посібник показує, як запустити автономний екземпляр kubelet.
У вас можуть бути різні причини для запуску автономного kubelet. Цей посібник спрямований на ознайомлення вас з Kubernetes, навіть якщо у вас немає багато досвіду роботи з ним. Ви можете слідувати цьому посібнику і дізнатися про налаштування вузла, основних (статичних) Podʼів та як Kubernetes керує контейнерами.
Після того, як ви пройдете цей посібник, ви можете спробувати використовувати кластер, який має панель управління для керування podʼами та вузлами, а також іншими типами обʼєктів. Ознайомтесь з прикладом в розділі Привіт, minikube.
Ви також можете запустити kubelet в автономному режимі для задоволення операційних потреб, таких як запуск панелі управління для високо доступного, стійкого до збоїв кластера. Цей посібник не охоплює деталі, необхідні для запуску високодоступної панелі управління.
Цілі
Встановлення cri-o та kubelet в системах Linux та їх запуск як служб systemd.
Запуск Podʼа для nginx що очікує запитів на порту TCP 80 за IP адресою Podʼа.
Дізнатись, як різні компоненти взаємодіють один з одним.
Увага:
Конфігурація kubelet, яка використовується у цьому посібнику, є небезпечною за своєю конструкцією і не повинна не повинна використовуватися в промисловому середовищі.
Перш ніж ви розпочнете
Адміністраторський (root) доступ до системи Linux, яка використовує systemd та iptables (або nftables з емуляцією iptables).
Доступ до Інтернету для завантаження компонентів, необхідних для роботи з підручником, зокрема
Стандартно kubelet не запускається, якщо на вузлі виявлено використання файлу підкачки (swap). Це означає, що підкачку необхідно або вимкнути, або налаштувати толерантність до неї у kubelet.
Примітка:
Якщо налаштувати kubelet на толерантність до підкачки, kubelet все одно конфігурує Podʼи (та контейнери в цих Podʼах) таким чином, щоб вони не використовували область підкачки. Дізнатися, як Podʼи можуть фактично використовувати доступну підкачку, можна в розділі управління памʼяттю підкачки на Linux-вузлах.
Якщо у вас увімкнена підкачка, вимкніть її або додайте параметр failSwapOn: false у файл конфігурації kubelet.
Щоб перевірити, чи увімкнена підкачка:
sudo swapon --show
Якщо команда не виводить жодної інформації, то підкачка вже вимкнена.
Щоб тимчасово вимкнути підкачку:
sudo swapoff -a
Щоб зробити це налаштування постійним після перезавантаження:
Переконайтеся, що підкачка вимкнена в файлі /etc/fstab або у файлі systemd.swap, залежно від того, як вона була налаштована на вашій системі.
Увімкнення пересилання пакетів IPv4
Щоб перевірити, чи увімкнено пересилання пакетів IPv4:
cat /proc/sys/net/ipv4/ip_forward
Якщо результатом є 1, пересилання вже увімкнено. Якщо результатом є 0, виконайте наступні кроки.
Щоб увімкнути пересилання пакетів IPv4, створіть конфігураційний файл, який встановлює параметр net.ipv4.ip_forward у значення 1:
sudo tee /etc/sysctl.d/k8s.conf <<EOF
net.ipv4.ip_forward = 1
EOF
Завантаження, встановлення та налаштування компонентів
Примітка: Цей розділ містить посилання на проєкти сторонніх розробників, які надають функціонал, необхідний для Kubernetes. Автори проєкту Kubernetes не несуть відповідальності за ці проєкти. Проєкти вказано в алфавітному порядку. Щоб додати проєкт до цього списку, ознайомтеся з посібником з контенту перед надсиланням змін. Докладніше.
Встановлення середовища виконання контейнерів
Завантажте найновіші доступні версії необхідних пакетів (рекомендовано).
Цей підручник пропонує встановити середовище виконання контейнерів CRI-O (зовнішнє посилання).
Існує кілька способів встановлення середовища виконання CRI-O, залежно від вашого дистрибутиву Linux. Хоча CRI-O рекомендує використовувати пакети deb або rpm, у цьому підручнику використовується скрипт статичного бінарного пакета проєкту CRI-O Packaging, щоб спростити процес і зробити його незалежним від дистрибутиву.
Скрипт встановлює та налаштовує додаткове необхідне програмне забезпечення, таке як cni-plugins для контейнерної мережі та crun і runc для запуску контейнерів.
Скрипт автоматично визначить архітектуру процесора вашої системи (amd64 або arm64), а також обере та встановить найновіші версії програмних пакетів.
Переконайтеся, що стандартний діапазон subnet (10.85.0.0/16) не перетинається з будь-якою з ваших активних мереж. Якщо є перетин, ви можете відредагувати файл і змінити його відповідно. Після зміни перезапустіть службу.
sudo tee /etc/kubernetes/kubelet.yaml <<EOF
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
webhook:
enabled: false # НЕ використовуйте у промислових кластерах!
authorization:
mode: AlwaysAllow # НЕ використовуйте у промислових кластерах!
enableServer: false
logging:
format: text
address: 127.0.0.1 # Restrict access to localhost
readOnlyPort: 10255 # НЕ використовуйте у промислових кластерах!
staticPodPath: /etc/kubernetes/manifests
containerRuntimeEndpoint: unix:///var/run/crio/crio.sock
EOF
Примітка:
Оскільки ви не встановлюєте промисловий кластер, у вас використовується звичайний HTTP порт (readOnlyPort: 10255) для неавтентифікованих запитів до API kubelet.
У цьому посібнику вебхук автентифікації вимкнено, а режим авторизації встановлено на AlwaysAllow. Ви можете дізнатися більше про режими авторизації та вебхук автентифікації, щоб правильно налаштувати kubelet в автономному режимі для вашого середовища.
Перегляньте розділ Порти та протоколи, щоб зрозуміти, які порти використовують компоненти Kubernetes.
Аргумент командного рядка --kubeconfig було навмисно пропущено у файлі конфігурації конфігураційного файлу служби. Цей аргумент задає шлях до файлу kubeconfig файл, який визначає, як підключатися до сервера API, вмикаючи режим сервера API. Якщо його не вказати, увімкнеться автономний режим.
В автономному режимі ви можете запускати Podʼи за допомогою маніфестів Pod'ів. Маніфести можуть або знаходитися у локальній файловій системі, або бути отриманими по HTTP з джерела конфігурації.
Створіть маніфест для Pod:
cat <<EOF > static-web.yaml
apiVersion: v1
kind: Pod
metadata:
name: static-web
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
EOF
Скопіюйте файл маніфесту static-web.yaml до теки /etc/kubernetes/manifests.
Мережевий втулок Podʼа створює мережевий міст (cni0) і пару інтерфейсів veth для кожного Podʼа (один з пари знаходиться всередині новоствореного Podʼа, а інший — на рівні хосту).
Зверніться до точки доступу до API kubelet за адресою http://localhost:10255/pods:
Приєднайтеся до сервера nginx за адресою http://<IP>:<Порт> (стандартний порт 80), в цьому випадку:
curl http://10.85.0.4
Вивід має бути подібним до:
<!DOCTYPE html><html>
<head>
<title>Welcome to nginx!</title>
...
Де шукати більш детальну інформацію
Якщо вам потрібно діагностувати проблему, пов'язану з роботою цього посібника, ви можете зазирнути в наступні теки для моніторингу та усунення несправностей:
На цій сторінці було розглянуто основні аспекти розгортання kubelet в автономному режимі. Тепер ви готові розгортати Podʼи та тестувати додаткову функціональність.
Зверніть увагу, що в автономному режимі kubelet не підтримує отримання конфігурацій Podʼів із панелі управління (оскільки немає підключення до панелі управління).
Також ви не можете використовувати ConfigMap або Secret для налаштування контейнерів у статичному Pod.
Що далі
Ознайомтесь з Hello, minikube, щоб дізнатися, як запускати Kubernetes з панеллю управління. Інструмент minikube допоможе вам налаштувати тренувальний кластер на вашому компʼютері.
8.1 - Підключення застосунків за допомогою Service
Модель Kubernetes для підключення контейнерів
Тепер, коли у вас є постійно працюючий, реплікований застосунок, ви можете дати до нього доступ з мережі.
Kubernetes передбачає, що кожен з Podʼів може спілкуватися з іншими, незалежно від того, на якому хості вони опиняються. Kubernetes надає кожному Podʼу власну IP-адресу, що є приватною адресою кластера, тому вам не потрібно явно створювати посилання між Podʼами або перенаправляти порти контейнера на порти хосту. Це означає, що контейнери всередині Podʼів можуть звертатися до портів один одного на localhost, і всі Podʼи в кластері можуть бачити один одного без NAT. У решті цього документа розглянуто, як ви можете запустити надійні сервіси на такій мережевій моделі.
У цьому посібнику використовується простий вебсервер nginx для демонстрації концепції.
Експонування Podʼів в кластер
Ми робили це в попередньому прикладі, але зробимо це ще раз і зосередимося на перспективі мережі. Створіть Pod nginx, і зверніть увагу, що він має специфікацію порту контейнера:
NAME READY STATUS RESTARTS AGE IP NODE
my-nginx-3800858182-jr4a2 1/1 Running 0 13s 10.244.3.4 kubernetes-minion-905m
my-nginx-3800858182-kna2y 1/1 Running 0 13s 10.244.2.5 kubernetes-minion-ljyd
Перевірте IP-адреси ваших Podʼів:
kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
POD_IP
[map[ip:10.244.3.4]][map[ip:10.244.2.5]]
Ви повинні мати можливість увійти у будь-який вузол у своєму кластері за допомогою SSH і використовувати інструмент, такий як curl, щоб робити запити до обох IP-адрес. Зверніть увагу, що контейнери не використовують порт 80 на вузлі, і немає жодних спеціальних правил NAT для маршрутизації трафіку до Podʼів. Це означає, що ви можете запустити кілька Podʼів nginx на одному й тому ж вузлі, використовуючи той же containerPort, і отримувати доступ до них з будь-якого іншого Podʼа або вузла у вашому кластері, використовуючи призначену IP адресу Podʼа. Якщо ви хочете організувати перенаправлення певного порту на хості Вузла на резервні Podʼи, ви можете це зробити — але модель мережі повинна передбачати, що вам не потрібно цього робити.
Отже, у нас є Podʼи, що працюють з nginx у плоскому адресному просторі, доступному для всього кластера. Теоретично, ви можете звертатися до цих Podʼів безпосередньо, але що станеться, коли вузол вийде з ладу? Podʼи загинуть разом з ним, і ReplicaSet всередині Deployment створить нові, з іншими IP-адресами. Це проблема, яку вирішує Service.
Service Kubernetes — це абстракція, яка визначає логічний набір Podʼів, що працюють десь у вашому кластері, і всі надають однакову функціональність. Коли створюється Service, йому призначається унікальна IP-адреса (яку називають clusterIP). Ця адреса привʼязана до тривалості життя Service і не змінюється, поки Service живий. Podʼи можуть бути налаштовані для звернення до Service і знати, що звʼязок з Service буде автоматично балансуватися на деякий Pod, що є членом Service.
Ви можете створити Service для ваших двох реплік nginx за допомогою kubectl expose:
Ця специфікація створить Service, який буде спрямований на TCP порт 80 на будь-якому Podʼі з міткою run: my-nginx і відкриє його на абстрактному порті Service (targetPort: це порт, на якому контейнер приймає трафік, port — це абстрактний порт Service, який може бути будь-яким портом, що використовується іншими Podʼами для доступу до Service). Перегляньте Service API обʼєкт, щоб побачити список підтримуваних полів у визначенні Service. Перевірте ваш Service:
kubectl get svc my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.0.162.149 <none> 80/TCP 21s
Як згадувалося раніше, Service підтримується групою Podʼів. Ці Podʼи експонуються через
EndpointSlices. Селектор Service буде оцінюватися безперервно, і результати будуть надсилатися у EndpointSlice, який підключений до Service за допомогою мітки. Коли Pod припиняє існування, він автоматично видаляється з EndpointSlices, який містить його як точку доступу. Нові Podʼи, що відповідають селектору Service, автоматично додаються до EndpointSlice для цього Service. Перевірте точки доступу та зауважте, що IP-адреси збігаються з Podʼами, створеними на першому кроці:
kubectl get endpointslices -l kubernetes.io/service-name=my-nginx
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
my-nginx-7vzhx IPv4 80 10.244.2.5,10.244.3.4 21s
Тепер ви повинні бути в змозі звертатися до nginx Service на <CLUSTER-IP>:<PORT> з будь-якого вузла у вашому кластері. Зауважте, що IP-адреса Service є повністю віртуальною, вона ніколи не передається мережею. Якщо вам цікаво, як це працює, ви можете прочитати більше про сервіс-проксі.
Доступ до Service
Kubernetes підтримує два основні режими знаходження Service — змінні середовища та DNS. Перший режим працює "з коробки", тоді як другий вимагає додаткового модуля CoreDNS cluster addon.
Примітка:
Якщо змінні середовища для сервісів не потрібні (через можливі конфлікти з очікуваннями програмам, занадто багато змінних для обробки, використання лише DNS тощо), ви можете вимкнути цей режим, встановивши прапорець enableServiceLinks у значення false в pod spec.
Змінні середовища
Коли Pod запускається на вузлі, kubelet додає набір змінних середовища для кожного активного Service. Це створює проблему з порядком. Щоб зрозуміти чому, перевірте середовище вашого працюючого Pod nginx (назва вашого Pod буде іншою):
kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
Зверніть увагу, що ваш Service не згадується. Це тому, що ви створили репліки перед створенням Service. Іншим недоліком цього є те, що планувальник може розмістити обидва Podʼи на одній машині, що призведе до припинення роботи всього Service, якщо він вийде з ладу. Ми можемо зробити це прям зараз, видаливши 2 Podʼи та очікуючи, поки Deployment їх відновить. Цього разу Service існує до реплік. Це забезпечить рівномірний розподіл ваших Podʼів на рівні планувальника (за умови рівної потужності всіх вузлів), а також правильні змінні середовища:
Kubernetes пропонує DNS-сервіс, який автоматично призначає DNS-імена іншим Serviceʼам. Ви можете перевірити, чи він працює у вашому кластері:
kubectl get services kube-dns --namespace=kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.0.0.10 <none> 53/UDP,53/TCP 8m
Далі в цьому розділі ми будемо вважати, що у вас є Service із довготривалою IP-адресою (my-nginx), і DNS-сервер, який присвоїв цій IP-адресі імʼя. Ми використовуємо надбудову CoreDNS (назва застосунку kube-dns), тому ви можете звертатися до Service з будь-якого Pod у вашому кластері, використовуючи стандартні методи (наприклад, gethostbyname()). Якщо CoreDNS не працює, ви можете увімкнути його, звірившись з README CoreDNS або інформацією з Встановлення CoreDNS. Запустімо ще один застосунок curl для перевірки цього:
kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt
Потім натисніть Enter і запустіть nslookup my-nginx:
До цього моменту ми отримували доступ до сервера nginx лише всередині кластеру. Перед тим як виставити Service в Інтернет, вам потрібно переконатися, що канал звʼязку захищений. Для цього вам знадобиться:
Самопідписні сертифікати для https (якщо у вас немає ідентифікаційного сертифіката)
Сервер nginx, налаштований для використання сертифікатів
Ви можете отримати все це з прикладу nginx https. Це вимагає встановлення інструментів go та make. Якщо ви не хочете їх встановлювати, тоді дотримуйтесь ручних кроків, описаних нижче. Коротко:
На цьому етапі ви можете отримати доступ до nginx сервера з будь-якого вузла.
kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
POD_IP
[map[ip:10.244.3.5]]
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>
Зверніть увагу, що ми використовували параметр -k для curl на останньому етапі, тому що ми нічого не знаємо про Podʼи, що виконують nginx під час генерації сертифіката, тому ми повинні сказати curl ігнорувати невідповідність CName. Створюючи Service, ми повʼязали CName, який використовується в сертифікаті, з фактичним DNS-імʼям, що використовується Podʼами під час пошуку Service. Перевірмо це з Podʼа (для простоти використовується той же Secret, Podʼу потрібен лише nginx.crt для доступу до Service):
Для деяких частин ваших застосунків ви можете захотіти експонувати Service на зовнішню IP-адресу. Kubernetes підтримує два способи: NodePorts та LoadBalancers. Service, створений у попередньому розділі, вже використовував NodePort, тому ваша репліка nginx HTTPS готова обслуговувати трафік з інтернету, якщо ваш вузол має публічну IP-адресу.
Тепер створімо Service знову, використовуючи хмарний балансувальник навантаження. Змініть Type Service my-nginx з NodePort на LoadBalancer:
kubectl edit svc my-nginx
kubectl get svc my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx LoadBalancer 10.0.162.149 xx.xxx.xxx.xxx 8080:30163/TCP 21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>
IP-адреса в колонці EXTERNAL-IP доступна в інтернеті. CLUSTER-IP доступна тільки всередині вашого кластера/приватної хмарної мережі.
Зверніть увагу, що у AWS тип LoadBalancer створює ELB, який використовує (довге) імʼя хосту, а не IP. Воно занадто довге, щоб поміститися в стандартному виводі kubectl get svc, тому вам потрібно виконати kubectl describe service my-nginx, щоб побачити його. Ви побачите щось на кшталт:
kubectl describe service my-nginx
...
LoadBalancer Ingress: a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
...
Застосунки, що працюють у кластері Kubernetes, знаходять і взаємодіють один з одним та з зовнішнім світом через абстракцію Service. У цьому документі пояснюється, що відбувається з Source IP пакетів, надісланих на різні типи Service, і як ви можете змінити цю поведінку відповідно до ваших потреб.
Перш ніж ви розпочнете
Терміни
У цьому документі використовуються наступні терміни:
Мережевий демон, який оркеструє управління віртуальними IP Service на кожному вузлі
Необхідні умови
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
У прикладах використовується невеликий вебсервер nginx, який повертає Source IP запитів, які він отримує через HTTP-заголовок. Ви можете створити його наступним чином:
Примітка:
Образ у наступній команді працює лише на архітектурах AMD64.
Експонувати простий застосунок через різні типи Serviceʼів
Зрозуміти, як кожен тип Services обробляє Source IP NAT
Зрозуміти компроміси, повʼязані зі збереженням Source IP
Source IP для Service з Type=ClusterIP
Пакети, надіслані на ClusterIP зсередини кластера, ніколи не обробляються Source NAT, якщо ви використовуєте kube-proxy в iptables mode, (станартно). Ви можете запитати режим kube-proxy, отримавши http://localhost:10249/proxyMode на вузлі, де працює kube-proxy.
kubectl get nodes
Результат схожий на цей:
NAME STATUS ROLES AGE VERSION
kubernetes-node-6jst Ready <none> 2h v1.13.0
kubernetes-node-cx31 Ready <none> 2h v1.13.0
kubernetes-node-jj1t Ready <none> 2h v1.13.0
Отримати режим проксі на одному з вузлів (kube-proxy слухає на порту 10249):
# Запустіть це в оболонці на вузлі, який хочете запитати.curl http://localhost:10249/proxyMode
Результат:
iptables
Ви можете протестувати збереження Source IP, створивши Service над source-ip застосунку:
NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services nodeport)NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }')
Якщо ви працюєте на хмарному провайдері, можливо, вам доведеться відкрити правило брандмауера для nodes:nodeport, зазначеного вище. Тепер ви можете спробувати звернутися до сервісу ззовні кластера через вказаний вище порт вузла.
for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done
Зверніть увагу, що це не правильні IP клієнтів, це внутрішні IP кластера. Це відбувається так:
Клієнт надсилає пакет до node2:nodePort
node2 замінює Source IP-адресу (SNAT) у пакеті своєю власною IP-адресою
node2 замінює Destination IP-адресу пакета на IP Podʼа
пакет спрямовується до node 1, а потім до точки доступу
відповідь Podʼа спрямовується назад до node2
відповідь Podʼа надсилається клієнту
Візуально це виглядає так:
Схема. Source IP Type=NodePort з використанням SNAT
Щоб уникнути цього, Kubernetes має функцію зберігати Source IP-адресу клієнта. Якщо ви встановите для service.spec.externalTrafficPolicy значення Local, kube-proxy надсилає проксі-запити лише до локальних точок доступу та не пересилає трафік до інших вузлів. Цей підхід зберігає Source IP-адресу. Якщо немає локальних точок доступу, пакети, надіслані до вузла, відкидаються, тому ви можете покладатися на правильну Source IP-адресу в будь-яких правилах обробки пакетів, які ви можете застосувати до пакета, який проходить до точки доступу.
Встановіть значення поля service.spec.externalTrafficPolicy наступним чином:
for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done
Вивід подібний до:
client_address=198.51.100.79
Зауважте, що ви отримали лише одну відповідь із правильною IP-адресою клієнта від одного вузла, на якому працює Pod точки доступу.
Ось що відбувається:
клієнт надсилає пакет на node2:nodePort, який не має кінцевих точок
пакет відкидається
клієнт надсилає пакет на node1:nodePort, який має точки доступу
node1 направляє пакет до точки доступу з правильною Source IP-адресою
Візуально:
Схема. Source IP Type=NodePort зберігає Source IP клієнта
Source IP для Service з Type=LoadBalancer
Пакети, які надсилаються на Service з Type=LoadBalancer, стандартно мають Source NAT адресацію, оскільки всі можливі для планування вузли Kubernetes у стані Ready мають право на навантаження рівномірного розподілу трафіку. Якщо пакети надходять на вузол без точки доступу, система передає їх на вузол з точкою доступу, замінюючи адресу Source IP на пакеті на IP-адресу вузла (як описано у попередньому розділі).
Ви можете перевірити це, експонувавши застосунок source-ip-app через балансувальник навантаження:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
loadbalancer LoadBalancer 10.0.65.118 203.0.113.140 80/TCP 5m
Потім надішліть запит на зовнішню IP-адресу цього Service:
curl 203.0.113.140
Вивід подібний наступному:
CLIENT VALUES:
client_address=10.240.0.5
...
Однак, якщо ви працюєте на середовищі Google Kubernetes Engine/GCE, встановлення поля service.spec.externalTrafficPolicy на значення Local змушує вузли без точок доступу Service видаляти себе зі списку вузлів, які мають право на рівномірно розподілений трафік, шляхом умисної невдачі перевірок стану.
Ви повинні негайно побачити, що поле service.spec.healthCheckNodePort виділене Kubernetes:
kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort
Вивід подібний наступному:
healthCheckNodePort:32122
Поле service.spec.healthCheckNodePort вказує на порт на кожному вузлі, на якому надається перевірка стану за адресою /healthz. Ви можете перевірити це:
kubectl get pod -o wide -l app=source-ip-app
Вивід подібний наступному:
NAME READY STATUS RESTARTS AGE IP NODE
source-ip-app-826191075-qehz4 1/1 Running 0 20h 10.180.1.136 kubernetes-node-6jst
Використовуйте curl, щоб отримати доступ до точки доступу /healthz на різних вузлах:
# Виконайте це локально на вузлі, який ви вибралиcurl localhost:32122/healthz
1 Service Endpoints found
На іншому вузлі ви можете отримати інший результат:
# Виконайте це локально на вузлі, який ви вибралиcurl localhost:32122/healthz
No Service Endpoints Found
Контролер, який працює на панелі управління відповідає за доступ до хмарного балансувальника навантаження. Той самий контролер також виділяє HTTP перевірки стану, що вказують на цей порт/шлях на кожному вузлі. Зачекайте приблизно 10 секунд, щоб вузли без точок доступу мають невдалі перевірки стану, а потім використовуйте curl, щоб запитати IPv4-адресу балансувальника навантаження:
curl 203.0.113.140
Вивід подібний наступному:
CLIENT VALUES:
client_address=198.51.100.79
...
Міжплатформна підтримка
Тільки деякі хмарні постачальники пропонують підтримку збереження Source IP-адреси через Serviceʼи з Type=LoadBalancer. Хмарний постачальник, на якому ви працюєте, може виконати запит на надання балансувальника навантаження кількома різними способами:
З проксі, що завершує зʼєднання клієнта та відкриває нове зʼєднання з вашими вузлами/точками доступу. У таких випадках Source IP-адреса завжди буде адресою хмарного балансувальника, а не адресою клієнта.
З сервісом пересилання пакетів (forwarder), таким чином, що запити від клієнта, надісліна до VIP балансувальника, потрапляють на вузол з Source IP-адресою клієнта, а не на проміжний проксі.
Балансувальники навантаження у першій категорії повинні використовувати узгоджений протокол між балансувальником навантаження та бекендом для передачі реальної IP-адреси клієнта, такі як HTTP Forwarded або X-FORWARDED-FOR заголовки, або протокол проксі. Балансувальники навантаження у другій категорії можуть використовувати функціонал, описаний вище, створюючи HTTP перевірку стану, що вказує на порт, збережений у полі service.spec.healthCheckNodePort Serviceʼа.
8.3 - Дослідження поведінки завершення роботи для Podʼів та їх точок доступу
Коли ви підʼєднали свій застосунок до Service, дотримуючись кроків, схожих на ті, що описані в Підключення застосунків за допомогою Service, у вас є постійно працюючий, реплікований застосунок, який викритий в мережі. Цей посібник допоможе вам розглянути процес завершення роботи для Podʼів та дослідити способи впровадження належного припинення зʼєднань.
Процес завершення роботи для Podʼів та їх точок доступу
Часто виникають випадки, коли потрібно завершити роботу Podʼа — чи то для оновлення, чи то для зменшення масштабу. Для поліпшення доступності застосунку може бути важливо реалізувати правильне завершення активних зʼєднань.
У цьому посібнику пояснюється процес завершення роботи для Podʼів у звʼязку з відповідним станом точки доступу та видаленням за допомогою простого вебсервера nginx для демонстрації концепції.
Приклад процесу з завершенням роботи точки доступу
apiVersion:apps/v1kind:Deploymentmetadata:name:nginx-deploymentlabels:app:nginxspec:replicas:1selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:terminationGracePeriodSeconds:120# extra long grace periodcontainers:- name:nginximage:nginx:latestports:- containerPort:80lifecycle:preStop:exec:# Real life termination may take any time up to terminationGracePeriodSeconds.# In this example - just hang around for at least the duration of terminationGracePeriodSeconds,# at 120 seconds container will be forcibly terminated.# Note, all this time nginx will keep processing requests.command:["/bin/sh","-c","sleep 180"]
Це дозволяє застосункам передавати свій стан під час завершення, а клієнтам (таким як балансувальники навантаження) реалізувати функціональність завершення зʼєднань. Ці клієнти можуть виявляти завершальні точки доступу та реалізувати спеціальну логіку для них.
У Kubernetes точки доступу, які завершуються, завжди мають свій статус ready встановлений як false. Це потрібно для забезпечення зворотної сумісності, щоб наявні балансувальники навантаження не використовували їх для звичайного трафіку. Якщо потрібно припинення обробки трафіку на Podʼі, що завершує роботу, фактична готовність може бути перевірені як умова serving.
Після видалення Podʼа, стару точку доступу також буде видалено.