Cómo estructurar un entorno Docker para varios servicios independientes

Introducción

Estructurar un entorno Docker para varios servicios independientes significa conseguir que cada aplicación tenga límites claros, pueda mantenerse con su propio ciclo de vida y solo comparta con las demás aquello que realmente necesita compartir. El objetivo no es crear el mayor número posible de contenedores ni convertir una pequeña infraestructura en una arquitectura de microservicios. El objetivo es evitar que varias aplicaciones diferentes terminen formando, por comodidad, un único bloque difícil de actualizar, diagnosticar o recuperar.

El problema suele aparecer de forma gradual. Una primera aplicación utiliza su propia base de datos. Después llega un segundo servicio que comparte el mismo proxy. Un tercero necesita conectarse a una API interna. Más adelante se añade monitorización, una herramienta de automatización y quizá una base de datos común. Si todas estas piezas terminan dentro de un único archivo Compose, en una única red y con dependencias cruzadas poco documentadas, Docker sigue ejecutando los contenedores, pero la infraestructura pierde independencia.

La independencia no significa aislamiento absoluto. Los servicios necesitan colaborar. Un proxy inverso debe llegar a varias aplicaciones; una plataforma de monitorización debe observar distintos proyectos; algunas aplicaciones pueden utilizar servicios externos comunes. La cuestión es distinguir una dependencia necesaria de un acoplamiento creado por comodidad.

En Docker, esta separación puede expresarse mediante proyectos independientes, redes privadas, redes compartidas únicamente cuando son necesarias, volúmenes propios, credenciales separadas y dependencias explícitas. De esta forma, una aplicación puede actualizarse, detenerse, migrarse o retirarse sin obligar a modificar todo el servidor.

Este artículo se centra en esa arquitectura de separación. La organización física de carpetas y nombres se desarrolla en cómo organizar correctamente los proyectos Docker, mientras que cómo diseñar una plataforma Docker para una pequeña empresa aborda la arquitectura general. Aquí la pregunta es más específica: ¿cómo deben convivir varios servicios Docker para seguir siendo independientes sin perder la capacidad de comunicarse?

Índice

Qué significa que dos servicios Docker sean independientes

Dos servicios son independientes cuando pueden evolucionar con un grado razonable de autonomía. No significa que nunca se comuniquen ni que estén instalados en servidores distintos.

En la práctica, una aplicación es más independiente cuando:

  • puede arrancarse y detenerse sin afectar innecesariamente a otras;
  • puede actualizarse con su propio calendario;
  • sus datos están claramente identificados;
  • sus credenciales no se reutilizan indiscriminadamente;
  • sus redes limitan comunicaciones no necesarias;
  • sus dependencias están documentadas;
  • puede trasladarse a otro host con cambios controlados;
  • puede retirarse sin dejar una cadena de componentes rotos;
  • sus fallos no derriban por diseño aplicaciones sin relación.

La independencia es, por tanto, una propiedad operativa. Un proyecto puede utilizar cinco contenedores y seguir siendo una sola aplicación independiente. Del mismo modo, dos contenedores aparentemente separados pueden estar fuertemente acoplados si comparten datos, credenciales y ciclos de despliegue.

Independencia técnica frente a independencia funcional

Una separación técnica no garantiza una separación funcional. Colocar cada componente en una red distinta o un archivo Compose diferente puede dar sensación de orden, pero si ambos deben actualizarse siempre juntos o acceder directamente a los mismos datos internos, siguen formando un conjunto acoplado.

La arquitectura debe reflejar la realidad del servicio. Primero se decide qué componentes pertenecen a una misma capacidad; después se expresa esa frontera mediante Docker.

Independencia no significa microservicios

Separar servicios no implica descomponer cada aplicación en decenas de piezas pequeñas. Para una infraestructura modesta, eso puede aumentar más la complejidad que la flexibilidad.

Un sistema puede tener:

  • una aplicación web con frontend, backend y base de datos dentro de un mismo proyecto;
  • un gestor documental independiente;
  • una plataforma de monitorización independiente;
  • un servicio de automatización independiente;
  • un proxy común para publicar varias aplicaciones.

Este modelo ya ofrece separación útil sin adoptar una arquitectura de microservicios.

El criterio es el ciclo de vida

Si varios componentes siempre se despliegan juntos, pertenecen a la misma aplicación y comparten cambios, suele tener sentido mantenerlos en un mismo proyecto.

Si dos aplicaciones solo comparten servidor, pero pueden cambiar, fallar o retirarse de forma independiente, conviene evitar que la definición de una dependa innecesariamente de la otra.

El artículo cómo organizar una infraestructura basada en servicios independientes desarrolla este criterio fuera del ámbito específico de Docker: la separación debe aportar límites claros sin convertir la infraestructura en una red inmanejable de piezas pequeñas.

