Разницу между объектами DeploymentConfig и Deployment в OpenShift для коллег я объяснял с помощью упрощения:

«Да, короче, деплойментконфиг — это та же штука, что и деплоймент, только поделка от Редхата».

Да-да, мне было лень вдаваться в подробности. Но теперь я хочу попробовать раскрыть более подробно, а заодно поделиться информацией с другими. Сухой оригинал можно найти тут (по версии 4.8, на которой и буду основывать текст статьи).

Для примеров я буду использовать простое приложение на Python и Dockerfile.

Дополнительные объекты ImageStream и BuildConfig

DeploymentConfig — это объект, чуть усовершенствованный по сравнению с Deployment. Он использует триггеры и дополнительные объекты, такие как ImageStream и BuildConfig.

ImageStream — это объект, который хранит в себе sha-хеш образов во внутреннем реджистри OpenShift и позволяет задать произвольный тег, независимо от того, что определено в реджистри. Стоит понимать, что это ещё один уровень абстракций Kubernetes, а самих образов в ImageStream нет. Теги ссылаются либо на другой ImageStream, либо на Dockerimage. Помимо этого, с его помощью можно закешировать образ во внутреннем реджистри и не тянуть каждый раз снаружи.

Создадим такой объект:

apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
  name: deploymentconfig-demo
  namespace: test-project

BuildConfig — это объект, который, по сути, является сценарием сборки образа. У него может быть много источников исходного кода (git, dockerfile и другие), стратегия сборки (Docker, S2I и другие). И назначение сборки — Dockerimage или ImageStreamTag.

Пример ниже для стратегии Docker:

apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
  name: deploymentconfig-demo
  namespace: test-project
spec:
  source:
    git:
      ref: main
      uri: 'https://github.com/kruchkov-alexandr/deploymentconfig-demo.git'
    type: Git
  strategy:
    type: Docker
    dockerStrategy:
      pullSecret:
        name: mysecret
  triggers:
    - type: ConfigChange
  output:
    to:
      kind: ImageStreamTag
      name: 'deploymentconfig-demo:latest'

Как примерно это работает:

  • триггером для запуска build является изменение конфигурации BuildConfig;

  • после создания этого объекта BuildConfig мы увидим, что он запустил объект build, в котором и происходит сборка;

  • build подключается к git-репозиторию, выполняется клонирование из ветки main;

  • по стратегии Docker читается содержимое файла Dockerfile (дефолтный путь в корне репозитория, но можно указать и другой путь);

  • для скачивания образа из директивы FROM используется логин и пароль из заранее созданного секрета с именем mysecret (в примере просто мой логин и пароль от гитхаба);

  • полученный образ пушится во внутренний репозиторий кластера OpenShift;

  • после сборки проставляется тег ImageStreamTag в контейнере ImageStream.

Есть масса триггеров и хуков, о них можно почитать тут. Можно хоть по коммиту в битбакете или гитхабе триггерить билд в OpenShift.

Далее для демонстрации триггера мы создадим новый объект DeploymentConfig:

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  name: deploymentconfig-demo
  namespace: test-project
spec:
  selector:
    app: deploymentconfig-demo
  replicas: 1
  template:
    metadata:
      labels:
        app: deploymentconfig-demo
    spec:
      containers:
        - name: deploymentconfig-demo
          env:
            - name: APP_COLOR
              value : blue
          image: image-registry.openshift-image-registry.svc:5000/test-project/deploymentconfig-demo:latest
          ports:
            - containerPort: 8080
  triggers:
  - type: ConfigChange 
  - imageChangeParams:
      automatic: true
      containerNames:
      - deploymentconfig-demo
      from:
        kind: ImageStreamTag
        name: deploymentconfig-demo:latest
    type: ImageChange 

Тут мы видим новый блок с триггерами, в котором происходит проверка на ImageStreamTag.

