Skip to content

14 | 单机 Compose 多项目共存

为什么单项目永远够用是错觉 ​

10 篇 Compose 多服务与网络 解决了「一个项目里的网络怎么分」。但当一台 2C4G 的服务器同时跑三套独立 Compose 时,问题就不一样了:

  • delivery-demo:博客 + PostgreSQL
  • nocodb:开源在线表格,自带 PostgreSQL
  • uptime-kuma:监控告警,自带 SQLite

这时候「同一台机器上的多套 Compose」才是新挑战。本课给出四个维度的解法:隔离 / 共享 / 入口 / 监控。

维度一:项目间隔离(默认就够了) ​

核心原则:每个项目独立 network,绝不让两个项目的进同一个网络。

bash
# 各自的网络会自动创建
delivery-demo_backend
nocodb_default
uptime-kuma_default

跨项目不要为了连通就合网络。前面 10 篇 讲过:

  • 多个项目默认互不串味——各自有独立的 <dir>_default 桥
  • port is already allocated 是因为端口映射到宿主机(共用宿主机的网络栈),不是桥之间串了

这意味着:不做任何配置,三套 Compose 默认就不会在容器层面互相干扰。

维度二:共享(独立项目 + external 网络) ​

如果 delivery-demo 和 nocodb 想共用一个 Redis(而不是各跑一份),正确做法不是让它们的网络互连,而是单独起第三个 Compose 项目跑共享服务,其他项目用 external: true 引用:

bash
# 1. 先创建共享网络
docker network create shared-infra
yaml
# shared-infra/compose.yaml
name: shared-infra
services:
  redis:
    image: redis:7-alpine
    volumes:
      - redis-shared:/data
    networks:
      - shared
volumes:
  redis-shared:
networks:
  shared:
    name: shared-infra-shared
    external: true     # 告诉 Compose 这张网络已经存在,不要再创建
yaml
# delivery-demo/compose.yaml
services:
  app:
    image: my-api:1.0
    networks:
      - backend
      - shared          # 显式加入共享网络
networks:
  backend: {}
  shared:
    external: true      # 引用上面那张
    name: shared-infra-shared

nocodb 的 compose 同理。三套项目通过第三方桥互相看见,而不是直接打通两个项目的网络栈。任意一套下线不影响其他两套的内部网络。

跨项目需求解法
共享 MySQL / Redis单独 Compose 项目跑共享服务,其他项目 external: true 引用
delivery-demo 想调 nocodb 的 API通过宿主机端口(127.0.0.1:port)或
监控采集多项目容器见下面「维度四」

反模式:「连不通就把 network mode 改成 host」——这是把 namespace 隔离整个拆掉,所有端口回到宿主机全局命名空间,所有问题都回来了。

维度三:反向代理统一入口 ​

每套 Compose 项目都把端口映射到宿主机,端口会很快耗尽。正确做法是用 Nginx / Caddy 做反向代理,按域名分流:

nginx
# /etc/nginx/conf.d/services.conf
server {
    listen 80;
    server_name blog.example.com;
    location / {
        proxy_pass http://127.0.0.1:18080;   # delivery-demo web
    }
}

server {
    listen 80;
    server_name nocodb.example.com;
    location / {
        proxy_pass http://127.0.0.1:18081;   # nocodb web
    }
}

Nginx 本身可以用系统包安装,也可以单独起一个 Compose 项目跑。三套被代理的应用仍然只绑 127.0.0.1,对外端口只有 Nginx 的 80/443。

经验值:服务器上每个 Compose 项目对外只暴露 1-2 个端口,通过反代按域名分发,内部全靠服务名通信。

维度四:跨项目监控采集 ​

Prometheus / cAdvisor 之类的监控组件需要采集多套 Compose 的容器指标。它们一般不加入应用网络,而是直接读宿主机的 docker.sock:

yaml
# monitoring/compose.yaml
services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - /var/run/docker.sock:/var/run/docker.sock:ro
  cadvisor:
    image: gcr.io/cadvisor/cadvisor
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro

cAdvisor 通过 docker.sock 看到宿主机上所有容器,不论它们属于哪个 Compose 项目。这绕开了「加入哪个网络才能采集」的难题。

把 /var/run/docker.sock 挂进容器等于给容器了宿主机 root 权限——这只适合完全受控的监控组件,对应用容器禁用。

反模式清单 ​

反模式后果
为连通把两个项目的网络合一张端口冲突、容器名冲突,串味回到起点
network_mode: host 解决「连不上」隔离全部丢失,端口回到宿主机全局命名空间
用宿主机的 localhost 连容器端口「127.0.0.1 在哪」要重新讲一遍,见 10 篇
把 docker.sock 挂给应用容器容器拿到宿主机 root 权限,只适合完全受控的监控组件
跨项目用服务名连服务名只在同一张 user-defined bridge 内解析,跨网默认不通

这条线以后怎么走 ​

本课解决了单机多套 Compose 的共存边界。下一步要把这套实践推到 里——镜像怎么进仓库、CI 怎么产出可发布的 digest、CD 怎么把它部署到目标环境。镜像分发与制品晋级见 23 篇 制品库与镜像仓库,CI/CD 边界划分见 17 篇 CI/CD 权责分离。

参考:Compose networks、external network。