Elegir correctamente la unidad de despliegue

La decisión fundamental consiste en definir qué componentes forman una unidad que debe administrarse conjuntamente.

Supongamos una aplicación compuesta por:

portal-interno
├── web
├── app
├── db
└── worker

Los cuatro servicios pueden pertenecer al mismo proyecto porque responden a una misma finalidad. El backend depende de la base de datos, el worker procesa tareas de la misma aplicación y el frontend utiliza el backend.

Ahora añadimos:

monitorizacion
automatizacion
gestor-documental

Estas aplicaciones tienen ciclos diferentes. No deberían incorporarse automáticamente al mismo archivo Compose solo porque se ejecutan en el mismo host.

Preguntas para definir la frontera

  • ¿los componentes prestan una misma capacidad?
  • ¿se actualizan normalmente juntos?
  • ¿comparten datos propios de una misma aplicación?
  • ¿uno carece de sentido sin el otro?
  • ¿se probarían conjuntamente?
  • ¿se restaurarían como una sola unidad?
  • ¿sería razonable moverlos juntos a otro servidor?

Cuantas más respuestas sean afirmativas, más sentido tiene agruparlos.

Cuándo varios contenedores deben pertenecer al mismo proyecto

Separar por separar puede empeorar la operación. Hay conjuntos de contenedores que representan una única aplicación y deben tratarse como tal.

Aplicación y base de datos dedicada

Si una base de datos pertenece exclusivamente a una aplicación y ambas se administran como una unidad, puede resultar razonable declararlas en el mismo proyecto, manteniendo siempre los datos persistentes correctamente separados del contenedor.

Frontend y backend de la misma aplicación

Cuando ambos comparten versiones, pruebas y despliegues, separarlos en proyectos distintos puede introducir trabajo adicional sin aportar independencia real.

Procesos auxiliares internos

Workers, tareas programadas, colas o cachés que solo tienen sentido dentro de una aplicación pueden mantenerse en el mismo proyecto.

Servicios estrechamente coordinados

Si una actualización requiere versiones compatibles y siempre se valida el conjunto, una definición común facilita comprender la relación.

El objetivo es que un proyecto represente una unidad operativa coherente, no necesariamente un contenedor.

Cuándo conviene separar aplicaciones en proyectos distintos

La separación aporta valor cuando dos componentes tienen ciclos de vida o responsabilidades diferentes.

Aplicaciones con finalidades distintas

Un gestor documental y una herramienta de monitorización no forman una misma aplicación aunque vivan en el mismo servidor.

Actualizaciones independientes

Si una aplicación puede actualizarse sin tocar la otra, resulta útil que sus archivos y recursos no estén acoplados.

Responsables diferentes

Cuando distintas personas administran servicios diferentes, separar proyectos facilita permisos, documentación y operación.

Criticidad distinta

Una aplicación crítica puede necesitar copias, monitorización y ventanas de cambio más estrictas que una herramienta auxiliar.

Posible migración futura

Si un servicio podría trasladarse a otro host cuando crezca, mantener una frontera limpia desde el principio reduce trabajo posterior.

Necesidades de red diferentes

Un servicio público y una aplicación exclusivamente interna no deberían compartir exposición solo porque están en el mismo archivo Compose.

Pensar en capacidades y no en contenedores

Una forma útil de evitar fragmentación consiste en pensar qué capacidad ofrece cada proyecto.

Proyecto Capacidad Posibles contenedores
proxy Entrada web común Proxy inverso y componentes auxiliares
portal-interno Aplicación de trabajo interna Web, backend, base de datos, worker
automatizacion Ejecución de flujos automatizados Aplicación, worker, base de datos
monitorizacion Observabilidad Recolector, base de métricas, interfaz
documentos Gestión documental Aplicación, base de datos, procesador auxiliar

Este enfoque evita convertir la infraestructura en una colección sin contexto de nombres de imágenes. El inventario debe hablar de servicios útiles, no únicamente de contenedores técnicos.

Usar las redes Docker como fronteras

Las redes son uno de los mecanismos más importantes para expresar qué servicios pueden comunicarse.

El patrón menos recomendable es situar todos los contenedores del servidor en una única red compartida. Funciona y simplifica las primeras pruebas, pero crea dos problemas:

  • servicios sin relación pueden alcanzarse entre sí;
  • la red deja de expresar arquitectura.

Una red útil no solo transporta tráfico. También documenta una frontera.

Principio de necesidad

Un contenedor debería conectarse únicamente a las redes que necesita para cumplir su función.

Por ejemplo, una base de datos interna puede necesitar comunicarse con el backend de su aplicación, pero no con el proxy inverso ni con la base de datos de otra aplicación.

