Cartridge (официальный legacy) и миграция на 3.x

Рейтинг: 45.3% · 9 голосов
Исчерпывающий курс по Tarantool 3.x: модель данных, движки memtx и vinyl, Lua и файберы, транзакции и MVCC, SQL, конфигурация (box.cfg и декларативная 3.x), репликация и Raft, шардирование vshard, эксплуатация, безопасность. 47 уроков со схемами.
Ответить
Аватара пользователя
denis_tnt
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Cartridge (официальный legacy) и миграция на 3.x

Сообщение denis_tnt »

Оглавление курса (47)
  1. Что такое Tarantool: in-memory СУБД и сервер приложений
  2. Архитектура изнутри: процесс, потоки, event-loop
  3. Установка и первый запуск: tt CLI, пакеты, Docker
  4. Интерактив: консоль, admin-консоль, первые команды
  5. Спейсы и кортежи: форматы, типы данных
  6. Типы индексов и их применимость
  7. Движки хранения: memtx vs vinyl
  8. DDL: схема, создание спейсов и индексов, миграции
  9. DML и выборки: insert/update/upsert, итераторы
  10. Персистентность: WAL, снапшоты, recovery
  11. Внутренности memtx: аллокаторы slab/arena, память
  12. Внутренности vinyl: LSM, компакция, тюнинг
  13. Lua и LuaJIT в Tarantool: box, модули, rocks
  14. Файберы: кооперативная многозадачность, каналы
  15. Транзакции: ACID, изоляция, MVCC
  16. Хранимые процедуры, модули, организация приложения
  17. net.box: удалённые вызовы, async
  18. Пулы соединений, балансировка, реконнект
  19. Ошибки и диагностика: box.error, pcall
  20. Типы и сериализация: MsgPack, decimal, datetime, uuid
  21. SQL в Tarantool: возможности и связь с box
  22. SQL: таблицы, JOIN, подзапросы, представления
  23. SQL: подготовленные выражения, транзакции, Lua-интероп
  24. Классическая конфигурация box.cfg (legacy 1.x/2.x)
  25. Декларативная конфигурация 3.x: config.yaml, иерархия
  26. Роли и приложения в 3.x
  27. Централизованная конфигурация: etcd / config storage
  28. tt CLI глубоко: разработка, сборка, запуск
  29. Cartridge (официальный legacy) и миграция на 3.x (вы здесь)
  30. Репликация: replicaset, топологии
  31. Механика репликации: WAL-стриминг, vclock
  32. Синхронная репликация и выборы лидера (Raft)
  33. Жизненный цикл узла: bootstrap, join, rejoin
  34. vshard: router/storage, виртуальные бакеты
  35. Решардинг и rebalancing бакетов
  36. Запросы поверх шардов: map-reduce, crud
  37. Мониторинг: метрики, Prometheus, Grafana
  38. Логирование и аудит
  39. Бэкапы и восстановление
  40. Безопасность: аутентификация, RBAC, TLS
  41. Производительность: профилирование, тюнинг
  42. Обновления: схема, rolling upgrade
  43. Деплой в продакшен: Docker, топология (официальные паттерны)
  44. Администрирование через официальный TCM (Tarantool Cluster Manager)
  45. Коннекторы: Python, Go, Java
  46. Ключевые модули (rocks): crud, metrics, queue, expirationd
  47. Capstone: шардированный отказоустойчивый кластер
Что такое Cartridge и зачем он был

Tarantool Cartridge - это официальный фреймворк для распределённых приложений, который много лет был штатным способом собирать кластер на Tarantool 1.10 и 2.x. До 3.x у самого ядра не было ни декларативной конфигурации, ни встроенной оркестрации топологии: голый instance умел только box.cfg() и репликацию. Cartridge закрыл эту дыру - он дал шаблон приложения, упаковку в RPM/DEB/tgz, веб-интерфейс администрирования, автоматический failover, встроенный шардинг через vshard и понятие ролей. По сути это был "application server" поверх Tarantool.
Главное, что нужно зафиксировать сразу: Cartridge несовместим с Tarantool 3.0 и выше. Он поддерживает только ветки 1.10 и 2.x. Это не баг, а архитектурное решение - в 3.x задачи Cartridge встроили прямо в ядро (декларативный config, роли, управление топологией). Поэтому Cartridge сегодня - официальный legacy, и каждый новый проект надо начинать на 3.x.
Как Cartridge устроен внутри

