Паттерн «ожидание внешнего оператора»

Уровень: advanced ~45 мин Практика: Umami (продолжение M5) (MIT)

Что нужно знать перед уроком

  • M6.05 — Паттерн «общий ресурс между релизами» — нужен опыт с lifecycle-аннотациями перед изучением зависимостей от ресурсов вне релиза

Что нужно знать перед уроком

M6 урок 4werf.io/deploy-dependency-* для зависимостей внутри одного релиза. Здесь — аналогичная по духу, но принципиально другая по механике задача: ожидание ресурса, создаваемого снаружи текущего релиза — сторонним оператором Kubernetes (например, cert-manager).

Теория

<id>.external-dependency.werf.io/resource — ожидание ресурса ВНЕ релиза

1
2
3
4
5
6
7
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Application.Instance.Name }}-app
  annotations:
    cert.external-dependency.werf.io/resource: certificates.v1.cert-manager.io/myapp-tls
spec: ...

Сценарий: ваш Deployment должен монтировать TLS-сертификат, но сам сертификат создаётся не вашим чартом напрямую, а сторонним оператором cert-manager (устанавливаемым отдельно, обычно через Module платформы, за пределами вашего Application-релиза) — оператор реагирует на отдельно созданный Certificate CR и через некоторое время (запрос к удостоверяющему центру, DNS-валидация) создаёт итоговый Secret с настоящим сертификатом. <id>.external-dependency.werf.io/resource указывает Nelm дождаться, пока указанный ресурс (certificates.v1.cert-manager.io/myapp-tls — формат <kind>.<version>.<group>/<name>) не появится и не станет готовым, прежде чем создавать зависящий от него Deployment.

Полный формат аннотации

1
2
3
4
metadata:
  annotations:
    cert.external-dependency.werf.io/resource: certificates.v1.cert-manager.io/myapp-tls
    cert.external-dependency.werf.io/name: myapp-production

<id> (здесь cert) — произвольный идентификатор, как и в deploy-dependency-<id> (M6 урок 4), позволяющий объявить несколько независимых внешних зависимостей на одном ресурсе. <id>.external-dependency.werf.io/resource — обязательная часть, задающая <kind>[.<version>.<group>]/<name> искомого ресурса. Дополнительный <id>.external-dependency.werf.io/name может уточнять пространство имён/дополнительную идентификацию, если требуется различать несколько похожих внешних ресурсов.

Чем external-dependency принципиально отличается от deploy-dependency

werf.io/deploy-dependency-<id><id>.external-dependency.werf.io/resource
Область поиска ресурсаТолько внутри текущего релиза NelmСнаружи релиза — любой ресурс кластера, созданный кем угодно (оператором, другим релизом)
Кто создаёт ожидаемый ресурсТот же самый чарт/релиз (Job, StatefulSet и т.д.)Внешний контроллер/оператор (cert-manager и подобные), не связанный с текущим Nelm-релизом
Типичный примерJob-миграция перед Deployment (M6 урок 4)Ожидание Certificate от cert-manager

Это разграничение отражает фундаментальное архитектурное различие: deploy-dependency работает внутри модели DAG самого Nelm-релиза (Nelm точно знает обо всех ресурсах своего релиза и их порядке создания), тогда как external-dependency — это опрос состояния произвольного внешнего ресурса кластера, за жизненным циклом которого Nelm не следит и не управляет им — только ждёт его появления/готовности перед продолжением собственного DAG.

Связь с общей моделью операторов в экосистеме Kubernetes

cert-manager — классический пример Kubernetes-оператора (Custom Controller, реагирующий на CR и доводящий кластер до желаемого состояния — тот же паттерн, что и Deckhouse-контроллеры, обзорно разобранные в M2 уроке 2). external-dependency — это универсальный механизм Nelm для интеграции с любым подобным оператором, не только cert-manager, — везде, где ваш Application должен дождаться результата работы стороннего асинхронного контроллера, прежде чем создавать зависящие от результата ресурсы.