Una red privada por aplicación como patrón base

Para muchos proyectos, una red propia creada por Compose es un buen punto de partida.

portal-interno
├── web
├── app
└── db

red: portal-interno_default

Otro proyecto puede tener:

automatizacion
├── app
├── worker
└── db

red: automatizacion_default

En este diseño, la base de datos de una aplicación no resulta visible automáticamente para la otra.

Ventajas

  • reduce comunicaciones accidentales;
  • evita colisiones de nombres de servicio;
  • facilita retirar un proyecto;
  • hace más comprensibles las dependencias;
  • permite aplicar el principio de mínimo acceso.

No es aislamiento absoluto

Las redes Docker ayudan a separar tráfico, pero la seguridad total depende también del host, los privilegios, los puertos publicados, los montajes y la configuración de cada aplicación.

Cuándo crear redes compartidas

Algunos componentes necesitan comunicarse con varios proyectos. En esos casos puede utilizarse una red común con una finalidad específica.

Ejemplo: red de entrada

red-entrada
├── proxy
├── portal-web
├── automatizacion-web
└── documentos-web

Cada aplicación puede mantener además su red privada:

portal_privada
├── portal-web
├── portal-app
└── portal-db

automatizacion_privada
├── automatizacion-web
├── automatizacion-worker
└── automatizacion-db

Así, los componentes que reciben tráfico del proxy comparten una red específica, pero las bases de datos permanecen dentro de sus fronteras.

Una red compartida debe tener una finalidad

No conviene crear una red denominada simplemente shared y conectar allí cualquier contenedor que necesite hablar con otro. Eso reproduce el problema de la red global.

Los nombres deben expresar propósito:

  • red-entrada;
  • red-monitorizacion;
  • red-integracion.

Revisa los miembros

Una red compartida debe auditarse. Si un proyecto se retira, sus contenedores no deben permanecer conectados mediante recursos residuales.

Compartir un proxy inverso sin mezclar proyectos

Un proxy inverso común es uno de los servicios compartidos más habituales en una plataforma Docker pequeña.

El proxy puede gestionar:

  • entrada HTTP y HTTPS;
  • dominios y subdominios;
  • certificados;
  • redirecciones;
  • enrutamiento hacia distintas aplicaciones.

Compartirlo no significa incluir todas las aplicaciones dentro de su proyecto.

Patrón recomendado

El proxy tiene su propio proyecto. Cada aplicación mantiene el suyo. Los servicios web que deben recibir tráfico se conectan además a una red externa compartida de entrada.

proxy
└── red-entrada

portal
├── red-portal
└── red-entrada

documentos
├── red-documentos
└── red-entrada

De esta manera, detener el gestor documental no obliga a modificar la definición del portal. La dependencia existe a nivel de tráfico, no de despliegue.

El proxy es una dependencia común

Si falla, varias aplicaciones pueden dejar de estar accesibles. Por eso debe estar documentado y monitorizado como componente compartido de infraestructura.

Separar bases de datos y evitar acoplamientos ocultos

Las bases de datos son uno de los lugares donde más fácilmente se pierde independencia.

Una base dedicada por aplicación

No significa necesariamente un contenedor de motor independiente para cada proyecto. Significa que cada aplicación debe tener una frontera clara sobre sus datos.

Dos posibilidades son válidas:

  • cada proyecto tiene su propio motor de base de datos;
  • varios proyectos utilizan un motor compartido, pero con bases, usuarios y permisos separados.

El riesgo del acceso cruzado

Una aplicación no debería consultar directamente las tablas internas de otra porque resulte cómodo. Esto crea una dependencia de implementación.

Si cambia un esquema, la segunda aplicación puede romperse sin que su propio proyecto haya cambiado.

Motor compartido no significa datos compartidos

Puede ser eficiente consolidar bases pequeñas en un mismo servidor de base de datos. La independencia se conserva si:

  • cada aplicación tiene su base o esquema claramente delimitado;
  • las cuentas tienen permisos mínimos;
  • las copias identifican qué datos pertenecen a cada servicio;
  • las migraciones de una aplicación no modifican datos de otra;
  • la dependencia del motor común está documentada.

Definir propiedad de los datos

Los servicios independientes necesitan saber qué sistema es propietario de cada dato importante.

Si dos aplicaciones pueden modificar libremente la misma información, la separación técnica pierde valor porque aparece una dependencia lógica difícil de controlar.

Fuente de verdad

Para cada tipo de información conviene establecer qué proyecto mantiene la versión válida.

Otros servicios pueden:

  • consultarla mediante una interfaz;
  • recibir una copia;
  • procesarla;
  • almacenar referencias;
  • mantener una caché.

