Розгортання додатків на кількох кластерах Kubernetes з Helm

Як Dailymotion використовує Kubernetes: розгортання додатків

Ми в Dailymotion почали використовувати Kubernetes у продакшені 3 роки тому. Але розгортати додатки на кількох кластерах щось ще задоволення, тому в останні кілька років ми намагалися покращити наші інструменти та робочі процеси.

З чого почалося

Тут ми розповімо, як ми розгортаємо наші програми на кількох кластерах Kubernetes по всьому світу.

Щоб розгорнути кілька об'єктів Kubernetes разом, ми використовуємо Кермо, і всі наші чарти зберігаються в одному репозиторії git. Щоб розгорнути повний стек програми з кількох сервісів, ми використовуємо так званий узагальнюючий чарт. По суті, це чарт, який оголошує залежності та дозволяє ініціалізувати API та його сервіси однією командою.

Ще ми написали невеликий скрипт Python поверх Helm, щоб робити перевірки, створювати чарти, додавати секрети та розгортати програми. Всі ці завдання виконуються на центральній платформі CI за допомогою docker-образу.

Перейдемо до суті.

Примітка. Коли ви читаєте це, перший реліз-кандидат Helm 3 вже був оголошений. Основна версія містить цілий набір покращень, покликаних вирішити деякі проблеми, з якими ми стикалися у минулому.

Робочий процес розробки чартів

Для додатків ми використовуємо розгалуження, і цей підхід вирішили застосувати до чартів.

  • гілка DEV використовується для створення чартів, які тестуватимуться на кластерах розробки.
  • Коли пул-реквест передається в майстер, вони перевіряються у стейджингу.
  • Нарешті, ми створюємо пул-реквест, щоб передати зміни у гілку prod і застосувати їх у продакшені.

Кожне середовище має свій приватний репозиторій, який зберігає наші чарти, і ми використовуємо Chartmuseum з дуже корисним API. Таким чином ми гарантуємо строгу ізоляцію між середовищами та перевірку чартів у реальних умовах, перш ніж використовувати їх у продакшені.

Репозиторії чартів у різних середовищах

Варто зазначити, що коли розробники відправляють гілку dev, версія їхнього чарту автоматично відправляється в dev Chartmuseum. Таким чином, всі розробники використовують один репозиторій dev, і потрібно уважно вказувати свою версію чарту, щоб випадково не використовувати чиїсь зміни.

Більш того, наш невеликий скрипт Python перевіряє об'єкти Kubernetes за специфікаціями Kubernetes OpenAPI за допомогою KubevalПерш ніж опублікувати їх у Chartmusem.

Загальний опис робочого процесу розробки чарту

  1. Налаштування завдань пайплайну за специфікацією gazr.io контролю якості (lint, unit-test).
  2. Надсилання образу docker з інструментами Python, які розгортають наші програми.
  3. Налаштування середовища на ім'я гілки.
  4. Перевірте файли yaml Kubernetes за допомогою Kubeval.
  5. Автоматичне збільшення версії чарту та його батьківських чартів (чартів, які залежать від чарту, що змінюється).
  6. Надсилання чарту в Chartmuseum, який відповідає його середовищу

Управління відмінностями у кластерах

Федерація кластерів

Був час, коли ми використовували федерацію кластерів Kubernetesде можна було оголошувати об'єкти Kubernetes з однієї кінцевої точки API. Але виникли проблеми. Наприклад, деякі об'єкти Kubernetes не можна було створити в кінцевій точці федерації, тому було складно обслуговувати об'єднані та інші об'єкти для окремих кластерів.

Щоб вирішити проблему, ми почали керувати кластерами незалежно, що значно спростило процес (використовували першу версію federation; у другій щось змінилося).

Георозподілена платформа

Зараз наша платформа розподілена по 6 регіонах — 3 локально та 3 у хмарі.


Розподілене розгортання

Глобальні значення Helm

4 глобальні значення Helm дозволяють визначати відмінності між кластерами. Для всіх наших чартів є мінімальні значення за промовчанням.

global:
  cloud: True
  env: staging
  region: us-central1
  clusterName: staging-us-central1

Глобальні значення