Роли (cluster roles)
Роль - это просто Lua-модуль с предопределёнными callback-функциями. Cartridge сегрегирует функциональность инстансов по ролям: все инстансы запускают один и тот же код приложения и знают про все объявленные роли, но реально включены на конкретном инстансе только нужные. Важная деталь механики: роли назначаются на replica set целиком, а не на отдельный инстанс - все инстансы одного набора реплик несут одинаковый набор ролей. Роли можно включать и выключать на лету, без рестарта.

Встроенных ролей две, обе про шардинг через vshard:
  • vshard-router - маршрутизирует запросы по bucket_id к нужному storage (compute-нагрузка);
  • vshard-storage - хранит и обслуживает подмножество данных (transaction-нагрузка).
Свои (custom) роли вы пишете сами: создание спейсов, хранимки, фоновые fiber-ы, HTTP-эндпоинты. Роль может объявить dependencies - тогда зависимые роли инициализируются первыми и автоматически.

Жизненный цикл роли
Cartridge вызывает у роли строго определённый набор функций в строго определённом порядке. При join инстанса или рестарте с включённой ролью порядок такой:
  • validate_config(conf_new, conf_old) - проверить новую конфигурацию (вызывается ДО автоматического box.cfg(), поэтому box-функции надо оборачивать в проверку type(box.cfg) == 'table');
  • init(opts) - инициализация: создать спейсы/индексы (только на мастере, через opts.is_master), запустить fiber, повесить HTTP-роуты;
  • apply_config(conf, opts) - применить конфигурацию (уже после box.cfg(), box-функции можно звать свободно, но создавать спейсы - только на мастере).
stop() вызывается только при выключении роли администратором (не при остановке инстанса). При смене лидера (failover) Cartridge заново зовёт validate_config() и apply_config() и передаёт обновлённый is_master.

Clusterwide-конфигурация и two-phase commit
Сердце Cartridge - распределённая конфигурация, единая для всего кластера (топология, vshard, failover, auth, ваши custom-секции). Каждый инстанс хранит копию config.yml в своём workdir. Изменение проходит через cartridge.confapplier.patch_clusterwide() и идёт двухфазным коммитом:
  • Preparation phase - патч рассылается всем живым инстансам, каждый прогоняет validate_config() всех ролей и пишет config.prepare.yml. Если хоть один отверг - Abort phase, все откатываются.
  • Commit phase - при общем успехе каждый инстанс атомарно подменяет активный конфиг на prepared и зовёт apply_config() всех ролей.
Если коммит частично упал, Cartridge не чинит это сам - нужен ручной ремонт. Отсюда правило: вся проверка должна жить в validate_config(), чтобы apply_config() уже не падал.

Топология и failover
Топология - это инстансы (каждый со своим instance UUID и advertise_uri), сгруппированные в replica sets (каждый со своим replicaset UUID). У набора есть failover priority - упорядоченный список, первый здоровый становится лидером (writable master). Режимов failover три:
  • disabled - лидер всегда первый в списке, автопереключения нет;
  • eventual - каждый инстанс сам решает, кто лидер, по статусу членства (протокол SWIM); не рекомендуется на больших highload-кластерах из-за гонок и "failover storms";
  • stateful - решение принимает один coordinator (тоже роль), а карта лидерства лежит во внешнем state provider: stateboard (отдельный инстанс Tarantool) или etcd. Coordinator берёт распределённый lock, и только держатель lock-а назначает лидеров.
Изображение
Топология Cartridge и путь миграции на Tarantool 3.x

Ключевые команды и код

Точка входа приложения (init.lua) - box.cfg() звать нельзя, его за вас вызовет кластер:

Код: Выделить всё

local cartridge = require('cartridge')
cartridge.cfg({
    advertise_uri = 'localhost:3301',
    cluster_cookie = 'super-cluster-cookie',
    http_port = 8081,
    roles = {'custom-role'},        -- список своих ролей
}, {
    memtx_memory = 1000 * 1024 * 1024,  -- это box-опции
})
Скелет custom-роли:

Код: Выделить всё

local function init(opts)
    if opts.is_master then          -- спейсы создаём только на лидере
        local s = box.schema.space.create('customer', {if_not_exists = true})
        s:format({{'customer_id','unsigned'},{'bucket_id','unsigned'},{'name','string'}})
        s:create_index('pk', {parts = {'customer_id'}, if_not_exists = true})
    end