Pero no deberían asumir que pueden modificar directamente el almacenamiento interno del sistema propietario.

Separación de volúmenes

Cada proyecto debe poder identificar sus propios datos persistentes. Compartir un mismo volumen entre aplicaciones diferentes solo debería hacerse cuando exista una necesidad clara y se comprenda quién puede leer y escribir.

La gestión detallada de volúmenes se tratará de forma específica dentro del clúster Docker. En esta arquitectura, la regla fundamental es que los datos compartidos deben ser una decisión explícita, no una consecuencia de haber montado la misma ruta por comodidad.

Representar dependencias entre servicios

La independencia no elimina dependencias. Las hace visibles.

Un proyecto puede depender de:

  • un proxy común;
  • DNS;
  • una base de datos externa;
  • un servidor de correo;
  • almacenamiento remoto;
  • una API de otra aplicación;
  • un servicio de identidad;
  • un proveedor externo;
  • una red Docker compartida.

Dependencia no es pertenencia

Que una aplicación necesite un servidor SMTP no significa que deba incorporarlo a su archivo Compose. Puede ser un servicio común gestionado de forma independiente.

Documenta dirección y sentido

No basta con saber que dos proyectos están relacionados. Conviene indicar quién depende de quién.

portal
  ├── depende de proxy para publicación
  ├── depende de SMTP para correo
  └── depende de su propia base de datos

monitorizacion
  └── observa portal, proxy y host

Esta representación ayuda a determinar el orden de recuperación y el impacto de una caída.

Qué puede y qué no puede resolver depends_on

Dentro de un proyecto Compose, depends_on puede expresar orden y dependencia entre servicios relacionados. Es útil, por ejemplo, para indicar que una aplicación necesita su base de datos.

Sin embargo, no debe confundirse con una solución completa a la disponibilidad.

Un proceso iniciado no siempre está listo

Una base de datos puede haber arrancado pero todavía no aceptar conexiones. Una aplicación puede estar ejecutándose pero seguir inicializando su esquema.

No expresa dependencias empresariales

Que un servicio necesite una API externa, un DNS o un almacenamiento remoto no queda resuelto por declarar un orden local de contenedores.

No debe unir proyectos independientes

Si dos proyectos separados solo funcionan porque se han convertido en una cadena rígida de arranque, conviene revisar si realmente son independientes o si necesitan mecanismos de reintento y tolerancia temporal.

Comprobar disponibilidad real entre servicios

Un entorno con varios servicios independientes necesita distinguir tres estados:

  • el contenedor existe;
  • el proceso está ejecutándose;
  • la función está disponible.

Solo el tercer nivel indica que una dependencia puede utilizarse realmente.

Health checks

Cuando la aplicación lo permite, una comprobación de salud puede verificar que el componente responde.

Reintentos

Los servicios deberían tolerar que una dependencia tarde unos segundos en estar disponible o sufra una interrupción temporal. Reiniciar toda la plataforma para recuperar una aplicación es una señal de acoplamiento excesivo.

Errores comprensibles

Si una aplicación depende de otra, los logs deben permitir distinguir un error interno de una dependencia no disponible.

Diseñar la comunicación entre proyectos

Dos proyectos Docker pueden comunicarse de varias formas. La elección debe depender de la relación entre ellos.

Red Docker compartida

Útil cuando ambos viven en el mismo host y necesitan comunicación directa controlada.

API HTTP

Permite crear una interfaz explícita que puede mantenerse aunque uno de los servicios se traslade a otro host.

Colas

Pueden desacoplar procesos cuando no se necesita una respuesta inmediata, aunque introducen un componente adicional que debe mantenerse.

Intercambio de archivos

Para algunos procesos sencillos, una exportación controlada puede ser más simple que una integración permanente.

Base de datos compartida

Debe utilizarse con precaución. Si dos aplicaciones modifican directamente las mismas tablas, dejan de ser realmente independientes.

Elegir el mecanismo más simple suficiente

No hace falta introducir mensajería, descubrimiento avanzado de servicios o gateways complejos si una llamada HTTP o una exportación controlada resuelve el problema.

Evitar publicar puertos innecesarios

En un entorno con varios proyectos, publicar cada puerto al host crea una superficie de red difícil de controlar.

Un contenedor puede comunicarse con otros dentro de una red Docker sin publicar su puerto externamente.

Ejemplo

Internet
  ↓
proxy:443
  ↓
red-entrada
  ↓
portal-web:8080
  ↓
red-portal
  ↓
portal-db:5432

En este esquema, los puertos 8080 y 5432 pueden utilizarse dentro de Docker sin quedar necesariamente publicados en el host.

