# Cartridge > Занятие 5 / 5 <br/> <br/> <br/> <br/> <br/> > Миша Филоненко > Продвинутый пользователь `Cartridg`-а [TOC] <style type="text/css"> * { tab-size: 4; } /* Resets & overrides */ .reveal dl { display: block; } .reveal pre { width: auto; box-shadow: none; } .reveal pre code { width: auto; max-height: initial; } .reveal blockquote { width: auto; } .reveal .slides .pdf-page { pointer-events: initial; } .reveal .slides { text-align: left; } .reveal section img { display:block; box-shadow: none; margin: initial; padding: initial; border: none; background: none; border-width: 0; } /* Font style defs */ html, .reveal { font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; } .reveal pre code { font-family: "PTM55F", "Monaco", monospace; } /* Color defs */ .reveal pre code { background: #2d2d2d; } /* Backgrounds */ .reveal .slide-background-content { background-color: #222; } .reveal .first .slide-background-content { background-color: #222; } .reveal section { background-color: #333; background-size: cover; background-image: url(https://slides.hb.bizmrg.com/tarantool/dark/page.png); } .reveal .slides section h1, .reveal .slides section h2, .reveal .slides section h3, .reveal .slides section h4, .reveal .slides section h5, .reveal .slides section h6{ background: rgba(0,0,0,0.25); } /* Font-sizes */ html { font-size: 32px; } .reveal { font-size: 1rem; } .reveal .slides section h1, .reveal .slides section h2, .reveal .slides section h3, .reveal .slides section h4, .reveal .slides section h5, .reveal .slides section h6{ font-size: 40px; } .reveal pre { font-size: 24px; line-height: 1.11; } .reveal pre code { font-size: inherit; } .reveal blockquote { font-size: 28px; } /* Paddings */ .reveal .slides section { width: 960px; height: 540px; } .reveal .slides > section, .reveal .slides > section > section { padding: 20px; } .reveal .slides > section > section { margin: 0 -20px; } .reveal .slides section h1, .reveal .slides section h2, .reveal .slides section h3, .reveal .slides section h4, .reveal .slides section h5, .reveal .slides section h6{ margin: -5px -20px 15px -20px; padding: 5px 20px; } .reveal dl { margin: 0; } .reveal dd { margin-bottom: 0.25em; } .reveal pre { /*margin: 10px -20px 20px -20px;*/ padding: 0; } .reveal pre code { padding: 10px 20px; } .reveal blockquote { margin: 20px -20px 20px -20px; padding: 0; } .reveal blockquote > p { margin: 0 0 0 40px; padding: 0 0 0 5px; } .reveal section img { margin: 0 auto; } /*** Helpers ***/ .reveal .slides section.center.present { display: grid !important; align-items: center; } .lr { display:grid; grid-gap: 0 50px; grid-template-columns: 1fr 1fr; } section.only-code pre { position: absolute; top: 10px; bottom: 10px; left: 0; right: 0; margin: 0; } section.only-code pre code { height: 100%; } /*** Modding ***/ .reveal .slides section .fragment.current-only { opacity: 1; visibility: visible; display: none; } .reveal .slides section .fragment.current-only.current-fragment { display: block; } .reveal .slides section li.fragment.current-only.current-fragment { display: list-item; } .reveal .slides section span.fragment.current-only.current-fragment { display: inline; } .reveal .slides section .fragment.visible-blurred { opacity: 1; visibility: visible; filter: blur(2px); } .reveal .slides section .fragment.visible-blurred.visible { filter: none; } .reveal .slides section .fragment.visible-blurred.visible.current-fragment { filter: none; text-shadow: 2px 2px #676767; } /*** Stylings ***/ .reveal section img.shape { border: none; background: none; padding: 0; margin: 0; } dd { font-style: italic; } dd * { font-style: initial; } .reveal pre code { overflow: hidden; } .reveal blockquote > p { border-left: solid #333 10px; } .reveal .slides section { overflow: hidden; } section.first *[data-title] { position: absolute; left : 0; bottom: 0px; } .reveal .slides section.first h1 { margin-top: 25px; background: none; } .reveal .slides section.first h2 { background: none; } section.first .author { position: absolute; left : 20px; bottom: 20px; } /***/ .reveal .slides section > div:first-child { height: 100%; } .reveal img, .reveal video, .reveal iframe { max-width: 100%; max-height: calc(100% - 58px); } </style> --- ## Содержание - Основные функции - Роли - Кластерная конфигурация - Упаковка приложений - Развертывание приложений - Пример кластера --- ### Cartridge Cluster - Горизонтальная масштабируемость <!-- .element: class="fragment" --> - Балансировка нагрузки <!-- .element: class="fragment" --> - Автоматическое переключение при сбое <!-- .element: class="fragment" --> - Централизованное управление кластером <!-- .element: class="fragment" --> - Автоматическая синхронизации настроек<!-- .element: class="fragment" --> - Кластеризация бизнес функций <!-- .element: class="fragment" --> ---- ### Например ```graphviz graph { rankdir = LR; splines="line"; bgcolor="transparent"; color="#cccccc"; fontcolor="#cccccc"; node[color="#cccccc"; bgcolor="grey" style="filled" fontcolor="black";]; edge[color="#cccccc"]; subgraph cluster{ label = "Storages"; style=dashed; node[shape=cylinder]; storage_msk[label="Moscow"]; storage_spb[label="Saint-Petersburg"]; storage_kn[label="Kazan'"]; } subgraph cluster_1 { label="Preprocessors" style=dashed; httpjson[label="HTTP API"]; } subgraph cluster_2 { label="Preprocessors" style=dashed; httpjson2[label="HTTP API"]; } httpjson -- storage_msk; httpjson -- storage_spb; httpjson -- storage_kn; httpjson2 -- storage_msk; httpjson2 -- storage_spb; httpjson2 -- storage_kn; } ``` ---- ### GUI - `html/js` графический интерфейс - Управление топологией - Авторизация - Правка конфигурации --- ## Как устроен кластер - Реплика — экземпляр, копия мастера, реализующий только чтение <!-- .element: class="fragment" --> - Лидер (мастер) — экземпляр, в который можно писать <!-- .element: class="fragment" --> - Набор реплик (`replica set`) — лидер + реплики <!-- .element: class="fragment" --> - Фейловер — механизм восстановления после отказа <!-- .element: class="fragment" --> --- ## Как узлы кластера видят друг друга - Пингуют друг друга по `udp brodcast` протоколу распространения слухов (`swim`) - Управляют друг другом по `net.box` `tcp/ip` протоколу - Имеют одинаковое значение `cluster cookie` --- ## Роль - Роль — Lua-модуль, реализующий некоторую логику - Роль назначается на репликасет — все реплики запустят Lua-модуль - Роль имеет доступ ко всему `Tarantool` - Роль может пользоваться модулем `cartridge` - `cartridge` предоставляет «кластерные» функции Note: Роли это Lua-модули, которые реализуют некоторые заданные для экземпляра функции и/или логику. ---- ### Жизненный цикл роли - `init` — включение роли - `validate_config` — проверка конфигурации - `apply_config` — применение конфигурации - `stop` — отключение роли Note: Жизненным циклом роли управляет Cartridge. Кластер вызывает функции роли в следующих случаях: - Функция init() обычно выполняется один раз: либо когда администратор включает роль, либо при перезапуске экземпляра. - Функции validate_config()/apply_config(): при обновлении конфигурации. - Функция stop() – только когда администратор отключает роль. Администраторы кластера могут работать с ролями либо через веб-интерфейс, либо через общедоступный API кластера. ---- ### net.box бизнес API роли ```lua= -- convertor.lua function convert() ... end return { ... convert = convert } ``` ---- ### Локальные зависимости от других ролей ```lua= -- convertor.lua return { role_name = 'convertor', dependencies = { -- embedded roles 'cartridge.roles.vshard-router', 'cartridge.roles.vshard-storage', -- custom roles 'imagemagick' } } ``` Note: Роли, которые будет инициализирована автоматически для каждого экземпляра с включенной ролью test_role. Кластер сначала инициализирует встроенные роли, а затем пользовательские в том порядке, в котором последние были перечислены в cartridge.cfg(). ---- ### Удаленные зависимости между ролями - `cartridge.rpc_call` — вызов функции роли - `vshard.router.callrw (-ro, -bro, -bre)` — вызов функции на сторадже --- ## Кластерная конфигурация - Содержит настройки вашего приложения - Например - Схему данных - Параметры подключения к Postgesql - Настройки таймаутов - Размеры преаллокаций - Конфигурация всего кластера — не отдельного узла ---- ### Работа с кластерной конфигурацией - Каждый экземпляр кластера хранит копию <!-- .element: class="fragment" --> - Валидация в коде роли <!-- .element: class="fragment" --> - Двухфазный алгоритм применения <!-- .element: class="fragment" --> Note: Анимация ---- ### Валидация конфигурации в коде роли ```lua= -- convertor.lua local function validate_config(cfg) local role = cfg['convertor'] or {} if role.title ~= nil then assert(type(role.title) == 'string', 'role.title must be a string') end return true end ``` ---- ### Алгоритм применения конфигурации в кластере - Проверка доступности всех нод <!-- .element: class="fragment" --> - `validate_config` на всех нодах <!-- .element: class="fragment" --> - Создание `config.prepare.yml` - Если ошибка -&gt; все удаляют `config.prepare.yml` - <!-- .element: class="fragment" --> config.prepare.yml -&gt; config.yml <!-- .element: class="fragment" --> - `apply_config` на всех экземплярах <!-- .element: class="fragment" --> - Если ошибка — ручное вмешательство <!-- .element: class="fragment" --> Note: Например функция set_secret() вызывает patch_clusterwide(), которая производит двухфазную фиксацию транзакций: - Кластер проверяет, можно ли применить новую конфигурацию ко всем экземплярам, кроме отключенных и исключенных. Все обновляемые экземпляры должны быть исправными и рабочими. - (Фаза подготовки) Кластер передает исправленную конфигурацию. Каждый экземпляр валидирует ее с помощью функции validate_config() каждой зарегистрированной роли. В зависимости от результата валидации: - В случае успеха (то есть возврата значения true) экземпляр сохраняет новую конфигурацию во временный файл с именем config.prepare.yml в рабочей директории. - (Фаза отмены) В противном случае экземпляр сообщает об ошибке, а все остальные экземпляры откатывают обновление: удаляют файл, который они, возможно, уже подготовили. - (Фаза фиксации) После успешной подготовки всех экземпляров кластер фиксирует изменения. Каждый экземпляр: - Создает жесткую ссылку активной конфигурации. - Атомарно заменяет активную на подготовленную. Атомарная замена неделима, то есть она может быть либо выполнена, либо не выполнена полностью, но не частично. - Вызывается функция apply_config() на каждой зарегистрированной роли. ---- ### Кластер куки - `<workdir>/.tarantool.cookie` - Одинаковая для всех узлова кластера - Используется для шифрования `udp` - Используется для пароля `net.box` `admin` - Вдохновлено `erlang.cookie` --- ## Цикл разработки - Сам `tarantool` - Фреймворк `cartridge` - Утилита `cartridge-cli` ---- ### Входная точка - `init.lua` - `cartridge.cfg` - Параметры всего кластера - Параметры текущего узла ---- ### Роли - Модули с бизнес функциями - И колбеками для оркестрации - Взаимодействие с другими ролями - Локально или удаленно - Взаимодействие с конфигурацией кластера ---- ### Ручной запуск - Одна нода - `tarantool init.lua` - Две и более - `cartridge start` ---- ### Автоматизированные тесты - `luatest` - Возможность запуска кластера - Доступ к любому узлу по `net.box` и другим протоколам - Возможность сценариев отказов - Остальное как и во всех фреймворках тестирования ---- ### Упаковка - `cartridge pack` - Файл проекта `rockspec` - Кастомные шаги/зависимости - `cartridge.pre-build` - `cartridge.post-build` - Результат `rpm`/`deb`/`targz`/`docker` - Версионирование `semver.org` (`major.minor.patch`) --- ## Деплой - Вручную пакетными менеджерами - `Ansible` роль `ansible-cartridge` ---- ### Конфигурация инстанса - Аргументы командной строки - Переменные окружения - Конфигурационный файл формата `YAML` - В файле `init.lua` ---- ### Кластерная конфигурация - Вкладка `Code` в админке - Или graphql api - Или lua api ---- ### Мониторинг - `prometheus` - `grapfana dashboard` --- ## В заключение - Tarantool - Vshard - Cartridge - Cartridge CLI - Ansible --- <!-- .slide: class="center" --> ### Заполните, пожалуйста, опрос о занятии по ссылке в чате - Мемы! <!-- .element: class="fragment" --> --- <!-- .slide: class="center" --> ### Спасибо за проявленное внимание
{"metaMigratedAt":"2023-06-15T17:40:06.427Z","metaMigratedFrom":"YAML","title":"OTUS Cartridge","breaks":true,"slideOptions":"{\"theme\":\"dark\",\"transition\":\"none\",\"backgroundTransition\":\"none\",\"width\":960,\"height\":540,\"margin\":0,\"allottedMinutes\":60}","contributors":"[{\"id\":\"b8f98924-71ad-44a5-89d6-3de16a5bd8ab\",\"add\":15632,\"del\":2373}]"}
    328 views
   Owned this note