Configuración de Cloud-init para Provisión de Servidores
Cloud-init es una herramienta potente para automatizar la configuración inicial de servidores en los principales proveedores en la nube. Proporciona una forma estándar de configurar instancias en el primer arranque usando sintaxis YAML cloud-config, permitiendo provisión de infraestructura como código sin herramientas externas. Esta guía cubre conceptos fundamentales de cloud-config, especificación de datos de usuario, instalación de paquetes, gestión de usuarios, scripts personalizados, provisión de archivos y características avanzadas.
Tabla de Contenidos
- Descripción General de Cloud-init
- Sintaxis Cloud-Config
- Instalación de Paquetes
- Gestión de Usuarios y Grupos
- Ejecución de Comandos
- Gestión de Archivos
- Configuración de Red
- Llamadas de Cloud-init
- Depuración de Cloud-init
- Conclusión
Descripción General de Cloud-init
Cloud-init es el estándar de la industria para inicialización de instancias en la nube. Se ejecuta en el primer arranque con privilegios de root, lo que lo hace perfecto para configuración inicial del sistema, instalación de paquetes, creación de usuarios y tareas de configuración personalizadas.
Beneficios clave:
- Estándar en Múltiples Proveedores: Funciona en AWS, Azure, GCP, DigitalOcean, Linode
- Sintaxis YAML Simple: Cloud-config proporciona configuración declarativa legible
- Arranque Rápido: Se configura en el primer arranque, no requiere software adicional
- Repetible: La misma configuración produce resultados consistentes cada vez
- Extensible: Soporte para scripts personalizados y módulos
- Listo para Integración: Funciona con Terraform, CloudFormation, Ansible
Flujo de trabajo de Cloud-init:
Lanzamiento de Instancia
↓
Agente Cloud-init Inicia (root)
↓
Obtener Datos de Usuario
↓
Analizar Cloud-Config
↓
Ejecutar Pasos de Inicialización
│
├── Módulo: Instalar Paquetes
├── Módulo: Crear Usuarios
├── Módulo: Escribir Archivos
├── Módulo: Ejecutar Comandos
└── Módulo: Configurar Servicios
↓
Inicialización Completada
Etapas de Cloud-init:
- Generator: Genera servicios adicionales si es necesario
- Local: Configuración temprana de red, montar sistemas de archivos
- Network: Redes completamente disponibles, ejecutar tareas dependientes de red
- Config: Configuración general
- Final: Última etapa, ejecutar personalización final
Sintaxis Cloud-Config
Cloud-config utiliza formato YAML comenzando con #cloud-config.
Estructura básica:
#cloud-config
# Comments start with #
package_update: true
package_upgrade: true
packages:
- nginx
- curl
- wget
runcmd:
- systemctl start nginx
- systemctl enable nginx
write_files:
- path: /etc/nginx/sites-available/default
content: |
server {
listen 80 default_server;
server_name _;
location / {
return 200 "Hello from $(hostname)";
}
}
Conversiones de tipos de datos:
#cloud-config
# String
region: us-east-1
# Number
port: 8080
# Boolean
enabled: true
disabled: false
# List
packages:
- nginx
- curl
# Dictionary/Map
environment:
APP_ENV: production
LOG_LEVEL: debug
# Multiline string
config: |
This is a multiline
configuration string
that preserves formatting
Instalación de Paquetes
Gestiona paquetes fácilmente con cloud-init.
Gestión de paquetes básica:
#cloud-config
# Update package index
package_update: true
# Upgrade existing packages
package_upgrade: true
# Upgrade distribution packages
distro_upgrade: true
# Install packages
packages:
- nginx
- curl
- wget
- htop
- git
- python3-pip
# Package reboot behavior
package_reboot_if_required: true
# Configure package manager
apt:
conf: |
APT::Acquire::http::Timeout "60";
APT::Acquire::ftp::Timeout "60";
Configuración de repositorio y PPA:
#cloud-config
# Add custom repository
apt:
sources:
docker.list:
source: deb [arch=amd64] https://download.docker.com/linux/ubuntu $RELEASE stable
keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
# Or add PPA
apt_sources:
- source: ppa:nginx/stable
- source: ppa:deadsnakes/ppa
# After adding repos, install packages
packages:
- docker-ce
- docker-ce-cli
- containerd.io
- python3.9
Configuración de paquetes:
#cloud-config
packages:
- nginx
- mysql-server
# Configure packages
package_update: true
package_upgrade: true
# Prevent interactive prompts
debconf_selections: |
mysql-server mysql-server/root_password password root
mysql-server mysql-server/root_password_again password root
mysql-server mysql-server/remove_databases_and_users boolean false
mysql-server mysql-server/skip-install boolean false
Gestión de Usuarios y Grupos
Crea y configura cuentas de usuario.
Crear usuarios:
#cloud-config
users:
- name: ubuntu
groups: sudo
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1... [email protected]
- name: app
groups: appgroup
shell: /usr/sbin/nologin
home: /opt/app
inactive: true
- name: devops
groups: [sudo, docker, wheel]
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1... [email protected]
- ssh-rsa AAAAB3NzaC1... [email protected]
# Create groups
groups:
- docker
- appgroup: [app, ubuntu]
# Ensure default user disabled
disable_root: true
Opciones de configuración de usuario:
#cloud-config
users:
- name: appuser
# Set user ID
uid: 1001
# Set group ID
gid: 1001
# Home directory
home: /opt/appuser
# Shell
shell: /bin/bash
# Group membership
groups: [sudo, adm]
# Add to sudoers
sudo: ['ALL=(ALL) NOPASSWD:ALL']
# Don't create home directory
no_create_home: false
# Create home directory with specific permissions
homedir: /home/appuser
# SSH keys for login
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1... [email protected]
- ssh-rsa AAAAB3NzaC1... [email protected]
# Disable password login
ssh_import_id: github:username
# Lock account
# lock_passwd: true
# Set expiration
expire: false
# Create with password (usually avoided for security)
# passwd: $1$salt$hashed_password
Gestión de grupos:
#cloud-config
groups:
- docker
- postgres
- www-data: [ubuntu]
- developers: [app, deploy, ubuntu]
users:
- name: deploy
groups: [developers, sudo]
- name: app
groups: developers
Ejecución de Comandos
Ejecuta scripts y comandos durante la inicialización.
Ejecución de comandos básica:
#cloud-config
# Run commands at final stage
runcmd:
- /opt/bin/setup.sh
- systemctl start nginx
- systemctl enable nginx
- echo "Server configured" | mail -s "Server Ready" [email protected]
# Run commands conditionally by list index
# First element runs before second, etc.
runcmd:
- ['sh', '-c', 'echo "$HOSTNAME is up" > /tmp/status.txt']
- systemctl restart nginx
Redirección de salida de comandos:
#cloud-config
runcmd:
# Redirect to file
- 'echo "Configuration log" > /var/log/init-log.txt'
# Append to file
- 'echo "Additional info" >> /var/log/init-log.txt'
# Suppress output
- 'systemctl start nginx > /dev/null 2>&1'
# Log with timestamp
- 'echo "$(date): Server configured" >> /var/log/cloud-init-custom.log'
Bootcmd para ejecución temprana:
#cloud-config
# Run commands at boot (before packages installed)
bootcmd:
- 'modprobe zfs'
- 'mkfs -t ext4 /dev/sdb1'
- 'mkdir -p /mnt/data'
- 'mount /dev/sdb1 /mnt/data'
# Then install packages
packages:
- nginx
- curl
# Then run runcmd
runcmd:
- systemctl start nginx
Scripts personalizados:
#cloud-config
packages:
- python3
# Write custom script
write_files:
- path: /opt/setup.py
permissions: '0755'
owner: root:root
content: |
#!/usr/bin/env python3
import subprocess
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("Running custom setup")
subprocess.run(['apt-get', 'install', '-y', 'nginx'])
subprocess.run(['systemctl', 'start', 'nginx'])
runcmd:
- /opt/setup.py
Gestión de Archivos
Crea y gestiona archivos durante la inicialización.
Escribir archivos:
#cloud-config
write_files:
- path: /etc/app/config.yml
owner: root:root
permissions: '0644'
content: |
app:
name: myapp
port: 8080
environment: production
logging:
level: info
file: /var/log/app.log
- path: /opt/app/run.sh
owner: app:app
permissions: '0755'
content: |
#!/bin/bash
cd /opt/app
./myapp --config /etc/app/config.yml
- path: /etc/systemd/system/app.service
owner: root:root
permissions: '0644'
content: |
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
User=app
WorkingDirectory=/opt/app
ExecStart=/opt/app/run.sh
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
- path: /etc/environment
append: true
content: |
APP_ENV=production
LOG_LEVEL=info
Codificación y compresión de archivos:
#cloud-config
write_files:
- path: /tmp/data.txt
encoding: base64
content: |
VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIGZpbGUK
- path: /opt/archive.tar.gz
encoding: gz+base64
content: H4sIAIWLpGUC/wvJyCzRUMgsS8wpTVWoVkgsS8wpTlWqAOJcJaX5JUWlJRnpxUX5JUWlJRmpxUWlJRmlmSWpyZkpGalFqWWpqUVQUqsUQGpqamlyUWlpRmlyUmlySmpRVGpJZWlJZllKUWlpZkpSZWlJUXlKZklrMxM9RWfMoRhiEqUSR+LKnFmZlZoKkSzhTLBqJTK0UBMsI0vMBb6yVnFJEkRhWhFgBTr3nMH0QzUSMqGCYHbPTi5JLEktLkksTs0rBsi55hUlJhWlKoEUlpQUl6ZmluQVpyZnlmSklmTkFpalJqakFhWnFpQWFqYWJAUVQyVkBUpgSmVOQWpJZUpiRWmmQWrDUsVKKJCOJCniBLSYC1EFWQ1sOQEhyAKG0o9KaLIFaMsRgO6y2oBl8yLmEzaLBqLqWvt9M27L+zJdmRpQMREONuA=
Copiar archivos desde origen:
#cloud-config
# Note: write_files copies content, not external files
# For copying from source during build, use provisioners in Packer
# or download during execution
write_files:
- path: /opt/setup.sh
permissions: '0755'
content: |
#!/bin/bash
# Download configuration from S3
aws s3 cp s3://config-bucket/app-config.yml /etc/app/config.yml
# Clone application from GitHub
git clone https://github.com/myorg/myapp /opt/app
Configuración de Red
Configura redes durante la inicialización.
Configuración de red:
#cloud-config
# Configure hostname
hostname: webserver-01
fqdn: webserver-01.example.com
prefer_fqdn_over_hostname: true
# Update /etc/hosts
manage_etc_hosts: true
# NTP configuration
ntp:
enabled: true
ntp_client: systemd-timesyncd
servers:
- 0.ubuntu.pool.ntp.org
- 1.ubuntu.pool.ntp.org
# DNS configuration
resolv_conf:
nameservers:
- 8.8.8.8
- 8.8.4.4
searchdomains:
- example.com
Configuración de red personalizada:
#cloud-config
# Wait for network to be ready
wait_for_network: true
wait_for_network_timeout: 60
# Disable IPv6
bootcmd:
- 'echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf'
- 'sysctl -p'
# Configure static IP (if not using DHCP)
write_files:
- path: /etc/netplan/99-custom-ip.yaml
content: |
network:
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 192.168.1.100/24
gateway4: 192.168.1.1
nameservers:
addresses: [8.8.8.8, 8.8.4.4]
runcmd:
- netplan apply
Llamadas de Cloud-init
Envía notificaciones después de completar la configuración.
Llamada a inicio para notificar finalización:
#cloud-config
phone_home:
url: http://example.com/callback?instance_id=$INSTANCE_ID&hostname=$HOSTNAME
post: all
tries: 10
# Or callback with webhook
phone_home:
url: https://webhook.example.com/cloud-init
post: all
tries: 3
retries: 5
Enviar notificación a monitoreo:
#cloud-config
packages:
- curl
write_files:
- path: /opt/notify.sh
permissions: '0755'
content: |
#!/bin/bash
INSTANCE_ID=$(ec2-metadata --instance-id | cut -d' ' -f2)
HOSTNAME=$(hostname)
# Send to monitoring system
curl -X POST https://monitoring.example.com/api/events \
-H "Content-Type: application/json" \
-d "{
\"event_type\": \"instance_ready\",
\"instance_id\": \"$INSTANCE_ID\",
\"hostname\": \"$HOSTNAME\",
\"timestamp\": \"$(date -Iseconds)\"
}"
runcmd:
- /opt/notify.sh
Depuración de Cloud-init
Soluciona problemas de ejecución de cloud-init.
Ver registros de cloud-init:
# Main cloud-init log
cat /var/log/cloud-init-output.log
# Detailed log
cat /var/log/cloud-init.log
# System log
journalctl -u cloud-init -n 50
# Watch logs in real-time
tail -f /var/log/cloud-init.log
Verificar estado de cloud-init:
# Current status
cloud-init status
# Show all status
cloud-init status --long
# Check if running
cloud-init status | grep -q "running"
# Show available modules
cloud-init modules --list-sources
Habilitar registro de depuración:
#cloud-config
# Enable debug output
debug: true
# Set log level
output: {all: '>> /var/log/cloud-init-custom.log'}
Ejecución manual para pruebas:
# Clean up cloud-init state
sudo cloud-init clean --logs --seed
# Re-run cloud-init
sudo cloud-init init
sudo cloud-init modules --mode=config
sudo cloud-init modules --mode=final
# Or single run
sudo cloud-init -d single --name=runcmd
Validar sintaxis cloud-config:
# Check cloud-config validity
cloud-init schema --config-file user-data.txt --annotate
# Validate specific module
cloud-init schema --config-file user-data.txt --annotate | grep runcmd
Conclusión
Cloud-init proporciona una forma estándar y simple de automatizar el aprovisionamiento de servidores en múltiples proveedores en la nube. Al dominar la sintaxis cloud-config, gestión de paquetes, creación de usuarios, ejecución de scripts y provisión de archivos, puedes eliminar la configuración manual de servidores y crear infraestructura reproducible. Combinado con herramientas de infraestructura como código como Terraform, cloud-init forma una base poderosa para implementación de infraestructura automatizada, consistente y escalable.


