Módulo 6: Finops For Tokens

8. Proyecto: el *cost gate* de Andes Cargo, extendido a tokens

Descripción

Este proyecto reúne todo lo que las lecciones 1 a 7 construyeron —bedrock-budget.rego, la calculadora con presupuesto integrada como step, el tagging completo de la carga de IA, la demostración de que el gate corta antes del apply, el criterio numérico de On-Demand vs. Provisioned Throughput— y confirma, con corridas reales de act, que los tres jobs heredados de finops-and-cost-guardrails-guidecost-estimate, cost-check, cost-tags— evalúan correctamente el Terraform y el presupuesto de tokens nuevos, corriendo en paralelo al security gate del Módulo 5, sin fusionarse nunca con él. Ningún job nuevo. Ningún carril nuevo. La prueba final de la tesis completa de este módulo.

Conexión con el módulo

El mismo ejercicio de auditoría dirigida que cada proyecto de cierre de módulo de este ecosistema practica: no "¿escribiste una política y extendiste un script?", sino "¿puedes demostrar, con evidencia ejecutada, que el gate heredado evalúa esas piezas nuevas exactamente como evalúa cualquier otra?". GENAI-COST-PROFILE.md (Módulo 2) tiene una fila que asigna a este módulo la responsabilidad de convertir su supuesto de volumen en un gate ejecutable — este proyecto es la prueba de que esa fila se cumplió.


Paso 1 — El estado del proyecto, antes de correr nada

andes-cargo-infra/ con todo lo de este módulo integrado:

find . -maxdepth 2 -type f -newer THREAT-MODEL.md -not -path "./.terraform/*" | sort

Qué esperar (literal — los archivos que este módulo agregó o modificó):

./.github/workflows/ci.yml
./.github/workflows/token-budget-demo.yml
./cost-policy/bedrock-budget.rego
./locals.tf
./bedrock.tf
./scripts/bedrock_cost_estimate.py
./scripts/test_bedrock_cost_estimate.py

Siete archivos, cinco de ellos ya heredados y solo tocados (ci.yml, locals.tf, bedrock.tf, bedrock_cost_estimate.py, test_bedrock_cost_estimate.py), dos genuinamente nuevos (cost-policy/bedrock-budget.rego, token-budget-demo.yml). THREAT-MODEL.md, policy/, y cualquier archivo del security gate del Módulo 5 no aparecen en esta lista — este módulo nunca los tocó.


Paso 2 — terraform plan, el proyecto completo con el tagging de esta guía incluido

terraform validate -no-color
terraform plan -input=false -no-color -out=tfplan

Qué esperar (literal — ejecutado para escribir esta lección):

Success! The configuration is valid.

Plan: 17 to add, 0 to change, 0 to destroy.

Diecisiete, el mismo número que el Módulo 5 ya confirmó — este módulo nunca agregó ni quitó ningún recurso, solo tags sobre dos que ya existían (lección 5).


Paso 3 — conftest, la biblioteca completa de cost-policy/

terraform show -json tfplan > tfplan.json
conftest test tfplan.json -p cost-policy/

Qué esperar (literal — ejecutado para escribir esta lección):

3 tests, 3 passed, 0 warnings, 0 failures, 0 exceptions

Los tres tests de la lección 5 de este módulo, todos pasando contra el proyecto completo: la regla de required-cost-tags.rego (heredada, ahora limpia gracias al tagging completo de la lección 5) y las dos de bedrock-budget.rego (nuevas de este módulo). Confirma también, una última vez, que policy/ (seguridad) sigue completamente intacto:

comm -12 <(ls policy/ | sort) <(ls cost-policy/ | sort)

Qué esperar (literal — salida vacía):

Cero archivos compartidos, la misma garantía de la lección 3, verificada una última vez con el proyecto en su estado final.


Paso 4 — act pull_request -j cost-tags: verde, real, independiente

act pull_request -e .github/act-events/pr-event.json -j cost-tags

Qué esperar (literal — ejecutado para escribir esta lección, con Docker real y la imagen catthehacker/ubuntu:act-latest):

