Guías · Nivel 3

Docker: montar y mantener contenedores en un servidor de operaciones

Instalar Docker, arrancar una herramienta interna con Compose, dejar los datos en volúmenes y saber qué mirar cuando un contenedor se reinicia solo.

Qué vas a conseguir

Un servidor Linux con Docker donde las herramientas internas (un panel, un Grafana, un runner de Jenkins) corren como contenedores: se arrancan con un fichero, se actualizan cambiando una versión y sus datos sobreviven a los reinicios. Y la rutina de diagnóstico cuando uno falla.

Antes de empezar

  • Un servidor Ubuntu o RHEL ya configurado (guías del Nivel 2), con 20 GB libres en /var/lib/docker.
  • Saber qué puertos va a exponer cada herramienta: el firewall del servidor tiene que dejarlos pasar.
  • Un lugar donde guardar los ficheros Compose bajo Git.

1. Instalar y comprobar

bash · srv-tools-01
curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh...sudo usermod -aG docker ansible && newgrp dockerdocker version --format '{{.Server.Version}}'27.3.1docker run --rm hello-world | head -3Hello from Docker!This message shows that your installation appears to be working correctly.docker system dfTYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLEImages          1         0         13.3kB    13.3kB (100%)Containers      0         0         0B        0BLocal Volumes   0         0         0B        0B

El usuario en el grupo docker equivale a root en ese servidor: solo quien administra la máquina debe estar en él.

2. La primera herramienta con Compose

docker-compose.yml
services:  grafana:    image: grafana/grafana-oss:11.3.0    container_name: grafana    restart: unless-stopped    ports:      - "3000:3000"    environment:      GF_SERVER_ROOT_URL: https://tools.contoso.local/grafana/      GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin    volumes:      - grafana-data:/var/lib/grafana    secrets:      - grafana_admin    healthcheck:      test: ["CMD-SHELL", "wget -qO- http://localhost:3000/api/health || exit 1"]      interval: 30s      timeout: 5s      retries: 3 volumes:  grafana-data: secrets:  grafana_admin:    file: ./secrets/grafana_admin.txt

Tres decisiones que te ahorran problemas: la versión fija de la imagen (nunca latest en un servidor), los datos en un volumen con nombre (no dentro del contenedor) y la contraseña en un secreto desde fichero, que va al .gitignore.

bash · srv-tools-01
mkdir -p ~/stacks/grafana/secrets && cd ~/stacks/grafanaopenssl rand -base64 24 > secrets/grafana_admin.txt && chmod 600 secrets/grafana_admin.txtecho "secrets/" >> .gitignoredocker compose up -d[+] Running 2/2 ✔ Volume "grafana_grafana-data"  Created ✔ Container grafana             Starteddocker compose psNAME      IMAGE                       STATUS                   PORTSgrafana   grafana/grafana-oss:11.3.0  Up 40 seconds (healthy)  0.0.0.0:3000->3000/tcpsudo firewall-cmd --add-port=3000/tcp --permanent && sudo firewall-cmd --reload

3. Cuando un contenedor se cae

bash · srv-tools-01
docker compose psNAME      IMAGE                       STATUSgrafana   grafana/grafana-oss:11.3.0  Restarting (1) 12 seconds agodocker compose logs --tail 30 grafanagrafana  | Error: ✗ failed to connect to database: open /var/lib/grafana/grafana.db: permission denieddocker inspect grafana --format '{{.State.ExitCode}} {{.State.Error}} {{.RestartCount}}'1  7docker run --rm -v grafana_grafana-data:/data alpine ls -ln /data | head -3total 1032-rw-r----- 1 0 0 1056768 Oct 22 07:40 grafana.dbdocker run --rm -v grafana_grafana-data:/data alpine chown -R 472:0 /datadocker compose restart grafana && sleep 20 && docker compose ps --format '{{.Name}} {{.Status}}'grafana Up 20 seconds (healthy)

El orden siempre es el mismo: ps (qué estado), logs --tail (qué dice), inspect (código de salida y reinicios). Un contenedor en Restarting con el mismo error en bucle no se arregla reiniciando: se arregla leyendo el log. Aquí el volumen había sido restaurado con root y Grafana corre como el usuario 472.

Si el problema es de red, docker exec -it grafana sh te mete dentro para hacer wget a lo que el contenedor no alcanza, y docker network inspect bridge enseña qué IP tiene cada uno.

4. Actualizar sin perder datos

bash · srv-tools-01
docker run --rm -v grafana_grafana-data:/data -v $PWD/backup:/backup alpine tar czf /backup/grafana-data-2026-10-22.tgz -C /data .sed -i 's/grafana-oss:11.3.0/grafana-oss:11.3.1/' docker-compose.ymldocker compose pull && docker compose up -d[+] Running 1/1 ✔ Container grafana  Starteddocker compose logs --tail 3 grafana | grep -i "http server listen"grafana  | logger=http.server t=2026-10-22T08:01:12Z level=info msg="HTTP Server Listen" address=[::]:3000

Copia del volumen, cambio de versión en el fichero, pull, up -d. Si la nueva versión falla, vuelves a la anterior con el mismo sed y, si hace falta, restauras el .tgz. El fichero Compose con el cambio de versión es el commit que va al ticket.

5. Limpieza mensual

bash · srv-tools-01
docker system dfTYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLEImages          9         3         4.1GB     2.7GB (65%)Build Cache     41        0         1.2GB     1.2GBdocker image prune -a --filter "until=720h"docker builder prune -fdocker system dfTYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLEImages          3         3         1.4GB     0B (0%)

Nunca ejecutes docker volume prune en un servidor sin revisar antes docker volume ls: borra los volúmenes que ningún contenedor usa en ese momento, incluidos los de un servicio parado a propósito.

Siguientes pasos

  • Pon un proxy inverso (Traefik o Caddy) delante de los contenedores para que todos salgan por 443 con certificado y sin abrir un puerto por herramienta.
  • Mete docker compose pull && up -d en el playbook de Ansible: la actualización de las herramientas pasa a ser una tarea más del parcheo.
  • Con más de un servidor o con alta disponibilidad, el salto es Kubernetes (Nivel 4); hasta entonces, Compose en un servidor bien respaldado es suficiente para las herramientas internas.

¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.

Tech Career Lab · newsletter

¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.

Apúntate y llévate las 33 ideas →