end

return {
    role_name = 'custom-role',
    dependencies = {'cartridge.roles.vshard-router'},
    init = init,
}
Развёртывание и быстрый старт (cartridge-cli):

Код: Выделить всё

cartridge create --name myapp
cartridge build
cartridge start -d
cartridge replicasets setup --bootstrap-vshard
cartridge failover set stateful --state-provider stateboard \
  --provider-params '{"uri":"localhost:4401","password":"passwd"}'
Путь миграции на Tarantool 3.x
В 3.x всё, что давал Cartridge, переехало в ядро: декларативный YAML-конфиг, роли как штатная сущность, управление топологией и replicaset-ами. Дорожная карта:

Код: Выделить всё

Шаг 1. Перейти с cartridge-cli на tt (он deprecated).
        tt init  -> команды cartridge становятся "tt cartridge ..."
Шаг 2. Заморозить запись и снять дамп схемы/данных со storage-ов.
Шаг 3. Переписать clusterwide config.yml -> декларативный config.yaml 3.x:
        groups -> replicasets -> instances; роли в секции roles/sharding.
Шаг 4. Перенести custom-роли на API ролей 3.x (validate/apply/init по новому).
Шаг 5. failover: stateboard/etcd -> штатный failover 3.x
        (etcd/config.storage как provider).
Шаг 6. Поднять 3.x-кластер, восстановить данные, проверить box.info.replication.
Утилита tt умеет работать с обоими мирами: флаг --cartridge для 2.x-приложений и --config для YAML-кластера 3.x, что упрощает переходный период. Для переноса схемы и накатки изменений данных есть модуль tarantool/migrations.

Частые заблуждения и грабли
  • "Cartridge заведётся на 3.x, если подобрать версию" - нет. Граница жёсткая: 1.10/2.x только. Никакая версия Cartridge 2.x не поддерживает ядро 3.0+.
  • Вызов box.cfg() руками в init.lua - запрещён, кластер делает это сам. Ручной вызов ломает bootstrap.
  • box-функции в validate_config() без проверки type(box.cfg)=='table' падают, потому что валидация может идти ДО box.cfg().
  • Создание спейсов/юзеров не под if opts.is_master - даёт коллизии репликации (объект создаётся и на мастере, и на реплике одновременно).
  • Роль включают "на инстанс" - на самом деле на весь replica set; индивидуально на одном инстансе набора роль не выключить.
  • При миграции/перебутстрапе путают UUID: replicaset UUID должны совпадать, а instance UUID - различаться, иначе новый кластер не поднимется по текущей bootstrap-политике.
  • eventual failover на highload - источник гонок и failover storms (ложные SWIM-гипотезы); в проде берите stateful с etcd.
  • Частичный сбой two-phase commit Cartridge не лечит автоматически - чините конфиг руками, поэтому вся логика проверок должна быть в validate_config().
Мини-лаба

Возьмите README быстрого старта и разверните локальный Cartridge-кластер: cartridge create / build / start -d, затем в веб-интерфейсе на http://localhost:8081 соберите топологию из роутера и одного storage-набора, включите роль vshard-storage и bootstrap-нете vshard. После этого откройте config.yml в workdir любого инстанса и найдите там секции topology, vshard и failover. Задание: на листе бумаги выпишите, какие из этих секций в Tarantool 3.x превратятся в groups/replicasets/instances декларативного config.yaml, а какие - в секцию sharding.

Контрольные вопросы
  • На какой уровень топологии назначаются роли в Cartridge - на инстанс или на replica set, и почему это важно при включении vshard-storage?
  • В каком порядке кластер вызывает функции роли при join инстанса, и почему box-функции в validate_config() надо оборачивать в проверку type(box.cfg) == 'table'?
  • Опишите две фазы patch_clusterwide(): что происходит в preparation и в commit, и что будет, если один инстанс отвергнет конфиг в validate_config()?
  • Чем stateful failover отличается от eventual по механике принятия решения о лидере, и какие два state provider он поддерживает?
👍3 ❤️7 🔥 😄 🤔
Ответить
← Предыдущая глава
tt CLI глубоко: разработка, сборка, запуск
Следующая глава →
Репликация: replicaset, топологии

Все главы курса «Tarantool: in-memory СУБД и сервер приложений с нуля до продакшена»

Поделиться темой: ✈ Telegram VK

Вернуться в «Tarantool: СУБД и сервер приложений»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость