Prefácio
Neste artigo falarei sobre a configuração de seus serviços utilizando o bundle Vault (KV e até agora apenas a primeira versão, ou seja, sem segredos de versionamento) e Pydantic (Settings) sob o patrocínio da Sitri .
Então, digamos que temos um aplicativo superapp com configurações configuradas no Vault e autenticação usando o approle, vamos configurar algo assim (vou deixar a configuração das políticas para acessar os motores secretos e os próprios segredos nos bastidores, já que isso é bastante simples e o artigo não é sobre isso):
Key Value --- ----- bind_secret_id true local_secret_ids false policies [superapp_service] secret_id_bound_cidrs <nil> secret_id_num_uses 0 secret_id_ttl 0s token_bound_cidrs [] token_explicit_max_ttl 0s token_max_ttl 30m token_no_default_policy false token_num_uses 50 token_period 0s token_policies [superapp_service] token_ttl 20m token_type default
.: , , secret_id_ttl , 0 .
SuperApp : , kafka faust .
Sitri
, 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=dev
export SUPERAPP_APP_NAME=superapp
export SUPERAPP_VAULT_API=https://your-vault-host.domain
export 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:
- vault_secret_path (Field arg)
- default_secret_path (Config class field)
- secret_path (provider initialization optional arg)
Mount point prioritization:
- vault_mount_point (Field arg)
- default_mount_point (Config class field)
- 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