Как это работает в связке, если не брать в расчёт service + route: 

  • в OpenShift создаём объекты ImageStream, BuildConfig и DeploymentConfig;

  • после создания DeplymentConfig автоматически будет создан объект ReplicationController, который создаст нужное количество экземпляров POD, в нашем случае 1 экземпляр;

  • разработчик получил готовый и работающий сервис в OpenShif;

  • далее разработчик отправляет новый коммит в git-репозитории;

  • репозиторий битбакета/гитхаба запускает вебхук (я его не рассматривал в статье), запуская Build от BuildConfig, или разработчик вручную запускает Build от BuildConfig в OpenShift (как в моём примере);

  • Build клонирует git-репозиторий от ветки main, берёт Dockerfile и запускает сборку, подставляя логин и пароль от заранее созданного секрета mysecret для образа из FROM, пушит собранный образ в репозиторий кластера и добавляет/обновляет тег ImageStreamtag в ImageStream;

  • при изменении тега в ImageStream происходит триггер запуска DeploymentConfig;

  • создаётся новый deployer POD с суффиксом -deploy, который контролирует процесс развёртывания;

  • создаётся ещё один ReplicationController, который создаёт новый экземпляр POD с нашим приложением;

  • после успешного старта контейнера внутри нового POD’а deployer POD завершает работу и переходит в состояние Completed;

  • предыдущий ReplicationController гасит предыдущий POD;

  • разработчик снова получил готовый и работающий сервис в OpenShift с пересобранным образом.

Я бы назвал это некой заменой инструмента CI/CD, упрощающей жизнь разработчику в публикации приложения. То есть разработчику не обязательно настраивать CI/CD процессы и утилиты, привлекать отдельного инженера девопса. Он может быстро опубликовать приложение в OpenShift с минимальными ресурсами и затраченным временем. Настроил git, включил хуки, задеплоил DeploymentConfig, коммитнул в репозиторий, приложение развернулось в OpenShift.

Надо также понимать, что если в вашем неймспейсе кластера есть дефолтные значения реквестов/лимитов ресурсов для POD’ов (объект LimitRange) и это значение, например, 100m для CPU limits, то для deployer POD необходимо 100m CPU. То есть если вы одновременно запустили редеплой 10 DeploymentConfig, то будет поднято 10 deployer POD, которые суммарно возьмут из квоты (объект ResourceQuota) 1000m CPU и будут их держать ещё в течение до 5 секунд после перехода в состояние Completed. В условиях ограничения ресурсов могут возникнуть сложности в одновременном деплое из-за нехватки ресурсов.

Стоит также упомянуть про небольшой баг особенность, которая идёт от версии к версии. Все деплойер POD’ы переходят в состояние Completed и не подчищаются за собой. 

Обходное решение: 

  1. Можно использовать revisionHistoryLimit:1, и тогда будет по 1 Completed деплойер POD’у на каждый из DeploymentConfig.

  2. Либо можно поставить «костыль» в скрипты/скедулер, нечто типа:

oc delete pod --field-selector=status.phase==Succeeded

Иначе у вас картина в кластере будет выглядеть следующим образом:


Применение DeploymentConfig не обязывает вас использовать ImageStream и BuildConfig.

Контроллеры ReplicaSet и ReplicationController

Вторая разница — это использование различных контроллеров:

  • у Deployment — ReplicaSet;

  • у DeploymentConfig — ReplicationController.

Replication Controller и RepliicaSet — это объекты API Kubernetes, которые следят, чтобы указанное количество экземпляров подов было запущено в неймспейсе (проекте) в любое время. Если один или несколько POD’ов завершаются с ошибкой, то контроллер создаст и запустит новый экземпляр, чтобы количество подов всегда соответствовало желаемому.

ReplicaSet является усовершенствованной версией ReplicationController и поддерживает множественный селектор.

Пример различия контроллеров ниже. Обратите внимание на spec.selector.

Для ReplicationController:

apiVersion: v1
kind: ReplicationController
metadata:
  name: example
  namespace: test-project
spec:
  replicas: 2
  selector:
    app: hello-openshift
  template:
    metadata:
      name: hello-openshift
      labels:
        app: hello-openshift
    spec:
      containers:
        - name: hello-openshift
          image: openshift/hello-openshift
          ports:
            - containerPort: 8080

Для ReplicaSet:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: example
  namespace: test-project
spec:
  replicas: 2
  selector:
    matchExpressions:
      - {key: app, operator: In, values: [hello-openshift]}
  template:
    metadata:
      name: hello-openshift
      labels:
        app: hello-openshift
    spec:
      containers:
        - name: hello-openshift
          image: openshift/hello-openshift
          ports:
            - containerPort: 8080