Ventajas

  • menor superficie de ataque;
  • menos colisiones de puertos;
  • arquitectura más clara;
  • menos reglas de firewall;
  • separación entre tráfico interno y externo.

Publicar solo por necesidad

Un puerto debe salir al host cuando un cliente externo a esa red necesita alcanzarlo. La depuración temporal no debería convertirse en una exposición permanente.

Separar credenciales entre proyectos

La reutilización de credenciales es otra forma de acoplamiento.

Una cuenta por aplicación cuando sea posible

Si varias aplicaciones utilizan un mismo motor de base de datos, cada una debería tener credenciales propias y permisos limitados a sus datos.

No compartir tokens administrativos

Una credencial global utilizada por todos los proyectos aumenta el impacto de una filtración y dificulta revocar acceso a una aplicación concreta.

Rotación independiente

Una credencial bien separada puede rotarse sin modificar servicios que no la utilizan.

Inventariar relaciones

Debe saberse qué proyecto consume cada secreto. Esto facilita evaluar impacto y retirar servicios.

Qué servicios puede tener sentido compartir

La independencia no obliga a duplicar todo. Algunos componentes pueden gestionarse como infraestructura común cuando el coste de mantener uno por proyecto sería innecesario.

Proxy inverso

Centralizar la entrada web puede simplificar certificados y exposición.

Monitorización

Una plataforma de observabilidad puede recibir métricas o comprobar varios proyectos.

Registro centralizado

Puede ser útil cuando el número de servicios justifica recopilar logs fuera de cada aplicación.

Servidor de correo

Varias aplicaciones pueden utilizar un mismo servicio SMTP mediante credenciales separadas.

Motor de base de datos

Puede compartirse si se mantienen bases, usuarios, permisos, copias y responsabilidades claramente separadas.

Almacenamiento externo

Varios servicios pueden utilizar una plataforma común de almacenamiento siempre que cada uno disponga de espacios y permisos definidos.

Compartir infraestructura es razonable cuando la dependencia queda clara y el componente común se gestiona como tal.

Qué componentes conviene no compartir por defecto

Algunos elementos se comparten con demasiada facilidad porque hacerlo reduce trabajo inicial, pero aumentan el acoplamiento.

Una única base para todas las aplicaciones

Mezclar tablas y permisos sin límites dificulta migrar o restaurar un proyecto.

Un mismo usuario administrativo

Impide saber qué servicio accede a qué recursos y aumenta el impacto de una credencial comprometida.

Una única red Docker global

Hace que aplicaciones sin relación puedan verse entre sí.

Un volumen de datos compartido sin propiedad

Puede acabar conteniendo información de varias aplicaciones sin reglas claras de escritura y copia.

Un único archivo Compose para todo

Convierte varias aplicaciones independientes en una unidad artificial de despliegue.

Una configuración común editable por todos

Un cambio destinado a un servicio puede afectar a los demás.

Aislar fallos y mantenimiento

Una arquitectura independiente debe permitir que un problema local permanezca local siempre que sea posible.

Fallo de aplicación

Si el gestor documental se detiene, el portal interno y la monitorización deberían seguir funcionando salvo que exista una dependencia real.

Mantenimiento

Actualizar una herramienta no debería obligar a detener proyectos sin relación.

Errores de configuración

Una variable incorrecta en un proyecto no debería modificar la configuración de todos.

Saturación

Un servicio con consumo descontrolado puede afectar al host completo aunque esté aislado en red. Por ello, la independencia también debe considerar recursos.

Componentes comunes

Los servicios compartidos son excepciones importantes. Si el proxy, el host o un motor de base de datos común falla, varios proyectos pueden verse afectados. Deben identificarse como puntos de fallo comunes.

Evitar que un servicio consuma los recursos de todos

Compartir host significa compartir CPU, memoria, disco y red. Los límites lógicos de Docker no eliminan esa competencia.

Detectar cargas dominantes

Una tarea de importación, una consulta defectuosa o una aplicación con fuga de memoria puede consumir recursos hasta afectar a los demás servicios.

Aplicar límites cuando aporten valor

Los proyectos especialmente variables pueden necesitar límites o reservas de recursos para impedir que un fallo local monopolice el host.

Separar cargas incompatibles

Si una aplicación necesita casi toda la memoria o produce operaciones de disco intensivas, quizá el problema ya no se resuelva únicamente con límites. Puede ser candidata a otro servidor.

La independencia tiene un límite físico

Mientras varios proyectos compartan host, una avería del sistema operativo, almacenamiento o hardware sigue siendo un fallo común.

Permitir actualizaciones independientes

Uno de los principales beneficios de separar proyectos es poder modificar cada aplicación con su propio calendario.

Una actualización no debería requerir redeploy global

