---
url: /sre/devops/intermediate/14-compose-coexist.md
description: 单台机器跑多套 Compose 时,如何避免项目间串味、共享中间件、反向代理统一入口以及反模式边界。
---
## 为什么单项目永远够用是错觉
[10 篇 Compose 多服务与网络](10-compose-network.md) 解决了「一个项目里的网络怎么分」。但当一台 2C4G 的服务器同时跑三套独立 Compose 时,问题就不一样了:
* `delivery-demo`:博客 + PostgreSQL
* `nocodb`:开源在线表格,自带 PostgreSQL
* `uptime-kuma`:监控告警,自带 SQLite
这时候「同一台机器上的多套 Compose」才是新挑战。本课给出四个维度的解法:**隔离 / 共享 / 入口 / 监控**。
## 维度一:项目间隔离(默认就够了)
**核心原则**:每个项目独立 network,**绝不**让两个项目的容器进同一个网络。
```bash
# 各自的网络会自动创建
delivery-demo_backend
nocodb_default
uptime-kuma_default
```
跨项目**不要**为了连通就合网络。前面 [10 篇](10-compose-network.md) 讲过:
* 多个项目默认**互不串味**——各自有独立的 `
_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 篇](10-compose-network.md) |
| 把 `docker.sock` 挂给应用容器 | 容器拿到宿主机 root 权限,只适合完全受控的监控组件 |
| 跨项目用服务名连 | 服务名只在同一张 user-defined bridge 内解析,跨网默认不通 |
## 这条线以后怎么走
本课解决了单机多套 Compose 的共存边界。下一步要把这套实践推到 CI/CD 流水线里——镜像怎么进仓库、CI 怎么产出可发布的 digest、CD 怎么把它部署到目标环境。镜像分发与制品晋级见 [23 篇 制品库与镜像仓库](../advanced/image-registry.md),CI/CD 边界划分见 [17 篇 CI/CD 权责分离](16-cicd-separation.md)。
参考:[Compose networks](https://docs.docker.com/compose/how-tos/networking/)、[external network](https://docs.docker.com/compose/how-tos/networking/#use-a-pre-existing-network)。