Трехстороннее слияние — способ применения изменений к ресурсам Kubernetes, который для каждого ресурса берет его предыдущую конфигурацию, новую конфигурацию и его текущее состояние, а затем вычисляет патч исправлений и применяет его к ресурсу в кластере. Этот метод используется командой kubectl apply.

werf полностью совместим с Helm 2, который использует применение патчей методом двухстороннего слияния. Этот метод подразумевает создание патча между предыдущим и новым состоянием ресурсов чарта.

werf is fully compatible with the Helm 2, which makes use of two-way-merge patches. This method implies creating patches between previous chart resource configuration and a new one. У метода двустороннего слияния есть проблема: конфигурация ресурса в чарте может расходиться с состоянием ресурса в кластере, если пользователь вносит изменения вручную (например, с помощью команды kubectl edit). Предположим, что в чарте у ресурса указано значение replicas=5, но после деплоя чарта конфигурация ресурса была изменена вручную — установлено replicas=3 с помощью команды kubectl edit. Последующий вызов деплоя не установит replicas=5, как вы могли ожидать, т.к. метод двухстороннего слияния не учитывает реальную конфигурацию ресурса, т.е. не учитывает его текущее состояние.

Методы обновления ресурсов

В настоящий момент werf переходит с двухстороннего метода обновления ресурсов, применяемого раньше по умолчанию, на трехсторонний и сейчас доступны следующие режимы работы:

  1. Двухстороннее слияние с патчами исправления.
  2. Наложение патчей трехстороннего слияния.

Двухстороннее слияние с патчами исправления

Используя этот метод, werf применяет к ресурсам патчи исправления, полученные в результате работы метода двухстороннего слияния. При обновлении ресурсов werf устанавливает у каждого ресурса чарта следующие аннотации:

  • debug.werf.io/repair-patch;
  • debug.werf.io/repair-patch-errors;
  • debug.werf.io/repair-messages.

В аннотацию debug.werf.io/repair-patch сохраняется расхождение текущего состояния ресурса и новой конфигурацией.

Этот патч необходимо применить вручную, чтобы привести текущее состояние ресурса в соответствии с конфигурацией ресурса, указанной в чарте.

Например, если у ресурса в аннотации указан следующий патч:

metadata:
  annotations:
    "debug.werf.io/repair-patch": '{"data":{"node.conf":"PROPER CONTENT"}}'

То пользователь может синхронизировать текущее состояние ресурса с его конфигурацией в чарте, выполнив следующую команду:

kubectl -n mynamespace patch cm/myconfigmap '{"data":{"node.conf":"PROPER CONTENT"}}'

Кроме того, в процессе деплоя werf выводит предупреждение (сообщение с префиксом WARNING) о сформированном патче, который должен быть применен администратором.

Аннотация debug.werf.io/repair-patch-errors содержит ошибки, возникшие в процессе создания патча. Процесс деплоя, в случае появления такой ошибки, не будет прерван.

Аннотация debug.werf.io/repair-messages содержит предупреждения, которые werf также выводит в журнал, плюс отладочную информацию, которая может быть полезна разработчику.

Наложение патчей трехстороннего слияния

С помощью этого метода werf будет применять метод трехстороннего слияния с наложением патчей, которые учитывают предыдущую конфигурацию ресурса, новую конфигурацию ресурса и текущее состояние ресурса в кластере. Такой патч должен изменить ресурс таким образом, чтобы он соответствовал описанной в чарте конфигурации.

Выбор метода обновления ресурсов

Для выбора метода обновления ресурсов werf использует переменную окружения WERF_THREE_WAY_MERGE_MODE или CLI-параметр --three-way-merge-mode, которые могут иметь одно из следующих значений:

  • “disabled” — не использовать патчи трехстороннего слияния ни для уже существующих, ни для новых релизов. Использовать двухстороннее слияние с патчами исправлений для всех релизов;
  • “enabled” — всегда использовать патчи трехстороннего слияния во время обновления — как для существующих, так и для новых релизов;
  • “onlyNewReleases” — новые релизы, созданные после включения этого режима, будут использовать патчи трехстороннего слияния во время обновления, в то время как уже существующие релизы продолжат обновляться с использованием двухстороннего слияния.

Начиная с 01.12.2019 метод обновления ресурсов по умолчанию — onlyNewReleases.

После 15.12.2019 метод обновления ресурсов по умолчанию будет — enabled.

После 01.03.2020 werf не будет использовать переменную окружения WERF_THREE_WAY_MERGE_MODE (и CLI-параметр --three-way-merge-mode), а всегда будет применять метод трехстороннего слияния.

Для переключения метода обновления ресурсов с двухстороннего на трехсторонний необходимо установить WERF_THREE_WAY_MERGE_MODE=enabled и наоборот — для переключения метода обновления ресурсов с трехстороннего назад на двухсторонний необходимо установить WERF_THREE_WAY_MERGE_MODE=disabled.

Работа совместно с HPA

Функционал горизонтального автомасштабирования подов (Horizontal Pod Autoscaler) автоматически изменяет количество реплик контроллера (Replication Controller, Deployment или ReplicaSet).

