Спрощення та рефакторинг конфігурацій мікросервісів за допомогою Cuelang та ArgoCD

💡 Усі статті, обговорення, новини про DevOps — в одному місці. Приєднуйтесь до DevOps спільноти!

Привіт! Мене звати Євген. Я DevOps-інженер у команді спеціалістів FinOps-платформи Uniskai від Profisea Labs.

Нещодавно перед нашою командою постала задача спрощення та рефакторингу конфігурацій мікросервісів. Ми вирішили переписати конфігурації мікросервісів з Helm на Cuelang.

Основні питання, які будемо розглядати:

  • порівняння Helm і Cuelang;
  • огляд можливостей Cuelang;
  • як створити власний ArgoCD-плагін.

Процес деплойменту

Інфраструктура проєкту включає середовища production, staging, та development, а також до 20 feature/bugfix-середовищ. Кожне з них містить власну версію понад 20 мікросервісів. Ми прагнемо максимально спростити конфігурації для всіх цих ресурсів.

Для деплойменту мікросервісів у кластери ми використовуємо ArgoCD. Він об’єднує набори YAML-файлів у Applications та розгортає їх у вказані Kubernetes Namespaces.

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

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

У цій статті ми розглянемо процес автоматизації деплойменту таких конфігурацій за допомогою власного ArgoCD-плагіну.

Існує багато інструментів для управління конфігураціями, і Viktor Farcic на каналі DevOps Toolkit має чудове відео, яке порівнює доступні рішення. В нашому випадку бажання реорганізувати, шаблонізувати та модернізувати конфігурації спонукало нас змінити інструмент Helm на більш сучасний, перспективний і гнучкий варіант, яким є Cuelang.

Helm проти Cuelang

Зараз ви можете задуматися, чому варто обрати Cuelang замість Helm. Ось основні переваги, які я виділив для себе.

З Cuelang можна:

  • працювати зі строго типізованими структурами даних, на відміну від роботи з текстом, як у Helm;
  • впроваджувати правила перевірки, щоб гарантувати, що бажані типи даних або значення відповідають вимогам для призначених полів;
  • тестувати ваші ресурси ще до того, як вони взаємодіють з Kubernetes API. Ви можете імпортувати схеми ресурсів Kubernetes з його кодової бази та застосувати їх синтаксичні обмеження до власних схем;
  • легко посилатися на поля ресурсів у конфігурації. Наприклад, у значенні змінної середовища одного мікросервісу можна посилатись на назву Kubernetes Service іншого мікросервісу.

Cuelang-шаблони

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

Окреслимо наступну структуру мікросервісів:

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

Для досягнення цієї мети ми можемо визначити шаблон за замовчуванням для кожного типу Kubernetes ресурсу. Ось приклад шаблону Kubernetes Service:

service: [ID=_]: {
    apiVersion: "v1"
    kind: "Service"
    metadata: {
        name: ID
        labels: {
           "app.kubernetes.io/name": ID
        }
        annotations: application: settings.options[ID].app
    }
    spec: {
        type: "ClusterIP"
        ports: [{
            port:       *80 | int
            targetPort: "http"
            protocol:   "TCP"
            name:       "http"
        }]
        selector: {
            "app.kubernetes.io/name": ID
        }
    }
}

Як ви можете побачити, Cuelang візуально схожий на JSON. [ID=_] визначає цю частину коду як шаблон. Кожен ресурс, створений на базі цього шаблону, може перезаписувати необхідні поля. Наприклад, якщо потрібно створити конкретний ресурс, можна задати лише ті поля, які відрізняються від шаблону:

service: "frontend": {}

Так, це все. Тут ми задали Service під назвою frontend, без модифікацій відносно його шаблону.

В рядку встановлення номера порту, можна побачити, як Cuelang дозволяє задавати обмеження. Тут номер порту повинен бути типу integer зі значенням за замовчуванням 80.

Рядок annotations ділить усі дійсні Kubernetes-ресурси на ArgoCD Applications. Додатково, ресурси можуть бути поділені на основі їхніх назв або будь-якого іншого критерію за допомогою інструментів, доступних в Cuelang, які ми розглянемо у наступній частині.

Cuelang-інструменти

Cuelang дозволяє розробляти власні інструменти. Наприклад, ось інструмент для експорту конфігурації Kubernetes Service у формат YAML:

application: string @tag(app)
objects: [
    for v in microserviceObjects
        for x in v
            if {x.metadata.annotations.application == application}
                if {x.kind != _|_} {x}
]
microserviceObjects: [
    service,
]

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

Потім створюємо команду, яка експортує ці ресурси у формат YAML. Ось як це виглядає в Cuelang:

import (
    "encoding/yaml"
    "tool/cli"
)
command: dump: {
    task: print: cli.Print & {
        text: yaml.MarshalStream(objects)
    }
}

Використання:

cue cmd -t app=frontend dump ./...

Cuelang + Golang = 🤝

В Cuelang ми можемо імпортувати Golang-структури та перетворювати їх на Cuelang-схеми.

Щоб це зробити, необхідно виконати декілька команд:

cue mod init my.best.project
go mod init my.best.project
go get k8s.io/api/core/v1
cue get go k8s.io/api/core/v1

І додати цей код до шаблону:

import (
    core "k8s.io/api/core/v1"
)
service: [string]: core.#Service

Тепер будь-яка синтаксична помилка призведе до припинення процесу експортування у формат YAML через несумісність із Kubernetes core API.

Cuelang + ArgoCD = 💖