Ці значення допомагають визначити контекст для наших додатків та використовуються для різних завдань: моніторинг, трасування, логування, здійснення зовнішніх викликів, масштабування тощо.

  • "cloud": у нас є гібридна платформа Kubernetes. Наприклад, наш API розгортається в зонах GCP і в датацентрах.
  • "env": деякі значення можуть змінюватися для неробочих середовищ. Наприклад, визначення ресурсів та конфігурації автомасштабування.
  • Region: ця інформація допомагає визначати розташування кластера і може використовуватися для визначення найближчих кінцевих точок для зовнішніх сервісів.
  • "clusterName": якщо і коли ми хочемо визначити значення для окремого кластера.

Ось конкретний приклад:

{{/* Returns Horizontal Pod Autoscaler replicas for GraphQL*/}}
{{- define "graphql.hpaReplicas" -}}
{{- if eq .Values.global.env "prod" }}
{{- if eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- else }}
minReplicas: 150
{{- end }}
maxReplicas: 1400
{{- else }}
minReplicas: 4
maxReplicas: 20
{{- end }}
{{- end -}}

Приклад шаблону Helm

Ця логіка визначена у допоміжному шаблоні, щоб не засмічувати Kubernetes YAML.

Оголошення програми

Наші інструменти розгортання базуються на кількох файлах YAML. Нижче наведено приклад того, як ми оголошуємо сервіс та його топологію масштабування (кількість реплік) у кластері.

releases:
  - foo.world

foo.world:                # Release name
  services:               # List of dailymotion's apps/projects
    foobar:
      chart_name: foo-foobar
      repo: [email protected]:dailymotion/foobar
      contexts:
        prod-europe-west1:
          deployments:
            - name: foo-bar-baz
              replicas: 18
            - name: another-deployment
              replicas: 3

Визначення сервісу

Це схема всіх кроків, які визначають наш процес розгортання. Останній крок розгортає програму одночасно на кількох робочих кластерах.


Кроки розгортання у Jenkins

А секрети?

Щодо безпеки, ми відстежуємо всі секрети з різних місць і зберігаємо їх в унікальному сховищі склеп в Парижі.

Наші інструменти розгортання отримують значення секретів з Vault і, коли приходить час розгортання, вставляють їх в Helm.

Для цього ми визначили зіставлення між секретами Vault і секретами, які потрібні нашим додаткам:

secrets:                                                                                                                                                                                                        
     - secret_id: "stack1-app1-password"                                                                                                                                                                                  
       contexts:                                                                                                                                                                                                   
         - name: "default"                                                                                                                                                                                         
           vaultPath: "/kv/dev/stack1/app1/test"                                                                                                                                                               
           vaultKey: "password"                                                                                                                                                                                    
         - name: "cluster1"                                                                                                                                                                           
           vaultPath: "/kv/dev/stack1/app1/test"                                                                                                                                                               
           vaultKey: "password"

  • Ми визначили загальні правила, які слід дотримуватися під час запису секретів у Vault.
  • Якщо секрет відноситься до певного контексту чи кластерапотрібно додати конкретний запис. (Тут контекст cluster1 має власне значення для секрету stack-app1-password).
  • Інакше використовується значення за замовчуванням.
  • Для кожного пункту в цьому списку секрет Kubernetes вставляється пара ключ-значення. Тому шаблон секрету у наших чартах дуже простий.

apiVersion: v1
data:
{{- range $key,$value := .Values.secrets }}
  {{ $key }}: {{ $value | b64enc | quote }}
{{ end }}
kind: Secret
metadata:
  name: "{{ .Chart.Name }}"
  labels:
    chartVersion: "{{ .Chart.Version }}"
    tillerVersion: "{{ .Capabilities.TillerVersion.SemVer }}"
type: Opaque

Проблеми та обмеження

Робота з кількома репозиторіями

Зараз ми поділяємо розробку чартів та додатків. Це означає, що розробникам доводиться працювати у двох репозиторіях git: один додатку, а другий — визначення його розгортання в Kubernetes. 2 репозиторія git — це 2 робочі процеси, і новачкові легко заплутатися.

Керувати узагальненими чартами клопітно

