Módulo 1 · Fundamentos que sostienen todo

Lección 05 — Linux y terminal

Procesos, permisos, logs, variables de entorno, SSH y scripting para operar tu backend.

Publicada
En esta lección
  1. Objetivos
  2. 1. Procesos: verlos, medirlos, señalarlos
  3. 2. Permisos y usuarios: mínimo privilegio
  4. 3. Logs: encontrar la aguja
  5. 4. Variables de entorno: la configuración que no se versiona
  6. 5. SSH: la puerta de producción
  7. 6. Scripting: automatiza lo que hagas tres veces
  8. Autoevaluación (respóndeme en el chat)

Stack: Django + DRF · Proyecto: TicketFlow · SO: Linux Estado: Publicada — impartición tras corregir la 04 Prerrequisito: Lección 04 — Estructuras de datos


Objetivos

Al terminar esta lección podrás:

  1. Ver, entender y operar los procesos de tu servidor (Gunicorn, Celery, Postgres) con ps, top/htop y señales.
  2. Gestionar permisos y usuarios con criterio de producción (principio de mínimo privilegio).
  3. Leer logs con journalctl, tail -f, grep, awk y encontrar el error en millones de líneas.
  4. Configurar el entorno con variables (12-factor adelantado) sin filtrar secretos.
  5. Conectarte a producción por SSH con claves y crear túneles seguros para depurar.

1. Procesos: verlos, medirlos, señalarlos

Tu TicketFlow en producción son varios procesos: Gunicorn (2-4), Celery (1-4), PostgreSQL, Redis, Nginx. Comandos del oficio:

bash
ps aux | grep gunicorn       # quién soy, cuánta memoria (RSS), cuánta CPU
top                          # en vivo; P ordena por CPU, M por memoria
htop                         # top de los buenos: árboles y colores
ss -tlnp                     # quién escucha en qué puerto (el "netstat" moderno)
uptime                       # carga media: 1, 5 y 15 minutos

La carga media se lee así: en una máquina de 2 vCPU, una carga de 2.00 es "justo llena", de 4.00 es "el doble de trabajo que núcleos" (los procesos esperan). Si uptime dice 8.00 sostenido, tu API va lenta aunque ningún error aparezca.

Señales: la forma elegante de hablar con un proceso:

SeñalQué haceUso típico
SIGTERM (15)apagado educadokill <pid>; Gunicorn termina las peticiones en curso
SIGKILL (9)apagado violentoúltimo recurso; el proceso ni se entera
SIGHUP (1)recarga configGunicorn lo usa para re-leer sin cortar el servicio
SIGUSR1/2definidas por la approtar logs de Gunicorn, workers de debug

Clave: kill -9 es la rendición: el proceso no puede guardar nada (logs, transacciones en curso). Primero SIGTERM y espera; SIGKILL solo si está colgado de verdad. Reiniciar con systemctl restart gunicorn manda SIGTERM y espera correctamente — por eso los servicios de producción viven en systemd, no en nohup.

2. Permisos y usuarios: mínimo privilegio

Regla de producción: tu app no corre como root. Nunca.

bash
sudo useradd -r -s /usr/sbin/nologin ticketflow   # usuario de sistema, sin shell
sudo chown -R ticketflow:ticketflow /srv/ticketflow
# en el service de systemd: User=ticketflow

Permisos en 3 grupos × 3 bits: rwx para usuario/grupo/otros. 755 = yo todo, los demás leen y ejecutan. 600 = solo yo leo/escribo (para secretos). 640 para .env: dueño lee, grupo lee, otros nada.

bash
chmod 600 /srv/ticketflow/.env        # los secretos: solo el usuario de la app
chmod 750 /srv/ticketflow             # nadie del sistema puede listar tu código
ls -la                                # mirar quién es quién antes de tocar
umask 027                             # permisos por defecto prudentes

Errores clásicos (y sus consecuencias): .env con 644 (cualquier usuario del servidor lee tus claves); correr Gunicorn como root (un RCE en tu app = servidor entero comprometido); chmod -R 777 como "arreglo rápido" (arreglo que es la vulnerabilidad).

3. Logs: encontrar la aguja

Un servidor sano genera millones de líneas. Las herramientas de corte:

bash
journalctl -u gunicorn -f                     # seguir el servicio en vivo
tail -n 100 error.log                         # lo último
grep -i "error" gunicorn-error.log            # filtrar
grep -i error gunicorn-error.log | awk '{print $1}' | sort | uniq -c | sort -rn | head

El pipeline de arriba es el comando de diagnóstico: cuenta errores agrupados por la primera columna, ordenados de más a menos. Con esto sabes si tienes un error repetido 5000 veces (un bug) o 5000 errores distintos (una tormenta).