Если включить HPA и явно указать в шаблоне чарта количество реплик (параметр spec.replicas), то при деплое работа HPA будет учитываться так, как если бы вручную изменили параметр spec.replicas. Т.е. если в результате работы HPA изменится количество реплик, то при деплое, независимо от метода обновления ресурсов, это изменение будет учтено и войдет либо в патч исправления, в случае двухстороннего метода слияния, либо ресурс будет изменен после наложения патча работы трехстороннего метода слияния.

Исключить такое поведение можно либо удалив из чарта явное указание реплик в параметре spec.replicas, либо установив у ресурса аннотацию werf.io/set-replicas-only-on-creation.

werf.io/set-replicas-only-on-creation

Данная аннотация указывает werf, что параметр spec.replicas ресурса должен изменяться в соответствии с конфигурацией чарта только при инициализации нового ресурса. Соответственно, werf будет игнорировать фактическое изменение параметра spec.replicas ресурса при последующих его обновлениях в процессе деплоя.

Эту аннотацию рекомендуется устанавливать при включенном HPA.

Работа совместно с VPA

Функционал вертикального автомасштабирования подов Vertical Pod Autoscaler может выдавать рекомендации по установлению ресурсам параметров CPU requests и memory requests либо может автоматически их изменять.

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

Исключить такое поведение можно либо удалив из чарта явное указание количества ресурсов (параметр resources), либо установив аннотацию werf.io/set-resources-only-on-creation annotation у соответствующего ресурса.

werf.io/set-resources-only-on-creation

Данная аннотация указывает werf, что параметр resources у объекта в кластере должен изменяться в соответствии с конфигурацией чарта только при инициализации нового объекта. Соответственно, werf будет игнорировать фактическое изменение параметра resources объекта кластера при последующих его обновлениях в процессе деплоя.

Эту аннотацию рекомендуется устанавливать при включенном VPA.

Принятие изменений ресурсов

Возможны 2 случая, когда указанный в чарте ресурс уже существует в кластере. Они рассматриваются далее.

Установка нового релиза

При установке нового релиза ресурс может уже существовать в кластере.

В этом случае werf завершит процесс деплоя с ошибкой. Ресурс, который уже существует в кластере, не будет помечен как принятый в релиз. Новый релиз будет установлен со статусом FAILED, и удаление этого релиза не приведет к удалению ресурсов, которые не были помечены как принятые в релиз.

Обновление релиза

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

Способы принятия существующего ресурса в релиз

Чтобы разрешить принятие в релиз конфигурации уже существующего ресурса необходимо установить аннотацию werf.io/allow-adoption-by-release=RELEASENAME в объекте ресурса в kubernetes (например, с помощью команды kubectl edit), объявить ресурс в шаблонах чарта и запустить процесс деплоя. При принятии ресурса в релиз, werf создаст патч с использованием метода трехстороннего слияния, чтобы привести состояние существующего ресурса к описанной в чарте конфигурации.

Обратите внимание, что изменение параметра WERF_THREE_WAY_MERGE_MODE не влияет на настройки принятия ресурсов, т.к. в любом случае применяется метод трехстороннего слияния.

Патчи трехстороннего слияния и принятие ресурса

При принятии существующего ресурса в релиз, всегда используются патчи трехстороннего слияния (параметр WERF_THREE_WAY_MERGE_MODE не влияют на это поведение).

ЗАМЕЧАНИЕ После принятия ресурса его действующий манифест может не полностью соответствовать конфигурации ресурса в чарте. Если у принимаемого в релиз ресурса установлены поля, не описанные в конфигурации ресурса в чарте, то эти поля не будут удалены, т.о. фактически останутся у ресурса, но будут отсутствовать в конфигурации ресурса в релизе.

Например, допустим что у нас есть существующий в кластере объект Deployment со следующей конфигурацией:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mydeploy1
  annotations:
    extra: data
  labels:
    service: mydeploy1
spec:
  replicas: 1
  selector:
    matchLabels:
      service: mydeploy1
  template:
    metadata:
      labels:
        mylabel: myvalue
        service: mydeploy1
    spec:
      containers:
      - name: main
        command: [ "/bin/bash", "-c", "while true; do date ; sleep 1 ; done" ]
        image: ubuntu:18.04
      - name: additional
        command: [ "/bin/bash", "-c", "while true; do date ; sleep 1 ; done" ]
        image: ubuntu:18.04

Допустим, что в шаблоне чарта у этого объекта следующая конфигурация:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mydeploy1
  labels:
    service: mydeploy1
spec:
  replicas: 2
  selector:
    matchLabels:
      service: mydeploy1
  template:
    metadata:
      labels:
        service: mydeploy1
    spec:
      containers:
      - name: main
        command: [ "/bin/bash", "-c", "while true; do date ; sleep 1 ; done" ]
        image: ubuntu:18.04

При принятии ресурса в процессе деплоя werf изменит replicas=1 на replicas=2. Остальные данные ресурса, такие как metadata.annotations.extra=data, spec.template.metadata.labels.mylable=myvalue и секция контейнера additional в spec.template.spec.containers[], останутся у реального объекта, т.к. они не включены в шаблон чарта. Пользователь должен удалить такие части объекта самостоятельно, если это необходимо.