Русский
Русский
English
Статистика
Реклама

Vault

VaultPydantic продолжение саги, локальная разработка

16.12.2020 02:21:17 | Автор: admin


Предыстория


Предыдущая статья


В предыдущей статье я писал о том, как сконфигурировать ваше приложение с помощью Sitri, однако, упустил момент с локальной разработкой, так как согласитесь, что не очень удобно разворачивать vault локально, да и хранить локальный конфиг в общем vault, особенно, если над проектом работают несколько человек неудобнее вдвойне.


В Sitri данная проблема решается достаточно просто с помощью локального режима для ваших классов настроек, то есть вам не придётся даже переписывать ничего или дублировать код, да и структура json файла для локального режима будет почти полностью повторять структуру секретов.


Итак, ну а теперь, давайте добавим в наш проектик буквально пару строк кода + я покажу, как со всем этим можно работать, если ваш проект локально запускается в docker-compose.


Готовим код


Для начала, давайте договоримся, что local_mode действует тогда, когда ENV = "local" :) Далее, я предлагаю немного отредактировать наш provider_config.py и создать там класс BaseConfig от которого мы будем наследовать в наших настройках Config классы. Мы сделаем это для того, чтобы не дублировать код, то есть в самих классах настроек будет только то, что специфично для них.


import hvacfrom sitri.providers.contrib.system import SystemConfigProviderfrom sitri.providers.contrib.vault import VaultKVConfigProviderfrom sitri.settings.contrib.vault import VaultKVSettingsconfigurator = SystemConfigProvider(prefix="superapp")ENV = configurator.get("env")is_local_mode = ENV == "local"local_mode_file_path = configurator.get("local_mode_file_path")def vault_client_factory() -> hvac.Client:    client = hvac.Client(url=configurator.get("vault_api"))    client.auth_approle(        role_id=configurator.get("role_id"),        secret_id=configurator.get("secret_id"),    )    return clientprovider = VaultKVConfigProvider(    vault_connector=vault_client_factory,    mount_point=f"{configurator.get('app_name')}/{ENV}",)class BaseConfig(VaultKVSettings.VaultKVSettingsConfig):    provider = provider    local_mode = is_local_mode    local_provider_args = {"json_path": local_mode_file_path}

Немного про local_provider_args в этом поле мы указываем аргументы для создания экземпляра JsonConfigProvider, они будут провалидированы и данный словарь должен соотвествовать схеме, поэтому не волнуйтесь это не какой-то грязный приём. Однако, если вы хотите создать экземпляр локального провайдера сами, то вы просто положить его в опциональное поле local_provider.


Теперь, мы можем спокойно наследовать конфиг классы от базового. Например, класс настроек для соединения с Kafka будет выглядеть так:


from typing import Any, Dictfrom pydantic import Fieldfrom sitri.settings.contrib.vault import VaultKVSettingsfrom superapp.config.provider_config import BaseConfig, configuratorclass KafkaSettings(VaultKVSettings):    mechanism: str = Field(..., vault_secret_key="auth_mechanism")    brokers: str = Field(...)    auth_data: Dict[str, Any] = Field(...)    class Config(BaseConfig):        default_secret_path = "kafka"        default_mount_point = f"{configurator.get('app_name')}/common"        local_mode_path_prefix = "kafka"

Как видите требуемые изменения минимальны. local_mode_path_prefix мы указываем, чтобы в нашем json файле сохранялась структура общего конфига. А теперь, давайте напишем этот самый json файл для локальной конфигурации:


{    "db":    {        "host": "testhost",        "password": "testpassword",        "port": 1234,        "user": "testuser"    },    "faust":    {        "agents":        {            "X":            {                "concurrency": 2,                "partitions": 5            }        },        "app_name": "superapp-workers",        "default_concurrency": 5,        "default_partitions_count": 10    },    "kafka":    {        "auth_data":        {            "password": "testpassword",            "username": "testuser"        },        "brokers": "kafka://test",        "mechanism": "SASL_PLAINTEXT"    }}

