6.2 KiB
Plan: Monitoreo Multi-Host Docker
Objetivo
Permitir que una sola instancia de DockerFlow se conecte a múltiples Docker daemons (local + remotos) y visualice todos los contenedores en un solo dashboard, agrupados por host.
Enfoque
Usar la API TCP de Docker con TLS. No se instala nada adicional en los servidores remotos — solo se configura el Docker daemon para aceptar conexiones TCP.
Fase 1: Configuración de hosts
1.1 Variable de entorno DOCKER_HOSTS
# Formato: nombre=tipo://dirección, separados por coma
# El host local usa socket, los remotos usan tcp+tls
DOCKER_HOSTS="local=unix:///var/run/docker.sock,server-a=tcp://192.168.1.10:2376,server-b=tcp://192.168.1.20:2376"
Si DOCKER_HOSTS no está definido, comportamiento actual (solo socket local). Retrocompatible.
1.2 Certificados TLS
# Directorio de certs por host
DOCKER_CERTS_DIR=./certs
# Estructura:
# certs/
# server-a/
# ca.pem
# cert.pem
# key.pem
# server-b/
# ca.pem
# cert.pem
# key.pem
Fase 2: Cambios en Backend
2.1 src/server/docker.ts — Multi-host connections
Actual: Una sola instancia de dockerode hardcodeada.
const docker = new Docker({ socketPath: "/var/run/docker.sock" });
Nuevo: Map de instancias dockerode por host.
interface DockerHost {
name: string;
client: Docker;
}
function createHosts(): DockerHost[] {
const hostsEnv = process.env.DOCKER_HOSTS;
if (!hostsEnv) {
return [{ name: "local", client: new Docker({ socketPath: "/var/run/docker.sock" }) }];
}
const certsDir = process.env.DOCKER_CERTS_DIR || "./certs";
return hostsEnv.split(",").map((entry) => {
const [name, url] = entry.split("=");
if (url.startsWith("unix://")) {
return { name, client: new Docker({ socketPath: url.replace("unix://", "") }) };
}
// tcp://host:port
const { hostname, port } = new URL(url.replace("tcp://", "https://"));
return {
name,
client: new Docker({
host: hostname,
port: parseInt(port),
ca: fs.readFileSync(`${certsDir}/${name}/ca.pem`),
cert: fs.readFileSync(`${certsDir}/${name}/cert.pem`),
key: fs.readFileSync(`${certsDir}/${name}/key.pem`),
}),
};
});
}
export const dockerHosts = createHosts();
2.2 src/shared/types.ts — Agregar campo host
export interface Service {
// ... campos existentes ...
host: string; // nombre del host (ej: "local", "server-a")
}
2.3 discoverServices() — Iterar sobre todos los hosts
export async function discoverServices(all, projects): Promise<Service[]> {
const allServices: Service[] = [];
for (const { name, client } of dockerHosts) {
const containers = await client.listContainers({ all: true });
const services = containers.map((c) => ({
// ... mapeo actual ...
host: name,
uid: `${name}/${project}/${serviceName}`, // incluir host en uid
}));
allServices.push(...services);
}
// filtrar por projects...
return allServices;
}
2.4 discoverConnections() — Conexiones solo dentro del mismo host
Las conexiones por red compartida solo aplican entre contenedores del mismo host. Agregar filtro:
// Solo conectar servicios del mismo host
if (app.host !== infra.host) continue;
2.5 getContainerLogs() y streamContainerLogs() — Resolver host
Actualmente usan docker.getContainer(id). Cambiar para recibir el host y usar el client correcto:
export async function getContainerLogs(hostName: string, id: string, tail = 200) {
const host = dockerHosts.find(h => h.name === hostName);
const container = host.client.getContainer(id);
// ... resto igual ...
}
2.6 src/server/watcher.ts — Stats y eventos multi-host
pollStats y watchDockerEvents deben iterar sobre todos los hosts. Cada host tiene su propio stream de eventos.
Fase 3: Cambios en Frontend
3.1 Agrupación visual por host
- Usar un borde/fondo coloreado alrededor de los nodos de cada host
- Mostrar label del host sobre cada grupo
- Colores distintos por host (auto-asignados)
3.2 Sidebar/filtro por host
- Agregar selector de host en la UI para filtrar la vista
- Opción "Todos" para ver todo junto
3.3 Panel de stats
- Mostrar en qué host está cada contenedor
- Badge con el nombre del host en cada nodo del grafo
Fase 4: Documentación de setup remoto
Guía para configurar un Docker daemon remoto
En el servidor remoto:
# 1. Generar certificados (una vez)
# Usar el script que incluiremos en tools/generate-certs.sh
# 2. Editar /etc/docker/daemon.json
{
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"],
"tls": true,
"tlscacert": "/etc/docker/ssl/ca.pem",
"tlscert": "/etc/docker/ssl/server-cert.pem",
"tlskey": "/etc/docker/ssl/server-key.pem",
"tlsverify": true
}
# 3. Reiniciar Docker
sudo systemctl restart docker
Script de generación de certs
Incluir tools/generate-certs.sh que genere CA + server cert + client cert.
Archivos a modificar
| Archivo | Cambio |
|---|---|
src/server/docker.ts |
Multi-host connections, refactor todas las funciones |
src/server/watcher.ts |
Stats y eventos por host |
src/server/index.ts |
Pasar host en log subscribe/unsubscribe |
src/shared/types.ts |
Campo host en Service, WSMessage updates |
src/client/App.tsx |
Agrupación visual, filtros, badges |
src/client/components/* |
Nodos con indicador de host |
Archivos nuevos
| Archivo | Descripción |
|---|---|
tools/generate-certs.sh |
Script para generar certificados TLS |
Orden de implementación
- Types (
hostfield) — 5 min docker.tsmulti-host — core del cambiowatcher.tsmulti-hostindex.tsajustes WebSocket- Frontend: badges y agrupación
- Script de certs + docs
- Testing con host local (simular con socket duplicado)
Consideraciones
- Retrocompatible: sin
DOCKER_HOSTS, funciona exactamente igual que ahora - Seguridad: nunca TCP sin TLS, los certs son obligatorios para hosts remotos
- Performance: cada host se consulta en paralelo con
Promise.all - Errores: si un host remoto no responde, mostrar el host como "offline" sin afectar los demás