Ми повинні переконатися, що новий інструмент управління конфігураціями сумісний з ArgoCD для розгортання мікросервісів у кластері. Оскільки ArgoCD підтримує лише три інструменти: Helm, Jsonnet та Kustomize, нам необхідно розробити власний плагін управління конфігурацією (Config Management Plugin, CMP) для автоматичного деплойменту конфігурацій, написаних на Cuelang.

Під «автоматично» маю на увазі, що ми хочемо зберігати конфігурації в форматі Cuelang без будь-яких додаткових ручних або частково автоматизованих трансформацій коду у формат YAML.

Ось діаграма, яка ілюструє, як відбувається цей процес:

Щоб досягнути цього, ми модифікували налаштування Helm у чарті ArgoCD наступним чином:

repoServer:
  initContainers:
    - name: cuelang-get-binary
      command:
        - "/bin/bash"
      args:
        - "-c"
        - "apt update;
          apt install -y wget;
          wget -q https://github.com/cue-lang/cue/releases/download/v0.7.0/cue_v0.7.0_linux_arm64.tar.gz;
          tar -xzf cue_v0.7.0_linux_arm64.tar.gz -C /cue"
      image: ubuntu:22.04
      volumeMounts:
        - mountPath: /cue
          name: cmp-cuelang-binary
  extraContainers:
    - name: cmp-cuelang
      command:
        - "/var/run/argocd/argocd-cmp-server"
      image: busybox:1.36
      securityContext:
        runAsNonRoot: true
        runAsUser: 999
      volumeMounts:
        - mountPath: /var/run/argocd
          name: var-files
        - mountPath: /home/argocd/cmp-server/plugins
          name: plugins
        - mountPath: /home/argocd/cmp-server/config/plugin.yaml
          subPath: cuelang.yaml
          name: argocd-cmp-cm
        - mountPath: /tmp
          name: cmp-tmp
        - mountPath: /cue
          name: cmp-cuelang-binary
  volumes:
    - name: argocd-cmp-cm
      configMap:
        name: argocd-cmp-cm
    - name: cmp-tmp
      emptyDir: {}
    - name: cmp-cuelang-binary
      emptyDir: {}

Ось конфігурація для CMP. Ми встановлюємо Cuelang binary та всі необхідні залежності.

Крім того:

configs:
  cmp:
    create: true
    plugins:
      cuelang:
        generate:
          command: ["sh", "-c"]
          args: ["/cue/cue cmd -t {your-parameters} dump ./..."]
        discover:
          fileName: "./*.cue"

Завдяки останньому рядку налаштувань, цей плагін автоматично керуватиме будь-яким ArgoCD Application, який містить файл Cuelang у своїй директорії.

Ви можете змінювати поле args для додавання власних динамічних значень або використання специфічних інструментів Cuelang. Основна мета цього рядка полягає у виведенні командою необхідних Kubernetes ресурсів у форматі YAML.

Також варто зазначити, що ця реалізація Config Management Plugin (CMP) не включає перевірку синтаксису Cuelang+Golang.

Структура проєкту

Коли всі інструменти налаштовані, ми можемо зосередитися на структурі проєкту. Ми використовуємо наступний підхід:

cue/
├─ settings/
│  ├─ options.cue
│  ├─ versions.json
├─ template/
│  ├─ template.cue
├─ tools/
│  ├─ *_tool.cue
├─ microservice1.cue
├─ microservice2.cue
├─ microservice3.cue
├─ ...

Структура включає теку template/ для зберігання шаблонів кожного типу Kubernetes-ресурсу. Є також тека tools/ для зберігання утиліт, таких як YAML exporter. Окрім того, існує тека settings/, призначена для зберігання конфігурацій, специфічних для різних середовищ, які включають версії мікросервісів, типи середовищ та зіставлення ресурсів Kubernetes із ArgoCD Applications.

Ці зіставлення реалізовані вручну за допомогою Kubernetes annotations. Наприклад, якщо ArgoCD Application вимагає двох Kubernetes Deployments, однакова анотація на обох дозволяє ArgoCD групувати їх.

Теки організовані у Cuelang packages згідно з їх назвами, з root package під назвою microservices.

Висновок

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

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

Корисні посилання

Cuetorials — чудова платформа, яка надає пояснення та приклади конфігурацій Cuelang.

Cue Kubernetes tutorial — детальний огляд процесу переведення Kubernetes конфігурацій у Cuelang. Ідеальний ресурс для початку вивчення Cuelang.

Працювали з Cuelang? Які ваші враження та успіхи? Поділіться в коментарях.

👍ПодобаєтьсяСподобалось5
До обраногоВ обраному3
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Дякую за статтю.

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

Тільки зараз помітив коментар

Маніфести для куба це ж yaml файли і в цьому ж і є причина чому helm появився і зараз cue — там просто нереальна кількість блоків які повторюються, тому без шаблонізації ніяк

дивився пару років тому в сторону cuelang і порівнював наприклад з тим же jsonnet і тоді не вразило це якась надбудова аля gotemplate хоча зара уже краще. Пізніше коли grafanalabs зарелізили tanka.dev з підтримкою helm чартів то рішення було очевидне в сторону jsonnet. Бо це суттєво полегшило розгортання різних aux сервісів в k8s.

Як при цьому відбувається автооновлення імедж тегів? Бо імедж апдейтер не підтримує плагіни

Нажаль, не використовували Image Updater і до рефакторингу, тому задача адаптації такого рішення не стояла. Як описав у статті, у ієрархії є окрема папка що зберігає дані, специфічні для окремих оточень. Серед них і список актуальних версій мікросервісів.

Дякую за змістовну й актуальну статтю.
Так само зліз на cdk8s.

Підписатись на коментарі