Ну, или просто скопипастим его из конца прошлой статьи. Как видите, тут всё весьма просто. Для наших дальнейших изысканий, переименуем main.py в корне проекта на __main__.py, чтобы можно было запустить пакет командой из docker-compose.


Запихиваем приложение в контейнер и наслаждаемся сборкой


Первое, что мы должны сделать это написать небольшой Dockerfile:


FROM python:3.8.3-busterENV PYTHONUNBUFFERED=1 \    POETRY_VIRTUALENVS_CREATE=false \    POETRY_VIRTUALENVS_IN_PROJECT=false \    POETRY_NO_INTERACTION=1RUN pip install poetryRUN mkdir /superapp/WORKDIR /superapp/COPY ./pyproject.toml ./poetry.lock /superapp/RUN poetry install --no-ansiWORKDIR /

Здесь мы просто устанавливаем зависимости и всё, так как он для локальной разработки, то код проекта мы не копируем.


Далее, нам понадобиться env-файл с необходимыми для local-mode переменными:


SUPERAPP_ENV=localSUPERAPP_LOCAL_MODE_FILE_PATH=/config.jsonSUPERAPP_APP_NAME=superapp

Как видите, ничего лишнего, никакой конфигурационной информации для Vault не нужно, так как при локальном режиме, приложение даже не попытается постучаться в Vault.


Ну и последнее, что мы должны написать сам docker-compose.yml файл:


# docker-compose config for local developmentversion: '3'services:  superapp:    command: python3 -m superapp    restart: always    build:      context: ./      dockerfile: Dockerfile    volumes:      - ./superapp:/superapp      - ./config.json:/config.json    env_file:      - .env.local

Как видите, тут тоже всё просто. Наш json-файл мы кладём в корень, как и указали выше в переменной окружения для контейнера.


Ну а теперь, запуск:


docker-compose up

Creating article_sitri_vault_pydantic_superapp_1 ... doneAttaching to article_sitri_vault_pydantic_superapp_1superapp_1  | db=DBSettings(user='testuser', password='testpassword', host='testhost', port=1234) faust=FaustSettings(app_name='superapp-workers', default_partitions_count=10, default_concurrency=5, agents={'X': AgentConfig(partitions=5, concurrency=2)}) kafka=KafkaSettings(mechanism='SASL_PLAINTEXT', brokers='kafka://test', auth_data={'password': 'testpassword', 'username': 'testuser'})superapp_1  | {'db': {'user': 'testuser', 'password': 'testpassword', 'host': 'testhost', 'port': 1234}, 'faust': {'app_name': 'superapp-workers', 'default_partitions_count': 10, 'default_concurrency': 5, 'agents': {'X': {'partitions': 5, 'concurrency': 2}}}, 'kafka': {'mechanism': 'SASL_PLAINTEXT', 'brokers': 'kafka://test', 'auth_data': {'password': 'testpassword', 'username': 'testuser'}}}

Как видите, всё успешно запустилось и информация из нашего json-фала успешно прошла все проверки и стала настройками для локальной версии приложения, юхху!


Код этой статьи я положил в отдельную ветку, так что можете зайти, склонить и протестировать всё самолично :)
branch

Подробнее..

Бэкапы для HashiCorp Vault с разными бэкендами

26.05.2021 10:15:20 | Автор: admin

Недавно мы публиковали статью про производительность Vault с разными бэкендами, а сегодня расскажем, как делать бэкапы и снова на разных бэкендах: Consul, GCS (Google Cloud Storage), PostgreSQL и Raft.

Как известно, HashiCorp предоставляет нативный метод бэкапа только для одного бэкенда Integrated Storage (Raft Cluster), представленного как GA в апреле прошлого года. В нем можно снять снапшот всего одним curlом и не беспокоиться о каких-либо нюансах. (Подробности смотрите в tutorial и документации по API.)