Si para cambiar una aplicación hay que reiniciar todos los proyectos, la frontera de despliegue está mal definida o existe un componente común demasiado acoplado.

Versiones propias

Cada proyecto debe poder registrar qué versiones utiliza sin depender de un archivo central gigantesco.

Pruebas por proyecto

La validación debe centrarse primero en la aplicación modificada y después en las dependencias reales con otros servicios.

Compatibilidad en integraciones

Cuando una aplicación ofrece una API a otra, los cambios deben mantener compatibilidad o introducir una transición controlada.

La estrategia detallada de actualización de contenedores tendrá su propio artículo dentro del clúster. Aquí la idea fundamental es que la estructura de proyectos debe permitir actualizar una aplicación sin convertir el cambio en una intervención global.

Separar recuperación por proyecto

Una infraestructura con varios servicios independientes debería poder recuperar una aplicación sin restaurar necesariamente todas las demás.

Identificar datos de cada proyecto

Debe saberse qué volúmenes, bases, archivos y configuraciones pertenecen a cada servicio.

Dependencias comunes

Si varios proyectos utilizan un motor compartido, la política de copia debe permitir recuperar una base individual cuando sea posible y también contemplar la recuperación del componente común.

Orden de recuperación

Las dependencias determinan qué debe levantarse primero. Por ejemplo:

1. host y Docker
2. redes compartidas
3. proxy o servicios comunes
4. base de datos necesaria
5. aplicación
6. comprobación funcional

Prueba independiente

Una buena prueba consiste en reconstruir un proyecto fuera del host original utilizando su definición, configuración autorizada y datos restaurados.

Monitorizar varios proyectos sin convertirlos en uno

La observabilidad puede centralizarse sin acoplar los despliegues.

Inventario central

Puede existir una visión común de proyectos activos, responsables, criticidad y dependencias.

Métricas comunes

CPU, memoria, disco y estado del host pueden observarse de forma central.

Checks por aplicación

Cada proyecto debería tener su propia comprobación funcional. Esto permite saber qué servicio falla realmente.

Logs identificables

Si se centralizan registros, deben conservar contexto de proyecto, servicio y entorno. Un único flujo de logs sin etiquetas vuelve a mezclar lo que la arquitectura intentaba separar.

Alertas con impacto

Una alerta debe permitir distinguir entre:

  • fallo de un contenedor auxiliar;
  • fallo de una aplicación completa;
  • fallo de un servicio compartido;
  • fallo del host.

Ejemplo de entorno con varios servicios independientes

Consideremos una plataforma con cuatro capacidades:

  • proxy inverso;
  • portal interno;
  • automatización;
  • monitorización.

La estructura lógica podría ser:

HOST DOCKER
│
├── PROYECTO: proxy
│   ├── proxy
│   └── red-entrada
│
├── PROYECTO: portal
│   ├── web
│   ├── app
│   ├── db
│   ├── red-portal
│   └── conexión a red-entrada
│
├── PROYECTO: automatizacion
│   ├── web
│   ├── worker
│   ├── db
│   ├── red-automatizacion
│   └── conexión a red-entrada
│
└── PROYECTO: monitorizacion
    ├── monitor
    ├── almacenamiento-metricas
    └── red-monitorizacion

Relaciones

  • el proxy puede alcanzar únicamente los servicios web publicados;
  • la base de datos del portal solo se comunica con el backend del portal;
  • la base de datos de automatización solo se comunica con su aplicación;
  • el portal no accede directamente a la base de automatización;
  • monitorización observa servicios mediante mecanismos definidos;
  • cada proyecto tiene sus propios datos, variables y credenciales.

Qué puede cambiar de forma independiente

La herramienta de automatización puede actualizarse sin redeplegar el portal. El portal puede migrarse a otro host manteniendo el proxy y la monitorización. El proyecto de monitorización puede sustituirse sin modificar los datos de las aplicaciones.

Qué sigue siendo compartido

Todos dependen del host. Las aplicaciones publicadas dependen además del proxy. Esas dependencias deben reconocerse, no ocultarse.

Cómo separar una plataforma Docker demasiado acoplada

Si todos los servicios ya están dentro de un único proyecto, no es necesario reconstruir la plataforma de una vez.

Paso 1. Inventariar componentes

Relaciona contenedores, redes, volúmenes, puertos y variables.

Paso 2. Agrupar por finalidad

Decide qué contenedores forman realmente una aplicación y cuáles pertenecen a capacidades diferentes.

Paso 3. Dibujar dependencias

Identifica comunicaciones, datos compartidos y servicios comunes.

Paso 4. Detectar acoplamientos innecesarios

