Спрощення та рефакторинг конфігурацій мікросервісів за допомогою Cuelang та ArgoCD
Привіт! Мене звати Євген. Я 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? Які ваші враження та успіхи? Поділіться в коментарях.
6 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів