Despliegue de Servidores con Infraestructura como Código (IaC) usando Terraform y Ansible
La automatización de servidores ha pasado de ser una ventaja competitiva a un requisito indispensable en cualquier infraestructura moderna. Gestionar decenas o cientos de servidores de forma manual no solo es ineficiente, sino que introduce un alto riesgo de error humano y deriva en el temido configuration drift. Aquí es donde la Infraestructura como Código (IaC) se convierte en tu mejor aliada.
En este artículo, exploraremos en profundidad el despliegue de servidores utilizando dos de las herramientas más potentes del ecosistema DevOps: Terraform para la orquestación y aprovisionamiento de la infraestructura, y Ansible para la configuración y gestión del estado interno de los sistemas. Verás cómo combinarlas te permite construir un pipeline de despliegue robusto, reproducible y escalable.
¿Por qué Terraform y Ansible? El Yin y el Yang del IaC
A menudo surge la pregunta: ¿por qué usar dos herramientas cuando una sola podría hacer "casi todo"? La respuesta está en la especialización. Aunque Ansible puede aprovisionar recursos en la nube y Terraform puede ejecutar scripts de configuración, cada uno brilla en su dominio específico.
Terraform: El Arquitecto de la Infraestructura
Terraform es una herramienta de orquestación declarativa. Su lenguaje, HCL (HashiCorp Configuration Language), te permite describir el estado deseado de tu infraestructura: redes, subredes, firewalls, instancias de servidores, balanceadores de carga, etc.
- Enfoque Declarativo: Dices qué quieres (una VM con 4GB de RAM y una IP pública), no cómo crearla.
- Gestión de Estado: Terraform mantiene un archivo de estado (
terraform.tfstate) que mapea los recursos reales con tu configuración. Esto permite detectar cambios y aplicar solo las diferencias. - Proveedores: Soporta cientos de proveedores (AWS, Azure, GCP, VMware, OpenStack, etc.) a través de un ecosistema de plugins.
Ansible: El Manitas de la Configuración
Ansible es una herramienta de gestión de configuración imperativa (aunque con sabor declarativo). Se conecta a los servidores ya creados (generalmente por SSH) y se asegura de que el software esté instalado, los servicios estén corriendo y los archivos de configuración tengan el contenido correcto.
- Sin Agente: No requiere instalar nada en el servidor de destino. Solo necesita Python y SSH.
- Idempotencia: Ejecutar un playbook una o cien veces produce el mismo resultado final. Si el servidor ya está configurado, no hace nada.
- Módulos: Ofrece miles de módulos para gestionar paquetes (
apt,yum), servicios (systemd), archivos (template,copy), usuarios, etc.
[INFO] La combinación Terraform + Ansible es el estándar de facto en la industria. Terraform se encarga del "hardware lógico" (la nube) y Ansible del "software" (el sistema operativo y las aplicaciones).
Flujo de Trabajo Ideal: Aprovisionar y Configurar
El flujo de trabajo típico para desplegar un servidor web con esta pila es el siguiente:
- Terraform crea la red, la subred, el grupo de seguridad y una instancia de servidor (por ejemplo, un EC2 en AWS o un droplet en DigitalOcean).
- Terraform llama a un playbook de Ansible (usando el provisioner
remote-execo, mejor, ellocal-execpara ejecutar Ansible desde tu máquina local o un runner de CI/CD). - Ansible se conecta a la IP pública del nuevo servidor, instala Nginx, configura el firewall (
ufw), despliega un sitio web de prueba y se asegura de que el servicio esté habilitado en el arranque.
Manos a la Obra: Ejemplo Práctico de Despliegue
Vamos a construir un ejemplo sencillo pero funcional. Desplegaremos un servidor web Nginx en DigitalOcean usando Terraform y luego lo configuraremos con Ansible.
1. Estructura de Directorios
infraestructura/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars
└── ansible/
├── inventory.yml
├── playbook.yml
└── roles/
└── nginx/
├── tasks/
│ └── main.yml
└── templates/
└── index.html.j2
2. Terraform: main.tf
Este archivo define el proveedor (DigitalOcean), la clave SSH, el droplet (servidor) y un output para obtener la IP.
# main.tf
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
}
resource "digitalocean_ssh_key" "default" {
name = "Mi Clave SSH"
public_key = file(var.public_key_path)
}
resource "digitalocean_droplet" "web" {
image = "ubuntu-22-04-x64"
name = "servidor-web-01"
region = "nyc3"
size = "s-1vcpu-1gb"
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
}
# Output para obtener la IP
output "server_ip" {
value = digitalocean_droplet.web.ipv4_address
}
3. Terraform: variables.tf y terraform.tfvars
# variables.tf
variable "do_token" {
description = "Token de API de DigitalOcean"
type = string
sensitive = true
}
variable "public_key_path" {
description = "Ruta a la clave pública SSH"
type = string
default = "~/.ssh/id_rsa.pub"
}
# terraform.tfvars
do_token = "dop_v1_your_super_secret_token_here"
4. El Trigger: Provisioner de Terraform para llamar a Ansible
Para que Terraform ejecute Ansible automáticamente después de crear el droplet, añadimos un provisioner local.
# Dentro de main.tf, dentro del recurso droplet
resource "digitalocean_droplet" "web" {
# ... configuración anterior ...
provisioner "local-exec" {
command = "ansible-playbook -i ansible/inventory.yml ansible/playbook.yml -e 'server_ip=${self.ipv4_address}'"
}
}
[WARNING] Usar
local-execpara ejecutar Ansible desde tu máquina local es práctico para desarrollo. En producción, deberías mover esto a un pipeline de CI/CD (GitLab CI, GitHub Actions, Jenkins) que ejecute Terraform y Ansible de forma aislada.
5. Ansible: Inventario Dinámico y Playbook
Creamos un inventario estático simple que recibe la IP como variable.
# ansible/inventory.yml
[webservers]
server-ansible ansible_host={{ server_ip }} ansible_user=root
Ahora, el playbook:
# ansible/playbook.yml
---
- name: Configurar servidor web Nginx
hosts: webservers
gather_facts: yes
become: yes
tasks:
- name: Actualizar cache de apt
apt:
update_cache: yes
cache_valid_time: 3600
- name: Instalar Nginx
apt:
name: nginx
state: present
- name: Asegurar que Nginx está corriendo y habilitado
systemd:
name: nginx
state: started
enabled: yes
- name: Copiar un index.html personalizado
template:
src: roles/nginx/templates/index.html.j2
dest: /var/www/html/index.html
notify: restart nginx
handlers:
- name: restart nginx
systemd:
name: nginx
state: restarted
Y la plantilla HTML:
<!-- ansible/roles/nginx/templates/index.html.j2 -->
<!DOCTYPE html>
<html>
<head>
<title>Servidor Desplegado con IaC</title>
</head>
<body>
<h1>¡Hola desde Terraform y Ansible!</h1>
<p>Servidor: {{ ansible_hostname }}</p>
<p>IP: {{ ansible_default_ipv4.address }}</p>
</body>
</html>
6. Ejecución
# Inicializar Terraform
cd infraestructura/
terraform init
# Ver el plan de ejecución
terraform plan
# Aplicar (crea el droplet y ejecuta Ansible)
terraform apply -auto-approve
# Destruir cuando ya no sea necesario
terraform destroy -auto-approve
Buenas Prácticas para un IaC Robusto
Implementar IaC no es solo escribir archivos .tf y .yml. Requiere disciplina y buenas prácticas.
Gestión del Estado de Terraform
El archivo terraform.tfstate es crítico. Si lo pierdes, Terraform no sabrá qué recursos gestiona. Nunca lo subas a Git (añádelo a .gitignore). En su lugar, usa un backend remoto:
# backend.tf
terraform {
backend "s3" {
bucket = "mi-bucket-terraform-state"
key = "produccion/servidores-web.tfstate"
region = "us-east-1"
}
}
También puedes usar Terraform Cloud, Azure Storage, o consul.
Control de Versiones y Entornos
- Git: Todo el código de infraestructura debe estar versionado en Git.
- Ramas: Usa ramas como
main(producción),stagingydevelop. - Módulos: Crea módulos de Terraform reutilizables para componentes comunes (VPC, balanceador, base de datos).
Seguridad
- Secretos: Nunca hardcodees tokens o contraseñas. Usa variables de entorno, un vault (como HashiCorp Vault) o el sistema de secretos de tu proveedor de nube.
- Ansible Vault: Para cifrar playbooks que contengan contraseñas, usa
ansible-vault encrypt playbook.yml.
[TIP] Combina Terraform con un remote state y bloqueo de estado para evitar que dos personas o pipelines modifiquen la misma infraestructura simultáneamente.
Casos de Uso Avanzados: Más Allá del Servidor Simple
La combinación Terraform + Ansible escala a escenarios mucho más complejos.
Clústeres de Alta Disponibilidad
Terraform puede crear un Auto Scaling Group (ASG) en AWS y un Load Balancer. Luego, Ansible puede configurar cada nueva instancia que se lance automáticamente (usando un golden image o un user-data script que llame a un playbook de Ansible Pull).
Despliegues Multi-Nube
Puedes tener un mismo playbook de Ansible configurando servidores en AWS, Azure y un datacenter on-premise, mientras que Terraform gestiona los recursos específicos de cada nube.
Integración con CI/CD
En un pipeline de GitLab CI/CD, podrías tener un job que ejecute terraform plan, otro que aplique los cambios después de aprobación manual, y un tercero que ejecute ansible-playbook contra los nuevos servidores.
# .gitlab-ci.yml (fragmento)
stages:
- plan
- apply
- configure
terraform-plan:
stage: plan
script:
- terraform init
- terraform plan -out=tfplan
artifacts:
paths: [tfplan]
terraform-apply:
stage: apply
script:
- terraform apply tfplan
only:
- main
ansible-configure:
stage: configure
script:
- ansible-playbook -i ansible/inventory.yml ansible/playbook.yml
needs: ["terraform-apply"]
Conclusión
Adoptar Infraestructura como Código con Terraform y Ansible transforma la gestión de servidores. Pasas de un arte manual y propenso a errores a una ingeniería de software disciplinada.
Terraform te da el poder de crear y destruir infraestructura compleja con un solo comando, mientras que Ansible te asegura que el software se configure exactamente como necesitas, sin desviaciones. Juntos, forman la base de cualquier estrategia moderna de automatización de servidores.
No importa si gestionas 5 servidores o 5000; el tiempo invertido en aprender estas herramientas se amortiza exponencialmente. Empieza con un pequeño proyecto de prueba, versiona tu código, y verás cómo la repetitividad y los errores desaparecen, dejando paso a una infraestructura predecible, auditable y escalable.