Busca:

  • bases compartidas sin necesidad;
  • redes globales;
  • credenciales comunes;
  • volúmenes montados por varios proyectos;
  • puertos publicados solo para comunicación interna;
  • servicios que se reinician juntos sin necesitarlo.

Paso 5. Extraer primero un proyecto sencillo

Elige una aplicación con pocas dependencias. Dale su propia definición, red y configuración.

Paso 6. Mantener únicamente las conexiones necesarias

Si necesita el proxy, conéctala a la red de entrada. Si consume una API externa, documenta esa dependencia. No restaures la red global por comodidad.

Paso 7. Probar arranque, parada y recuperación

La nueva unidad debe poder operarse sin intervenir en el resto.

Paso 8. Continuar de forma gradual

Cada separación debe reducir complejidad. Si una extracción produce más dependencias que antes, conviene revisar si esa frontera era correcta.

Errores frecuentes

Confundir un contenedor con un servicio independiente

Una aplicación puede necesitar varios contenedores y seguir siendo una sola unidad funcional.

Crear un único archivo Compose para todo el host

Acopla aplicaciones sin relación y convierte cualquier mantenimiento en un cambio global.

Crear un archivo Compose por contenedor

Produce la fragmentación contraria: componentes que forman una sola aplicación quedan artificialmente separados.

Usar una red global

Facilita las primeras conexiones, pero elimina fronteras y permite comunicaciones innecesarias.

Publicar todos los puertos

La comunicación interna no necesita pasar siempre por el host.

Compartir una base de datos sin separar permisos

Un motor común puede ser razonable; una cuenta común con acceso a todo no.

Acceder directamente a tablas de otra aplicación

Crea una dependencia que puede romperse con cambios internos.

Compartir un volumen entre aplicaciones sin propietario claro

Dificulta copias, recuperación y retirada.

Reutilizar las mismas credenciales

Aumenta impacto de incidentes y dificulta revocación.

Duplicar componentes comunes innecesariamente

La independencia no exige tener un proxy o una plataforma de monitorización distinta por aplicación.

Convertir todo en microservicios

Puede aumentar despliegues, redes, secretos y puntos de fallo sin una necesidad real.

No registrar dependencias compartidas

Un servicio parece independiente hasta que se descubre durante una caída que dependía de otro componente no documentado.

Actualizar varios proyectos juntos por costumbre

Si no existe una dependencia real, esa práctica elimina la ventaja de tener ciclos de vida separados.

No probar la caída de un servicio

La independencia se comprueba observando qué ocurre cuando un componente deja de estar disponible.

Suponer que Docker elimina los fallos comunes

Varios proyectos continúan compartiendo host, kernel, almacenamiento y red física. La separación lógica no equivale a alta disponibilidad.

Lista de comprobación

  • ¿Cada proyecto representa una capacidad o aplicación reconocible?
  • ¿Los contenedores estrechamente relacionados permanecen juntos?
  • ¿Las aplicaciones con ciclos independientes están separadas?
  • ¿Cada proyecto tiene su propia red privada cuando la necesita?
  • ¿Las redes compartidas tienen una función explícita?
  • ¿Solo los servicios necesarios están conectados a redes comunes?
  • ¿El proxy inverso está separado de las aplicaciones?
  • ¿Los puertos internos permanecen sin publicar cuando es posible?
  • ¿Las bases de datos de proyectos diferentes están lógicamente separadas?
  • ¿Cada aplicación utiliza sus propias credenciales?
  • ¿Los permisos de base de datos están limitados?
  • ¿Cada dato importante tiene un sistema propietario?
  • ¿Se evita acceder directamente a tablas internas de otros servicios?
  • ¿Los volúmenes compartidos tienen una necesidad y propietario claros?
  • ¿Las dependencias entre proyectos están documentadas?
  • ¿Se conoce el sentido de cada dependencia?
  • ¿Los servicios toleran retrasos temporales de sus dependencias?
  • ¿Existen comprobaciones de salud útiles?
  • ¿Un fallo local puede permanecer local?
  • ¿Los recursos del host están protegidos frente a consumos anómalos?
  • ¿Cada proyecto puede actualizarse de forma independiente?
  • ¿Cada proyecto puede restaurarse sin recuperar toda la plataforma?
  • ¿Los servicios comunes están identificados como dependencias compartidas?
  • ¿La monitorización distingue proyectos y servicios?
  • ¿Es posible retirar una aplicación sin afectar innecesariamente a las demás?

Preguntas frecuentes

¿Cada aplicación Docker debe tener su propio archivo Compose?

Cuando las aplicaciones tienen ciclos de vida independientes, suele ser una buena práctica mantener proyectos separados. Sin embargo, los contenedores que forman una misma aplicación pueden y suelen compartir una definición Compose.

¿Debo crear una red Docker distinta para cada proyecto?

Como punto de partida, una red privada por proyecto ayuda a limitar comunicaciones. Algunos servicios también pueden conectarse a redes compartidas con una finalidad concreta, como una red de entrada utilizada por el proxy inverso.

¿Dos proyectos Compose pueden comunicarse entre sí?

Sí. Pueden conectarse a una red Docker externa compartida o comunicarse mediante interfaces publicadas, según la arquitectura. Lo importante es que la comunicación sea deliberada y esté documentada.

¿Compartir un proxy inverso rompe la independencia?

No. El proxy puede ser un servicio común de infraestructura. Las aplicaciones siguen siendo proyectos independientes si mantienen sus propios ciclos de despliegue, redes privadas, datos y configuraciones.

¿Puedo compartir PostgreSQL o MariaDB entre varios proyectos?

Sí, siempre que se mantengan bases o esquemas, usuarios, permisos, copias y responsabilidades claramente diferenciados. Debe reconocerse además que el motor se convierte en una dependencia común.

¿Es mejor una base de datos en cada proyecto?

No siempre. Aislar completamente puede simplificar algunas dependencias, pero aumenta componentes que administrar. La elección debe valorar criticidad, recursos, copias, rendimiento y capacidad operativa.

¿Es mala práctica usar container_name para conectar proyectos?

Depender de nombres globales fijados manualmente puede reducir flexibilidad y provocar colisiones. Cuando los proyectos comparten una red, es preferible diseñar nombres y resolución de servicios de forma consciente, evitando dependencias innecesarias de una instancia concreta.

¿Una red compartida equivale a una red insegura?

No necesariamente, pero amplía qué servicios pueden alcanzarse. Por eso debe utilizarse solo cuando existe una necesidad y limitar sus miembros a los proyectos que realmente deben comunicarse.

¿Cómo sé si dos contenedores deben estar en el mismo proyecto?

Si forman una misma aplicación, se despliegan y prueban juntos, comparten ciclo de vida y uno carece de sentido sin el otro, normalmente pertenecen a la misma unidad de despliegue.

¿Separar proyectos evita que un fallo afecte a los demás?

Reduce algunos fallos lógicos y operativos, pero todos los proyectos de un mismo host siguen compartiendo recursos físicos, kernel y Docker Engine. Un fallo del host puede afectar al conjunto.

¿Necesito Kubernetes para gestionar servicios independientes?

No. Una pequeña infraestructura puede mantener varios proyectos independientes con Docker y Compose. Una plataforma de orquestación más compleja solo debería introducirse cuando sus capacidades respondan a necesidades reales.

¿Separar servicios implica duplicar monitorización, proxy y almacenamiento?

No. Algunos componentes pueden compartirse como infraestructura común. La clave es reconocerlos como dependencias compartidas y evitar que esa centralización obligue a unir los ciclos de vida de todas las aplicaciones.

Conclusión

Estructurar un entorno Docker para varios servicios independientes exige definir fronteras antes de definir conexiones. Cada aplicación debe representar una unidad operativa coherente, con sus propios datos, credenciales, redes privadas y ciclo de mantenimiento, mientras que los componentes compartidos deben existir únicamente cuando aporten una función común clara.

La independencia no se consigue multiplicando contenedores, sino reduciendo dependencias innecesarias. Un proyecto puede contener varios servicios estrechamente relacionados y seguir siendo más independiente que diez contenedores separados que comparten datos, credenciales y cambios.

Las redes Docker permiten expresar buena parte de esta arquitectura. Una red privada por proyecto limita comunicaciones; una red compartida puede conectar únicamente los componentes que necesitan colaborar. De esta forma, un proxy puede publicar varias aplicaciones sin acceder a sus bases de datos, y una herramienta de monitorización puede observar distintos proyectos sin formar parte de sus despliegues.

Los datos y las credenciales son fronteras igualmente importantes. Cada aplicación debería saber qué información le pertenece, qué dependencias externas consume y qué permisos necesita. Compartir un motor de base de datos o una infraestructura común puede ser razonable; compartir indiscriminadamente cuentas, tablas o volúmenes no.

La prueba práctica de una buena separación consiste en preguntar si un proyecto puede detenerse, actualizarse, restaurarse, migrarse o retirarse sin obligar a reconstruir servicios que no tienen relación funcional con él. Cuanto más clara sea esa respuesta, más mantenible será la plataforma.

Una infraestructura Docker pequeña no necesita parecerse a una gran arquitectura distribuida. Necesita proyectos bien delimitados, dependencias visibles y mecanismos sencillos de comunicación. Esa combinación permite crecer manteniendo control técnico sin convertir cada nueva aplicación en una pieza más de un bloque monolítico difícil de entender.