Як ми вже говорили, узагальнені чарти дуже зручні для визначення залежностей та швидкого розгортання кількох додатків. Але ми використовуємо --reuse-values, щоб уникнути передачі всіх значень щоразу, коли ми розгортаємо програму, що входить у цей узагальнений чарт.

У робочому процесі безперервного постачання ми маємо лише два значення, які змінюються регулярно: кількість реплік і тег образу (версія). Інші, більш стабільні значення змінюються вручну, і це досить складно. Більше того, одна помилка у розгортанні узагальненого чарту може призвести до серйозних збоїв, як ми переконалися на власному досвіді.

Оновлення кількох файлів конфігурації

Коли розробник додає нову програму, йому доводиться змінювати кілька файлів: оголошення програми, список секретів, додавання програми залежно, якщо вона входить до узагальненого чарту.

Дозволи Jenkins надто розширені в Vault

Нині у нас є один AppRole, Що читає всі секрети з Vault.

Процес відкату не автоматизовано

Для відкату потрібно виконати команду на кількох кластерах, а це може призвести до помилок. Ми виконуємо цю операцію вручну, щоб гарантовано вказати правильний ідентифікатор версії.

Ми рухаємось у бік GitOps

Наша мета

Ми хочемо повернути чарт у репозиторій програми, яку він розгортає.

Робочий процес буде таким самим, як для розробки. Наприклад, коли гілка відправляється в майстер, розгортання запускатиметься автоматично. Основна різниця між таким підходом та поточним робочим процесом буде в тому, що все буде керуватися в git (саме додаток та спосіб його розгортання в Kubernetes).

Переваг кілька:

  • набагато зрозуміліше для розробника. Простіше навчитися застосовувати зміни в локальному чарті.
  • Визначення розгортання служби можна вказати там же, де код служби.
  • Управління видаленням узагальнених чартів. Сервіс має свій випуск Helm. Це дозволить керувати життєвим циклом програми (відкат, апгрейд) на найменшому рівні, щоб не торкатися інших послуг.
  • Переваги git для керування чартами: скасування змін, журнал аудиту і т. д. Якщо потрібно скасувати зміну чарту, це можна зробити за допомогою git. Розгортання запускається автоматично.
  • Можна подумати про вдосконалення робочого процесу розробки за допомогою таких інструментів, як лісок, з яким розробники можуть тестувати зміни у контексті, наближеному до продакшену.

Двоетапна міграція

Наші розробники використовують цей робочий процес уже 2 роки, тому нам потрібна максимально безболісна міграція. Тому ми вирішили додати проміжний етап на шляху до мети.
Перший етап простий:

  • Ми зберігаємо схожу структуру для налаштування розгортання програм, але в одному об'єкті з іменем DailymotionRelease.

apiVersion: "v1"
kind: "DailymotionRelease"
metadata:
  name: "app1.ns1"
  environment: "dev"
  branch: "mybranch"
spec:
  slack_channel: "#admin"
  chart_name: "app1"
  scaling:
    - context: "dev-us-central1-0"
      replicas:
        - name: "hermes"
          count: 2
    - context: "dev-europe-west1-0"
      replicas:
        - name: "app1-deploy"
          count: 2
  secrets:
    - secret_id: "app1"
      contexts:
        - name: "default"
          vaultPath: "/kv/dev/ns1/app1/test"
          vaultKey: "password"
        - name: "dev-europe-west1-0"
          vaultPath: "/kv/dev/ns1/app1/test"
          vaultKey: "password"

  • 1 реліз на програму (без узагальнених чартів).
  • Чарти в репозиторії git програми.

Ми поговорили з усіма розробниками, тому процес міграції вже почався. Перший етап, як і раніше, контролюється з використанням платформи CI. Скоро я напишу ще одну посаду про другий етап: як ми перейшли на робочий процес GitOps з Потік. Я розповім, як ми всі налаштували і з якими труднощами зіткнулися (кілька репозиторіїв, секрети тощо). Слідкуйте за новинами.

Тут ми спробували описати наш прогрес у робочому процесі розгортання програм за останні роки, який призвів до думок про підхід GitOps. Ми ще не досягли мети і повідомлятимемо про результати, але зараз переконані, що правильно зробили, коли вирішили все спростити та наблизити до звичок розробників.

Джерело: habr.com

Додати коментар або відгук