Gerenciamento de Configuração
O gerenciamento de configuração separa o que muda por ambiente de como a automação o aplica. Projetos de infraestrutura em Python usam templates, arquivos de configuração em camadas e stores de segredos para que dev, staging e prod permaneçam estruturalmente idênticos sem copiar pilhas inteiras.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
from pydantic_settings import BaseSettings, SettingsConfigDict
class AppSettings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
environment: str = "dev"
database_url: str
log_level: str = "info"
settings = AppSettings() # lê variáveis de ambiente e opcionalmente .env localmente# app.env.j2
APP_ENV={{ environment }}
DATABASE_URL={{ database_url }}
LOG_LEVEL={{ log_level }}Quando usar isso:
- Mesmo template, valores diferentes entre dev/stage/prod
- Segredos fora do git com referências injetadas no momento da implantação
- Validação de configuração antes da aplicação com modelos Pydantic
- Flags de recursos e limites que diferem por ambiente
- Ansible/Pulumi/Terraform consumindo um esquema de configuração
Exemplo de Trabalho
Configuração YAML em camadas, renderização Jinja2 e stub de busca de segredos para um script de implantação.
"""render_config.py - carrega a configuração do ambiente, mescla segredos, renderiza o template."""
from __future__ import annotations
import json
from pathlib import Path
import yaml
from jinja2 import Environment, FileSystemLoader, select_autoescape
ROOT = Path(__file__).parent
CONFIG_DIR = ROOT / "config"
def load_yaml(path: Path) -> dict:
with path.open() as f:
return yaml.safe_load(f) or {}
def load_config(environment: str) -> dict:
base = load_yaml(CONFIG_DIR / "base.yaml")
env = load_yaml(CONFIG_DIR / f"{environment}.yaml")
merged = {**base, **env, "environment": environment}
return merged
def fetch_secret(name: str) -> str:
# Produção: boto3.client("secretsmanager").get_secret_value(...)
return f"SECRET::{name}"
def render(template_name: str, context: dict) -> str:
env = Environment(
loader=FileSystemLoader(ROOT / "templates"),
autoescape=select_autoescape(),
)
template = env.get_template(template_name)
return template.render(**context)
if __name__ == "__main__":
cfg = load_config("staging")
cfg["database_password"] = fetch_secret("db/password")
output = render("service.env.j2", cfg)
print(output)
print(json.dumps({"rendered_for": cfg["environment"]}))# config/base.yaml
log_level: info
service_port: 8080# config/staging.yaml
database_host: staging.db.internal
feature_new_ui: true# templates/service.env.j2
APP_ENV={{ environment }}
LOG_LEVEL={{ log_level }}
PORT={{ service_port }}
DATABASE_HOST={{ database_host }}
DATABASE_PASSWORD={{ database_password }}
FEATURE_NEW_UI={{ feature_new_ui }}O que isso demonstra:
- A mesclagem de YAML base + ambiente mantém a estrutura DRY (Don't Repeat Yourself)
- Segredos buscados no momento da renderização, nunca commitados em templates
- Jinja2 renderiza arquivos de ambiente finais para injeção no template do Ansible ou em contêineres
- Linha de log JSON registra qual ambiente foi renderizado para auditoria
Mergulho Profundo
Como Funciona
- Camada de dados: YAML/JSON/TOML por ambiente + validação de esquema
- Camada de segredos: SSM, Secrets Manager, Vault ou store de segredos de CI
- Camada de template: Jinja2, envsubst ou variáveis do Terraform/Pulumi
- Camada de aplicação: Ansible, Kubernetes ou systemd carregam artefatos renderizados
- Fluxo de alterações: editar configuração no git → validar → renderizar → implantar → verificar
Configuração vs. Segredos
| Item | Armazenamento | Exemplo |
|---|---|---|
| Região, tamanho da instância | Git (YAML de ambiente) | instance_type: t3.small |
| Senha do banco de dados, chaves de API | Gerenciador de segredos | arn:aws:secretsmanager:... |
| Flags de recursos | Git ou serviço de flags remoto | feature_x: false |
| Substituições em tempo de execução | Variáveis de ambiente | LOG_LEVEL=debug |
Notas de Python
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings
class DeploySettings(BaseSettings):
environment: str
db_password: SecretStr = Field(alias="DATABASE_PASSWORD")
def redacted(self) -> dict:
return self.model_dump(mode="json", exclude={"db_password"})Armadilhas
- Segredos em
prod.yaml- credenciais no histórico do git para sempre. Correção: armazene ARNs ou nomes de segredos, resolva em tempo de execução. - Templates bifurcados por ambiente - desvio quando um ambiente recebe uma correção. Correção: um template, apenas dados específicos do ambiente.
- Validação ausente - erros de digitação chegam à produção como implantações falhas. Correção: modelos Pydantic ou JSON Schema em CI.
- Arquivos renderizados legíveis por qualquer pessoa -
chmod 644em.envcom senhas. Correção:0600de propriedade apenas para o usuário do serviço. - Templates com entrada não confiável - SSTI se os templates incluírem conteúdo do usuário. Correção: mantenha os templates internos; nunca incorpore strings de usuário em Jinja2.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Variáveis de ambiente codificadas apenas em CI | Projetos minúsculos de ambiente único | Múltiplos ambientes com estrutura compartilhada |
| Configuração ao vivo Consul/etcd | Ajuste dinâmico em tempo de execução | Infraestrutura deseja alterações versionadas e revisadas |
| Apenas configuração Pulumi | Todas as configurações são parâmetros de recursos de nuvem | Configuração de nível de aplicativo também necessária nos hosts |
.env commitado por desenvolvedor | Conveniência local de desenvolvimento apenas | Qualquer ambiente compartilhado ou de produção |
FAQs
A configuração deve ficar no git ou em um store de segredos?
Parâmetros não secretos pertencem ao git com overlays de ambiente. Segredos pertencem a um gerenciador com rotação; o git armazena apenas referências.
Por que Jinja2 para equipes de infraestrutura?
O Ansible o usa nativamente, a sintaxe é familiar e ele suporta condicionais para blocos opcionais sem duplicar arquivos inteiros.
Como pydantic-settings se encaixa?
Os aplicativos leem variáveis de ambiente em tempo de execução com validação. A infraestrutura renderiza esses arquivos de ambiente a partir de YAML em camadas + segredos durante a implantação.
Como evitar que a configuração de staging atinja os bancos de dados de produção?
Strings de conexão separadas por arquivo de ambiente, convenções de nomenclatura (staging.db.internal) e verificações de CI que falham se nomes de host prod aparecerem em arquivos não-prod.
As variáveis do Terraform podem substituir isso?
Para recursos de nuvem, sim. Arquivos de ambiente de aplicativos em VMs/contêineres ainda se beneficiam da renderização fora das saídas do Terraform.
Com que frequência devo rotacionar segredos?
Automatize a rotação no Secrets Manager/SSM e re-renderize as configurações. Documente o rollback se a rotação quebrar serviços dependentes.
E quanto à configuração 12-factor?
Armazene a configuração em variáveis de ambiente em tempo de execução. Seu passo de renderização produz essas variáveis para Kubernetes, Lambda ou systemd.
Como testar templates?
pytest com dicionários de contexto fixos afirma que as strings renderizadas contêm as chaves esperadas e nunca vazam tokens de placeholder como {{.
Os padrões devem ficar em base.yaml ou no código?
Padrões operacionais seguros em base.yaml; padrões de código apenas para fallbacks de desenvolvimento local. Documente substituições no README por ambiente.
Como auditar quem alterou a configuração de produção?
Git blame no YAML de ambiente mais logs de implantação mostrando o SHA renderizado e a identidade do operador das reivindicações OIDC da CI.
Relacionado
- Noções Básicas de Automação de Infraestrutura - separação de ambientes
- Ansible - módulo template e group_vars
- Secrets Manager & SSM - resolução de segredos AWS
- Configuração e Segredos em Produção - injeção em tempo de execução
- Melhores Práticas de Automação de Infraestrutura - regras de configuração versionada
Versões da Stack: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+ e uv 0.6+.