Управление развертыванием контейнерных приложений в Kubernetes с Argo Rollouts

Управление развертыванием контейнерных приложений в Kubernetes с Argo Rollouts

    Сложности развертывания контейнерных stateful-приложений и как их решить с Argo Rollouts и Kubernetes Downward API: примеры YAML-конфигураций канареечного развертывания Spark-приложения.

    Расширение стратегий развертывания в Kubernetes с Argo Rollouts

    Мы уже писали, в чем сложности оркестрации параллельных заданий на платформе Kubernetes и как их можно решить с помощью Argo Workflows — контейнерного движка рабочих процессов с открытым исходным кодом. Однако, помимо Argo Workflows, в экосистеме Argo есть еще один полезный инструмент, который пригодится для управления контейнерными распределенными приложениями — Argo Rollouts. Это контроллер Kubernetes, который включает набор пользовательских определений ресурсов (CRD, Custom Resource Definition) для реализации расширенных возможностей развертывания приложений в Kubernetes, в т.ч. сине-зеленое и канареечное развертывание. Подробнее про стратегии развертывания мы писали здесь. В Kubernetes есть объект Deployment, который позволяет разработчикам автоматически развертывать приложения на узлах кластера этой платформы управления контейнерными приложениями. Однако, в объекте Deployment отсутствуют популярные стратегии развертывания, в частности, сине-зеленое и канареечное. Argo Rollouts, реализованный как Kubernetes CRD, расширяет объекта Deployment, позволяя использовать эффективные стратегии развертывания. По умолчанию он поддерживает только одну службу/приложение. Однако, это можно исправить, изменив соответствующие настройки.

    Пример конфигурации Argo Rollouts для развертывания новой версии Spark-приложения с использованием стратегии канареечного развертывания может выглядеть как следующий YAML-файл:

    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: spark-app
      namespace: default
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: spark-app
      template:
        metadata:
          labels:
            app: spark-app
        spec:
          containers:
          - name: spark-container
            image: my-spark-app:2.0  # Текущая версия приложения
            ports:
            - containerPort: 8080
            readinessProbe:
              httpGet:
                path: /health
                port: 8080
              initialDelaySeconds: 5
              periodSeconds: 10
      strategy:
        canary:
          steps:
          - setWeight: 20  # Отправка 20% трафика на новую версию
          - pause:
              duration: 60s  # Пауза для мониторинга
          - setWeight: 50  # Увеличение до 50%
          - pause:
              duration: 60s
          - setWeight: 100  # Полный переход на новую версию
          analysis:
            templates:
            - templateName: rollout-analysis
            args:
            - name: success-rate
              value: "95"
      analysis:
        templates:
        - name: rollout-analysis
          metrics:
          - name: success-rate
            interval: 1m
            count: 3
            successCondition: result >= 95
            provider:
              prometheus:
                address: http://prometheus-server.default.svc.cluster.local
                query: |
                  sum(rate(http_requests_total{app="spark-app", status=~"2.."}[1m])) 
                  / 
                  sum(rate(http_requests_total{app="spark-app"}[1m])) * 100

    В этой конфигурации канареечного развертывания сперва 20% трафика перенаправляется на новую версию Spark-приложения. Затем после временной паузы (1 минута) для мониторинга стабильности доля трафика увеличивается до 50% трафика. После новой паузы выполняется полный переход на новую версию. Также используется шаблон rollout-analysis, который проверяет показатель success-rate, рассчитываемый как отношение успешных HTTP-запросов (статусы 2xx) к общему числу запросов за 1 минуту. По условию не менее 95% запросов должны быть успешными.

    Развертывание stateful-приложений

    Argo Rollouts довольно просто использовать для stateless-приложений. Он поддерживает несколько распространенных провайдеров трафика, включая Kubernetes API Gateway, и может автоматически включать или отключать производственный трафик в новой версии приложения в случае сине-зеленого развертывания или постепенно переключать трафик для канареечного. Но для stateful-приложений, которые зависят от базы данных или очереди, возможностей Argo Rollouts недостаточно. Однако, многие Spark- или Flink-приложения распределенной обработки данных являются stateful, поскольку данные в реальном времени поступают непрерывно, и для их корректной обработки часто требуется хранить состояние между событиями. Например, для вычисления скользящих средних или агрегаций по окнам времени необходимо запоминать предыдущие данные. Кроме того, многие аналитические задачи включают в себя операции, которые зависят от предыдущих значений данных. Stateful-обработка позволяет выполнять такие операции, как группировка, сортировка или объединение данных на основе накопленного состояния. Наконец, хранение состояния позволяет системе восстанавливаться после сбоев без потери данных.

    Возвращаясь к проблеме развертывания stateful-приложений, отметим, что стабильное приложение отслеживает производственную очередь или базу данных, где хранятся состояния. После запуска новой, предварительной, версии приложения она тоже будет забирать рабочие данные из производственной очереди, чего следует избежать в тестовом режиме. Разумеется, можно просто приостановить развертывание и вручную перенаправить новую версию приложения в другую очередь. Это решение будет работать, но оно не очень изящное. Можно сделать по-другому, используя Argo Rollouts вместе с Kubernetes Downward API.

    Развертывание новой версии stateful-приложения
    Развертывание новой версии stateful-приложения

    Kubernetes Downward API – это встроенный механизм Kubernetes, который позволяет передавать любые метки Pod в контейнерное приложение, монтируя их как файлы или переменные среды.

    Принцип работы Kubernetes Downward API
    Принцип работы Kubernetes Downward API

    Kubernetes Downward API отлично сочетается с эфемерными метками Argo Rollouts, позволяющими указать контроллеру, что надо автоматически добавлять или удалять определенные метки в развертывание. Эти метки можно преобразовать в конфигурацию приложения с помощью API Downward. Чтобы приложение знало об этих изменениях, необходимо обеспечить автоматическую перезагрузку параметров конфигурации при обновлении файлов, которые их содержат. Это реализуется на стороне кода приложения. В результате процесс развертывания будет выглядеть так:

    Развертывание новой версии stateful-приложения с Kubernetes Downward API и Argo Rollouts
    Развертывание новой версии stateful-приложения с Kubernetes Downward API и Argo Rollouts

    Пример манифеста Rollout для Spark-приложения, который использует Downward API для передачи меток в конфигурацию приложения, может выглядеть так:

    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: spark-app-rollout
      namespace: default
    spec:
      replicas: 3
      strategy:
        canary:
          steps:
            - setWeight: 25
            - pause:
                duration: 10m
            - setWeight: 50
            - pause:
                duration: 10m
            - setWeight: 100
      selector:
        matchLabels:
          app: spark-app
      template:
        metadata:
          labels:
            app: spark-app
            # Эфемерная метка, управляемая Argo Rollouts
            rollout-pod-template-hash: "{{.Rollout.Status.CurrentPodHash}}"
        spec:
          containers:
            - name: spark-container
              image: spark:3.1.1
              ports:
                - containerPort: 8080
              env:
                - name: APP_VERSION
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.labels['rollout-pod-template-hash']
              # Использование Downward API для передачи меток в приложение
              volumeMounts:
                - name: config
                  mountPath: /etc/config
                  readOnly: true
          volumes:
            - name: config
              downwardAPI:
                items:
                  - path: "app-version"
                    fieldRef:
                      fieldPath: metadata.labels['rollout-pod-template-hash']

    В этом примере, также, как и в прошлом, используется стратегия канареечного развертывания, чтобы постепенно увеличивать количество подов с новой версией приложения с мониторингом стабильности. Эфемерная метка rollout-pod-template-hash служит уникальным идентификатором версии приложения и автоматически обновляется контроллером Argo Rollouts при каждом изменении шаблона пода. Эта эфемерная метка передается внутрь контейнера с помощью переменной окружения APP_VERSION. Конфигурация тома монтируется как конфигурационный файл, содержащий значение метки. Это полезно, когда приложение читает конфигурацию из файловой системы.

    Вышеприведенный манифеста надо сохранить в YAML-файл и применить его в kubectl, используя команду apply –f. Отслеживать статус Rollout можно также через CLI-утилиту, вызвав в kubectl команду argo rollouts get rollout или использовать интерфейс пользователя Argo Rollouts для наглядного мониторинга.

    Для развертывания новой версии Spark-приложения следует обновить образ контейнера в манифесте Rollout и снова применить изменения. После этого Argo Rollouts автоматически начнет процесс канареечного развертывания с обновленной конфигурацией, используя Downward API для передачи новых меток в приложение.

    Научитесь работать с Kubernetes и Argo Workflows на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

    Источники

    1. https://codefresh.io/blog/progressive-delivery-for-stateful-services-using-argo-rollouts/
    2. https://codefresh.io/blog/multi-service-progressive-delivery-with-argo-rollouts/
    3. https://argo-rollouts.readthedocs.io/en/stable/features/ephemeral-metadata/
    4. https://codefresh.io/learn/argo-rollouts/
    [elementor-template id="13619"]