[ci/cost-tags] ⭐ Run Set up job
[ci/cost-tags]   ✅  Success - Set up job
[ci/cost-tags] ⭐ Run Main Check out andes-cargo-infra
[ci/cost-tags]   ✅  Success - Main Check out andes-cargo-infra [10.474493750s]
[ci/cost-tags] ⭐ Run Main Set up Terraform
[ci/cost-tags]   ✅  Success - Main Set up Terraform [2.382368s]
[ci/cost-tags] ⭐ Run Main Terraform init
[ci/cost-tags]   ✅  Success - Main Terraform init [12.872904625s]
[ci/cost-tags] ⭐ Run Main Terraform plan
[ci/cost-tags]   | Plan: 17 to add, 0 to change, 0 to destroy.
[ci/cost-tags]   ✅  Success - Main Terraform plan [4.653669500s]
[ci/cost-tags] ⭐ Run Main Convert plan to JSON
[ci/cost-tags]   ✅  Success - Main Convert plan to JSON [2.078965667s]
[ci/cost-tags] ⭐ Run Main Install conftest
[ci/cost-tags]   ✅  Success - Main Install conftest [1.807026542s]
[ci/cost-tags] ⭐ Run Main Cost tag policy check (conftest -p cost-policy/)
[ci/cost-tags]   | 3 tests, 3 passed, 0 warnings, 0 failures, 0 exceptions
[ci/cost-tags]   ✅  Success - Main Cost tag policy check (conftest -p cost-policy/) [314.555458ms]
[ci/cost-tags] ⭐ Run Complete job
[ci/cost-tags]   ✅  Success - Complete job
[ci/cost-tags] 🏁  Job succeeded

Job succeeded — dentro de un contenedor Docker efímero, terraform init/plan/show -json desde cero, conftest descargado desde cero, y el mismo 3 tests, 3 passed del Paso 3 confirmado otra vez. cost-tags nunca necesitó ningún token de Infracost ni ningún segundo checkout — la misma razón exacta por la que este job es el más simple de verificar de punta a punta bajo act local, ya establecida por finops-and-cost-guardrails-guide Módulo 4, lección 7.


Paso 5 — cost-check: la pieza nueva, verificada donde sí puede correr

cost-estimate/cost-check (heredados, finops-and-cost-guardrails-guide Módulo 3) tienen una limitación real y ya documentada, no nueva de este módulo: el segundo checkout de cost-estimate (ref: ${{ github.event.pull_request.base.ref }}) necesita un remoto real de Git, que act local, sin un repositorio real de GitHub detrás, no puede satisfacer (finops-and-cost-guardrails-guide Módulo 3, lección 6). Como cost-check tiene needs: cost-estimate, correrlo aislado con act pull_request -j cost-check también ejecuta cost-estimate primero, y falla en el mismo punto:

act pull_request -e .github/act-events/pr-event.json -j cost-check

Qué esperar (literal — ejecutado para escribir esta lección; el mismo límite, ya documentado, de finops-and-cost-guardrails-guide Módulo 3, lección 6):

