Infrastructure as Code (IaC)
Proposito
Guia y ejemplos de como gestionar la infraestructura del sistema como codigo. Todo cambio de infra se versiona, se revisa, y se aplica de forma reproducible.
1. Principios de IaC
- Todo en version control. La infraestructura se define en archivos, no en consolas web.
- Reproducible. El mismo codigo produce el mismo resultado, siempre.
- Inmutable preferido. En vez de modificar un servidor, crea uno nuevo y reemplaza.
- Documentacion viva. El codigo ES la documentacion de la infraestructura.
2. Stack de IaC del Proyecto
| Componente |
Herramienta |
Alternativas |
| Infraestructura cloud |
Terraform |
Pulumi, CloudFormation |
| Configuracion de servidores |
Ansible |
Chef, Puppet |
| Contenedores (futuro) |
Docker + Docker Compose |
Podman |
| Orquestacion (futuro) |
Kubernetes (si escala) |
ECS, Docker Swarm |
Estructura de archivos
infrastructure/
terraform/
main.tf
variables.tf
outputs.tf
environments/
staging.tfvars
production.tfvars
main.tf - Recursos principales
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
# Base de datos PostgreSQL
resource "digitalocean_database_cluster" "postgres" {
name = "${var.project_name}-db-${var.environment}"
engine = "pg"
version = "17"
size = var.db_size
region = var.region
node_count = var.db_node_count
maintenance_window {
day = "sunday"
hour = "04:00:00"
}
}
# Firewall de BD: solo acceso desde la app
resource "digitalocean_database_firewall" "postgres_fw" {
cluster_id = digitalocean_database_cluster.postgres.id
rule {
type = "droplet"
value = digitalocean_droplet.app_server.id
}
}
# Servidor de aplicacion
resource "digitalocean_droplet" "app_server" {
name = "${var.project_name}-app-${var.environment}"
size = var.app_size
image = "ubuntu-24-04-x64"
region = var.region
ssh_keys = [var.ssh_key_id]
tags = ["${var.project_name}", var.environment]
}
# DNS
resource "digitalocean_record" "api" {
domain = var.domain
type = "A"
name = var.environment == "production" ? "api" : "staging-api"
value = digitalocean_droplet.app_server.ipv4_address
}
variables.tf
variable "project_name" {
description = "Nombre del proyecto"
type = string
default = "turnos-medicos"
}
variable "environment" {
description = "Ambiente: staging o production"
type = string
}
variable "region" {
description = "Region de DigitalOcean"
type = string
default = "nyc3"
}
variable "db_size" {
description = "Tamano de la instancia de BD"
type = string
default = "db-s-1vcpu-1gb"
}
variable "db_node_count" {
description = "Numero de nodos de BD"
type = number
default = 1
}
variable "app_size" {
description = "Tamano del droplet de la app"
type = string
default = "s-2vcpu-4gb"
}
variable "domain" {
description = "Dominio principal"
type = string
}
variable "ssh_key_id" {
description = "ID de la SSH key en DigitalOcean"
type = string
}
environments/production.tfvars
environment = "production"
db_size = "db-s-1vcpu-1gb"
db_node_count = 1
app_size = "s-4vcpu-8gb"
domain = "turnosmedicos.com"
4. Ejemplo: Ansible para Configuracion del Servidor
playbook.yml
---
- name: Configurar servidor de aplicacion
hosts: app_servers
become: yes
vars:
node_version: "20"
app_dir: /opt/turnos-medicos
pm2_instances: 3
tasks:
- name: Instalar Node.js
shell: |
curl -fsSL https://deb.nodesource.com/setup_{{ node_version }}.x | bash -
apt-get install -y nodejs
- name: Instalar PM2
npm:
name: pm2
global: yes
- name: Crear directorio de la app
file:
path: "{{ app_dir }}"
state: directory
owner: deploy
group: deploy
- name: Configurar PM2 ecosystem
template:
src: templates/ecosystem.config.js.j2
dest: "{{ app_dir }}/ecosystem.config.js"
- name: Configurar Nginx como reverse proxy
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/turnos-medicos
notify: Reload Nginx
- name: Habilitar sitio en Nginx
file:
src: /etc/nginx/sites-available/turnos-medicos
dest: /etc/nginx/sites-enabled/turnos-medicos
state: link
notify: Reload Nginx
- name: Configurar firewall (UFW)
ufw:
rule: allow
name: "{{ item }}"
loop:
- 'Nginx Full'
- 'OpenSSH'
- name: Habilitar firewall
ufw:
state: enabled
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloaded
5. Workflow de IaC
1. Cambiar archivos de infra en una branch
2. PR con terraform plan como comentario
3. Review por otro ingeniero
4. Merge a main
5. terraform apply (manual o via CI)
6. Verificar que la infra esta correcta
Comandos clave
# Ver que va a cambiar (SEGURO, no modifica nada)
terraform plan -var-file=environments/staging.tfvars
# Aplicar cambios (CUIDADO, modifica infraestructura real)
terraform apply -var-file=environments/staging.tfvars
# Destruir infraestructura (MUY PELIGROSO)
terraform destroy -var-file=environments/staging.tfvars
6. Seguridad en IaC
| Practica |
Implementacion |
| No hardcodear secrets |
Usar variables de ambiente o vault |
| State file seguro |
Terraform state en backend remoto (S3/DO Spaces) con encriptacion |
| Acceso minimo |
Service accounts con permisos especificos |
| Audit trail |
Todo cambio via PR, nunca directo en consola |
| Rotacion de credentials |
Automatizar rotacion de API keys cada 90 dias |
Checklist de Completitud
Archivos relacionados