Частые ошибки и подводные камни

  • Путать external-dependency с deploy-dependency и пытаться указать в external-dependency ресурс из своего же чарта. Хотя технически это может сработать (ресурс, созданный своим же релизом, тоже «существует в кластере»), концептуально это неверный инструмент — для внутренних зависимостей предназначен именно deploy-dependency, дающий более точную интеграцию с DAG собственного релиза.
  • Забыть указать версию/группу API в <kind>.<version>.<group>, если у ресурса есть несколько версий API. Неточное указание может привести к тому, что Nelm будет искать не ту версию ресурса, которая реально создаётся оператором в вашем кластере.
  • Не учитывать, что время ожидания external-dependency зависит от стороннего оператора, вне контроля вашего Application. Если cert-manager по какой-то причине не может выпустить сертификат (проблема с DNS-валидацией, квоты удостоверяющего центра), ваш деплой будет ждать неопределённо долго — стоит сочетать с разумным no-activity-timeout (M7 урок 4), чтобы не блокировать CI бесконечно.

Практика в кластере

🧩 Практика урока: Umami (продолжение M5) · лицензия MIT
  1. Если в вашем практическом кластере доступен cert-manager (или его аналог из платформы Deckhouse), создайте Certificate для тестового домена Umami и настройте Deployment на ожидание итогового Secret:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: umami-tls
spec:
  secretName: umami-tls-secret
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
  dnsNames:
    - "umami.local"
1
2
3
4
5
6
7
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Application.Instance.Name }}-backend
  annotations:
    tls.external-dependency.werf.io/resource: secrets.v1/umami-tls-secret
spec: ...
  1. Задеплойте и понаблюдайте, что Deployment реально ожидает появления Secret, создаваемого cert-manager асинхронно:
1
2
nelm release install -n course-m6-umami -r umami-cert-demo . --auto-rollback
d8 k get certificate,secret -n course-m6-umami -w
  1. Если cert-manager недоступен в вашем кластере, смоделируйте сценарий на любом другом асинхронно создаваемом ресурсе (например, ConfigMap, создаваемый отдельным тестовым скриптом с задержкой) для отработки самого механизма ожидания.

Практика разработки

Практики разработки в этом уроке нет — весь материал относится к аннотациям, применённым в практике выше.

Шпаргалка команд урока

1
2
<id>.external-dependency.werf.io/resource: <kind>[.<version>.<group>]/<name>
<id>.external-dependency.werf.io/name: <optional-additional-identification>

Вопросы для самопроверки

В чём принципиальное отличие external-dependency от deploy-dependency?

Разграничение отражает архитектурное различие: DAG собственного релиза vs опрос произвольного внешнего ресурса кластера.

Источник: Deckhouse — Nelm-аннотации

Какой типичный пример использования external-dependency приведён в официальной документации?

cert-manager — классический пример оператора, чей результат работы Application должен дождаться перед продолжением.

Источник: Deckhouse — Nelm-аннотации

Что означает произвольная часть <id> в аннотации <id>.external-dependency.werf.io/resource?

Аналогично deploy-dependency-<id>, id используется для различения нескольких аннотаций на одном ресурсе.

Источник: Deckhouse — Nelm-аннотации

Управляет ли Nelm жизненным циклом ресурса, указанного в external-dependency?

external-dependency — это опрос состояния, а не управление ресурсом, в отличие от ресурсов собственного релиза.

Источник: Deckhouse — Nelm-аннотации

Какой риск нужно учитывать при использовании external-dependency?

Если внешний оператор не может создать/подготовить ресурс, деплой будет ждать неопределённо долго без ограничения таймаута.

Источник: Рекомендуемая литература

Рекомендуемая литература

Официальная документация

Статьи и блоги

Книги

  • Michael Hausenblas, Stefan Schimanski. Programming Kubernetes. 2019. ISBN 978-1492047107.

Связанные материалы