[ci/cost-estimate]   | [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +refs/heads/main*:refs/remotes/origin/main* +refs/tags/main*:refs/tags/main*
[ci/cost-estimate]   | The process '/usr/bin/git' failed with exit code 1
[ci/cost-estimate]   ❌  Failure - Main Check out the base branch (before the change) [40.539506084s]
[ci/cost-estimate] 🏁  Job failed
Error: Job 'cost-estimate' failed

Esta falla no es del step nuevo de la lección 4Enforce the token budget— ni de nada que este módulo haya construido: ocurre antes, en el segundo checkout heredado, exactamente el mismo punto exacto que finops-and-cost-guardrails-guide ya documentó como limitación de act local sin repositorio remoto real. La lección 6 de este módulo ya resolvió esto con la misma técnica exacta que esa guía hermana usó: un workflow de prueba dedicado, que aísla la lógica del step nuevo sin necesitar el checkout que falla aquí:

act workflow_dispatch -j token-budget-check-demo -W .github/workflows/token-budget-demo.yml

Qué esperar (literal — el mismo resultado ya confirmado en la lección 6):

[token-budget-demo/token-budget-check-demo] ⭐ Run Main Enforce the token budget (scripts/bedrock_cost_estimate.py)
[token-budget-demo/token-budget-check-demo]   | TOTAL MONTHLY COST                           $0.42
[token-budget-demo/token-budget-check-demo]   ✅  Success - Main Enforce the token budget (scripts/bedrock_cost_estimate.py) [150.438625ms]
[token-budget-demo/token-budget-check-demo] 🏁  Job succeeded

El ledger honesto de esta pieza, en una tabla:

PiezaEstado en este entorno de escritura
ci.yml extendido con el step del presupuesto de tokens, YAML completoReal — el archivo completo, correcto, sin errores de sintaxis
cost-tags (Paso 4)Literal, act pull_request -j cost-tags realJob succeeded, sin ninguna limitación
Segundo checkout de cost-estimate (ref: explícito a la rama base)Limitación heredada y ya documentada de act local, sin remoto real — funciona sin problema en GitHub.com real (finops-and-cost-guardrails-guide Módulo 3, lección 6)
El step nuevo (Enforce the token budget) en síLiteral, probado dos veces con evidencia real — directamente (Módulo 6, lección 4) y vía token-budget-demo.yml (Módulo 6, lección 6), PASS y FAIL
infracost scan con token realPendiente de una cuenta real de Infracost — el mismo límite exacto que finops-and-cost-guardrails-guide Módulo 2/3 ya documentaron, sin ningún cambio nuevo de este módulo

Paso 6 — En paralelo al security gate del Módulo 5, nunca fusionado

ci.yml, al cierre de este módulo, tiene seis jobs — los mismos seis que finops-and-cost-guardrails-guide ya dejó, con el step de tokens agregado dentro de uno de ellos:

   CARRIL DE SEGURIDAD (Módulo 5)         CARRIL DE COSTO (este módulo)

   policy-check                           cost-estimate
        │                                      │
        ▼                                      ▼
   iac-scan                               cost-check
        │                                 (+ el step nuevo de tokens,
        ▼                                  Módulo 6, lección 4)
   verify-artifact

                                           cost-tags
                                           (evalúa bedrock-budget.rego
                                            automáticamente, sin
                                            ningún cambio de YAML)

   Sin needs:: cruzado en NINGUNA dirección entre los dos carriles

policy-check/iac-scan/verify-artifact (Módulo 5) no mencionan a cost-estimate/cost-check/cost-tags en absoluto, y viceversa — la misma arquitectura de dos carriles independientes que finops-and-cost-guardrails-guide Módulo 4, lección 7 ya estableció en YAML real, extendida ahora con la pieza de tokens dentro del carril que ya existía, nunca como un tercer carril nuevo. cost-tags (Paso 4), el job más simple de verificar de punta a punta, ya lo demuestra: corre completamente aislado, sin needs: hacia ningún job de seguridad ni de costo, Job succeeded en menos de treinta segundos.


Cómo defender este trabajo en una entrevista

"¿Por qué no construyeron un job de CI específico para presupuestar tokens?" — la respuesta vive en la lección 1 de este módulo: un cost gate bien diseñado generaliza a dimensiones de costo que nadie tenía en mente cuando se construyó. cost-tags no distingue el type de un recurso de Terraform —evalúa cualquier archivo .rego que encuentre en cost-policy/—; cost-check es, simplemente, el lugar correcto para cualquier verificación de "¿esto excede lo aceptable?", sin importar de qué eje de costo se trate. Construir un job nuevo habría sido la señal de que el gate original estaba mal diseñado — la prueba de este proyecto es exactamente lo contrario.

"¿Cómo saben que la política nueva realmente funciona, no solo que existe?" — la respuesta vive en la lección 3: un FAIL real, honesto, contra el estado real del proyecto (bedrock.tf sin el tag todavía), seguido de un PASS real en la lección 5, después de corregir el HCL — nunca "debería funcionar", siempre "corrió, y esto fue lo que pasó".

"¿Qué pasa si Infracost, como en el Módulo 2, no puede resolver el costo de una carga de IA?" — la respuesta vive en la lección 2: la razón técnica exacta (ningún recurso de Terraform representa una invocación), y la solución que este módulo construyó específicamente para ese vacío — una calculadora propia, corrida como step independiente, nunca dependiente de que Infracost resuelva algo que estructuralmente no puede.


El proyecto completo, en un vistazo

  andes-cargo-infra/
  ├── cost-policy/
  │   ├── required-cost-tags.rego         (heredado, finops M4 -- sin cambios)
  │   └── bedrock-budget.rego             ← nuevo de este módulo (L3)
  ├── policy/                             (heredado, M5 -- sin ningún cambio)
  ├── locals.tf                           (M6.5: CostCenter/Owner completos +
  │                                         ai_workload_tags nuevo)
  ├── bedrock.tf                          (M6.5: tags = local.ai_workload_tags
  │                                         en 2 de sus recursos)
  ├── scripts/
  │   ├── bedrock_cost_estimate.py        (M2.7, extendido L4: --monthly-budget)
  │   ├── test_bedrock_cost_estimate.py   (M2.7, +2 tests L4 -- 14/14)
  │   └── check-cost-threshold.sh         (heredado, finops M3 -- sin cambios)
  ├── GENAI-COST-PROFILE.md               (M2.8 -- referenciado, no editado
  │                                         permanentemente; L6 lo cita como
  │                                         escenario propuesto)
  └── .github/workflows/
      ├── ci.yml
      │   ├── policy-check    (heredado, M5 -- sin cambios)
      │   ├── iac-scan        (heredado, M5 -- sin cambios)
      │   ├── verify-artifact (heredado, M5 -- sin cambios)
      │   ├── cost-estimate   (heredado, finops M3 -- sin cambios)
      │   ├── cost-check      (finops M3 + 1 step nuevo, L4)
      │   └── cost-tags       (heredado, finops M4 -- evalúa
      │                        bedrock-budget.rego automáticamente)
      └── token-budget-demo.yml           ← nuevo de este módulo (L6)

Errores comunes

Agregar un cuarto carril "por si acaso" antes de confirmar que los tres jobs de costo existentes bastan (de anticipar una necesidad que no existe). Qué pasa: alguien, al preparar este proyecto, escribe un job nuevo en ci.yml "para estar seguro". Cómo detectarlo: si tu ci.yml, al llegar a este proyecto, tiene más de seis jobs. Cómo corregirlo: los Pasos 3 a 5 de esta lección son la prueba —corrida de verdad, no supuesta— de que los tres jobs de costo heredados, con un solo step nuevo, bastan.

Confundir la limitación del segundo checkout (Paso 5) con un error de este módulo, y tratar de "arreglarla" reescribiendo cost-estimate (de diagnóstico incorrecto). Qué pasa: alguien ve Job failed en cost-estimate y concluye que este módulo rompió algo. Cómo detectarlo: si tu instinto ante ese FAIL es modificar el YAML de cost-estimate. Cómo corregirlo: el Paso 5 de esta lección lo documenta con precisión — es la misma limitación, ya conocida, que finops-and-cost-guardrails-guide Módulo 3, lección 6 documentó antes de que este módulo existiera. No es un problema nuevo, y no debería "arreglarse" tocando un job que ningún proyecto de esta guía modifica.

Presentar solo el resultado del Paso 4 (cost-tags, verde) y omitir el ledger honesto del Paso 5 (de pulir en exceso el resultado). Qué pasa: alguien, al preparar este proyecto como pieza de portfolio, muestra únicamente la corrida exitosa y evita mencionar la limitación de cost-estimate. Cómo detectarlo: si tu presentación de este proyecto no menciona en ningún lugar por qué act pull_request sin -j no correría los seis jobs de punta a punta en este entorno. Cómo corregirlo: la tabla del Paso 5 existe exactamente para esto — la misma disciplina de honestidad de ejecución que cada capstone de este ecosistema exige, nunca ocultar un límite real detrás de un resultado parcial más favorable.


Ejercicios

Ejercicio 1 — Corre act pull_request -j cost-tags tú mismo, y predice el número exacto de tests antes de correrlo, contando las reglas de los dos archivos de cost-policy/. Verifica tu predicción contra el resultado real.

Ver solución

Tres: una regla en required-cost-tags.rego (heredada) más dos en bedrock-budget.rego (Módulo 6, lección 3) — la misma cuenta que el Paso 3 de esta lección ya confirmó con la corrida real (3 tests, 3 passed).

Ejercicio 2 — Explica por qué cost-check (con el step nuevo de tokens) y cost-tags (con bedrock-budget.rego) están en jobs distintos, aunque ambos verifican algo relacionado con la carga de IA. Retoma el criterio de "una responsabilidad, un job" del Módulo 6, lección 4, Ejercicio 3.

Ver solución

cost-check responde "¿este cambio va a costar más de lo aceptable?" (aplicado a dos ejes: infraestructura vía Infracost, y volumen de tokens vía la calculadora). cost-tags responde una pregunta completamente distinta: "¿este gasto se puede atribuir a alguien?". Aunque ambas preguntas terminan aplicándose, en parte, a la misma carga de IA, son preguntas de naturaleza diferente —una sobre magnitud, otra sobre asignación—, la misma distinción que ya separó cost-check de cost-tags desde que finops-and-cost-guardrails-guide los diseñó como jobs independientes, sin needs: cruzado, en su Módulo 4.

Ejercicio 3 — Diseña, en prosa, la corrida de act pull_request que esperarías si alguien introdujera, a propósito, el error de la lección 3 (Workload ausente) en bedrock.tf antes de abrir este PR. ¿Cuál job fallaría, y el security gate del Módulo 5 se vería afectado?

Ver solución

cost-tags fallaría —específicamente en el step Cost tag policy check (conftest -p cost-policy/), con el mismo mensaje de FAIL que la lección 3 de este módulo ya mostró (missing required cost allocation tag "Workload")—, terminando en 🏁 Job failed. El security gate del Módulo 5 (policy-check/iac-scan/verify-artifact) no se vería afectado en absoluto: sin ningún needs: cruzado entre los dos carriles, esos tres jobs seguirían corriendo, y probablemente terminando en verde, exactamente en paralelo — la misma independencia entre carriles que el Paso 6 de esta lección ya diagramó, y la razón exacta por la que un Pull Request puede fallar en costo sin que eso oscurezca, ni retrase, la verificación de seguridad del mismo cambio.


Resumen y siguiente paso

Este proyecto confirmó, con corridas reales de act y conftest, la tesis completa de este módulo: cost-policy/, extendido con bedrock-budget.rego, pasa limpio (3 tests, 3 passed) contra el proyecto completo; cost-tags corre verde de punta a punta bajo act pull_request (Job succeeded); el step nuevo del presupuesto de tokens, probado dos veces con evidencia real (directamente y vía token-budget-demo.yml), pasa a $0,42 y falla a $8,40; y el carril de costo completo sigue corriendo en paralelo al security gate del Módulo 5, sin ningún needs: cruzado, exactamente como el Módulo 1 de esta guía prometió. El único límite real —el segundo checkout de cost-estimate bajo act local— es una limitación heredada, ya documentada por finops-and-cost-guardrails-guide antes de que este módulo existiera, no un hallazgo nuevo ni un defecto de este trabajo.

Antes de cerrar este módulo deberías poder: correr act pull_request -j cost-tags contra este proyecto sin mirar ninguna lección anterior; explicar, con evidencia ejecutada, por qué ningún job nuevo era necesario; y defender, frente a las tres preguntas de entrevista de esta lección, por qué "extender", no "reconstruir", fue la decisión correcta de principio a fin.

Con esto, el Módulo 6 de genai-on-aws-production-guide queda completo: el presupuesto de una carga usage-based de tokens, verificado con Rego real, una calculadora propia integrada como gate determinista, tagging de asignación completo (incluido un hallazgo honesto sobre CostCenter/Owner), la prueba en ambas direcciones de que el gate corta antes del apply, el criterio numérico exacto de cuándo Provisioned Throughput sí sería la decisión correcta, y el cost gate completo, verde en su pieza más simple de verificar, en paralelo al security gate del Módulo 5. El Módulo 7 abre la siguiente capa: el vocabulario SLI/SLO de sre-and-incident-response-guide, aplicado por primera vez a métricas específicas de una carga de IA.

Recursos

  1. nektosact.com — User Guide — referencia completa de act, incluida la ejecución de un job aislado con -j.
  2. finops-and-cost-guardrails-guide, Módulo 4, lección 7 (07-hands-on-wiring-cost-tags-into-ci-yml.md) — el origen exacto de cost-tags, el job que este proyecto extiende sin cambios de YAML.
  3. finops-and-cost-guardrails-guide, Módulo 3, lección 6 — el origen documentado de la limitación del segundo checkout, citada de nuevo en el Paso 5 de esta lección.
  4. Este módulo, lecciones 3 a 6 — el origen de cada pieza que este proyecto integra al gate.