Паттерн «Job-миграция перед Deployment»
Что нужно знать перед уроком
- M6.03 — Два обязательных модуля одновременно — нужен опыт с зависимостями от Module перед переходом к паттернам Nelm внутри самого релиза
- M1.10 — Jobs/CronJobs — нужно понимать неизменяемость полей Job из Kubernetes
Что нужно знать перед уроком
M1 урок 10 — Job и его неизменяемые после создания поля. M4 урок 4 — стадии Nelm (Render/Plan/Apply). Здесь — первый из ключевых паттернов Nelm-аннотаций для управления порядком внутри одного релиза (в отличие от M6 уроков 1–3, которые были про зависимости от внешних cluster-wide Module).
Теория
Проблема: обычный порядок манифестов в чарте недостаточен
Если у вашего Application есть Go-бэкенд (Umami), которому перед каждым запуском новой версии нужно применить миграцию схемы БД, интуитивно кажется, что достаточно просто разместить Job миграции раньше Deployment в файлах чарта. Но Nelm (как и Helm) не гарантирует порядок применения ресурсов по порядку файлов в templates/ — на стадии Apply (M4 урок 4) операции без явно указанных зависимостей могут выполняться параллельно. Без явной аннотации Deployment может запуститься до завершения миграции — новый код бэкенда обратится к ещё не мигрированной схеме БД.
Решение — связка двух аннотаций
| |
werf.io/delete-policy: before-creation на самом Job решает первую проблему: поля Job (в частности, spec.template) неизменяемы после создания (M1 урок 10) — при каждом новом деплое с потенциально изменившейся командой/образом миграции обычный kubectl apply/Server-Side Apply завершился бы ошибкой попытки изменить неизменяемое поле. delete-policy: before-creation указывает Nelm удалить предыдущий Job перед созданием нового при каждом деплое — эффективно «пересоздавая» его заново, а не пытаясь обновить.
werf.io/deploy-dependency-migrate: state=ready,kind=Job,name=db-migrate на Deployment решает вторую проблему: Deployment ждёт, пока указанный Job (по имени db-migrate, без префикса инстанса — Nelm сопоставляет по факту имени ресурса в релизе) не достигнет состояния ready (для Job это означает успешное завершение — Succeeded), и только затем начинает создание/обновление самого Deployment. Формат аннотации: state=ready|present[,name=][,namespace=][,kind=][,group=][,version=] — id в имени аннотации (migrate в deploy-dependency-migrate) произвольный, используется только для того, чтобы можно было объявить несколько независимых deploy-dependency-* аннотаций на одном ресурсе с разными id.
Почему нельзя обойтись обычным порядком манифестов
Это не просто «Nelm не заботится о порядке файлов» — сама модель DAG на стадии Plan (M4 урок 4) строится из явных связей между ресурсами (через такие аннотации), а не из текстового порядка YAML-документов в файлах чарта. Параллелизм на стадии Apply — намеренная оптимизация производительности деплоя (независимые ресурсы применяются одновременно, ускоряя общее время nelm release install); без явных deploy-dependency-* аннотаций Nelm не имеет оснований считать Job и Deployment зависимыми друг от друга, даже если по смыслу приложения зависимость очевидна человеку, читающему код.
Частые ошибки и подводные камни
- Полагаться на порядок файлов в
templates/вместо явных аннотаций. Nelm (и Helm) не гарантирует порядок применения по порядку файлов — только явные аннотации создают реальную зависимость в DAG. - Забыть
werf.io/delete-policy: before-creationнаJobмиграции. Без него второй и последующий деплои завершатся ошибкой из-за попытки изменить неизменяемые поля уже существующегоJob. - Указывать в
deploy-dependency-migrateневерныйkind/name, не совпадающий с реальным именем ресурсаJobв манифесте. Nelm сопоставляет зависимость по этим полям буквально — опечатка в имени ресурса сделает аннотацию бесполезной без явной ошибки на этапе Render (проблема проявится только на Apply, когда Nelm не найдёт ресурс для ожидания).
Практика в кластере
- Добавьте
Job-миграцию и связывающую аннотацию в шаблоны Umami:
| |
- Добавьте
werf.io/deploy-dependency-migrateв существующийDeploymentбэкенда:
| |
- Задеплойте и понаблюдайте за порядком выполнения:
| |
Убедитесь, что Job переходит в Completed до появления Pod’ов нового Deployment.
- Повторите деплой второй раз и убедитесь, что старый
Jobбыл корректно удалён и пересоздан, а не привёл к ошибке неизменяемых полей:
| |
Практика разработки
Практики разработки в этом уроке нет — весь код относится к шаблонам, применённым в практике выше.
Шпаргалка команд урока
| |
Полная таблица всех аннотаций порядка/зависимостей — M7 урок 2.
Вопросы для самопроверки
Параллелизм на стадии Apply — намеренная оптимизация, работающая без явных deploy-dependency аннотаций.
Источник: Deckhouse — Nelm-аннотации
Без этой аннотации второй деплой завершился бы ошибкой попытки изменить неизменяемые поля существующего Job.
Источник: Deckhouse — Nelm-аннотации
В отличие от state=present (просто существование), ready для Job подразумевает успешное завершение.
Источник: Deckhouse — Nelm-аннотации
id используется только для различения нескольких аннотаций зависимости на одном и том же ресурсе.
Источник: Deckhouse — Nelm-аннотации
Render — чистая шаблонизация без проверки существования ресурсов; несоответствие имени станет очевидно только при построении/выполнении DAG.
Источник: Deckhouse — Nelm-аннотации
Рекомендуемая литература
Официальная документация
Статьи и блоги
- Разворачиваем приложение в кластере Kubernetes под управлением Deckhouse c помощью werf — Flant на Habr.
- Вышла werf 2.0: новый движок развёртывания Nelm — Flant на Habr.
- Готовим Gitea со вкусом werf CI/CD в кластере Deckhouse. Часть 3 — Flant на Habr.
Книги
- Michael Hausenblas, Stefan Schimanski. Programming Kubernetes. 2019. ISBN 978-1492047107.
Связанные материалы
- Предыдущий урок: M6.03 — Два обязательных модуля одновременно.
- Следующий урок: M6.05 — Паттерн «общий ресурс между релизами».
- Полный справочник аннотаций Nelm: M7.
✓ Урок пройден — все вопросы самопроверки отвечены верно