Напрямую создавать объекты ReplicaSet или ReplicationController не нужно. Необходимо использовать создание высокоуровневого объекта Deployment или DeploymentConfig соответственно. Говоря простым языком, нужно создавать объект DeploymentConfig, который автоматически создаст объект ReplicationController, который, в свою очередь, создаст необходимое количество экземпляров POD’ов и будет следить за их количеством.

Автооткат в случае неудачи деплоя

У DeploymentConfig есть возможность автоматического отката в случае неудачного деплоя. Deployer POD сам контролирует процесс деплоя и в случае неудачи автоматически удалит новую версию, откатив на успешную последнюю.

Поддержка кастомных стратегий развёртывания

DeploymentConfig поддерживает кастомные стратегии развёртывания, кроме стандартных Recreate и дефолтной RollingUpdate

Например, OpenShift позволяет определить отдельный контейнер, который отвечает за весь процесс сборки или деплоя. 

Пример:

strategy:
  type: Custom
  customParams:
    image: docker.io/imagestrategy:latest
    command: [ "command", "arg1" ]

Дополнительные примеры можно посмотреть тут.

Поддержка prehook и posthook

DeploymentConfig поддерживает pre-hook и post-hook для стратегии развёртывания.

Пример, как это можно использовать для стратегии Rolling:

...
spec:
...
  strategy:
    type: Rolling
    rollingParams:
      updatePeriodSeconds: 1 
      intervalSeconds: 1 
      timeoutSeconds: 120 
      maxSurge: "20%" 
      maxUnavailable: "10%" 
      pre: {} 
      post: {}
...

Как это работает:

  • запускается pre-hook;

  • количество экземпляров POD’а скалируется вверх для нового ReplicationController исходя из maxSurge;

  • количество экземпляров POD’а скалируется вниз для предыдущего ReplicationController исходя из maxUnavailable;

  • повторяется масштабирование во время деплоя до тех пор, пока новый ReplicationController не наберёт нужного количества экземпляров POD’а, а старый ReplicationController не станет равным 0;

  • запускается post-hook.

Заключение

Надеюсь, ничего важного не упустил и информация была полезной. Я хоть немного раскрыл разницу между этими двумя объектами в кластере OpenShift. В зависимости от процесса разработки вы можете использовать то, что подходит больше всего.