В остальных же случаях придется выкручиваться самим, придумывая, как правильно реализовывать бэкап. Очевидно, что во время резервного копирования Vault часть данных может меняться, из-за чего мы рискуем получить некорректные данные. Поэтому важно искать пути для консистентного бэкапа.

1. Consul

В Consul существует встроенный механизм для создания и восстановления снапшотов. Он поддерживает point-in-time backup, поэтому за консистентность данных можно не переживать. Однако есть существенный недостаток: на время снапшота происходит блокировка, из-за чего могут возникнуть проблемы с операциями на запись.

Для создания такого снимка необходимо:

1. Подключиться к инстансу с Consul и выполнить команду:

# consul snapshot save backup.snapSaved and verified snapshot to index 199605#

2. Забрать файл backup.snap (заархивировать и перенести в место для хранения бэкапов).

Для восстановления будет схожий алгоритм действий с выполнением другой команды на сервере с Consul:

# consul snapshot restore backup.snap

В HA Vault-кластере активный узел будет являться лидером во время экспорта данных или моментального снимка. После восстановления данных Vault в Consul необходимо вручную снять эту блокировку, чтобы кластер Vault мог выбрать нового лидера.

Выполните команду после восстановления данных для снятия блокировки:

# consul kv delete vault/core/lock

Работа со снапшотами обычно происходит быстро (см. примеры в конце статьи) и обычно не вызывает трудностей.

2. Google Cloud Storage

С GCS всё оказалось сложнее: подходящих вариантов для создания снапшотов/бэкапов бакета найти не удалось. Предусмотрена поддержка версионирования, но с ним восстановить сразу весь бакет на определенный момент времени нельзя можно только по одному файлу. В теории это решается скриптом для взаимодействия со всеми файлами, но если учесть размер Vaultа и количества файлов в нем, скорее всего такой скрипт будет работать слишком долго. Если мы хотим получить консистентные данные, то снятия дампа придется на время останавливать Vault.

А для копирования данных в GCS предусмотрен удобный способ Data Transfer. С его помощью можно создать полную копию бакета, а дальше уже работать с ним по своему усмотрению.

