DKP001 - internal
935538483843Descripción
Cuenta interna de Dekuple. Aloja el ERP (adlperformance-es/erp) migrado a AWS ECS Fargate, además de otros proyectos. Región eu-west-1.
Contexto
| Campo | Valor |
|---|---|
| Cuenta AWS | 935538483843 (DKP001 / interna) |
| Región | eu-west-1 |
| Proyecto principal | ERP — adlperformance-es/erp (Ruby on Rails) |
| Cómputo | AWS ECS Fargate (migrado desde Heroku) |
| Base de datos | Amazon RDS PostgreSQL 16 |
| Repositorio | git@bitbucket.org:adlperformance-es/erp.git |
| Dominios | erp.dekuple.es (web), api.adlperformance.es (API), vouchers.adlperformance.es (descarga de bonos) |
El ERP corre sobre tres "caras" servidas por el mismo código Rails (diferenciadas por subdominio): web (panel de administración), api (JSON:API para terceros/fidelity) y vouchers (descarga/emisión de bonos). El procesamiento en segundo plano usa delayed_job (worker) y las tareas programadas corren en EventBridge Scheduler.
Arquitectura
Inventario de recursos
Clusters ECS
| Cluster | Uso |
|---|---|
erp-pro | Producción: servicios web + worker |
erp-pro-jobs | Producción: tareas cron (EventBridge → RunTask) |
erp-stg | Staging |
erp-erpaws | Entorno de pruebas/validación |
erp-sandbox | Sandbox |
Servicios y task definitions (producción)
| Recurso | Valor |
|---|---|
| Servicio web | erp-pro-web-service (3 tareas, autoescalado min 3 / max 10) |
| Servicio worker | erp-pro-worker-service (1 tarea) |
| Task def web | erp-pro-web |
| Task def worker | erp-pro-worker |
| Task def migraciones | erp-pro-migrate (comando rails db:migrate) |
| Task def crons | erp-pro-jobs (base; el comando se sobreescribe por cron) |
| Recursos por tarea | 1 vCPU / 4 GB (web), 1 vCPU / 2 GB (worker) |
Las task definitions siguen el patrón
erp-{entorno}-{web|worker|migrate|jobs}para todos los entornos (pro, stg, sandbox, erpaws).
Otros recursos
| Recurso | Nombre / ID |
|---|---|
| RDS (BD) | erp-pro.cn8ki0c2wmx1.eu-west-1.rds.amazonaws.com — db.t4g.medium, PostgreSQL 16.13, Single-AZ, backup 7 días |
| ALB | erp-alb (erp-alb-2143361097.eu-west-1.elb.amazonaws.com) |
| NAT Gateway | nat-0036d7ea52c0bf011 — EIP de salida 52.19.27.247 (IP que ven las APIs externas) |
| ElastiCache | erp-pro-valkey (Redis/Valkey — caché + Rack::Attack) |
| ECR | repositorio erp |
| CodeBuild | proyecto erp-build |
| Secrets Manager | erp/{entorno}/rails-env (variables de entorno), erp/{entorno}/google-cloud-credentials |
| Subnets de despliegue | subnet-0c2c4062be3739d25 (a), subnet-0dcf38f41ba7f9465 (b) |
| Security Group tareas | sg-0b72462dfcf29d749 |
| Bastión (acceso BD) | EC2 i-005c0b5152ae2efa8 (34.247.89.83) |
Despliegue (CodeBuild)
El despliegue es manual y guiado desde el repo del ERP, con un script interactivo que dispara CodeBuild. No hay CI/CD automático por push (se lanza a mano cuando se quiere desplegar).
1. Lanzar el despliegue: bin/aws-codebuild.sh
Script interactivo (se ejecuta en local, dentro del repo erp):
./bin/aws-codebuild.sh
Qué hace:
- Valida que estás en la cuenta AWS correcta (
935538483843). - Menú para elegir la rama (la actual /
master/ otra). - Menú multi-selección para elegir entornos (
pro,stg,sandbox,erpaws). - Por cada entorno, lanza
aws codebuild start-builddel proyectoerp-buildpasando variables de entorno override (IMAGE_TAG= SHA corto del commit,ENTORNO,SECRET_NAME,CLUSTER,TASK_FAMILY, servicios, subnets, SG…). - Espera a que terminen todos los builds y reporta OK / FAIL por entorno.
Seguimiento: consola de CodeBuild → proyecto erp-build → build history.
2. Qué hace el build: buildspec.yml
El proyecto CodeBuild erp-build ejecuta, por entorno:
| Fase | Acciones |
|---|---|
| pre_build | Login en ECR. Lee el secret erp/{entorno}/rails-env y genera los --build-arg a partir de su JSON. Usa la última imagen de ECR como --cache-from. |
| build | docker build -f Dockerfile.aws con los build-args → imagen etiquetada con el SHA del commit. |
| post_build | 1) docker push a ECR.2) Registra nuevas revisiones de las task defs web, worker, jobs (si existe) y migrate con la nueva imagen.3) Lanza la tarea de migraciones ( rails db:migrate), espera y aborta si el exit code ≠ 0.4) update-service de web y worker → despliegue rolling. |
Importante: las variables de entorno de la app viven en Secrets Manager (
erp/{entorno}/rails-env) y se inyectan comosecrets[](valueFrom) en las task definitions. Un secret env var sobreescribe cualquierENVdel Dockerfile. Al cambiar un secret, el Lambdaerp-secrets-redeploy(reglaerp-secrets-changed) fuerza un redeploy automático del servicio.
Variables de entorno (Secrets Manager)
Las variables de entorno de la aplicación se almacenan en AWS Secrets Manager con el patrón erp/{entorno}/rails-env (ej. erp/pro/rails-env, erp/stg/rails-env). Son un JSON clave-valor que se inyecta en las task definitions de ECS como secrets[] (valueFrom).
Cómo modificar una variable existente o añadir una nueva
- Editar el secret en Secrets Manager (consola o CLI):
# Ver el valor actual
aws secretsmanager get-secret-value \
--secret-id erp/pro/rails-env \
--profile interna --region eu-west-1 | jq -r .SecretString | jq .
# Actualizar (PUT) — pegar el JSON completo con la clave modificada/añadida
aws secretsmanager put-secret-value \
--secret-id erp/pro/rails-env \
--secret-string '{"CLAVE":"valor", ...}' \
--profile interna --region eu-west-1
- El redeploy es automático. Al hacer
PutSecretValueoUpdateSecretsobre cualquier secret que empiece porerp/, una regla de EventBridge (erp-secrets-changed) dispara el Lambdaerp-secrets-redeploy, que ejecutaforceNewDeploymenten los servicios web y worker del entorno correspondiente.
| Secret modificado | Servicios que se redespliegan |
|---|---|
erp/pro/rails-env | erp-pro-web-service + erp-pro-worker-service |
erp/stg/rails-env | erp-stg-web-service + erp-stg-worker-service |
erp/sandbox/rails-env | erp-sandbox-web-service + erp-sandbox-worker-service |
erp/erpaws/rails-env | erp-erpaws-web-service + erp-erpaws-worker-service |
El nuevo deploy tarda ~2-3 minutos (rolling update). Las tareas nuevas arrancan con el valor actualizado del secret; las antiguas se drenan sin interrupción.
Añadir una variable nueva (referenciada en task def)
Si la variable es completamente nueva (no existía antes en la task definition), además de añadirla al JSON del secret, hay que registrar una nueva revisión de la task definition que la referencie:
# 1. Añadir la clave al secret (paso anterior)
# 2. Registrar nueva revisión de la task def incluyendo el nuevo secret
# (o relanzar un deploy con bin/aws-codebuild.sh, que regenera las task defs)
En la práctica, si vas a hacer un deploy próximamente, basta con añadir la clave al secret — el buildspec ya lee todas las claves del JSON y las registra en la task def automáticamente.
Eliminar una variable
Para eliminar una variable que ya está referenciada en las task definitions:
- Primero quitar la referencia de la task definition (nueva revisión sin esa clave en
secrets[]). - Después eliminar la clave del JSON en Secrets Manager.
Si se hace al revés, las tareas nuevas fallarán con ResourceInitializationError porque ECS intenta resolver un secret que ya no existe.
Verificar el estado tras un cambio
# Comprobar que el deploy se lanzó (buscar "deployment" en estado PRIMARY con fecha reciente)
aws ecs describe-services --cluster erp-pro \
--services erp-pro-web-service erp-pro-worker-service \
--profile interna --region eu-west-1 \
| jq '.services[].deployments[] | {status, desiredCount, runningCount, createdAt}'
# Ver logs del Lambda para confirmar ejecución
aws logs tail /aws/lambda/erp-secrets-redeploy --since 10m \
--profile interna --region eu-west-1
Lanzar tareas puntuales (one-off)
Para ejecutar un comando puntual (consola, rake, runner) se usa run-task sobre el cluster, con la task def del entorno y la configuración de red:
aws ecs run-task --profile interna --region eu-west-1 \
--cluster erp-pro \
--task-definition erp-pro-web \
--launch-type FARGATE \
--network-configuration 'awsvpcConfiguration={subnets=[subnet-0823deb691ff89838,subnet-04dcf043484842a0a],securityGroups=[sg-0b72462dfcf29d749],assignPublicIp=DISABLED}' \
--overrides '{"containerOverrides":[{"name":"erp-pro-web","command":["bundle","exec","rails","runner","puts Reference.count"]}]}'
Los logs salen en CloudWatch Logs, grupo /ecs/erp-pro-web (stream ecs/erp-pro-web/<task-id>). Sirve para diagnósticos read-only sin tocar el servicio.
Tareas programadas (Scheduler)
Los crons se ejecutan con Amazon EventBridge Scheduler (no Heroku Scheduler). Cada schedule dispara un RunTask en el cluster erp-pro-jobs usando la task def base erp-pro-jobs, sobreescribiendo el comando por cron. Logs en /ecs/erp-pro-jobs.
Schedule (grupo erp-pro-cron) | Hora (UTC) | Comando |
|---|---|---|
erp-pro-cron-shipments-to-ftp-0100 | 01:00 | rails shipments:to_ftp 6 |
erp-pro-cron-to-bigquery-0230 | 02:30 | rake to_bigquery:all_with_active_storage |
erp-pro-cron-auto-download-0300 | 03:00 | rake shipments:auto_download_pending |
erp-pro-cron-send-btp-0400 | 04:00 | rake send_btp_shipments |
erp-pro-cron-remove-old-notifications-0500 | 05:00 | rails notification:remove_old_notifications |
erp-pro-cron-failed-shipments-alert-0900 | 09:00 | rails failed_shipments:daily_alert |
erp-pro-cron-to-bigquery-1300 | 13:00 | rake to_bigquery:all_with_active_storage |
erp-pro-cron-send-btp-1400 | 14:00 | rake send_btp_shipments |
Modificar / añadir un cron
update-schedule reemplaza la definición completa, así que hay que reenviar todos los campos. Para deshabilitar uno:
S=$(aws scheduler get-schedule --name <nombre> --group-name erp-pro-cron --profile interna --region eu-west-1 --output json)
aws scheduler update-schedule --name <nombre> --group-name erp-pro-cron \
--schedule-expression "$(echo "$S"|jq -r .ScheduleExpression)" \
--schedule-expression-timezone "$(echo "$S"|jq -r .ScheduleExpressionTimezone)" \
--flexible-time-window "$(echo "$S"|jq -c .FlexibleTimeWindow)" \
--target "$(echo "$S"|jq -c .Target)" \
--state DISABLED --profile interna --region eu-west-1
Monitorización de crons
El Lambda erp-pro-task-monitor revisa cada mañana las ejecuciones de la madrugada y envía un informe por email vía SNS (erp-pro-tasks-monitoring). ⚠️ Su criterio de fallo es sensible: puede marcar FALLO cuando el cron registra una excepción que en realidad maneja (p.ej. Net::SFTP::StatusException "file already exists" en auto_download, o un exit de un rake task). Ante un "FALLO", revisar el log real en /ecs/erp-pro-jobs antes de alarmar.
Integraciones externas y red de salida
Todo el tráfico saliente de las tareas Fargate sale por la NAT Gateway → EIP 52.19.27.247 (esa es la IP que hay que dar de alta en los whitelist de los proveedores). Las tareas corren en subredes privadas con assignPublicIp=DISABLED.
| Integración | Uso | Salida |
|---|---|---|
| BuyBox | Catálogo y emisión de códigos (gift) | NAT 52.19.27.247 |
| BTP (v1/v2) | Tracking y envíos físicos | NAT 52.19.27.247 |
| AGCOD | Gift cards de Amazon | NAT (auth AWS4) |
| Mailgun | Emails (api.eu.mailgun.net) | NAT |
| BigQuery / GCS | Export analítico (cron to_bigquery) | NAT |
| SFTP couriers | Ficheros de envíos (crons to_ftp / auto_download) | NAT |
| Sentry | Errores | salida directa |
Acceso a la base de datos
El RDS está en una VPC de la cuenta interna. El acceso administrativo (DBeaver / psql) se hace a través del bastión i-005c0b5152ae2efa8 (34.247.89.83) mediante túnel SSH (DBeaver soporta SSH tunnel nativo), restringido por Security Group a IPs concretas. Grafana (servidor kuma, otra cuenta) consume un datasource PostgreSQL contra esta BD.
Las credenciales de la BD y los accesos están en Secrets Manager / gestor de contraseñas — no se documentan aquí.
Notas operativas
Observabilidad
Grafana
Dashboards del ERP en: https://grafana.dekupleapp.com/dashboards/f/bfnthktnyeepsf/erp
Acceso: login con cuenta Google corporativa (@dekuple.es).
| Dashboard | Qué muestra |
|---|---|
| ALB - erp-alb | Requests, latencias, códigos HTTP, hosts no sanos |
| ERP - Códigos: Detalle y Seguimiento | Trazabilidad de códigos y canjes |
| ERP - Entregas y Operación (Resumen) | KPIs operativos de entregas |
| WAF - erp-waf | Reglas WAF, bloqueos, requests por país |
| Logs - erp-pro-web | Logs aplicación producción (web) |
| Logs - erp-pro-worker | Logs worker producción (delayed_job) |
| Logs - erp-stg-web | Logs staging web |
| Logs - erp-stg-worker | Logs staging worker |
| Logs - erp-sandbox-web | Logs sandbox web |
| Logs - erp-sandbox-worker | Logs sandbox worker |
| Logs - erp-erpaws-web | Logs erpaws web |
| Logs - erp-erpaws-worker | Logs erpaws worker |
Sentry
Excepciones de aplicación (Rails) se envían a Sentry automáticamente. Acceso por SSO en https://sentry.io → proyecto ERP.
CloudWatch
Los logs brutos están en CloudWatch Logs (región eu-west-1):
/ecs/erp-pro-web,/ecs/erp-pro-worker— producción/ecs/erp-pro-jobs— tareas cron/ecs/erp-stg-web,/ecs/erp-sandbox-web, etc. — otros entornos
Las alarmas CloudWatch (5xx, hosts no sanos, memoria) notifican vía SNS al tópico erp-pro-alerts.
- Variables de entorno: en
erp/{entorno}/rails-env(Secrets Manager), inyectadas comosecrets[]individuales en las task defs. Para eliminar una clave referenciada, primero quitar la referencia de todas las task defs (nuevas revisiones) y luego la clave del secret; al revés rompe el arranque (ResourceInitializationError). - Generación de PDF de bonos: usa
wkhtmltopdfcon Qt parcheado (instalado vía.deboficial enDockerfile.aws); elWKHTMLTOPDF_PATHdel secret debe apuntar al binario. - Autoescalado web: target-tracking por CPU (70%) y memoria (75%), min 3 / max 10 tareas.
- Alarmas: CloudWatch (5xx, hosts no sanos, memoria) → SNS
erp-pro-alerts(email). Sentry activo para excepciones de aplicación.