El orden importa
Una línea
La VPC de la API ya está en código. Marta añade lo que le faltaba: la base de datos de la API, con su grupo de seguridad y su contraseña, y lo que depende de ella: la propia API, que la usa, y una alarma que vigila sus conexiones.
El saldo de Ana sigue en Cuentasver en el mapa, en el CPD. En esta base de datos está lo que la app guarda por su cuenta: el móvil que Ana registró, sus destinatarios de confianza, sus avisos. Poca cosa, hasta que desaparece.
Unas semanas después, alguien pide mover la base de datos a la subred de la otra zona, donde está el resto de la API. En el código es cambiar una línea: privada_a por privada_b. El plan dice -/+, en rojo. Este episodio va del orden en que Terraform hace las cosas, que casi nunca importa, y del día en que importa mucho.
La base de datos de la API
Las tres columnas de siempre: código, estado y nube. La VPC y sus dos subredes privadas ya están aplicadas; el código ya tiene lo nuevo. A la izquierda, un reemplazo sigue el orden por defecto: destruir y luego crear. A la derecha, la base de datos lleva create_before_destroy: crear el nuevo, mover lo que depende de él y destruir el viejo. Arriba de cada panel, lo que ve Ana en la app.
Aplica y mira en qué orden aparecen los recursos. Después mueve la base de datos de zona, lee el plan y aplica. Los interruptores añaden prevent_destroy a la base de datos, o le ponen un nombre fijo en lugar de un prefijo (esto último vuelve a empezar). La salida del apply marca cada oleada y el reloj cuenta lo que tardaría de verdad.
Qué ha pasado
Nadie escribe el orden
En el código no hay ningún «primero esto, luego aquello». La base de datos dice subred = nube_subred.privada_a.id y grupo = nube_grupo.bd.id, y la app dice bd = nube_bd.api.id. Cada referencia es una flecha: la base de datos depende de la subred, del grupo y de la contraseña; la app, de la base de datos. Con esas flechas, Terraform construye un grafo de dependencias y de él saca el orden.
El apply avanza por oleadas. En la primera va todo lo que no espera a nada nuevo: el grupo y la contraseña, a la vez. En la segunda, la base de datos, que necesitaba los dos. En la tercera, la app y la alarma, a la vez otra vez. Lo que va en la misma oleada no se necesita entre sí y se hace en paralelo (Terraform lanza hasta diez operaciones a la vez), así que el apply tarda lo que tarda la cadena más larga: aquí, casi todo es la base de datos, que tarda unos siete minutos en crearse.
Destruir va al revés: primero lo que depende de otros, al final aquello de lo que dependen. La app y la alarma, antes que la base de datos; la base de datos, antes que su subred; la VPC, la última, cuando ya no le queda nada dentro. Pulsa Destruir el entorno y mira las oleadas: salen del mismo grafo, leído al revés.
Hay cambios que no se pueden hacer en caliente
Casi todo se cambia sin más: más copias de la app, otro umbral en la alarma. Pero hay atributos que la nube no deja cambiar en un recurso vivo. Una base de datos no se puede mover de subred: hay que crear otra en la nueva. El plan lo dice así:
# nube_bd.api must be replaced
-/+ nube_bd.api {
~ subred = "subnet-4d2f" -> "subnet-5b91" # forces replacement
}
# nube_app.api will be updated in-place
~ nube_app.api {
~ bd = "db-8a31" -> (known after apply)
}
Plan: 1 to add, 2 to change, 1 to destroy.
El -/+ es un reemplazo: destruir el recurso y crear otro. Y como el nuevo tendrá otro id, lo que apunta a él cambia también: la app y la alarma pasan a known after apply. El marcador lo pinta en rojo porque un reemplazo es una destrucción, aunque el resumen diga «1 to add».
Cuándo hace daño: el -/+ que nadie leyó
A la izquierda, el orden por defecto: primero se destruye la base de datos, luego se crea la nueva, luego se cambia la app para que apunte a ella. Entre la primera oleada y la última, la app no tiene base de datos: unos doce minutos de errores. Y cuando por fin la tiene, la nueva está vacía. Terraform crea recursos, no copia datos. Ana abre la app, le pide volver a registrar el móvil, y sus destinatarios de confianza no están. Igual que los de otros cuarenta y un mil clientes.
A la derecha, create_before_destroy invierte el orden: primero la base de datos nueva, luego la app se mueve a ella, y al final se destruye la vieja. No hay ni un segundo sin base de datos. Pero mira lo que ve Ana: la nueva también está vacía. El orden arregla el hueco, no los datos. Para una pieza sin estado, como un balanceador o una plantilla de máquinas, create_before_destroy es todo lo que hace falta; para una base de datos, no basta.
Y tiene su propia trampa. Activa el nombre fijo y vuelve a moverla. Durante un momento tienen que existir las dos bases de datos, y la nube no deja que dos se llamen igual: el apply falla en la primera oleada. No rompe nada, pero tampoco mueve nada. Por eso los recursos con create_before_destroy suelen llevar un prefijo en lugar de un nombre (prefijo_nombre = "api-bd-"), y la nube completa el resto.
prevent_destroy: el freno que frena también lo bueno
Activa prevent_destroy. Cualquier plan que destruya la base de datos, también un reemplazo, pasa a ser un error: Instance cannot be destroyed. Nada se toca. Es la protección contra el -/+ que nadie lee.
El precio es que bloquea igual el cambio legítimo: mientras esté puesto, la base de datos no se puede mover de ninguna manera. Para hacerlo, alguien tiene que quitarlo a propósito, en un cambio que otro revisa, y eso es justo lo que se busca: que destruir una base de datos exija una decisión, no un descuido. Ojo con un detalle: prevent_destroy vive en el bloque del recurso, así que si alguien borra el bloque entero, la protección se va con él y el plan dice - sin más. Las bases de datos gestionadas tienen además su propia protección contra el borrado, en la nube, que no depende del código.
Mover una base de datos con datos no es un cambio de una línea, es una migración: crear la nueva junto a la vieja, copiar los datos (una réplica, una copia de seguridad que se restaura, una ventana de mantenimiento), cambiar la app y solo entonces quitar la vieja. Terraform puede hacer cada paso, pero no sabe que hacen falta.
Criterio
| El recurso que se reemplaza | Qué usar | Por qué |
|---|---|---|
| Sin datos y sin nada que dependa de él (una alarma, una regla) | El orden por defecto | El hueco dura segundos y nadie lo nota |
| Sin datos, pero con dependientes en uso (un balanceador, un certificado, una plantilla de máquinas) | create_before_destroy, con prefijo en el nombre | Sin corte; el nombre fijo haría fallar el apply |
| Con datos (una base de datos, un disco, un bucket) | prevent_destroy y la protección de la nube; moverlo es una migración planificada | Ningún orden conserva los datos |
| Un nombre en el código que se quiere cambiar | Un bloque moved | Renombrar en el código, sin más, también es un -/+ |
-
¿El recurso guarda datos que no se pueden recrear?Si se pierde, ¿alguien lo nota?prevent_destroy, y los cambios de red o de motor como migración
-
¿Hay algo en uso que dependa de él?Tráfico que pasa por él, piezas que lo referencian.create_before_destroy, y un nombre que pueda repetirse
- El orden por defecto. Y en cualquier caso, leer el plan: un -/+ nunca es un cambio pequeño.
En el mundo real
El grafo se puede ver: terraform graph lo escribe en formato DOT, y el paralelismo se ajusta con -parallelism (diez por defecto). Cuando una dependencia no sale de una referencia (una app que necesita que exista un permiso, aunque no use su id) se declara a mano con depends_on. Y si hace falta reemplazar algo aunque el código no haya cambiado, terraform apply -replace=... lo fuerza; replace_triggered_by hace que un recurso se reemplace cuando cambia otro.
Los reemplazos accidentales más habituales no vienen de mover una base de datos, sino de renombrar: cambiar nube_bd.api por nube_bd.principal en el código hace que Terraform vea un recurso que sobra y otro que falta, y propone destruir uno y crear el otro. Desde Terraform 1.1, un bloque moved le dice que es el mismo. En AWS, un cambio en el grupo de subredes o en el identificador de una base de datos RDS puede obligar a reemplazarla, y RDS tiene su propia deletion_protection; Azure y Google tienen protecciones equivalentes, y también locks de recurso que impiden borrarlo desde cualquier sitio.
Para revisar planes grandes sin perderse un -/+, hay herramientas que resumen el plan y marcan las destrucciones, y comprobaciones en el pipeline que exigen una aprobación extra si el plan destruye algo. Es la idea del marcador de esta serie: que las destrucciones se vean aunque el plan tenga cien líneas.
Siguiente episodio
Hasta aquí, Marta trabaja sola y el estado vive en su portátil. Entonces Jon, del equipo de la app, necesita añadir una caché a la misma infraestructura. Clona el repositorio, ejecuta terraform plan, y el plan quiere crearlo todo.
← Todos los episodios · Episodio 1: Alguien lo cambió a mano · Cuentas en el mapa de BCPS Bank