Последовательность действий:

  1. Выключаем Vault.

  2. Заходим в Data Transfer (https://console.cloud.google.com/transfer/cloud).

  3. Выбираем исходный бакет, вводим название бакета, куда переносить данные, и выбираем разовую операцию. Здесь возможно установить и операцию по расписанию, но она не учитывает специфики Vault (тот факт, что сервис необходимо остановить Vault на время её выполнения), поэтому оставляем разовый запуск.

  4. После успешного копирования запускаем Vault.

  5. С созданным бакетом можно работать: например, скачать его содержимое при помощи gsutil, заархивировать все данные и отправить на долгосрочное хранение.

В зависимости от количества ключей продолжительность бэкапа может сильно изменяться: от нескольких минут и практически до бесконечности. Ниже будет приведена таблица с примерными значениями.

Скриншоты страницы при создании трансфера и после завершения:

3. PostgreSQL

Базу в PostgreSQL достаточно забэкапить любыми доступными для этой СУБД способами не сомневаюсь, что они хорошо известны инженерам, уже работающим с PgSQL. Инструкции по выполнению операций для настройки бэкапов и восстановления данных на нужный момент времени (PITR, Point-in-Time Recovery) описаны в официальной документации проекта. Также хорошую инструкцию можно найти в этой статье от Percona.

Не останавливаясь на технических деталях по этим операциям (они уже раскрыты в многочисленных статьях), отмечу, что у PostgreSQL получился заметно более быстрый бэкап, чем у вариантов выше. Для простоты действий, мы замеряли бэкап и восстановление обычными pg_dump и pg_restore (да, это упрощенное измерение, однако использование схемы с PITR не повлияет значительно на скорость).

4. Integrated Storage (Raft)

Однако обзор не был бы полным без самого удобного и простого на сегодняшний день бэкенда Raft. Благодаря тому, что Raft полностью дублирует все данные на каждый из узлов, выполнять снапшот можно на любом сервере, где запущен Vault. А сами действия для этого предельно просты.

Для создания снимка необходимо:

. Зайти на сервер/pod, где запущен Vault, и выполнить команду:

# vault operator raft snapshot save backup.snapshot
  1. Забрать файл backup.snapshot (заархивировать и перенести в место для хранения бэкапов).

Для восстановления команду на сервере с Vault надо заменить на:

# vault operator raft snapshot restore backup.snapshot

Как работать с Raft и со снапшотами, хорошо описано в официальной документации.

Простой бенчмарк

Не претендуя на полноценное исследование о производительности, сделаем простой замер скорости создания бэкапов и восстановления из них для всех упомянутых бэкендов для Vault.

Загрузка данных в Vault

Перед началом тестирования загрузим данные в Vault. Мы для этого использовали официальный репозиторий от HashiCorp: в нем есть скрипты для вставки ключей в Vault.

Протестируем на двух коллекциях: 100 тысяч и 1 млн ключей. Команды для первого теста:

export VAULT_ADDR=https://vault.service:8200export VAULT_TOKEN=YOUR_ROOT_TOKENvault secrets enable -path secret -version 1 kvnohup wrk -t1 -c16 -d50m -H "X-Vault-Token: $VAULT_TOKEN" -s write-random-secrets.lua $VAULT_ADDR -- 100000

Эту операцию проделаем для всех инсталляций Vault, а затем повторим её для другого количества ключей.

Результаты

После загрузки данных мы сделали бэкап для всех 4 бэкендов. Бэкап для каждого hosted-бэкенда снимался на однотипной машине (4 CPU, 8 GB RAM).

Результаты сравнения по бэкапу и восстановлению:

100k backup

100k restore

1kk backup

1kk restore

Consul

3.31s

2.50s

36.02s

27.58s

PosgreSQL

0.739s

1.820s

4.911s

24.837s

GCS*

1h

1h24m

12h

16h

Raft

1.96s

0.36s

22.66s

4.94s

* Восстановление бэкапа в GCS может происходить в нескольких вариантах:

  1. Мы просто переключаем Vault на бэкапный бакет. В таком случае достаточно перезапустить Vault с новым конфигом и все сразу же заработает.

  2. Загрузить файлы бэкапа в бакет через утилиту gsutil и уже после этого переключать. Результаты для такого варианта будут зависеть от количества файлов и их размера в таблице приведен результат именно для такого варианта.

NB. В этом мини-тестировании сравниваются решения разного рода: single-экземпляр PostgreSQL против кластеров Consul и Raft против сетевого распределённого хранилища (GCS). Это может показаться не совсем корректным, потому что у таких бэкендов и разные возможности/свойства (отказоустойчивость и т.п.). Однако данное сравнение приведено исключительно как примерный ориентир для того, чтобы дать понимание порядковой разницы в производительности. Ведь это зачастую является одним из факторов при выборе того или иного способа.

Выводы

PostgreSQL занимает уверенное первое место по скорости создания бэкапа. Raft не так сильно отстает от лидера на небольшом объеме секретов, но разница заметно возрастает при увеличении количества данных. Однако в то же время Raft явно лидирует в скорости восстановления.

Если сравнивать удобство, то Raft и Consul максимально простые: для выполнения бэкапа и восстановления достаточно выполнить буквально одну команду. GCS же предоставляет встроенный функционал в UI для копирования бакетов: на мой вкус, это немного сложнее, однако для других пользователей может быть плюсом, что все действия выполняются мышкой в браузере. Но в GCS есть существенная проблема с отсутствием гарантий по времени создания снапшотов: одинаковый набор данных может бэкапиться как за 1 час, так и за 3-4 часа.

Помимо производительности и удобства есть и другой критерий надежность. Вывод, казалось бы, довольно банален: чем менее надежен бэкенд, тем легче его бэкапить. Хотя Vault сам по себе позиционируется как приложение категории cloud native, его бэкап полностью зависит от выбранного бэкенда, и во многих случаях мы получим простой, что недопустимо для такого важного сервиса:

  • В Consul можно ожидать проблем с чтением и записью при бэкапе/восстановлении данных.

  • А у PostgreSQL при восстановлении.

  • У GCS проблема другого характера: нет гарантий на скорость копирования.

Получается, что все эти решения имеют серьёзные недостатки, которые могут быть недопустимы в production. Понимая это, в HashiCorp и создали своё оптимальное решение Integrated Storage (Raft). В нём получается делать бэкапы полностью беспростойно и при этом быстро.

В итоге: если вам важна максимальная скорость для снятия и восстановления бэкапов, то подойдут PostgreSQL и Raft. В первом случае, однако, надо повозиться с удобством: чтобы все правильно настроить и автоматизировать, придется потратить время (и иметь экспертизу). Наш текущий выбор пал на Integrated Storage (Raft) как самый простой в использовании и надежный бэкенд.

P.S.

Читайте также в нашем блоге:

Подробнее..

Конфигурируем сервис с помощью Vault и Pydantic

09.12.2020 04:19:27 | Автор: admin

image


Предисловие


В данной статье я расскажу о конфигурации для вашей сервисов с помощью связки Vault (KV и пока только первой версии, т.е. без версионирования секретов) и Pydantic (Settings) под патронажем Sitri.


Итак, допустим, что у нас есть приложение superapp с заведёнными конфигами в Vault и аутентификацией с помощью approle, примерно так настроим (настройку policies для доступа к секрет-энжайнам и к самим секретам я оставлю за кадром, так как это достаточно просто и статья не об этом):


Key                        Value---                        -----bind_secret_id             truelocal_secret_ids           falsepolicies                   [superapp_service]secret_id_bound_cidrs      <nil>secret_id_num_uses         0secret_id_ttl              0stoken_bound_cidrs          []token_explicit_max_ttl     0stoken_max_ttl              30mtoken_no_default_policy    falsetoken_num_uses             50token_period               0stoken_policies             [superapp_service]token_ttl                  20mtoken_type                 default

Прим.: естественно, что если у вас есть возможность и приложение выходит в боевой режим, то secret_id_ttl лучше делать не бесконечным, выставляя 0 секунд.


SuperApp требует конфигурации: подключения к базе данных, подключение к kafka и faust конфигурации для работы кластера воркеров.


Подготовим Sitri


В базовой документации библиотеки есть простой пример, конфигурирования через vault-провайдер, однако, он не охватывает все возможности и может быть полезным, если ваше приложение конфигурируется достаточно легко.


Итак, для начала сконфигурируем vault-провайдер в условном файле provider_config.py:


import hvac  from sitri.providers.contrib.vault import VaultKVConfigProvider  from sitri.providers.contrib.system import SystemConfigProvider  configurator = SystemConfigProvider(prefix="superapp")  ENV = configurator.get("env")  def vault_client_factory() -> hvac.Client:      client = hvac.Client(url=configurator.get("vault_api"))      client.auth_approle(          role_id=configurator.get("role_id"),    secret_id=configurator.get("secret_id"),    )      return client  provider = VaultKVConfigProvider(      vault_connector=vault_client_factory, mount_point=f"{configurator.get('app_name')}/{ENV}"  )

В данном случае мы достаём из среды с помощью системного провайдера несколько переменных для конфигурирования подключения к vault, т.е. изначально должны быть экспортированы следующие переменные:


export SUPERAPP_ENV=devexport SUPERAPP_APP_NAME=superappexport SUPERAPP_VAULT_API=https://your-vault-host.domainexport SUPERAPP_ROLE_ID=535b268d-b858-5fb9-1e3e-79068ca77e27 # Примерexport SUPERAPP_SECRET_ID=243ab423-12a2-63dc-3d5d-0b95b1745ccf # Пример

В примере предполагается, что базовый mount_point к вашим секретам для определённой среды будет содержать имя приложения и имя среды, поэтому мы и экспортировали SUPERAPP_ENV. Путь до секретов отдельных частей приложения мы будем определять в settings-классах далее, поэтому в провайдере secret_path мы оставляем пустым.


Классы настроек


Начнём по пунктам и разнесём три класса настроек (БД, Kafka, Faust) по трём разным файлам.


Настройки БД


from pydantic import Field  from sitri.settings.contrib.vault import VaultKVSettings  from superapp.config.provider_config import provider  class DBSettings(VaultKVSettings):      user: str = Field(..., vault_secret_key="username")      password: str = Field(...)      host: str = Field(...)      port: int = Field(...)      class Config:          provider = provider          default_secret_path = "db"

Итак, как видите, конфиг. данные для базы у нас достаточно простые. Этот класс будет по-умолчанию смотреть в секрет superapp/dev/db, так, как мы указали в config классе, в остальном здесь простые pydantic поля, но в одном из них присутствует extra-аргумент vault_secret_key он нужен тогда, когда ключ в секрете не совпадает по имени с pydantic полем в нашем классе, если его не указывать, то провайдер будет искать ключ по имени поля.


Например, в нашем тестовом приложении, предполагается, что в секрете superapp/dev/db, есть ключи password и username, но мы хотим, чтобы последний был помещён в поле user для удобства и краткости.


Поместим в вышеозначенный секрет следующие данные для примера:


{  "host": "testhost",  "password": "testpassword",  "port": "1234",  "username": "testuser"}

Для первого класса из тройки, я покажу, как легко можно всё это запустить, чтобы данные собрались сами:


db_settings = DBSettings()pprint(db_settings.dict())# -> # {#     "host": "testhost",#     "password": "testpassword",#     "port": 1234,#     "user": "testuser"# }

Настройки Kafka


from typing import Dict, Any  from pydantic import Field  from sitri.settings.contrib.vault import VaultKVSettings  from superapp.config.provider_config import provider, configurator  class KafkaSettings(VaultKVSettings):      mechanism: str = Field(..., vault_secret_key="auth_mechanism")      brokers: str = Field(...)      auth_data: Dict[str, Any] = Field(...)      class Config:          provider = provider          default_secret_path = "kafka"          default_mount_point = f"{configurator.get('app_name')}/common"

В данном случае, представим, что инстанс кафки для разных сред нашего сервиса один, поэтому секрет хранится по пути superapp/common/kafka


{  "auth_data": "{\"password\": \"testpassword\", \"username\": \"testuser\"}",  "auth_mechanism": "SASL_PLAINTEXT",  "brokers": "kafka://test"}

Класс настройки поймёт комплексный тип данных Dict[str, Any] и распарсит его в словарь, то есть при заполнении наших настроек будут следующие данные:


{    "auth_data":    {        "password": "testpassword",        "username": "testuser"    },    "brokers": "kafka://test",    "mechanism": "SASL_PLAINTEXT"}

Так же, если секрет будет задан напрямую в json, например так:


{  "auth_data": {    "password": "testpassword",    "username": "testuser"  },  "auth_mechanism": "SASL_PLAINTEXT",  "brokers": "kafka://test"}

То класс настроек тоже сможет правильно разложить данные.


P.S.
Так же, secret_path и mount_point можно задавать на уровне полей, чтобы провайдер запросил конкретные значения из разных секретов (если это требуется). Приведу цитату с приоритезацией пути секрета и точки монтирования из документации:


Secret path prioritization:
  1. vault_secret_path (Field arg)
  2. default_secret_path (Config class field)
  3. secret_path (provider initialization optional arg)


Mount point prioritization:
  1. vault_mount_point (Field arg)
  2. default_mount_point (Config class field)
  3. mount_point (provider initialization optional arg)


Настройки Faust и отдельных воркеров


from typing import Dict  from pydantic import Field, BaseModel  from sitri.settings.contrib.vault import VaultKVSettings  from superapp.config.provider_config import provider  class AgentConfig(BaseModel):      partitions: int = Field(...)      concurrency: int = Field(...)  class FaustSettings(VaultKVSettings):      app_name: str = Field(...)      default_partitions_count: int = Field(..., vault_secret_key="partitions_count")      default_concurrency: int = Field(..., vault_secret_key="agent_concurrency")      agents: Dict[str, AgentConfig] = Field(default=None, vault_secret_key="agents_specification")      class Config:          provider = provider          default_secret_path = "faust"

superapp/dev/faust:


{  "agent_concurrency": "5",  "app_name": "superapp-workers",  "partitions_count": "10"}

В данном случае, по-умолчанию у нас есть глобальные значения кол-ва партиций в кафке и concurrency для агентов. Таким образом, по-умолчанию наши настройки будут выгружены так:


{  "agents": None,  "app_name": "superapp-workers",  "default_concurrency": 5,  "default_partitions_count": 10}

Например, у нас есть агент X с настройками:


{  "partitions": 5,  "concurrency": 2}

Наш секрет в связи с этим должен выглядеть следующим образом:


{  "agent_concurrency": "5",  "agents_specification": {    "X": {      "concurrency": "2",      "partitions": "5"    }  },  "app_name": "superapp-workers",  "partitions_count": "10"}

Как и ожидалось данные корректно смапились и типы значений были преобразованы так, как указано в модели AgentConfig:


{    "agents":    {        "X":        {            "concurrency": 2,            "partitions": 5        }    },    "app_name": "superapp-workers",    "default_concurrency": 5,    "default_partitions_count": 10}

Совмещаем в единый конфиг класс


from pydantic import BaseModel, Field  from superapp.config.database_settings import DBSettings  from superapp.config.faust_settings import FaustSettings  from superapp.config.kafka_settings import KafkaSettings  class AppSettings(BaseModel):      db: DBSettings = Field(default_factory=DBSettings)      faust: FaustSettings = Field(default_factory=FaustSettings)      kafka: KafkaSettings = Field(default_factory=KafkaSettings)

Совместим наши классы настроек в одну модель, применив default_factory для автоматического сбора при инициализации модели всех наших данных.


Давайте запустим наш код и проверим, как всё сработается вместе:


from superapp.config import AppSettings  config = AppSettings()  print(config)  print(config.dict())

Получаем общий вывод всей конфигурации приложения:


db=DBSettings(user='testuser', password='testpassword', host='testhost', port=1234) faust=FaustSettings(app_name='superapp-workers', default_partitions_count=10, default_concurrency=5, agents={'X': AgentConfig(partitions=5, concurrency=2)}) kafka=KafkaSettings(mechanism='SASL_PLAINTEXT', brokers='kafka://test', auth_data={'password': 'testpassword', 'username': 'testuser'})

{    "db":    {        "host": "testhost",        "password": "testpassword",        "port": 1234,        "user": "testuser"    },    "faust":    {        "agents":        {            "X":            {                "concurrency": 2,                "partitions": 5            }        },        "app_name": "superapp-workers",        "default_concurrency": 5,        "default_partitions_count": 10    },    "kafka":    {        "auth_data":        {            "password": "testpassword",            "username": "testuser"        },        "brokers": "kafka://test",        "mechanism": "SASL_PLAINTEXT"    }}

Счастье, радость, восторг!


У нас получилась такая структура тест-проекта:


superapp config    app_settings.py    database_settings.py    faust_settings.py    __init__.py    kafka_settings.py    provider_config.py __init__.py main.py

Послесловие


Как видите настройка достаточно проста с Sitri, после неё мы получаем чёткую схему конфигурации с нужными нам типами данных у значений, даже если в vault по-умолчанию они хранились строками.


Пишите комментарии по поводу либы, кода или общие впечатления. Буду рад любому отзыву!


P.S. Код из статьи я залил на github https://github.com/Egnod/article_sitri_vault_pydantic

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru