Operator intro

Оператор #

Доклад

  • сущность, управляющая другими сущностями в кластере
  • содержит шаблоны поведения
  • основные задачи:
    • облегчение жизни операторов k8s
    • уменьшение количества микроменеджмента (автоматизация типовых задач по обслуживанию кластера)

Оператор обслуживает весь жизненный цикл (например масштабирование, шардирование и т.д.; в отличии от Helm, который только ставит пакеты)

При построении оператора исходить из того, что он будет управлять необходимой сущностью как одним ресурсом (в качестве примера, оператор для кластера CH, который воспринимает его как одну сущность)

Манифест для однонодной инсталяции CH:

apiVersion: "clickhouse.altinity.com/v1"    # NB - apiVersion ссылается на другой URL
kind: "ClickHouseInstallation"              # NB - кастомная сущность
metadata:
  name: "demo-01"
spec:
  configuration:
    clusters:
      - name: "demo

Persistent storage #

Local storages:

  • emptyDir
    • доступ к каталогу на хост-машине через демон контейнеризации
    • работает со скоростью доступа к локальному диску
    • данные приколочены к ноде, при перемещении пода, данные теряются
  • hostPath
    • использование конкретного каталога на хостовой машине
    • при перемещении на другую ноду данные не переносятся
  • local
    • все то же самое, но локальный каталог управляется самим k8s

Взаимодействие оператора с k8s #

kubectl apply -> новые объекты -> API -> Etcd

Controller - сущность, которая реализует изменения в наборе объектов, которые были записаны в Etcd.

Custom Resource Definition - описание “структуры данных”

Operator (Custom Resource Controller) - контроллер, который может создать кастомный ресурс. До его появления в кластере, даже если кастомный ресурс будет добавлен в Etcd, ничего не произойдет, потому что дефолтный контроллер не знает что с ним делать.

Оператор может исполняться снаружи кластера.

Цикл обработки событий оператором #

  • с помощью K8s API подписывается на те или иные события
  • при наступлении события, реагирует на него тем или иным образом
    • составление плана действий
    • исполнение поставленного плана

Разделение ответственности между k8s и оператором #

k8s - отвечает за системные ресурсы, базовый набор объектов Оператор - действует только в своей предметной области

Если на момент наступления события, оператор не мог его обработать, k8s продублирует отправку этого события (то есть, видимо, должна быть обратная связь). Т.о., задача оператора, в том числе, обеспечение идемпотентности.

Jobs & CronJobs #

Вспомним, как в Kubernetes реализована концепция остановки подов. Когда приходит время остановить под, то есть все контейнеры в поде, контейнерам посылается sigterm-сигнал и Kubernetes ждёт определённое время, чтобы приложение внутри контейнера отреагировало на этот сигнал.

В нашем случае приложение — это простой bash-скрипт с бесконечным циклом, реагировать на сигнал некому. Kubernetes ждёт время, которое задано в параметре graceful shutdown. По дефолту — 30 секунд. То есть если за 30 секунд приложение на sigterm не среагировало, дальше посылается sigkill и процесс с pid 1 внутри контейнера убивается, контейнер останавливается. Поле restartPolicy

При проверке backoffLimit поды у нас перезагружались. При этом в манифесте указан параметр restartPolicy: Never. Но когда мы смотрели, как работает опция backoffLimit, поды перезагружались. Здесь нет противоречия: если вы посмотрите на весь yaml-файл, то заметите, что этот параметр относится не к Job, а к спецификации контейнера, который запускается внутри пода.

apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  backoffLimit: 2
  activeDeadlineSeconds: 60
  template:
    spec:
      containers:
      - name: hello
        image: busybox
        args:
        - /bin/sh
        - -c
        - date; echo Hello from the Kubernetes cluster
      restartPolicy: Never

Этот параметр говорит kubelet, что делать с контейнером после того, как он был завершён с ошибкой. По умолчанию стоит политика Always, то есть если у нас контейнер в поде завершился, kubelet этот контейнер перезапускает. Причем, все остальные контейнеры в поде продолжают работать, а перезапускается только упавший контейнер.

Это политика по умолчанию, и если её применить в Job, то Job-контроллер не сможет получить информацию о том, что под был завершён с ошибкой. С его точки зрения под будет очень долго выполняться, а то, что kubelet перезапускает упавший контейнер, Job-контроллер не увидит.

Если вы укажете только backoffLimit, но забудете указать restartPolicy, то Job будет выполняться бесконечно. Поэтому в Job надо всегда указывать: backoffLimit (количество попыток), activeDeadlineSeconds (общее время), restartPolicy: Never (сказать kubelet, чтобы он никогда не перезапускал контейнер в поде; если контейнер в поде упал, то и сам под считается упавшим, то есть завершённым. Пусть Job-контроллер разбирается, что произошло).