写完代码,跑通测试,离"上线"还有一道硬坎——你的服务到底部署在什么环境上?一台物理机、一个机房、还是云上的几个可用区?网络怎么打通、资源怎么分配、数据库和 Redis 放哪里?这些问题全归"部署架构"管。它不是画几张拓扑图交差,而是把软件的每一层需求映射到物理环境的每一层能力上。
部署架构覆盖的三层现实
部署架构要回答的核心问题:软件需要什么,物理环境能给什么,中间怎么对齐?
1. 网络层——连通性与隔离
网络是部署架构的地基。你需要决定:
- VPC / 子网划分:哪些服务对公网暴露,哪些只在内部通信。
- 跨可用区 / 跨机房链路:同城双活还是异地灾备,延迟容忍多少。
- 安全组与防火墙规则:入站出站端口白名单,避免"全开"的惯性操作。
一个常见失误:应用层和数据库层放在同一个子网,公网流量直达数据库端口。正确做法是至少分出前端子网(挂负载均衡)和后端子网(只允许内部访问),数据库子网再单独隔离。
2. 主机层——CPU、内存与密度
每台主机(无论物理机还是云虚拟机)的 CPU 核数和内存大小,决定了你能在这台机器上塞多少进程。关键决策点:
- 大机器 vs 小机器:一台 64 核 256GB 的机器跑所有服务,还是 8 台 8 核 32GB 的机器各跑一个?前者运维简单但单点风险大;后者资源碎片多但故障域小。
- 混部与独占:日志收集 Agent 可以和业务进程混部;Redis 如果做持久化存储,最好独占一台机器,避免内存被挤占后触发 OOM。
- 超卖比例:云上通常允许 CPU 超卖 2-4 倍,内存一般不超卖。如果你自建集群,要明确每台机器的实际可用量,而不是看标称值。
3. 基础软件服务层——数据库、Redis 及中间件
数据库和缓存不是"装上就行",它们的位置直接影响部署架构的可靠性:
- 数据库主从分布:主库放在可用区 A,从库放在可用区 B,写流量走 A,读流量可以就近走 B。
- Redis 集群模式:单实例只适合缓存场景;如果用作会话存储或分布式锁,至少 Sentinel 三节点,甚至 Cluster 六节点起步。
- 中间件依赖方向:服务 A → Redis → MySQL,依赖链不能成环,否则故障会级联放大。
用 Docker Compose 画一张小型部署架构图
纸上谈兵不如动手落地。下面是一个典型中小项目的部署架构,用 docker-compose.yaml 把网络隔离、资源限制、基础服务位置全部声明出来——这就是部署架构的可执行版本。
# docker-compose.yaml — 小型项目部署架构声明
version: "3.9"
networks:
frontend: # 对外网络,挂反向代理
driver: bridge
backend: # 内部网络,应用与基础服务通信
driver: bridge
internal: true # 不暴露到宿主机
services:
# ── 前端网关 ──
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
networks:
- frontend
- backend # 能访问后端应用,但不能直连数据库
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
# ── 业务应用 ──
app:
image: myapp:latest
networks:
- backend # 只在内部网络,不对外
environment:
- DB_HOST=mysql
- DB_PORT=3306
- REDIS_HOST=redis
- REDIS_PORT=6379
deploy:
resources:
limits:
cpus: "2.0"
memory: 1G
replicas: 2 # 两个实例,故障域缩小
# ── 数据库(独占资源) ──
mysql:
image: mysql:8.0
networks:
- backend
volumes:
- mysql-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PWD}
- MYSQL_DATABASE=myapp
deploy:
resources:
limits:
cpus: "2.0"
memory: 2G # 独占 2GB,不与其他进程挤占
# ── Redis 缓存 ──
redis:
image: redis:7-alpine
networks:
- backend
command: ["redis-server", "--maxmemory", "512mb", "--maxmemory-policy", "allkeys-lru"]
deploy:
resources:
limits:
cpus: "0.5"
memory: 768M # 预留 256M 给 Redis 进程自身开销
volumes:
mysql-data:
运行前改什么:
myapp:latest替换成你自己的应用镜像名。${DB_ROOT_PWD}在.env文件里设置,不要硬编码。replicas: 2在单机 Docker Compose 下会启动两个容器;如果切到 Swarm 或 Kubernetes,这个字段直接生效为多实例调度。--maxmemory 512mb和deploy.resources.limits.memory: 768M之间留了 256MB 缓冲,防止 Docker OOM Kill 先于 Redis 自身淘汰策略触发。
启动命令:
# 启动全部服务
docker compose up -d
# 查看各服务资源占用
docker stats --no-stream
# 验证网络隔离:从宿主机无法直连 mysql 3306
# (因为 backend 是 internal 网络,只有 nginx 和 app 能访问)
mysql -h 127.0.0.1 -P 3306 -u root -p # 应该连接失败
这个 Compose 文件把部署架构的三层全部声明了:网络隔离(frontend/backend 分离)、主机资源(CPU/内存 limits)、基础服务位置(MySQL 和 Redis 只挂在 backend 网络)。从这张文件出发,你可以逐步替换为 Kubernetes 的 Deployment + Service + NetworkPolicy,架构骨架不变。
从单机到集群:部署架构的演进路线
上面的 Compose 例子跑在一台机器上,适合开发和小流量场景。当流量增长或可靠性要求提高,部署架构需要演进:
| 阶段 | 网络变化 | 主机变化 | 基础服务变化 |
|---|---|---|---|
| 单机 | 全部 bridge 网络 | 一台机器所有服务 | MySQL/Redis 同机部署 |
| 多机同机房 | 前端子网 + 后端子网 | 应用 2-4 台,数据库独占 | MySQL 主从分离,Redis Sentinel |
| 跨可用区 | 两个 VPC 对等连接 | 每可用区至少 2 台应用 | MySQL 跨 AZ 主从,Redis Cluster |
| 多区域/异地 | VPN / 专线打通 | 每区域独立集群 | 数据同步(异步复制或双写) |
每次演进的核心动作都是先拆网络,再拆主机,最后拆基础服务。反过来做(先加机器但不改网络拓扑)往往导致流量绕路、安全边界模糊。
常见踩坑与决策清单
- 数据库和缓存放同一台机器——内存争抢,Redis 被 OOM Kill 后缓存雪崩。分开部署,哪怕是小规格机器。
- 所有端口对公网开放——至少数据库和 Redis 的端口绝不能暴露。用安全组或
internal: true网络隔离。 - 只看 CPU 不看内存——Java 应用和 Redis 都是内存密集型,内存不够比 CPU 不够更致命。先算内存预算。
- 没有资源 limits——一个失控的进程可以吃掉整台机器的内存,拖垮同机所有服务。务必设置 limits。
- 忽略依赖方向——画一张依赖图,确保方向是单向的:应用 → 缓存 → 数据库,不能反过来。
上线前快速检查清单:
- ✅ 网络是否分出前端和后端子网?
- ✅ 数据库端口是否对公网不可达?
- ✅ 每个服务是否设置了 CPU 和内存 limits?
- ✅ Redis 是否配置了
maxmemory和淘汰策略? - ✅ MySQL 数据目录是否挂了持久卷,重启不丢数据?
- ✅ 应用是否至少两个实例,单实例挂掉不影响整体?
部署架构不是一次性画完就结束的文档,而是随流量、可靠性需求、成本约束持续演进的活设计。从一张 docker-compose.yaml 开始,把网络、资源、基础服务的位置写清楚,后续迁移到 Kubernetes 或多云时,这张文件就是你的架构起点。