Configuración y secretos en producción
Los servicios de Python en producción leen la configuración del entorno, la validan con modelos de configuración de Pydantic 2 y cargan los secretos desde almacenes de la plataforma; nunca desde archivos incrustados en imágenes de contenedor o en git.
Receta
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=None)
database_url: SecretStr
log_level: str = "info"
environment: str = Field(default="dev")
settings = Settings()Cuándo usar esto:
- Cada contenedor/Lambda desplegado en AWS, K8s o PaaS
- Separación de la configuración por entorno con la misma etiqueta de imagen
- Rotación de credenciales sin reconstruir imágenes
- Auditar quién puede leer qué secretos a través de IAM/RBAC
Ejemplo de trabajo
Configuración con .env local opcional solo en desarrollo, producción a partir de variables de entorno inyectadas + obtención de SSM al inicio.
import os
from functools import lru_cache
import boto3
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(extra="ignore")
environment: str = "dev"
database_url: SecretStr = Field(alias="DATABASE_URL")
ssm_parameter: str | None = Field(default=None, alias="API_KEY_PARAM")
@lru_cache
def get_settings() -> Settings:
return Settings()
def resolve_api_key(settings: Settings) -> str:
if settings.environment == "dev" and os.environ.get("API_KEY"):
return os.environ["API_KEY"]
if not settings.ssm_parameter:
raise RuntimeError("API_KEY_PARAM es requerido en producción")
ssm = boto3.client("ssm")
value = ssm.get_parameter(Name=settings.ssm_parameter, WithDecryption=True)
return value["Parameter"]["Value"]
if __name__ == "__main__":
s = get_settings()
print(s.environment, resolve_api_key(s)[:3] + "***")Lo que esto demuestra:
SecretStrevita el registro accidental del secreto en texto plano enrepr- Producción usa la referencia del nombre del parámetro SSM, no el valor secreto en la variable de entorno
lru_cachesingleton de configuración por proceso coincide con el patrón de dependencia de FastAPI
Profundización
Reglas de Configuración de 12-Factor
- Almacenar la configuración en el entorno
- Separación estricta de la configuración del código
- Tratar los servicios de respaldo como recursos adjuntos
Inyección por Plataforma
| Plataforma | Mecanismo |
|---|---|
| Kubernetes | envFrom de Secret/ConfigMap |
| ECS | secretos de definición de tarea desde SSM |
| Lambda | extensión de env + Secrets Manager |
| GitHub Actions | secretos de entorno |
Notas de Python
# Dependencia de FastAPI
from fastapi import Depends
def settings_dep() -> Settings:
return get_settings()
@app.get("/health")
def health(s: Settings = Depends(settings_dep)):
return {"env": s.environment}Errores comunes
- Archivo
.enven la imagen de producción - secretos en capas. Solución:dotenvsolo para desarrollo; inyección de plataforma en producción. - Registrar
settings.model_dump()- filtra secretos. Solución:model_dump(exclude={"database_url"})oreprpersonalizado y redactado. - Misma configuración de imagen para staging/producción - se borra la base de datos incorrecta. Solución: ARNs de secretos específicos del entorno solo en el manifiesto de despliegue.
- Entorno legible por cualquiera en
/proc- riesgo de escape de contenedor mitigado por RBAC y secretos mínimos por pod. - Recarga en caliente de configuración sin reinicio - raro en aplicaciones web de Python; documentar que se requiere reinicio después de la rotación de secretos.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Agente sidecar de Vault | Credenciales de BD dinámicas | Secretos estáticos simples de SSM |
| Sealed Secrets | Secretos cifrados en GitOps en git | SSM ya es estándar |
| Archivos de configuración en volumen | YAML grande no secreto | Secretos en el mismo archivo |
Preguntas frecuentes
¿pydantic-settings vs os.environ?
Settings proporciona validación, tipos y alias; vale la pena para cada servicio de producción.
¿Cómo cargan Flask/Django la configuración?
Módulo de configuración de Django desde la variable de entorno; Flask app.config.from_prefixed_env() o un wrapper de pydantic.
¿Cómo roto la contraseña de la base de datos?
Ventana de doble contraseña en RDS + reinicio escalonado de pods para recoger el nuevo valor de SSM.
¿ConfigMap para secretos en K8s?
Antipatrón - usar recurso Secret u operador de secretos externos a SSM.
¿Cómo pruebo la configuración?
monkeypatch.setenv en pytest antes de get_settings.cache_clear().
¿Indicadores de funciones (feature flags)?
Indicadores no secretos en ConfigMap/variable de entorno; indicadores dinámicos a través de LaunchDarkly/etc. cuando sea necesario.
¿Cómo se relaciona esto con la plantilla de infraestructura?
La infraestructura renderiza los manifiestos de despliegue con los ARNs de secretos; la aplicación recupera los valores en tiempo de ejecución - ver Gestión de Configuración en automatización de infraestructura.
¿Límites de tamaño de variables de entorno de Lambda?
4KB de variables de entorno en total - usar SSM/Secrets para blobs de configuración grandes.
¿Cómo documento las variables de entorno requeridas?
Tabla en README + descripciones de campos de Settings + comentarios en el manifiesto de despliegue.
¿Valores predeterminados seguros?
Solo para valores predeterminados de desarrollo no secretos; la producción que omite un campo requerido debe fallar rápidamente al inicio.
Relacionado
- Secrets Manager & SSM - obtención con boto3
- Gestión de Configuración - artefactos de despliegue de plantillas
- Dockerizar Python - sin secretos en la imagen
- Kubernetes para aplicaciones Python - secretRef
- Mejores prácticas de despliegue - lista de verificación de configuración
Versiones de la pila: Esta página se escribió para Python 3.14.0 (estable 3.14, mantenimiento 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+ y uv 0.6+.