aldaas — БД с заготовлеными данными для разработки, CI/CD и всех-всех-всех
Каждому твоему разработчику нужна база, похожая на продакшн, но она очень тяжелая, чтобы каждое утро выкачивать её к разработчику в его локальную среду. Теперь есть решение — поможет aldaas.
базы с гигабайтами данных как сервис
Содержание
— Предисловие
— Как работает aldaas
— Настройка инфраструктуры для aldaas
— Вывод
Предисловие
Придя со стартапа на 4–8 человек в команду побольше, я начал погружаться в CI/CD для монорепы, в каждой ветке мы хотели поднимать все окружение полностью с помощью docker, около 15 сервисов, очереди и… БД. Полагаю почти у всех такая проблема, всегда есть боевая база данных и вторая для всех-всех-всех.
Картина маслом
Сначала, конечно, есть предложение, а давайте «пустые» БД поднимать и накатывать на них схему, и вот, пожалуйста, можно уже миграции катать, и все вроде хорошо.
Решение на коленке
Но, в конце концов, к тебе приходит команда и говорит, что данные все равно нужны — реальные данные (понятное дело без критических данных пользователей, платежные карты и все такое, но в этой статье не про это).
И тут ты начинаешь искать готовые решения или изобретать велосипеды. Не очень помню удалось ли мне что-то нагуглить в 2018 году, но, видимо, ничего подходящего не обнаружив, я, вооруженный rancher 1.x, собрал свой saas с docker и zfs, решение можно было масштабировать добавлением нод в кластер rancher, соединение было по tcp напрямую в открытый порт контейнера, поэтому все это нужно было разворачивать в закрытом контуре в одной сети со станциями разработчиков и ранерами для CI/CD.
Решение тогда нас очень ускорило, каждый разработчик мог ломать свою базу данных и никак не влиять на соседей, миграции стали работать как должны. В один день мы поймали очень долгую неоптимизированную миграцию, и наш DBA (дядя Женя, который круче всех умел в sql на тот момент) помог оптимизировать её, и вместо 15 минут на машине разработки и в CI/CD, она прошла меньше минуты. При этом данные в saas базах уже были пожаты в 10 раз, к тому же мы выделяли по 1 гигабайту для каждой saas БД, специально отлавливая медленные запросы, на боевой БД миграция могла бы обрабатываться несколько часов, катастрофа предотвращена в зачатке (аплодисменты). Ну и конечно было удобно, что базы можно было выкидывать и сразу брать новые, каждое утро новый слепок с продакшн был доступен всем желающим, а кто хочет, может играться со своей копией хоть целый месяц, zfs тогда позволял оптимально использовать пространство на воркерах. Ну и самое важное, БД размером в 10+ гигов выкачивать к себе не нужно, на стороне клиента только легкий прокси сервер.
После я ушел в другую компанию, а сервисом еще долгое время пользовались на старом месте работы (а может и сейчас он работает), сегодня в 2022–24 я пришел в новую команду и опять наблюдаю проблему с единственной БД для разработки и staging окружения, опять проблемы миграции и аварий с sql. Но уже с новыми знаниями kubernetes и новым уровнем лени я написал aldaas — App with Large Data as a service. ZFS заменил на ceph c глубокой интеграцией в kubernetes благодаря стараниям rook.io, серверную часть писать не хотелось и я сделал ставку на argo workflows+events. Про настройку rook.io и argo в этой статье я тоже кратко расскажу. Также хотелось бы не зависеть от различных VPN и доставлять доступы до базы в любую инфраструктуру или окружение, не сильно беспокоясь об утечках. На глаза попался tcp-over-websocket, да, есть проблема в разрыве соединений, но я подумал — это не баг, а фича, приложение должно нормально обрабатывать реконнект к БД. Так, имея единую точку входа через nginx по websocket«ам, можно коннектиться к разным БД по разным path в урле.
Как работает aldaas
Подготовка БД снапшотов
argo events слушает minio/s3 сервер на наличие новых бекапов, как только новый бекап загрузился argo event запускает workflow, а в нем:
создаётся новый диск в Ceph rbd
поднимается чистая БД с примонтированым диском ceph
в БД загружается бекап (самая долгая задача)
останавливается БД
создается Ceph snapshot от диска силами kubernetes volume snapshot
потом из снапшота снова поднимается диск с проверочной БД, и выполняется тестовая команда
останавливается проверочная БД, и удаляется снапшот диск
на снапшот ставится лейбл status=ready
Удаление старых снапшотов
Argo по крону удаляет все старые снапшоты (и их workflow) с лейблом status=ready, кроме самого нового (команда в одну строку)
Заказ БД для клиентов
есть argo workflow, который как раз запускают клиенты
ищет самый свежий снапшот с лейблом status=ready
создаёт из снапшота диск и поднимает pod с этим диском, в котором бд, прокси и TTL киллер (для выключения простаивающей БД)
поднимается сервис и ingress с уникальным path (и токеном)
На клиентской стороне простой скрипт, который либо проверяет есть ли что-то на локальной FS для подключения к уже запущенной БД (с прошлого раза, если вы, например, остановили контейнер по каким-то причинам), либо заказывает новую, запуская выше упомянутый клиентский workflow, также складывая на диск кешированный ID для БД. Также можно задать TTL на клиенте для БД, по умолчанию 300 секунд. Если остановить или удалить контейнер на клиенте то в aldaas вашу БД удалит TTL киллер.
Рекордное время поднятия БД — 30 секунд, ну тут вопрос к настройке argo. Конечно можно и без него, и время сократится до 5–10 секунд, но я лентяй и не хочу писать свою серверную часть.
Настройка инфраструктуры для aldaas
Для начала посоветую развернуть всё на чистом стенде, а то вдруг заденете условный ceph или настройки текущего argo.
Нам нужен kubernetes с VolumeSnapshot GA. Я использую rancher и через rke2 поднял 1.26.10. VolumeSnapshot понадобится как раз для того, чтобы выполнять магию поднятия слепков БД.
Сильно вдаваться в подробности с настройкой и установкой rook ceph не буду, просто скажу что нам нужен storage class и snapshot storage class в kubernetes, вместо ceph можно использовать любой другой storage provider, лишь бы снапшоты поднимались быстро, оба класса нужно будет передать aldaas.
Всю логику и серверную часть я оставил на Argo, сам argo server нужно вытащить наружу через ingress и поставить на него авторизацию для argo cli В helm лежит rbac, который нужно будет добавить на сервис аккаунт Argo workflow, потому что нам нужны будут права на взаимодействия с volumes и snapshots, jobs (из коробки в Argo workflow их не хватает), и также уточнить имя сервис аккаунта (serviceAccountName), которым будет орудовать aldaas для поднятия клиентского workflow. Также не помешает заменить секретный токен (tunnel.token), который и обеспечит безопасность по веб сокетам, и ваш домен (domain), по которому клиенты будут резолвить aldaas базы. Также я попытался заложить установку нескольких aldaas в один namespace, для этого нужно для всех остальных установок отключить EventBus (EventBus.enabled)
Давайте разберем values.yaml. Тут у нас стоит преднастроенный postgres: latest, бекап будем грузить из postgres в формате .sql.gz
# SA сервис аккаунт, с которым будут стартoвать argo wf
serviceAccountName: argo-workflow
# версии kubectl, jq (внезапно)
kubectl: v1.27.9 # я велосипедист, версия и для контейнера, и для бинаря
jq: 1.7.1 # только для бинаря
# в пустом NS создается default argo event bus
EventBus:
enabled: true
# домен по которому клиенты будут подключаться к aldaas базам (может быть доменом от argo server)
domain:
# единственный докер имадж с argo cli, tcp-over-websockets
# и aldaas скриптом, но в нем только bash в 30 строк
tunnel:
image: ghcr.io/negashev/aldaas
tag: main
port: 8080
# секурный токен для вашего aldaas инстанса
token: 9fKLQhjGWrHZ3pOSQLKmWlUxoJXkCJqbFs8WeT3EEG8AwKEL4B8YavzXYOApVaXfi2ZVLx77YhsbVyDJTY0l8maSiYKEm6WDyopM
ingress:
# тут берем наш tls секрет для домена или включаем аннотацию
tlsSecretName: ""
annotations:
# kubernetes.io/tls-acme: "true"
# два k8s сторадж класса, чтобы магия работала
rook:
storageClassName: ceph-block
volumeSnapshotClassName: ceph-block
# это s3 конфигурация для argo events
s3:
existingSecret: ""
accesskey: ""
secretkey: ""
host: minio.server
port: 80
bucket: backup
insecure: true
prefix: spilo/acid-database/shasum/logical_backups
suffix: .sql.gz
# Само приложение, из которого будут подниматься aldaas БДшки
# в нем же будет и инициализация БД для загрузки бекапов
application:
image: postgres
tag: latest
port: 5432
# ну тут и размер тех самых дисков (и бекап и для aldaas - логично же, aldaas тупо будет из диска для бекапа)
storage: 10Gi
# папка, в которую мантится диск
mount: /var/lib/postgresql/data
# вдруг ты аргументы добавить хочешь
args:
command:
# понятное дело понадобятся конфигурации через ENV
env:
- name: POSTGRES_PASSWORD
value: passw0rd
- name: POSTGRES_USER
value: aldaas
- name: POSTGRES_DB
value: my_db_name
# адекватные ресурсы... тут уже зависит от ваших потребностей, node-autoscaler в помощь
resources:
requests:
cpu: 100m
memory: 1Gi
limits:
cpu: 1000m
memory: 1Gi
# а это уже пода, которая будет накатывать наш бекап
# ALDAAS_HOST_DAEMON адрес важный, он берется как раз от нашей базы для наката бекапа, генерируется в wf
restore:
args:
- gunzip -c /backup.sql.gz | psql -h $ALDAAS_HOST_DAEMON -p 5432 -U $POSTGRES_USER -d $POSTGRES_DB
command:
- sh
- -c
env:
- name: PGPASSWORD
value: passw0rd
- name: POSTGRES_USER
value: aldaas
- name: POSTGRES_DB
value: my_db_name
resources:
requests:
cpu: 100m
memory: 1Gi
limits:
cpu: 1000m
memory: 1Gi
# а тут мы уже в самом конце проверяем все ли в порядке с базой, поднятой из снапшота
# уже после бекапа имадж берется
check:
args:
- psql -h $ALDAAS_HOST_DAEMON -p 5432 -U $POSTGRES_USER -d $POSTGRES_DB -c "\l"
command:
- sh
- -c
Поставить нужные значения, думаю, не сложно :) устанавливаем чарт в неймспейс aldaas
Получили примерно такой вывод, тут можно использовать клиент с этими настройками, но сначала нужно сделать первый снапшот
После заходим в argo server и наблюдаем в ивентах, кроне и шаблонах такие картины:
Сенсор на создание снапшота и сенсор на удаление клиентских БД по TTL
Крон задача на удаление устаревших снапшотов
шаблон workflow, который будет тригериться из клиентского контейнера
В идеальном мире) если мы настроили хелм правильно, можно загрузить бекап с любым именем в формате .sql.gz, и сенсор запустит workflow, который начнет готовить снапшот из бекапа базы данных
Тригернулся workflow
Если все прошло удачно, должна поднятся БД с диском, в неё загружен дамп, БД остановлена, с диска сделан снапшот, потом из снапшота сделан новый диск, и на нем снова поднята БД, в которую выполняется тестовая команда (тут уже сами сможете придумать что-то своё, типа селекта на проверку целостности данных или их актуальности).
Зеленый пайплайн, результатом которого будет ceph диск с данными, загруженными из бекапа, снапшот этого диска c лейблом status=ready
А теперь, когда у нас все взлетело, можно попробовать запуститься на клиентской стороне
docker run -it \
-p 5432:5432 \
-e ALDAAS_PORT=5432 \
-e ALDAAS_NAME=aldaas-chart \
-e ALDAAS_TOKEN=9fKLQhjGWrHZ3pOSQLKmWlUxoJXkCJqbFs8WeT3EEG8AwKEL4B8YavzXYOApVaXfi2ZVLx77YhsbVyDJTY0l8maSiYKEm6WDyopM \
-e ARGO_SERVER=argo.negash.ru \
-e ARGO_HTTP1=true \
-e ARGO_TOKEN='[REDACTED]' \
-e ARGO_NAMESPACE=aldaas \
ghcr.io/negashev/aldaas:main
немного о переменных
ALDAAS_PROTOCOL — протокол по умолчанию wss
ALDAAS_NAME — имя нужной нам aldaas установки
ALDAAS_TOKEN — соответственно секурити
ALDAAS_PORT — порт БД, который проксируем из aldaas
ALDAAS_SERVER — по умолчанию равен ARGO_SERVER
ALDAAS_TTL — ttl для базы по умолчанию 300 секунд, если остановить прокси, то БД удалится через это время
остальные настройки ARGO_* для argo cli соответственно, ARGO_NAMESPACE — тот NS, в который поставили aldaas helm chart.
На выходе клиента получаем такие логи: argo cli заказал нам БД, a tcp-over-websockets запустил до неё прокси, все это произошло за 38 секунд. В самой БД у нас данных условно 10+ гигов, мы их не качали к себе на машину, а подняли удаленно
Запустился контейнер, и теперь можно попробовать к нему подключиться по 5432 порту
После того как увидели первое соединение по ws[s] — все отлично, это сервис пинга, чтобы ttl киллер не убил вашу БД, пока контейнер с прокси работает. Теперь, соответственно, сколько раз вы запустите такой контейнер, столько уникальных БД с данными и получите.
Можно выдать разработчикам, пусть не боятся дропнуть таблицу — если что-то пойдет не так, разработчик просто пересоздаст контейнер с прокси, и новый слепок продовой БД снова доступен, даже чинить ничего не надо. Поставьте aldaas прокси в CI/CD для тестов миграций, и вы не пропустите в прод ничего медленного или несогласованного!
Вывод
— ругаем
Это, конечно, железо, ceph скушает у вас ресурсов. Можно использовать другие провадеры снапшотов, но нужны бенчмарки. Также aldaas не почистит ваши бекапы от критических данных — это вы должны сделать сами
— хвалим
Вы экономите много времени на точность ваших CI/CD тестов, у ваших разработчиков не болит голова от поcтоянных пересечений или аварий миграций, можно устраивать взрывы в БД и не бояться что-то сломать! Это kubernetes и ceph, значит можно горизонтально масштабироваться, можно использовать другие базы, например mysql, mariadb, clickhouse, cassanda и т. д.
Версия сырая, пулреквесты приветствуются
— надо бы helm прилизать
— переехать с tcp-over-websockets на что-то другое, с basic auth например или grpc
— убрать с клиента argo cli и дописать сервер для быстрого создания слепков БД
— сделать очереди и держать всегда готовыми 3–4 слепка БД для мгновенной выдачи клиенту (не ждать kubernetes)