aws-wl-htg-pro
aws-wl-htg-pro es la cuenta AWS que aloja toda la carga de trabajo productiva del proyecto IRIS: las aplicaciones que ven los usuarios finales (iris-api, iris-chat, iris-frontend, myhtg), sus bases de datos, los servicios auxiliares (SuiteCRM, Galopin) y la plataforma de cluster que los soporta.
La infraestructura se gestiona como código en dos repositorios complementarios:
aws-infrastructure/aws-wl-htg-pro/— Terraform que define todo lo que vive en AWS: VPC, EKS, RDS, ElastiCache, S3, IAM, EC2.kubernetes-clusters/kubernetes-htg-pro/— manifiestos GitOps (Kustomize + Helm) de la plataforma del cluster: ArgoCD, Karpenter, External Secrets, observabilidad.
Las aplicaciones de negocio se reconcilian desde un tercer conjunto de repositorios (infra-iris-* con Helm charts) a través de ArgoCD; aquí se documenta únicamente la plataforma sobre la que corren.
Datos clave
| Concepto | Valor |
|---|---|
| Cuenta AWS | 200702211100 |
| Alias | aws-wl-htg-pro |
| Región | eu-south-2 (Spain) |
| Región ECR (compartida) | eu-west-1 |
| Acceso administrativo | AWS IAM Identity Center → AWSAdministratorAccess |
| Repositorio Terraform | aws-infrastructure/aws-wl-htg-pro |
| Repositorio GitOps | kubernetes-clusters/kubernetes-htg-pro |
Acceso
Todo el acceso humano se canaliza por AWS IAM Identity Center en eu-west-1. No existen usuarios IAM con credenciales estáticas.
aws sso login --profile htg-pro
aws sts get-caller-identity --profile htg-pro
Para operar el cluster:
aws eks --region eu-south-2 update-kubeconfig --name htg-pro --profile htg-pro
kubectl get nodes
El rol SSO AWSAdministratorAccess está asociado al cluster como AmazonEKSClusterAdminPolicy mediante una EKS Access Entry, de forma que el login SSO concede acceso cluster-admin sin necesidad de tocar el aws-auth ConfigMap.
Red
La VPC htg-pro ocupa el bloque 10.10.192.0/18 y se divide en tres tipos de subredes replicadas en las tres zonas de disponibilidad de eu-south-2:
| Tipo | Función | Tamaño |
|---|---|---|
| Públicas | ALBs, NAT Gateway, bastion | /24 |
| Privadas | Nodos EKS, pods y todas las cargas internas | /20 |
| Database | Subnet group dedicado a RDS/Aurora y ElastiCache, sin ruta a internet | /24 |
La VPC se aprovisiona con el módulo terraform-aws-modules/vpc/aws y habilita un único NAT Gateway (single_nat_gateway = true) para abaratar costes; toda la salida de tráfico de las subredes privadas pasa por ese NAT.
Las subredes están etiquetadas para autodescubrimiento del controlador:
kubernetes.io/role/elb = 1en las públicas ykubernetes.io/role/internal-elb = 1en las privadas, para que el AWS Load Balancer Controller pueda decidir dónde colocar los ALBs.karpenter.sh/discovery = htg-proen las privadas para que Karpenter sepa dónde lanzar nodos sin hardcodear IDs.
Cluster EKS
Control plane
| Parámetro | Valor |
|---|---|
| Nombre | htg-pro |
| Versión Kubernetes | 1.33 |
| Endpoint público | Habilitado |
| Modo de autenticación | API_AND_CONFIG_MAP |
| OIDC provider | oidc.eks.eu-south-2.amazonaws.com/id/796CA0EFA9D1593A9366F128AB595613 |
El control plane lo gestiona AWS. El cluster utiliza EKS Access Entries (modo API) para la autorización, lo que evita gestionar manualmente el ConfigMap aws-auth.
Arquitectura de cómputo
El cluster no tiene Node Groups gestionados ni Auto Scaling Groups: toda la capacidad de cómputo proviene de Karpenter sobre Fargate.
graph LR
subgraph Fargate
Karpenter[Karpenter controller]
end
subgraph EC2["Nodos EC2 (Bottlerocket)"]
Default["NodePool default<br/>(t3)"]
Compute["NodePool compute<br/>(c5/m5/c7/m7/c8/m8)"]
end
Karpenter -->|aprovisiona| Default
Karpenter -->|aprovisiona| Compute
Karpenter corre sobre un Fargate Profile que selecciona el namespace karpenter. Cuando hay pods pendientes, observa los requirements y lanza una instancia EC2 ajustada al tamaño exacto necesario. Cuando los nodos quedan vacíos o infrautilizados, los consolida.
Add-ons gestionados
Add-ons EKS aplicados por el módulo Terraform:
| Add-on | Notas |
|---|---|
vpc-cni |
Asigna IPs de la VPC a los pods |
kube-proxy |
Reglas iptables para Services |
coredns |
DNS interno |
eks-pod-identity-agent |
Habilita Pod Identity (alternativa a IRSA) |
aws-ebs-csi-driver |
Volúmenes EBS para PVCs |
aws-efs-csi-driver |
Volúmenes EFS (v2.3.1) |
EBS y EFS CSI usan Pod Identity Association (no IRSA) con los roles EBS_CSI_DriverRole y EFS_CSI_DriverRole respectivamente.
Add-ons de plataforma (EKS Blueprints)
El módulo aws-ia/eks-blueprints-addons/aws añade:
| Componente | Propósito |
|---|---|
| AWS Load Balancer Controller | Crea ALBs/NLBs a partir de Ingress y Service type=LoadBalancer |
| Metrics Server | Métricas de CPU/memoria para HPA y kubectl top |
| CloudWatch Agent | Envía métricas del cluster a CloudWatch |
AWS for Fluent Bit (0.1.35) |
Envía logs de los pods a CloudWatch Logs |
Fluent Bit está configurado con dos pipelines en paralelo. Los logs etiquetados como infra-iris-api-* se parsean como JSON (con Reserve_Data y lift del campo context) y se envían a un log group dedicado por namespace; el resto va a un log group común:
| Log group | Contenido |
|---|---|
/aws/eks/htg-pro/infra-iris-api/<namespace> |
Logs estructurados de los entornos iris-api |
/aws/eks/htg-pro/aws-fluentbit-logs |
Resto de logs de pods |
La retención está fijada a 14 días.
Plataforma GitOps (kubernetes-htg-pro)
Sobre el cluster EKS se instala una capa de plataforma definida en kubernetes-clusters/kubernetes-htg-pro. Está organizada por componentes bajo dos directorios:
platform/ # ArgoCD, External Secrets, Karpenter, Reloader, Prometheus Operator, Prometheus Adapter
observability/ # Prometheus, PMM, PerfectScale
Cada componente sigue el mismo patrón: values/values.yaml para Helm, resources/ para manifiestos raw y additional-resources/ para ExternalSecret y similares. ArgoCD es la excepción: usa Kustomize base/overlay porque tiene que arrancar antes de poder gestionarse a sí mismo.
ArgoCD
ArgoCD es el motor GitOps del cluster. Reconcilia tanto el resto de componentes de plataforma como las aplicaciones de negocio.
| Parámetro | Valor |
|---|---|
| URL | https://argocd.htg-express.com |
| Manifiesto base | argoproj/argo-cd stable/manifests/ha/install.yaml (HA) |
| Ingress | ALB internet-facing, TLS 1.3 (ELBSecurityPolicy-TLS13-1-2-2021-06) |
| Certificado | arn:aws:acm:eu-south-2:200702211100:certificate/b8332795-02f0-468b-beed-f429be577751 |
| Backend | HTTPS con condición Content-Type: application/grpc para argocd-grpc |
Bootstrap. ArgoCD es el único componente que no puede gestionarse a sí mismo en el primer despliegue. Se instala manualmente con:
kubectl apply -k platform/argocd/overlays
A partir de ahí, cualquier cambio en ArgoCD se hace por merge request.
Autenticación. El acceso local de admin está deshabilitado (admin.enabled: "false"). Todos los usuarios autentican vía AWS IAM Identity Center mediante un conector SAML configurado en Dex contra https://portal.sso.eu-west-1.amazonaws.com/.... Los grupos sincronizados desde el SAML assertion se mapean a roles RBAC de ArgoCD:
| Grupo SSO | Rol ArgoCD |
|---|---|
AWSAdministrators |
role:applications |
ExternalDevTeam |
role:applications |
HTGDevTeam |
role:applications |
Linube |
role:admin |
El rol role:applications da acceso completo a aplicaciones, applicationsets, logs y exec scoped al proyecto applications.*, y read-only sobre proyectos, repos y clusters.
Acceso a Secrets Manager. ArgoCD lee algunas configuraciones (por ejemplo el repo de Helm charts) desde AWS Secrets Manager. Para ello se ha aprovisionado el rol IAM ArgoCD con permisos secretsmanager:GetSecretValue sobre argoCD/* y se asocia al ServiceAccount argocd-secret-manager vía IRSA. El SecretStore aws-secrets-manager-store enlaza External Secrets con ese rol.
Acceso a ECR. Los Helm charts viven en el ECR compartido 203965864736.dkr.ecr.eu-west-1.amazonaws.com (cuenta CI/CD). El acceso es cross-account sin credenciales estáticas:
- Un
ECRAuthorizationTokende External Secrets corre sobre el ServiceAccountargocd-aws-ac-htg-cicd-ecr, que tiene anotado el rolarn:aws:iam::203965864736:role/ecr-access-shared-htg-pro. - Cada hora el generator emite un token y External Secrets lo materializa como un
Secretaws-ac-htg-cicd-ecrcon la etiquetaargocd.argoproj.io/secret-type: repository. - ArgoCD lo detecta automáticamente y lo usa como credencial para pull de los charts OCI.
El rol cross-account está definido en la cuenta CI/CD aws-ac-htg-cicd (913305982008) bajo aws-infrastructure/aws-ac-htg-cicd/, en el módulo ecr_access_htg_pro.
External Secrets Operator
Se despliega con valores por defecto. Materializa secretos desde AWS Secrets Manager en Secret nativos de Kubernetes para que las aplicaciones no se acoplen a AWS. Los SecretStore se definen por aplicación y autentican con IRSA, sin credenciales estáticas en el cluster.
Karpenter (resources)
El controlador de Karpenter lo despliega Terraform (helm_release.karpenter versión 1.8.2), pero los recursos que definen cómo aprovisionar nodos viven aquí.
EC2NodeClass default: usa el alias bottlerocket@latest para la AMI, asigna a las instancias el IAM role htg-pro y descubre subredes y SGs por el tag karpenter.sh/discovery: htg-pro.
NodePool default — workloads generales:
- Solo arquitectura
amd64sobre hypervisor Nitro - Familia
t3(burstables) enspotyon-demand - Límite de 64 vCPU
expireAfter: 720h(rotación cada 30 días)- Consolidación
WhenEmptyOrUnderutilizedcon margen de 1 minuto y disruption budget del 10 %
NodePool compute — workloads intensivos en CPU:
- Familias
c5,m5,c7a,c7i,m7a,m7i,c8a,c8i,m8a,m8i - Mismo límite y políticas de disruption que
default - Taint
karpenter.sh/nodepool=compute:NoSchedule, de forma que solo entran cargas que lo tolereran explícitamente
Los pods que necesitan capacidad de cómputo declaran su tolerancia en sus valores Helm.
Reloader
Despliegue con valores Helm por defecto. Observa los namespaces y reinicia automáticamente cualquier Deployment, StatefulSet o DaemonSet cuyo ConfigMap o Secret asociado cambie. Esto elimina la necesidad de un kubectl rollout restart cuando External Secrets sincroniza nuevas credenciales.
Prometheus Operator y Prometheus Adapter
El cluster ejecuta Prometheus para alimentar HPAs con métricas personalizadas (el iris-api y iris-chat escalan por número de procesos PHP-FPM, no por CPU).
- Operator instalado desde el manifest oficial
prometheus-operator/prometheus-operator?ref=v0.90.1con su propio namespaceprometheus-operator. - Prometheus: 1 réplica, 24h de retención, 10 GiB sobre
gp2, descubrePodMonitor/ServiceMonitorfiltrados por la etiquetaprometheus: main. - Prometheus Adapter: expone una métrica derivada
phpfpm_process_utilizationcalculada como(100 / phpfpm_total_processes) * phpfpm_active_processes, que es la que consumen los HPAs.
Observabilidad externa
PMM (Percona Monitoring and Management)
Monitoriza los clusters Aurora y las instancias RDS de la cuenta.
| Parámetro | Valor |
|---|---|
| URL | https://pmm.htg-express.com |
| Imagen | percona/pmm-server (latest gestionada por el operator) |
| Almacenamiento | 25 GiB sobre gp2 |
| ServiceAccount | pmm-service-account → arn:aws:iam::200702211100:role/PMM |
El rol PMM permite a la herramienta listar instancias RDS, leer métricas de CloudWatch, consumir logs de Enhanced Monitoring (RDSOSMetrics) y recuperar el secreto pmm-* que contiene la contraseña inicial.
PerfectScale
Recomendaciones continuas de rightsizing y optimización de coste sobre los workloads del cluster.
| Parámetro | Valor |
|---|---|
| ServiceAccount | perfectscale → arn:aws:iam::200702211100:role/Perfectscale |
| Cluster name | htg-pro |
El secreto perfectscale-* se sincroniza con External Secrets a partir del rol IAM dedicado.
Bases de datos
Toda la persistencia transaccional vive en el subnet group de base de datos de la VPC. Ninguna instancia es accesible directamente desde internet; se accede por la VPC (cluster EKS) o por túnel SSH a través del bastion.
Aurora MySQL — iris-api-pro
Cluster Aurora Serverless v2 que respalda al backend principal iris-api. Es el dato más crítico de la cuenta.
| Parámetro | Valor |
|---|---|
| Engine | Aurora MySQL 8.0.mysql_aurora.3.11.1 |
| Topología | 2 instancias db.serverless (writer + reader) |
| Escalado | 2 – 32 ACUs |
| Cifrado en reposo | Sí (KMS) |
| Backup | 14 días, ventana 02:00-03:00 |
| Deletion protection | Habilitado |
| Parameter group | app-cmsws-opta-pro-params (performance_schema=1, TZ Europe/Paris) |
| Master password | Gestionado por AWS (manage_master_user_password) en Secrets Manager |
Los ingresos están restringidos al CIDR privado de la VPC (cluster EKS) y al SG del bastion. La password rota automáticamente y la consumen las aplicaciones vía External Secrets sobre el secreto infraIrisApi/pro-*.
RDS MySQL — galopin-pro
Base de datos del servicio Galopin (consultada por iris-api).
| Parámetro | Valor |
|---|---|
| Engine | MySQL 8.0.45 |
| Tipo | db.t4g.xlarge |
| Storage | 20 – 50 GiB gp3 |
| Backups | Sí (retención por defecto) |
| Deletion protection | Habilitado |
| Publicly accessible | Sí (sigue restringido por SG) |
| Owner tag | linube |
Los ingresos al SG galopin-pro-db están restringidos al SG principal del cluster EKS y al SG del bastion.
RDS MySQL — suite-crm-pro
Base de datos de la instancia productiva de SuiteCRM, que corre sobre la EC2 bs1264.
| Parámetro | Valor |
|---|---|
| Engine | MySQL 8.4.7 |
| Tipo | db.t4g.small |
| Storage | 20 – 30 GiB gp3 |
| Deletion protection | Habilitado |
Solo se permite acceso desde el SG del bastion y desde el SG de bs1264.
Acceso desde local
ssh -p 27 \
-L 3306:<endpoint-cluster>.cluster-XXXX.eu-south-2.rds.amazonaws.com:3306 \
-N <usuario>@<eip-bastion>
Una vez establecido el túnel, conectar a 127.0.0.1:3306 con las credenciales recuperadas desde Secrets Manager.
Cache y colas (ElastiCache Redis)
iris-api consume dos clusters Redis distintos por motivos operativos: uno para caché efímera y otro para colas de trabajo persistentes. Las políticas de eviction están afinadas a cada uso.
iris-api-pro-cache
Caché de respuestas HTTP, datos calculados y similares.
| Parámetro | Valor |
|---|---|
| Engine | Redis 7.1 |
| Tipo de nodo | cache.t3.small |
| Réplicas | 2 (multi-AZ, failover automático) |
| Encriptación en reposo | Sí |
| Encriptación en tránsito | No (favorece latencia; el dato no es sensible) |
| Política de eviction | allkeys-lru |
| Ventana de mantenimiento | sun:05:00-sun:06:00 |
iris-api-pro-queues
Colas de trabajos (worker) y sesiones; aquí sí importa la durabilidad.
| Parámetro | Valor |
|---|---|
| Engine | Redis 7.1 |
| Tipo de nodo | cache.m7g.large |
| Réplicas | 2 (multi-AZ, failover automático) |
| Encriptación en reposo | Sí |
| Encriptación en tránsito | Sí |
| Política de eviction | volatile-lru |
| Snapshots | Retención 7 días, ventana 04:00-05:00 |
| Ventana de mantenimiento | sun:06:00-sun:07:00 |
Los SGs iris-api-pro-redis-cache e iris-api-pro-redis-queues solo aceptan conexiones desde el SG del cluster EKS y desde el SG del bastion.
Almacenamiento (S3)
| Bucket | Propósito |
|---|---|
iris-api-files-pro |
Adjuntos y ficheros gestionados por iris-api (Laravel) |
iris-chat-pro |
Adjuntos del módulo iris-chat |
Ambos buckets son privados. Las aplicaciones acceden a ellos vía IRSA con los roles InfraIrisApiPro e InfraIrisChatPro (ver sección siguiente).
Identidades IAM
Todas las cargas que necesitan permisos en AWS los obtienen vía IRSA (IAM Roles for Service Accounts), nunca con credenciales estáticas. El trust policy de cada rol está limitado al sub exacto del ServiceAccount.
| Rol IAM | ServiceAccount | Recursos a los que accede |
|---|---|---|
ArgoCD |
argocd/argocd-secret-manager |
Secrets Manager (argoCD/*) |
InfraIrisApiPro |
infra-iris-api-pro/infra-iris-api-pro |
Secrets Manager (infraIrisApi/pro-*), S3 (iris-api-files-pro, iris-chat-pro) |
InfraIrisChatPro |
infra-iris-chat-pro/infra-iris-chat-pro |
Secrets Manager (infraIrisChat/pro-*), S3 (iris-chat-pro) |
Perfectscale |
perfectscale/perfectscale |
Secrets Manager (perfectscale-*) |
PMM |
pmm/pmm-service-account |
Secrets Manager (pmm-*), rds:DescribeDBInstances, CloudWatch metrics, RDSOSMetrics logs |
EBS_CSI_DriverRole |
kube-system/ebs-csi-controller-sa (Pod Identity) |
EBS CSI |
EFS_CSI_DriverRole |
kube-system/efs-csi-controller-sa (Pod Identity) |
EFS CSI |
Además, los ServiceAccount infra-iris-*-pro-ecr (uno por aplicación) tienen anotado el rol cross-account arn:aws:iam::203965864736:role/ecr-access-shared-htg-pro para hacer pull de las imágenes desde el ECR de la cuenta CI/CD.
Instancias EC2 auxiliares
Dos instancias EC2 viven fuera del cluster, en subredes públicas con EIP:
bs1256 — Bastion
| Parámetro | Valor |
|---|---|
| AMI | AlmaLinux 9.7 (ami-04dbfc31cc9bc45bb) |
| Tipo | t3.small |
| Disco | 50 GiB gp3 |
| EIP | prevent_destroy = true |
| Puerto SSH | 27 (no estándar) |
| Acceso | Cuentas nominales + clave personal |
| IMDS | v2 obligatorio |
El bastion es el único punto de entrada SSH a la red. Está en tres SGs:
bastion: egress total (4/6, TCP/UDP).linube_access: ingress SSH/monitorización desde la operación de Linube.staff_access: SSH al puerto 27 desde cualquier origen (controlado por usuario + clave).
bs1264 — SuiteCRM productivo
| Parámetro | Valor |
|---|---|
| AMI | AlmaLinux 9.7 |
| Tipo | t3.xlarge |
| Disco | 50 GiB gp3 |
| EIP | prevent_destroy = true |
| Puertos abiertos | 80 y 443 desde internet |
| Base de datos | RDS suite-crm-pro |
SuiteCRM no encajaba en el modelo Kubernetes/Helm del resto del stack, así que se mantiene como una EC2 tradicional con su propio SG y EIP.
Integración con la cuenta CI/CD
La cuenta aws-ac-htg-cicd (913305982008) aloja el ECR compartido 203965864736.dkr.ecr.eu-west-1.amazonaws.com donde se publican todas las imágenes y los Helm charts del proyecto IRIS. Los workloads de esta cuenta los consumen sin credenciales estáticas mediante un rol cross-account:
- En la cuenta CI/CD se define
ecr-access-shared-htg-pro(móduloecr_access_htg_proenaws-infrastructure/aws-ac-htg-cicd/), con una trust policy que admite a los ServiceAccountargocd-aws-ac-htg-cicd-ecr,infra-iris-api-pro-ecr,infra-iris-chat-pro-ecr,infra-iris-frontend-pro-ecreinfra-iris-myhtg-pro-ecr. - Cada uno de esos ServiceAccount en
htg-proestá anotado coneks.amazonaws.com/role-arnapuntando al rol cross-account. - ArgoCD se autentica además vía un
ECRAuthorizationTokenque renueva la credencial cada hora (ver sección ArgoCD).