🐧 DevOps e infraestructura — contenido
Linux a nivel administrador, systemd, servidores y Nginx, redes y firewalls, Docker, Kubernetes, CI/CD, monitoreo y cloud. Teoría, comandos y práctica.
DevOps e infraestructura — contenido
Un backend no vale nada si no sabes operarlo. Esta página te da el contenido completo de infraestructura: de la terminal al servidor de producción, pasando por systemd, Nginx, firewalls, Docker y Kubernetes. Primero a mano (vanilla), luego con herramientas — la regla de oro del DevOps serio.
Las wikis del sitio tienen detalle por tema: Comandos Linux, Bash, Nginx, Docker, Firewalls. Y las páginas de la ruta tienen los recursos: Linux, Servidores, Redes y seguridad, Infraestructura moderna, Cloud.
Linux: el sistema operativo de los servidores
Linux domina los servidores (y los contenedores son Linux). Los conceptos que debes dominar a nivel administrador:
El árbol de directorios
/ raíz
/etc/ configuración del sistema (nginx.conf, sshd_config)
/var/log/ logs (nginx/access.log, syslog, journald)
/var/www/ web roots
/home/ usuarios
/tmp/ temporales
/usr/bin/ binarios de sistema
/proc/ archivos virtuales del kernel (procesos, memoria)
/sys/ interfaz del kernel con el hardware
💡 /proc y /sys son ilusiones: leer
cat /proc/meminfoo/proc/1234/statusNO lee archivos de disco; el kernel genera esos “archivos” en el momento. Por eso el SO es «una fábrica de ilusiones».
Permisos y usuarios (lo viste en Fundamentos SO)
ls -la
-rw-r--r-- 1 ana dev 4096 mar 3 12:00 app.log
drwxr-xr-x 2 ana dev 4096 mar 3 12:00 proyecto
chmod 755 script.sh # rwxr-xr-x
chmod 644 config.conf # rw-r--r--
chown ana:dev archivo # cambiar dueño:grupo
adduser nuevo_usuario
usermod -aG sudo nuevo_usuario # añadir al grupo sudo
Procesos y systemd
systemd es el gestor de servicios moderno de casi todas las distros. Sustituye al viejo init.
systemctl status nginx # ¿estado de nginx?
systemctl start/stop/restart nginx
systemctl enable nginx # arrancar al boot
systemctl reload nginx # recargar config sin cortar
journalctl -u nginx -f # logs del servicio en vivo
Cada servicio es una unit en /etc/systemd/system/:
# /etc/systemd/system/miapp.service
[Unit]
Description=Mi aplicación
After=network.target postgresql.service
[Service]
User=ana
WorkingDirectory=/var/www/miapp
ExecStart=/usr/bin/node server.js
Restart=on-failure # si se cae, reinícialo
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
⚠️ Restart=on-failure es lo mínimo para un servicio de producción. Y nunca ejecutes servicios como root si no es imprescindible: cada servicio, su usuario.
Logs
journalctl -u nginx -f # systemd: seguir los logs de un servicio
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
grep "5[0-9][0-9]" /var/log/nginx/access.log | tail # errores 5xx
Servidores: Nginx a mano
Antes de Docker, monta Nginx a mano. Entenderás cada pieza.
Instalación y primeros pasos
apt update && apt install -y nginx
systemctl enable --now nginx
nginx -t # validar configuración
systemctl reload nginx
Virtual hosts (server blocks)
Cada sitio es un server {} en /etc/nginx/sites-available/ con un symlink en sites-enabled/:
server {
listen 80;
server_name miproyecto.com www.miproyecto.com;
root /var/www/miproyecto;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Reverse proxy: el patrón que usarás todo el día
Nginx recibe por fuera y reenvía a tu app (Node, PHP-FPM, Go) que escucha en un puerto interno:
server {
listen 80;
server_name api.miproyecto.com;
location / {
proxy_pass http://127.0.0.1:8080; # tu app
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Internet ──▶ :80/:443 Nginx (público) ──▶ 127.0.0.1:8080 app (privado)
💡 Por qué Nginx delante: TLS/HTTPS (certificados), estáticos ultrarrápidos, caché, load balancing, rate limiting, gzip, y tu app no queda expuesta directamente. Detalle en wiki: Nginx.
HTTPS con Let’s Encrypt
apt install certbot python3-certbot-nginx
certbot --nginx -d miproyecto.com -d www.miproyecto.com
# renueva automáticamente con un timer de systemd
Load balancing con Nginx
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
Redes y seguridad
Resumen de los conceptos y comandos esenciales (a fondo en wiki: Firewalls y Redes y seguridad):
Firewalls: iptables / nftables / UFW
El firewall filtra el tráfico por reglas. UFW es la capa amigable sobre iptables:
ufw default deny incoming # bloquear todo lo que entra por defecto
ufw allow ssh # excepto SSH
ufw allow 80,443/tcp # y web
ufw enable
ufw status verbose
SSH hardening
# /etc/ssh/sshd_config
Port 22
PermitRootLogin no # NUNCA root por SSH
PasswordAuthentication no # solo claves
PubkeyAuthentication yes
AllowUsers ana deploy
ssh-keygen -t ed25519 # genera clave (ed25519 > rsa)
ssh-copy-id ana@servidor # copia la clave pública al servidor
⚠️ Reglas de oro de SSH: sin login root, sin contraseñas (solo claves), puerto 22 o alterno, y fail2ban para bloquear a los que intentan entrar por fuerza bruta.
fail2ban
apt install fail2ban
fail2ban-client status sshd # ver IPs baneadas
# banea IPs tras N intentos fallidos de SSH
Infraestructura moderna
Docker: empaquetar y aislar
Docker empaqueta tu app y sus dependencias en una imagen y la ejecuta en un contenedor aislado (mismo kernel que el host, pero con su propio filesystem y procesos).
# Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
docker build -t miapp .
docker run -d -p 8080:3000 --name app miapp
docker ps
docker logs -f app
docker exec -it app sh
💡 Imagen vs contenedor: la imagen es la receta/plantilla (immutable); el contenedor es la instancia ejecutándose. Compón con
docker-compose.ymlla app + BBDD + Redis:
# docker-compose.yml
services:
app:
build: .
ports: ["8080:3000"]
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secreto
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Kubernetes: orquestar contenedores
Kubernetes (k8s) orquesta muchos contenedores: los reparte, los reinicia, escala, balancea, gestiona el estado.
Control plane (brain): Worker nodes (muscles):
API server, scheduler, kubelet, kube-proxy
etcd (el "cerebro", Raft) ejecutan pods
| Concepto | Qué es |
|---|---|
| Pod | La unidad mínima (1+ contenedores juntos) |
| Deployment | «Quiero N réplicas de esta app» (declarativo) |
| Service | IP estable + balanceo a los pods |
| Ingress | Entrada HTTP externa (rutas, TLS) |
| ConfigMap / Secret | Configuración / secretos |
| etcd | El almacén de config del clúster (consenso Raft) |
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: miapp
spec:
replicas: 3
selector:
matchLabels: { app: miapp }
template:
metadata:
labels: { app: miapp }
spec:
containers:
- name: miapp
image: miapp:latest
ports: [{ containerPort: 3000 }]
kubectl apply -f deployment.yaml
kubectl get pods
kubectl rollout status deployment/miapp
kubectl scale deployment miapp --replicas=10
kubectl logs -f deployment/miapp
💡 Declarativo: tú dices el estado deseado («3 réplicas»), Kubernetes hace el trabajo de llegar ahí y mantenerlo. Eso es la esencia de k8s y de Terraform.
Terraform y Ansible
- Terraform (IaC): declaras la infraestructura (servidores, VPCs, DNS) y Terraform la crea/mantiene/destruye. Infraestructura declarada, no clicada.
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = { Name = "web" }
}
terraform init && terraform plan && terraform apply
- Ansible: configura los servidores (instala paquetes, copia configs, reinicia servicios) mediante SSH y playbooks en YAML. Agente-less (no instala nada en los servidores).
# playbook.yml
- hosts: web
tasks:
- name: Instalar nginx
apt: name=nginx state=present
- name: Copiar config
template: src=default.conf.j2 dest=/etc/nginx/sites-available/default
- name: Recargar nginx
service: name=nginx state=reloaded
ansible-playbook -i hosts playbook.yml
CI/CD: integrar y desplegar solos
CI (Continuous Integration): cada push compila, corre tests y analiza. CD (Continuous Delivery/Deployment): si pasa, se despliega.
push ──▶ CI: build + test + lint ──▶ (pasa) ──▶ CD: deploy a staging/prod
Con GitHub Actions:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npm ci && npm test
- run: npm run build
💡 La calidad del CD depende de la de los tests: si tus tests son buenos, desplegar cada merge es seguro (trunk-based + CI verde).
Monitoreo y observabilidad
La observabilidad = 3 pilares:
| Pilar | Qué responde | Herramienta |
|---|---|---|
| Métricas | ¿Cuánto? (QPS, latencia, errores, CPU) | Prometheus + Grafana |
| Logs | ¿Qué pasó? | Loki, ELK (Elasticsearch) |
| Trazas (traces) | ¿Dónde tardó? (en qué servicio) | Jaeger, OpenTelemetry |
Métricas: HTTP 5xx rate, latencia p95, QPS, uso de CPU/RAM
Logs: "ERROR: connection to db timed out"
Traces: petición → gateway → auth → db (55ms en db)
💡 p95/p99: la latencia «típica» engaña. Mide p95 (el 95% de las peticiones tardan menos que esto) y p99: es donde se esconden los problemas reales.
Alertas
Define SLO (objetivo de servicio: «99.9% de peticiones en menos de 500 ms») y alerta cuando lo violas. Si no tienes una métrica de «petición servida correctamente», no puedes saber si tu sistema está sano.
Cloud
Los tres grandes: AWS, GCP, Azure. Conceptos equivalentes en los tres:
| Concepto | AWS | GCP | Azure |
|---|---|---|---|
| Servidor virtual | EC2 | Compute Engine | VMs |
| Contenedores | EKS | GKE | AKS |
| BBDD gestionada | RDS | Cloud SQL | SQL DB |
| Almacenamiento | S3 | GCS | Blob |
| Colas | SQS | Pub/Sub | Queue |
| Caché | ElastiCache | Memorystore | Cache |
| Funciones | Lambda | Cloud Functions | Functions |
💡 Aprende conceptos, no consolas: una VPC, un balanceador, un object store y una cola existen en los tres. Elige uno (AWS es el más usado) y saca una certificación (Cloud Practitioner → Solutions Architect Associate) como syllabus estructurado.
Cheatsheet de comandos que debes saber
systemctl status/start/restart/reload/enable <svc>
journalctl -u <svc> -f
nginx -t && systemctl reload nginx
ufw status verbose
ss -tulpn # puertos en escucha
tail -f /var/log/nginx/*.log
docker ps / logs / exec -it
kubectl get pods / logs -f / apply -f
terraform plan / apply
ansible-playbook playbook.yml
Práctica propuesta
- Monta un LEMP a mano (sin Docker): Ubuntu + Nginx + PHP-FPM + MySQL. Sirve un
index.phpreal con conexión a BBDD. - Ponle HTTPS con Let’s Encrypt. Verifica el certificado con
curl -vy en el navegador. - Configura ufw para que solo pasen SSH, 80 y 443. Comprueba que no puedes entrar al 8080.
- Hardenea SSH: sin root, solo claves, fail2ban. Prueba a entrar con contraseña y verifica que se banea.
- Dockeriza una app tuya (Dockerfile + compose con BBDD). Reconstruye la imagen y verifica que el entorno es reproducible.
- Crea un pipeline de CI que corra tus tests en cada push (GitHub Actions).
- Monta Prometheus + Grafana y grafica la latencia y los 5xx de tu Nginx.
- Despliega un sitio en un cloud (el free tier de cualquiera) con Terraform declarando la infra.
Para profundizar
- The Linux Command Line: el clásico gratis, con traducción al español.
- OverTheWire Bandit: práctica de terminal jugando.
- The Art of Command Line: la referencia de fluidez de terminal.
- roadmap.sh/devops: el mapa visual completo de DevOps.
- Comandos Linux, Bash, Nginx, Docker, Firewalls: las wikis del sitio.
- Linux: la ruta, Servidores y Nginx, Redes y seguridad, Infraestructura moderna, Cloud.
- Sigue con 🔧 Backend transversal.