Комментарии (12)


  1. Foreglance
    23.02.2022 10:17

    Объекты DeploymentConfig накапливаются до определённого количества (типа retention-history limit) или сразу удаляются по завершению работы его pod-а ? В любом случае если возможно указать экземпляр DeploymentConfig в OwnerReferences ReplicaSet-а его pod-a, этот pod будет удалён автоматически с удалением экземпляра DeploymentConfig


    1. tonkomoto
      23.02.2022 11:21

      Они обновляются кмк, но автор ответит лучше.

      при удалении реплика сет просто поднимет новый - деплоймент конфиг те же яйца только в профиль от редхата.

      контроллеры - постоянная величина, она просто есть :) никто никуда ничего не удаляет после завершения работы пода


      1. tonkomoto
        23.02.2022 11:53
        +1

        самое забавное - что ReplicationController, это старая версия ReplicaSet, о котором умлочали в "статье"


      1. Foreglance
        23.02.2022 12:01

        Здесь я перепутал - ReplicaSet создаётся Deployment-ом, а вот конфигурация деплоймента самого компонента DeploymentConfig запускает создание ReplicationController-а (и его pod-a).


        1. tonkomoto
          23.02.2022 12:08

          простите - что? :)

          DPC-RS ок, конфигурация компонента ? вы имеете в виду количество реплик?


          1. Foreglance
            23.02.2022 12:13

            Я имел ввиду конфигурацию развёртываемого компонента, описанного в DeploymentConfig. Впервые о деталях OpenShift деплоймента читаю, так что путаюсь.


            1. tonkomoto
              23.02.2022 12:15
              +1

              это автор запутал, DPC - объект OCP, тут нет никакого рокет-сайнс, Редхат просто создал такое вот в API своей системы, если посмотреть на прмеры, оно прям простое.


        1. tonkomoto
          23.02.2022 12:10

          коллеги, какой ReplicationController я не понимаю - это старая сущность, автор вещал о ОКД4+, в свежем кубере нет этого


  1. tonkomoto
    23.02.2022 10:35

    А зачем вы отдали билд на OCP?

    почему не отдать его на Jenkins/GCI/Bamboo/etc ?

    почему отказались от helm?

    imagestreams - imho плохо, зачем тегировать, и отдавать на контейнерную платформу, какой OCP и является, такую важную часть процесса, как сборка и обновление?

    как вы смотрите и дебажите билды? что у вас с IAC - в плане сборки?


  1. tonkomoto
    23.02.2022 11:17

    "— это та же штука, что и деплоймент," - это не так - вы сами описываете различия в этих сущностях.

    Далее - я бы для первой статьи о "особенностях" рассмотрел скорее темплейты OCP, чем сравнение "родной" сущности и кастомной.

    "Я бы назвал это некой заменой инструмента CI/CD, упрощающей жизнь разработчику" - сложно с Вами согласиться. Разработчик должен получать доступ до пайпалайна, как понимать как он работает. Главная особенность OCP - доступ до терминала, логов, мониторинга, да и в принципе через GUI решение OCP.

    Вносить изменения вместе с командой DevOps - а так же, иметь гибкие настройки пайплайна, втч и обращение к секретам, которые не лежат в b64 etcd - вы же банкинг??? =).

    А было бы здорово, например использовать Vault? Вы рассматривали интеграцию с волтом при такой сборке?

    Как можно таким прямым инструментом заменить тот же jenkins/gitlab-ci? вопрос в теорию, конечно.

    "реквестов/лимитов ресурсов для POD’ов" - если Вы пишете о лимитах, то добавьте еще квоты и для самих контейнеров, они так же присутствуют в OCP и чаще более важны, чем то, о чем вы пишите. Вы же наверняка используете пре-стартеры/сопутствующие и сайдкары?

    Так же - для знакомства коллег с OCP - приложили бы пример с лимитами к NS, раз у нас тут ямлы.

    oc delete pod --field-selector=status.phase==Succeeded -> вы используете это на продуктиве? Тут все же странно, есть прекрасный хелм, о котором я писал выше.

    "из maxSurge;" - стоит добавить, что это необязательное значение. Многие компании работают на OKD3, не совсем актуально. Хотя сущности, которые Вы описали - есть в обоих версиях. Тут могу ошибаться.

    "Поддержка prehook и posthook" - про HPA расскажите пожалуйста, и кастомные метрики, информация которую Вы взяли из базы знаний редхат не так интересна, ее, я думаю все читали.

    Еще несколько вопросов, если у Вас будет время ответить:

    • "Build клонирует git-репозиторий от ветки main" - мы работаем только по мастеру? Какой это юзкейс для команд? Вы автобилдите что-то? А кастомные ветки? Прошу пояснить.

    • "репозиторий битбакета или гитхаба" - других систем хранения кода нет/не поддерживается?

    • "пушит собранный образ в репозиторий кластера"

    Как вы пушите в репозиторий кластера и за ним следите? GC? правила версионирования? Это реджестри внутри OCP? Какие правила для обновления для этого необходимы? По каким правилам вы пушите - latest? Или 7ver? Или что вы используете и как тегируете? Как происходит тегирование образа? по ветке/хеш комиту? - тут то и интересно, а не копипаст.

    • "создаётся новый deployer POD с суффиксом -deploy, который контролирует процесс развёртывания";

    как контролирует?

    или вы имеете ввиду - что создается дефолтный под с суффиксом деплой при разворачивании компонента? тогда зачем об этом писать?

    • "создаётся ещё один ReplicationController, который создаёт новый экземпляр POD с нашим приложением""

    т.е создается именно новый контроллер? зачем? объясните пожалуйста

    • после успешного старта контейнера внутри нового POD’а deployer POD завершает работу и переходит в состояние Completed;

    как это работает? Readiness проба отработала? Как вы аккумулируете логи? и анализируете ли их?

    И самое главное - как вы интегрируете такую сборку с QG?


    1. Foreglance
      23.02.2022 12:08

      Если не ошибаюсь - здесь описана работа деплоймента в OpenShift


      1. tonkomoto
        23.02.2022 12:11
        +1

        да это понятно, я описал выше вопросы, к автору статьи, документация изучена и ясна.