Qué debe contener una línea de log útil (lo formalizamos en la Lección 45):

2026-09-28T10:14:22Z INFO [req_id=ab12cd] POST /api/reservations/ 201 45ms user=1042

Nivel, timestamp UTC, identificador de correlación, acción, resultado, duración, actor. Sin req_id, depurar un incidente multi-servicio es adivinación.

Y la regla de retención: los logs rotan (logrotate, journald con límite de tamaño) o llenan el disco — y un disco lleno es la forma #2 de caerse (la #1 es la BD creciendo sin control; lo verás en el Módulo 2).

4. Variables de entorno: la configuración que no se versiona

Las claves, contraseñas y ajustes por entorno viven en el entorno, no en el código (12-factor: Lección 27 completa):

bash
# .env (fuera de Git: la .gitignore ya lo hace desde la Lección 00)
DEBUG=0
SECRET_KEY=...
DATABASE_URL=postgres://ticketflow:...@localhost/ticketflow
CELERY_BROKER_URL=redis://localhost:6379/0

# cargarlas en tu shell de desarrollo
set -a; source .env; set +a

En Python: os.environ["SECRET_KEY"] (y con django-environ o pydantic-settings, validadas al arrancar). Dos reglas duras:

  1. Un .env.example versionado con todos los nombres y ningún valor real. Quien se incorpore al proyecto copia, rellena y funciona.
  2. Los secretos no van en logs ni en tracebacks: DEBUG=1 en producción imprime tu configuración al mundo (por eso DEBUG=0 es la primera comprobación de cualquier auditoría).

5. SSH: la puerta de producción

Acceso por claves, nunca por contraseña:

bash
ssh-keygen -t ed25519 -C "tu@correo"          # una clave por máquina (o por proyecto)
ssh-copy-id usuario@ticketflow.app            # instalar la pública en el servidor
ssh -i ~/.ssh/ed25519 usuario@ticketflow.app

~/.ssh/config para no teclear:

text
Host prod
    HostName ticketflow.app
    User deploy
    IdentityFile ~/.ssh/ed25519

Y los dos trucos que cambian tu vida de backend:

bash
# Túnel: la BD de producción en tu localhost (solo lectura, con tu usuario de app)
ssh -L 5433:localhost:5432 prod
# ahora: psql -h localhost -p 5433 -U ticketflow ticketflow

# Ejecutar sin abrir shell interactiva (para scripts de despliegue)
ssh prod "systemctl status gunicorn --no-pager"

Clave: el túnel es para depurar con permiso; el acceso a datos de producción se rige por RGPD y política de la empresa (Lección 56). Menos acceso del que necesitas y siempre auditable.

6. Scripting: automatiza lo que hagas tres veces

Todo lo anterior se combina en scripts pequeños y legibles. Ejemplo real: comprobar la salud completa de TicketFlow:

bash
#!/usr/bin/env bash
# healthcheck.sh — salud de TicketFlow en 4 comprobaciones
set -euo pipefail                       # falla rápido, variables obligatorias, pipes estrictos

for url in "http://127.0.0.1:8000/api/health/" "http://127.0.0.1:8000/api/events/"; do
  code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
  if [[ "$code" != 200 ]]; then
    echo "FALLO $url -> $code" >&2
    exit 1
  fi
done
systemctl is-active --quiet gunicorn celery
echo "OK: web y workers vivos"

Lo mínimo de bash que evita el 90% de los sustos: set -euo pipefail (salir ante el primer error), comillas en todas las variables ("$var"), y [[ ]] en vez de [ ]. Y la regla del oficio: si lo has hecho a mano tres veces, es un script; si lo ejecuta un cron, es un job con logs y alerta (Lección 31).


Autoevaluación (respóndeme en el chat)

  1. ¿Por qué systemctl restart gunicorn y no kill -9 a los workers? ¿Qué se pierde con SIGKILL?
  2. Tu /srv/ticketflow/.env tiene permisos 644 y el servidor tiene 5 usuarios. ¿Qué riesgos concretos hay y cómo los cierras?
  3. El pipeline grep... | awk | sort | uniq -c | sort -rn | head: explica cada eslabón y qué pregunta responde en un incidente.
  4. ¿Por qué DEBUG=1 en producción es la primera finding de cualquier auditoría? ¿Qué se filtra exactamente?
  5. Diseña el deploy.sh mínimo para TicketFlow: pull, instalar deps, migrar, recolectar estáticos, recargar servicios. ¿Qué orden has elegido y por qué el "migrar" va antes de recargar?

Continúa con los ejercicios. Las soluciones solo tras intentarlo.