主题
中大型互联网企业:全量架构蓝图
这份文档要解决一个问题:一个转行的人,怎么知道"真实互联网公司的那套东西"到底长什么样。
你在网上搜到的架构图,往往只有一张"用户 → Nginx → 应用 → 数据库"的简笔画; 你在公司里接触到的,往往只是自己负责的那一小块。两头都看不到全貌。
本文按一线互联网公司(日活百万到千万级)的真实形态,把一整套架构从外到内、 从业务线到支撑线全部铺开:四大环境、混合云、CDN/WAF、四层七层负载、K8s 与 PaaS、 数据库一主两从与分库分表、缓存链路、消息队列、全链路监控、日志收集、 配置中心、灰度发布、容灾与安全。
读法建议:第 0~2 章先建立"公司里有哪些系统、它们各自干什么"的地图; 第 3~8 章逐层往下钻,每一层都讲清"它解决什么问题、高可用怎么做的、常见坑在哪"; 第 9~11 章讲支撑线(监控/日志/发布)和演进路线。 全文出现的服务器数量、配置档位都给了三档对照表,你可以按自己手头的机器资源选一档照着落地。
先记住一句话
中大型架构的所有复杂度,几乎都来自两个词:高可用(坏了不能停)和可扩展(涨了要能扛)。 你看到任何一个"多出来"的组件,只要问一句"它是在解决高可用还是可扩展",答案通常立刻就有了。
目录导航
| 章节 | 内容 | 你会得到什么 |
|---|---|---|
| 第 0 章 | 全局地图:一家公司到底有多少套系统 | 先看清森林,再进树林 |
| 第 1 章 | 四大环境:dev / test / uat / prod | 为什么要有四套,怎么隔离 |
| 第 2 章 | 服务器规划三档对照表 | 20 台 / 60 台 / 120 台怎么分配 |
| 第 3 章 | 混合云与网络分区 | 自建机房 + 公有云怎么连起来 |
| 第 4 章 | 流量入口全链路 | DNS → CDN → WAF → ELB/SLB → 网关 |
| 第 5 章 | 应用层:K8s 与 PaaS 平台 | 容器编排、服务网格、发布平台 |
| 第 6 章 | 数据层:数据库、缓存、搜索 | 一主两从、分库分表、缓存链路 |
| 第 7 章 | 中间件全家桶 | MQ、配置中心、注册中心、分布式事务 |
| 第 8 章 | 存储与文件 | 对象存储、NAS、备份 |
| 第 9 章 | 三条支撑线:监控 / 日志 / 发布 | 业务线、监控线、收集线齐备 |
| 第 10 章 | 安全、容灾与应急 | 高可用做到什么程度算合格 |
| 第 11 章 | 演进路线:小公司怎么长成这个样子 | 别一步到位,按阶段长 |
| 第 12 章 | 附录:术语速查与自检清单 | 随时回来查 |
第 0 章 全局地图:一家公司到底有多少套系统
在进入细节之前,先建立一张鸟瞰图。一家成熟互联网公司的技术资产,可以分成 5 个平面(Plane)。
图 0-1 五大平面 + 一条交付线:一张图理解公司里所有系统的分工
为什么要有这张图? 因为转行的人最容易犯的错,是把"公司技术栈"理解成"一堆软件的列表"。 实际上它们是有层级的:你在第 ② 层改一个配置,可能要在第 ④ 层(配置中心)操作, 观察结果要在第 ⑤ 层(监控)看,出问题回滚要用 ★ 层(发布系统)。 搞清楚"我现在动的这个东西在哪一层、会影响哪一层",是运维最基本的素养。
五个平面里,哪个最容易被新人忽略?
第 ⑤ 观测平面,以及第 ④ 支撑平面。 新人通常能理解"应用要跑在服务器上",但很难意识到: 一个 500 万日活的产品,观测系统的服务器数量往往和业务服务器数量是同一个量级。 日志、指标、链路三类数据每天能产生几十 TB。这不是"辅助功能",而是产线的一部分。
0.1 真实业务线长什么样
技术架构不是凭空来的,它是被业务逼出来的。以一家典型的电商 + 内容社区公司为例:
| 业务线 | 典型系统 | 技术特征 | 对架构的硬要求 |
|---|---|---|---|
| 交易 | 商品、购物车、订单、支付、退款 | 强一致、事务、幂等 | 数据库不能丢数据,链路可对账 |
| 营销 | 优惠券、秒杀、拼团、积分 | 瞬时高并发、可降级 | 秒杀必须独立集群,不能拖垮主站 |
| 内容 | 帖子、视频、评论、Feed 流 | 读多写少、海量存储 | 缓存命中率、CDN 成本、推荐算力 |
| 用户 | 登录、注册、实名、账号 | 全局强依赖 | 挂了全站登不上,必须最高可用等级 |
| 消息 | 站内信、Push、IM | 长连接、海量并发 | 长连接网关、消息不丢不乱序 |
| 数据 | 报表、BI、风控、推荐特征 | 离线 + 实时计算 | 数仓、实时链路、不影响在线库 |
| 基础 | 工单、审批、运营后台 | 内部使用、低并发 | 与生产隔离,避免误操作 |
一个非常典型的认知误区
新人常以为"公司就一个网站"。 实际上一个电商 App 打开首页,可能同时调用了 20~50 个后端服务: 用户信息、商品详情、库存、价格、推荐、广告、优惠券、风控、统计…… 这也是为什么必须有"服务治理"——服务数量一多,人工管理连接关系就彻底不可行了。
第 1 章 四大环境:dev / test / uat / prod
1.1 为什么必须要四套环境
一句话:因为你不能在"正在为几百万用户服务"的机器上,试自己刚写的代码。
但光有"生产"和"测试"两套,也不够。真实公司要拆四套,是因为不同阶段要验证的东西完全不同:
| 环境 | 全称 | 谁在用 | 主要验证什么 | 数据 | 对外 |
|---|---|---|---|---|---|
| dev | Development 开发环境 | 开发自己 | 代码能不能跑通、单元能不能过 | 造的数据(随便改) | 否 |
| test | Test / SIT 测试环境 | 测试工程师 | 功能对不对、接口通不通 | 造的测试数据 | 否 |
| uat | User Acceptance Test 验收环境 | 产品、业务方、客户 | 像用户一样用一遍,业务逻辑对不对 | 接近生产的数据(脱敏) | 部分(预发布域名) |
| prod | Production 生产环境 | 真实用户 | ——(它就是最终效果) | 真实数据 | 是 |
一个记忆方法
- dev = 「我写的代码能不能跑」→ 关心编译和启动
- test = 「这个功能符不符合需求文档」→ 关心功能正确性
- uat = 「业务方/客户愿不愿意收货」→ 关心业务正确性和体验
- prod = 「用户在用」→ 关心稳定、性能、数据安全
每往上一层,对"配置的真实度"和"数据的真实度"要求都更高,但"操作的随意度"都更低。 dev 上你可以随手 kubectl delete,prod 上连登录都要走跳板机和审批。
1.2 环境隔离,到底要隔离哪些东西
这是新人最容易想当然的地方:以为"换个数据库地址就叫隔离了"。真实的隔离至少有 7 个维度:
| 隔离维度 | dev | test | uat | prod |
|---|---|---|---|---|
| 网络 | 独立 VPC/VLAN | 独立子网 | 独立子网 | 独立 VPC + 最小权限 |
| K8s 集群 | 共用小集群 或 独立 | 独立集群 | 独立集群(配置贴近 prod) | 独立集群(多可用区) |
| 数据库 | 单实例、随便重置 | 单实例 | 主从(备库可读) | 一主两从 + 高可用切换 |
| Redis | 单实例 | 单实例 | 主从 | Cluster 集群 / 主从+哨兵 |
| 中间件 | 共用一套 | 共用一套 | 独立 Nacos/RocketMQ | 独立全量高可用集群 |
| 域名 | dev-xxx.公司域名 | test-xxx.公司域名 | uat-xxx.公司域名 | xxx.公司域名 |
| 权限 | 开发可自管 | 测试可部署 | 受限 | 最小权限 + 双人复核 + 全程审计 |
| 配置 | 硬编码可接受 | 集中管理 | 集中管理 | 配置中心,动态生效,可回滚 |
关于「数据」的红线
任何环境都不允许把生产数据直接拷到非生产环境。 真实公司要做的是"脱敏":手机号 138****8888、身份证、银行卡、地址全部替换, 库名表结构保持一致,但数据是假的。 原因:生产数据泄露 = 数据安全事件 = 罚款 + 刑责(中国《个人信息保护法》)。 这条线是绝对不能碰的,不是"建议"。
1.3 环境的成本现实:并不是四套都是 1:1
如果四个环境完全 1:1 复制生产,成本会变成 4 倍,没有公司能承受。 真实做法是按需缩容:
| 环境 | 相对生产的规格 | 常见做法 |
|---|---|---|
| dev | 5%~10% | 所有微服务挤在一个小集群,副本数全为 1 |
| test | 10%~20% | 副本数 1~2,数据库单实例 |
| uat | 30%~50% | 副本数接近生产,但机器规格降一档 |
| prod | 100% | 全量高可用,多可用区 |
但这会导致一个经典问题:uat 压测通过,生产还是炸。 原因是 uat 的机器数、连接数、数据量都和 prod 不同。 所以成熟公司会额外建压测环境(perf/stress),或者在生产做全链路压测(用影子库 + 染色流量, 在不影响真实用户的前提下压测生产集群)。这也是为什么你会在招聘 JD 里看到"全链路压测"这个词。
第 2 章 服务器规划:三档对照表
下面给出的 IP 规划使用 192.168.0.0/24(内网私网段,你可以按自己的环境改)。 注意:这里只列"主要角色服务器",真实环境一台物理机/云主机上会跑多个容器或虚拟机。
2.1 三档规模总览
| 档位 | 服务器总数 | 适用场景 | 所在环境 |
|---|---|---|---|
| 精简版 | 约 20 台 | 自建学习环境、创业公司 MVP | dev/test 合一、uat/prod 分离 |
| 标准版 | 约 60 台 | 日活几十万,中厂标准形态 | 四环境齐全,高可用基本到位 |
| 完整版 | 120 台以上 | 日活百万到千万,大厂形态 | 多可用区、混合云、异地容灾 |
怎么选
- 你如果只有 3~5 台机器:先照"精简版",并且把 dev/test/uat 三环境合一,只保留 prod 独立。
- 有 10~20 台:可以直接从精简版完整落地,这是最推荐的学习路径。
- 标准版和完整版不要一步到位,它们是"演进目标",看懂即可(第 11 章给演进路径)。
2.2 精简版(约 20 台)详细规划
这是最推荐你实际落地的一档。目标:把架构的"形状"完整走通。
| 编号 | 主机名 | IP | 配置 | 角色 | 主要软件 |
|---|---|---|---|---|---|
| 1 | ops-runner | 192.168.0.10 | 2C4G | 运维跳板 / 堡垒机 | Jumpserver 或 sshd + 审计 |
| 2 | gitlab | 192.168.0.11 | 4C8G | 代码仓库 | GitLab CE(或 Gitea,见备注) |
| 3 | jenkins-ci | 192.168.0.12 | 4C8G | CI 服务器(dev/test/uat 共用) | Jenkins + Maven + Node + Docker |
| 4 | jenkins-prod | 192.168.0.13 | 4C8G | CI 服务器(prod 专用) | Jenkins(独立,权限收紧) |
| 5 | harbor | 192.168.0.14 | 4C8G | 镜像仓库 | Harbor(自带 PostgreSQL + Redis) |
| 6 | nexus | 192.168.0.15 | 2C4G | 制品仓库 | Nexus 3(Maven/npm 私服) |
| 7 | k8s-master-1 | 192.168.0.21 | 4C8G | K8s 控制平面 | kube-apiserver / etcd |
| 8 | k8s-master-2 | 192.168.0.22 | 4C8G | K8s 控制平面 | 同上(两master 即最低可用配置) |
| 9 | k8s-master-3 | 192.168.0.23 | 4C8G | K8s 控制平面 | 同上(3 台才满足 etcd 多数派) |
| 10 | k8s-node-1 | 192.168.0.24 | 8C16G | 工作节点 | kubelet / containerd |
| 11 | k8s-node-2 | 192.168.0.25 | 8C16G | 工作节点 | 同上 |
| 12 | k8s-node-3 | 192.168.0.26 | 8C16G | 工作节点 | 同上(3 节点才谈得上高可用) |
| 13 | mysql-master | 192.168.0.31 | 8C32G | 数据库主库 | MySQL 8.0 + 半同步 |
| 14 | mysql-slave-1 | 192.168.0.32 | 8C32G | 数据库从库 1 | MySQL 8.0(同步复制) |
| 15 | redis-1 | 192.168.0.41 | 4C16G | 缓存 | Redis 7(主)+ Sentinel |
| 16 | redis-2 | 192.168.0.42 | 4C16G | 缓存 | Redis 7(从)+ Sentinel |
| 17 | nacos-1 | 192.168.0.51 | 4C8G | 注册/配置中心 | Nacos 集群节点 1 |
| 18 | nacos-2 | 192.168.0.52 | 4C8G | 注册/配置中心 | Nacos 集群节点 2 |
| 19 | mq-1 | 192.168.0.61 | 4C16G | 消息队列 | RocketMQ NameServer + Broker |
| 20 | monitor | 192.168.0.71 | 8C32G | 监控 / 日志 | Prometheus + Grafana + ELK |
精简版的取舍(很重要,要明确知道自己省掉了什么):
| 省掉的组件 | 后果 | 什么时候必须补上 |
|---|---|---|
| 独立的 test/uat 环境 | 测试会污染开发环境,uat 与 prod 配置有差异 | 团队超过 5 人 |
| 第二个 Redis 集群 | Redis 挂了缓存全失效,数据库可能被压垮 | 上线前 |
| Elasticsearch 独立集群 | 日志和监控抢资源;日志查询慢 | 日志量 > 100GB/天 |
| SkyWalking APM | 出了问题只能靠猜,排查靠 grep 日志 | 微服务数量 > 10 |
| CDN / WAF | 静态资源全走源站,带宽贵且没有攻击防护 | 有真实用户访问 |
| 异地容灾 | 机房整体故障 = 服务全停 | 有 SLA 承诺(≥99.9%) |
| 分库分表 | 单表数据量涨到几千万后查询变慢 | 单表 > 2000 万行 |
2.3 标准版(约 60 台)详细规划
标准版的关键变化:四环境分离、每环境独立一套、所有核心组件至少双节点。
| 分组 | 数量 | 配置 | 说明 |
|---|---|---|---|
| 公共基础(跨环境) | 6 | 4C8G ~ 8C16G | 跳板机、GitLab(主备)、Jenkins 主节点、Harbor(双节点)、Nexus、VPN |
| dev 环境 | 6 | 4C8G | K8s 单 master + 2 node(或复用)、MySQL 单实例、Redis 单实例、Nacos 单机 |
| test 环境 | 8 | 4C8G | K8s 单 master + 2 node、MySQL 主从、Redis 主从、Nacos 双节点 |
| uat 环境 | 10 | 8C16G | K8s 3 master + 3 node、MySQL 一主一从、Redis 主从+哨兵、Nacos 三节点 |
| prod 应用集群 | 12 | 16C32G | K8s 3 master + 9 node(跨 3 可用区) |
| prod 数据集群 | 9 | 16C64G | MySQL 一主两从(3)+ Redis Cluster(3)+ MQ(3) |
| prod 中间件 | 6 | 8C16G | Nacos 三节点 + ES 三节点 |
| prod 网关/入口 | 5 | 8C16G | Nginx/LB 双节点 + API 网关双节点 + 跳板 |
| 观测平台 | 8 | 8C32G ~ 16C32G | Prometheus 双实例、Grafana、SkyWalking OAP×2、ES 日志集群×3、Kibana |
合计约 60 台(含虚拟机),全部覆盖高可用最低要求。
2.4 完整版(120 台以上)的关键增量
完整版不是"标准版乘以 2",而是增加了几个维度的能力:
| 增量能力 | 具体做法 | 服务器增量 |
|---|---|---|
| 同城双活 | 同城两个机房都能承接全量流量,任一机房故障用户无感 | +30 台 |
| 异地多活 / 异地容灾 | 异地机房数据同步,主机房灾难时切换(RTO 分钟级) | +40 台 |
| 单元化(Set 化) | 按用户 ID 分片,每个单元自成闭环,跨单元调用极少 | 架构级改造 |
| 存储与计算分离 | 数据库上云 RDS、缓存上云、对象存储上云 | 自建机器减少,云成本上升 |
| 大数据平台 | Hadoop/Spark/Flink 集群、数据仓库、实时计算 | +30 台 |
| AI / 推荐平台 | GPU 服务器、模型服务、特征平台 | +10 台(GPU 机器昂贵) |
| 安全体系 | WAF 集群、堡垒机集群、日志审计、态势感知 | +8 台 |
一个反直觉的事实
到了完整版这个体量,"服务器数量"这个说法本身就开始失效了。 因为大量算力跑在云上(弹性伸缩的 Pod、Serverless 函数、云数据库), 机器数是动态的。这时运维的核心指标从"机器数"变成了 "成本(单位请求成本)、可用性(SLA 达成率)、变更效率(一天能发多少次版)"。
第 3 章 混合云与网络分区
3.1 什么是混合云,为什么大公司都是混合云
混合云 = 自建机房(IDC)+ 公有云(阿里云/腾讯云/AWS)同时使用。
为什么不全用云,或全自建?因为两者各有不可替代的优势:
| 维度 | 自建 IDC | 公有云 |
|---|---|---|
| 单位算力成本 | 长期看便宜(3 年以上折旧完更便宜) | 短期便宜,长期贵 |
| 弹性能力 | 差(买机器要几周) | 极强(分钟级扩容) |
| 运维负担 | 自己负责硬件、电力、网络 | 云厂商负责,你只管道内的 |
| 数据合规 | 数据在自己手里 | 需评估合规要求 |
| 突发流量 | 扛不住(大促只能提前备机器) | 完美(弹性伸缩 + 按量付费) |
| 故障域 | 单机房故障影响大 | 多可用区天然隔离 |
真实公司的典型组合("稳态 + 敏态"):
图 3-1 混合云的本质:稳态放自建,敏态放公有云,中间用专线打通
混合云最核心的三个技术问题
- 网络怎么通:专线(成本高、延迟低、稳定)vs VPN(便宜、走公网、抖动大)vs 云企业网。 真实场景:核心链路走专线,非核心走 VPN,专线还要做主备双线。
- 数据怎么同步:跨云同步要走公网或专线,延迟通常在 1~20ms, 所以跨云的主从复制通常只能做异步,也就意味着有数据丢失窗口。
- 服务怎么发现:跨云的服务注册要特别小心,注册中心(Nacos)不能跨云乱注册, 否则一个云上的服务挂了,另一个云上的调用方会一直超时重试。 做法:按可用区/云划分子集群,注册中心做区域隔离。
3.2 网络分区规划(真实公司的做法)
真实公司的网络是分层 + 分区的。一个标准的三层网络模型:
图 3-2 网络安全分区:四层分区,越往下越重要、越不该被直接暴露
VPC / 子网划分示例(标准版):
| 分区 | 网段 | 说明 |
|---|---|---|
| 公网/ LB | 192.168.0.0/26 | 负载均衡 VIP |
| DMZ | 192.168.0.64/26 | 网关、反向代理 |
| 应用区 | 192.168.1.0/24 | K8s Pod 网段 |
| 数据区 | 192.168.2.0/24 | MySQL / Redis / MQ |
| 管理区 | 192.168.0.128/25 | Jenkins / 监控 / 跳板 |
| K8s Service | 10.96.0.0/12 | 集群 ClusterIP 段 |
| K8s Pod | 10.244.0.0/16 | Calico/Flannel 分配的 Pod IP |
三个真实踩过的坑
- Pod 网段和办公网段撞了:同事在办公室访问集群里的服务,发现路由到了自己电脑。 原因:Pod 用了
192.168.0.0/16,和公司办公网完全重叠。规划时一定要先问清全公司网段。 - K8s Service 网段和 VPC 撞了:
10.96.0.0/12和云上 VPC 的10.0.0.0/8有重叠, 导致部分 Pod 无法访问云上数据库。规划时必须避开10.0.0.0/8里已用的部分。 - 节点数超过 110 个后 Pod IP 不够:默认 Pod CIDR 是
/24(每节点 254 个 IP), 一个节点跑 200 个 Pod 就会失败。规划时给每个节点留/23或更大。
3.3 混合云的真实落地形态(三层部署结构)
真实公司的混合云不是"机房 + 云"这么笼统,而是按业务特性分三层部署:
图 3-3 混合云的三层落地结构:弹性层 / 核心层 / 灾备层
三层结构的核心思想
"把最需要弹性的放云上,把最需要掌控的放机房,把最需要安全的放远端。"
这三个诉求天然冲突,所以必须分层:
- 弹性 → 云(分钟级扩 100 台)
- 掌控与成本 → 自建(长期成本低、网络拓扑自己说了算)
- 安全与合规 → 异地(物理隔离,灾难时还有一份)
转行者最需要建立的认知:混合云不是"落后"或"过渡"状态, 它是互联网公司的长期稳态选择。全云和全自建都有无法解决的短板, 所以头部公司(阿里、腾讯、字节、美团)都是混合云。
3.4 云上资源清单(你会在控制台看到的东西)
新人第一次登录云控制台会懵:几百个云产品。实际常用的就这些:
| 类别 | 云产品 | 自建对应 | 用途 |
|---|---|---|---|
| 计算 | ECS/CVM(云主机)、ACK/TKE(托管 K8s) | 物理机、kubeadm | 跑服务 |
| 网络 | VPC、子网、安全组、NAT 网关 | VLAN、iptables | 网络隔离 |
| CLB/ELB/SLB(负载均衡) | LVS/Nginx | 流量分发 | |
| EIP(弹性公网 IP) | 公网 IP | 对外访问 | |
| 专线 / VPN 网关 / 云企业网 | 光纤 / IPsec | 混合云互联 | |
| 存储 | 云盘(ESSD/SSD)、NAS、OSS/COS | SAS/SAN、NFS、MinIO | 数据存储 |
| 数据库 | RDS、Redis、MongoDB、PolarDB | 自建 MySQL/Redis | 托管数据库 |
| 中间件 | 云 MQ、云 ES、微服务引擎 MSE | 自建 RocketMQ/ES/Nacos | 托管中间件 |
| 安全 | WAF、DDoS 高防、云防火墙、堡垒机、KMS | 自建 | 安全防护 |
| 观测 | 云监控、SLS(日志服务)、ARMS(APM) | Prometheus/ELK/SkyWalking | 观测 |
| 运维 | OOS(运维编排)、云助手、资源编排 ROS | Ansible/Terraform | 自动化 |
| CDN | CDN / 全站加速 DCDN | 自建(成本极高,基本不做) | 加速 |
该自建还是用云产品?(决策表)
| 场景 | 建议 | 理由 |
|---|---|---|
| 数据库 | 用云 RDS(除非有专门 DBA 团队) | 高可用、备份、监控都是开箱即用,自建容易出事 |
| Redis | 用云 Redis | 同上 |
| K8s | 自建(大规模)/ 用托管(小规模) | 自建可控,托管省心 |
| 对象存储 | 一定用云 | 自建成本远超收益 |
| CDN | 一定用云 | 自建无法实现 |
| Nacos/MQ | 自建 | 云托管版本贵,且公司需要定制 |
| 监控告警 | 自建 Prometheus | 与业务深度耦合,云产品不够灵活 |
第 4 章 流量入口全链路
这是整份文档最实用的一章。一个用户点击 App,请求经过了多少层? 把它彻底搞清楚,你就能理解为什么需要这么多组件。
4.1 一次请求的完整旅程
图 4-1 一次请求的七跳旅程:理解它,你就有了排查问题的坐标系
为什么要这么多层?每层删掉会怎样?
| 删掉 | 后果 |
|---|---|
| DNS 智能解析 | 所有用户都解析到同一个 IP,跨网延迟巨大,单点故障 |
| CDN | 静态资源全走源站,带宽费用翻 10 倍,海外用户加载 10 秒 |
| WAF | 直接被刷、被拖库、被 CC 打挂 |
| 四层 LB | 没有入口级负载均衡,Nginx 单机成为瓶颈且是单点 |
| 七层 LB | 无法按域名分流(一台机器上跑多个网站),无法做 TLS 卸载 |
| API 网关 | 鉴权/限流代码写进每个微服务,重复且无法统一治理 |
关键认知:每层的存在都是为了让上一层"不用管这件事"。 这叫关注点分离(Separation of Concerns), 是整个 IT 架构里最重要的设计思想,没有之一。
4.2 四层负载 vs 七层负载(面试必问,工作必用)
| 维度 | 四层(L4) | 七层(L7) |
|---|---|---|
| 工作在哪层 | TCP/UDP 传输层 | HTTP/HTTPS 应用层 |
| 代表软件 | LVS、F5、云 ELB/SLB、HAProxy(mode tcp) | Nginx、HAProxy(mode http)、Envoy、Ingress |
| 看得到什么 | 只看 IP 和端口 | 能看到 URL、Header、Cookie、Body |
| 性能 | 极高(十万~百万级 QPS) | 中等(万级 QPS) |
| 能否按 URL 路由 | ❌ 不能 | ✅ 能 |
| 能否改请求内容 | ❌ | ✅(改 Header、重写 URL) |
| 能否卸载 TLS | ❌(只透传) | ✅ |
| 典型用途 | 入口第一层、数据库负载(如 MySQL 代理) | 业务路由、灰度、限流 |
真实公司的搭配方式(记住这个组合):
公网 VIP(L4)→ Nginx 集群(L7)→ 业务服务L4 在前面扛量 + 做 HA,L7 在后面做精细路由。这是 90% 公司的标准答案。
一个常见误解
"我们用了 K8s,是不是就不需要 Nginx 了?" 不是。K8s 的 Ingress 本质就是一个 Nginx/Envoy 控制器, 它只是把 Nginx 的配置从"手写 nginx.conf"变成了"声明式 YAML + 自动重载"。 底层还是七层负载的原理,一点没变。
4.3 CDN:不只是"加速静态资源"
新人常以为 CDN 就是"缓存图片"。真实场景里 CDN 承担了更多职责:
| 能力 | 说明 |
|---|---|
| 静态资源缓存 | 图片/CSS/JS/视频,命中直接返回,回源率通常 < 10% |
| 动态加速 | 通过最优路径回源(走 CDN 厂商的专线骨干网),降低跨地域延迟 |
| 边缘计算 | 在边缘节点跑 JS/WASM,做 A/B 测试、鉴权、改写(如 Cloudflare Workers) |
| 视频分发 | HLS/DASH 切片分发,直播使用 RTMP/WebRTC 推流 |
| 下载分发 | 大文件、App 安装包分发(节流、断点续传) |
| DDoS 缓解 | 海量边缘节点天然分散攻击流量 |
| 证书托管 | HTTPS 证书在 CDN 侧统一管理 |
CDN 的成本核心是"回源率"。回源率从 10% 降到 5%,带宽成本直接减半。 所以运维要关注的核心指标是:缓存命中率、回源带宽、5xx 率。
缓存刷新与预热(生产事故高发区)
- 刷新(Purge):把 CDN 上的旧文件删掉,下次请求回源拉新的。 发版后前端文件更新但 CDN 还有缓存 → 用户看到旧页面,就是这个原因。
- 预热(Prefetch):发版前主动把新文件推到 CDN 边缘,避免第一批用户回源慢。
- 正确做法:前端构建时给文件名加 hash(
app.a3f9c2.js), 这样发版后文件名变了,URL 也变了,天然绕过缓存问题,不需要手动刷新。 这就是所谓的**"前端资源指纹"**,是前端工程化的基本要求。
4.4 负载均衡算法:不只是轮询
| 算法 | 原理 | 适用 |
|---|---|---|
| 轮询 Round Robin | 依次分发 | 后端性能均匀 |
| 加权轮询 | 按权重分配 | 机器配置不同 |
| 最少连接 | 给当前连接数最少的节点 | 请求耗时差异大 |
| IP Hash | 同 IP 固定到同一后端 | 需要会话保持 |
| 一致性哈希 | 节点增减时影响最小的 key | 分布式缓存路由 |
| 最短响应时间 | 给响应最快的节点 | 混合部署、性能不均 |
| P2C(Power of Two Choices) | 随机选两个,挑好的 | 现代服务网格首选 |
会话保持是个坑
"用户登录后,下一次请求打到了另一台机器,又要求重新登录"——这就是会话问题。 三种解法,优劣分明:
| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 会话粘滞(Sticky Session) | 按 Cookie/IP 固定到同一后端 | 改造成本低 | 后端挂掉会话丢失;负载不均 |
| 会话复制 | 各节点间互相同步 session | 无状态 | 节点多了同步开销爆炸 |
| 集中存储(推荐) | session 存 Redis,所有节点共享 | 无状态、可任意扩缩容 | 多一次 Redis 调用 |
现代做法一定是第三种。 这就是"应用要无状态(Stateless)"的含义—— 只有无状态,K8s 才能自由地在任何时候把 Pod 杀掉重建、扩容缩容。
第 5 章 应用层:K8s 与 PaaS
5.0 先看一张完整的生产环境全景图
前面四章讲了入口和网络,这一章开始讲"后端真正跑在哪儿"。 先建立一张 prod 环境的完整部署全景(下面这张图把后面第 5~9 章的内容都包含了, 现在看不懂没关系,读完再来回看):
图 5-0 生产环境全景:从用户到数据层,四条平面各司其职
看这张图的正确方式
先只看从用户到数据库的竖线(业务线),那是主线。 然后看两侧的支撑平面和观测平面,它们不是"附加功能",而是产线的一部分。 最后注意每层都是多个方框——这就是"高可用"在图纸上的样子: 任何一个方框消失,服务都还能跑。
5.1 从"部署在虚拟机上"到"部署在 K8s 上"
虚拟机时代的问题:一台机器上改了一个环境变量,另一台忘了改,行为不一致; 扩容要在几分钟到几十分钟;一台机器上跑多个应用会互相抢资源。
容器时代解决:把应用和它的运行环境打包成镜像,处处一致;秒级启动。
K8s 时代解决:容器多了以后,谁来调度、谁来重启、谁来负载、谁来滚动升级? K8s 就是"容器的操作系统"。
| 概念 | 通俗解释 | 运维关心什么 |
|---|---|---|
| Pod | 最小调度单位,一个或多个容器的集合 | 重启次数、OOMKilled、就绪探针 |
| Deployment | 声明"我要 3 个副本、用哪个镜像" | 副本数、滚动更新状态 |
| StatefulSet | 有状态应用(如数据库),Pod 有稳定名字和存储 | 启动顺序、PVC 绑定 |
| DaemonSet | 每个节点都跑一份(如日志采集器) | 是否所有节点都覆盖到 |
| Service | 一组 Pod 的固定访问入口 + 负载均衡 | ClusterIP/NodePort/LB |
| Ingress | 七层入口,按域名/路径路由到 Service | 证书、路由规则 |
| ConfigMap / Secret | 配置和密码,与镜像解耦 | 改配置不用重新构建镜像 |
| HPA | 按 CPU/QPS 自动扩缩容 | 阈值设置、扩容上限 |
| PVC / StorageClass | 持久化存储声明 | 存储类是否可用、容量 |
| Namespace | 逻辑隔离(按环境/团队/项目) | 资源配额、网络策略 |
三个最常被问到的 K8s 问题
- 滚动更新怎么保证不中断? K8s 会先起新 Pod,等它就绪(readinessProbe 通过)后,再把旧 Pod 摘掉。 关键配置:
maxSurge(可以多起几个)和maxUnavailable(可以少几个)。 如果 readinessProbe 没配,K8s 会认为 Pod 一起来就可用,结果流量打进来时应用还没初始化完 → 502。 - Pod 一直 CrashLoopBackOff 怎么办?
kubectl describe pod看事件 →kubectl logs --previous看上一次崩溃的日志。 常见原因:配置缺失、连不上数据库、内存不够被 OOMKill、启动命令写错。 - liveness 和 readiness 有什么区别? readiness = "我能接流量了吗"(失败则从 Service 端点摘除,不重启); liveness = "我还活着吗"(失败则重启容器)。 混用这两个是新手最常犯的致命错误——把 liveness 配成依赖数据库, 结果数据库抖动一下,全部 Pod 一起被重启,引发雪崩。
5.2 什么是 PaaS 平台
PaaS = Platform as a Service(平台即服务)。 在公司内部语境里,它通常指的是内部研发效能平台 / 发布平台。
转行的人容易以为"PaaS 就是阿里云/腾讯云",其实不是。公司内部的 PaaS 长这样:
图 5-2 PaaS 平台:八大能力,把底层工具包装成研发能自助使用的门面
PaaS 的价值在于"屏蔽复杂度": 开发同学不需要会写 K8s YAML,点几下就能把服务部署到测试环境; 反过来,运维通过 PaaS 统一收口,所有变更都走平台,天然有审计、有规范、有回滚。
PaaS 与运维的关系(很多人搞反了)
新人担心"有了 PaaS 是不是就不需要运维了"。 恰恰相反:PaaS 把重复的、无脑的操作自动化了, 运维的精力被释放去做架构设计、稳定性治理、成本优化、故障处理这些真正难的事。 PaaS 是运维能力的放大器,不是替代品。 而且 PaaS 平台本身就需要运维来维护(它就是一套运行在 K8s 上的微服务)。
5.4 K8s 生产集群的完整组件清单
一个"能上生产"的 K8s 集群,除了 K8s 自身,还要装一堆周边组件。 这张表建议收藏——它同时也是你运维 K8s 时的"责任范围清单"。
| 类别 | 组件 | 作用 | 没有它会怎样 |
|---|---|---|---|
| 网络 | CNI(Calico / Cilium / Flannel) | Pod 网络互通 | Pod 之间无法通信 |
| CoreDNS | 集群内服务名解析 | 服务间不能用域名调用 | |
| Ingress Controller(Nginx/Traefik) | 七层入口 | 外部流量进不来 | |
| MetalLB / 云 LB Controller | 裸机环境提供 LoadBalancer 类型 Service | 无法用 LB 类型 Service | |
| 存储 | CSI 驱动(Ceph/NFS/云盘) | 动态供给 PV | 有状态应用无法存储 |
| StorageClass | 定义存储"档位" | PVC 无法自动绑定 | |
| 可观测 | metrics-server | 提供 CPU/内存指标(HPA 依赖) | HPA 无法工作 |
| Prometheus + Grafana | 集群监控 | 瞎 | |
| Fluent Bit / Filebeat | 日志采集(DaemonSet) | 日志丢失 | |
| 安全 | RBAC | 权限控制 | 任何人能做任何事 |
| NetworkPolicy | Pod 间网络隔离 | 一个 Pod 被攻破可横向移动 | |
| Pod Security Standards | 禁止特权容器 | 容器逃逸风险 | |
| OPA Gatekeeper / Kyverno | 策略引擎(禁止 latest 镜像等) | 规范无法落地 | |
| Trivy / Clair | 镜像漏洞扫描 | 带着 CVE 上线 | |
| 发布 | Argo CD / Flux | GitOps 部署 | 手工 kubectl apply |
| Argo Rollouts | 金丝雀/蓝绿 | 只能滚动更新 | |
| Helm / Kustomize | 模板化与多环境差异化 | YAML 复制粘贴 | |
| 运维 | Velero | 集群级备份与恢复 | 集群灾难无法恢复 |
| Cluster Autoscaler | 节点自动伸缩 | 资源不够要人工加机器 | |
| Descheduler | 重新平衡 Pod 分布 | 资源碎片化 | |
| KubeVirt / KubeSphere | 虚拟化管理/运维平台 | 运维界面简陋 |
生产 K8s 的 6 条硬性规范(真实公司一定会查)
- 必须配 request 和 limit。只配 limit 不配 request 会导致调度不准; 都不配则一个 Pod 可能吃光节点,引发连锁驱逐(节点雪崩的常见原因)。
- 必须配探针。readiness 决定流量,liveness 决定重启。 没有它,滚动更新会"假成功",流量打到还没准备好的 Pod。
- 必须禁用
:latest标签。latest 不可复现,回滚时不知道回到哪个版本。 强制用image@sha256:...或明确的语义化版本。 - 必须配反亲和性。同一服务的 3 个副本要打散到 3 个节点, 否则一个节点挂掉 = 服务全挂("看起来 3 副本,实际是单点")。
- 必须配 PDB(PodDisruptionBudget)。保证节点维护驱逐 Pod 时, 服务至少有 N 个副本可用。
- 必须配 ResourceQuota / LimitRange。防止某个团队把集群资源吃干。
5.5 一个生产级 Deployment 应该长什么样
下面这份 YAML 是生产规范的最小集合,每一项都标注了"为什么"。 你可以把它当作模板,检查自己的服务有没有漏。
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: prod
spec:
replicas: 3 # 多副本 = 高可用的前提
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多起 1 个 Pod
maxUnavailable: 0 # ★ 关键:保证升级期间可用副本不减少
selector:
matchLabels: { app: order-service }
template:
metadata:
labels: { app: order-service }
spec:
affinity:
podAntiAffinity:
# ★ 强制把 3 个副本打散到不同节点,避免"共死"
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels: { app: order-service }
topologyKey: kubernetes.io/hostname
containers:
- name: app
image: harbor.internal/prod/order-service:1.4.2 # ★ 明确版本,禁用 latest
ports: [{ containerPort: 8080 }]
resources:
requests: { cpu: "500m", memory: "1Gi" } # ★ request 影响调度
limits: { cpu: "2000m", memory: "2Gi" } # ★ limit 防止吃光节点
readinessProbe: # ★ 决定"能否接流量"
httpGet: { path: /actuator/health/readiness, port: 8080 }
initialDelaySeconds: 30 # 给应用启动留时间
periodSeconds: 5
failureThreshold: 3
livenessProbe: # ★ 决定"是否重启"
httpGet: { path: /actuator/health/liveness, port: 8080 }
initialDelaySeconds: 60
periodSeconds: 10
env:
- name: NACOS_ADDR
value: "nacos-1:8848,nacos-2:8848,nacos-3:8848"
- name: DB_PASSWORD
valueFrom:
secretKeyRef: { name: order-db-secret, key: password } # ★ 密码用 Secret
lifecycle:
preStop:
exec:
# ★ 优雅停机:先从注册中心摘除,等待长连接排空,再退出
command: ["sh", "-c", "curl -X POST localhost:8080/actuator/offline; sleep 15"]
terminationGracePeriodSeconds: 45 # 要大于 preStop 的 sleep 时间
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: order-service-pdb, namespace: prod }
spec:
minAvailable: 2 # ★ 节点维护时至少保留 2 个副本
selector:
matchLabels: { app: order-service }三个最致命的配置错误
| 错误 | 后果 | 原因 |
|---|---|---|
maxUnavailable: 1 且副本数 = 1 | 升级期间服务完全中断 | 唯一副本被删掉了 |
| livenessProbe 检查数据库连通性 | 数据库抖动 → 全部 Pod 被重启 → 雪崩 | 探针不该依赖外部系统 |
terminationGracePeriodSeconds 小于 preStop sleep | Pod 被强杀,长连接被切断 | 用户看到"连接中断" |
5.6 服务网格(Service Mesh)
微服务多了以后,每个服务都要处理:超时、重试、熔断、限流、链路追踪、mTLS 加密。 如果每个语言各写一套 SDK,升级一次要改几十个仓库,这是灾难。
Service Mesh 的思路:把这些能力从业务代码里"抽出来",放到一个 Sidecar 代理(如 Envoy)里。 业务代码完全无感,所有流量自动经过代理。
| 方案 | 形态 | 代表 | 优缺点 |
|---|---|---|---|
| SDK 模式 | 能力在代码里(jar 包) | Spring Cloud Alibaba、Dubbo | 性能好,但绑定语言,升级难 |
| Sidecar 模式 | 每个 Pod 加一个代理容器 | Istio、Linkerd | 语言无关,统一治理;但多一跳,复杂 |
| Node 级代理 | 每个节点一个代理 | Cilium Service Mesh | 性能好,但隔离性弱于 Sidecar |
中小公司不要盲目上 Istio
Istio 功能强大,但引入的复杂度也很高(控制面组件多、排障链路变长、资源占用增加)。 真实建议:微服务数量 < 50 个、团队 < 20 人时, 用 Spring Cloud Alibaba(Nacos + Sentinel + Seata)+ 网关就够了。 Istio 更适合多语言、超大规模、有专门基础架构团队的公司。 技术选型的第一原则是:用你团队能维护得住的最简单方案。
第 6 章 数据层:数据库、缓存、搜索
数据层是整个架构里最不能出问题的一层。应用挂了可以重启,数据丢了就真没了。
6.1 MySQL 一主两从:到底是什么
"一主两从"= 1 个主库(Master)+ 2 个从库(Slave)。
图 6-1 MySQL 一主两从:写请求走主库,读请求分摊到从库
为什么要两个从库,不能一个?
| 从库 | 用途 | 为什么需要独立 |
|---|---|---|
| 从库 1 | 分担读流量 | 大多数业务读:写 = 10:1 甚至 100:1,读库要能扩展 |
| 从库 2 | 备份 + 高可用切换 | 在从库上做备份(mysqldump/xtrabackup)不能影响读库性能;主库挂了,从库 2 升主,从库 1 改指向新主 |
这就是"一主两从"的完整理由:一个承载读、一个承接备份与容灾。 如果只有一个从库,备份时读性能下降,主库挂了也没有冗余的切换候选。
复制模式:异步 / 半同步 / 全同步
| 模式 | 主库什么时候返回成功 | 数据安全 | 性能 | 使用场景 |
|---|---|---|---|---|
| 异步复制 | 写完 binlog 立刻返回,不等从库 | ⚠️ 主库宕机可能丢数据 | 最快 | 日志类、可容忍丢失 |
| 半同步(推荐) | 等至少一个从库确认收到 | 主库宕机不丢 | 中 | 核心交易业务标配 |
| 全同步(组复制 MGR) | 等所有从库确认 | 最安全 | 最慢 | 金融级一致性 |
半同步的关键参数:rpl_semi_sync_master_wait_for_slave_count=1。 注意:半同步在从库响应超时后会自动降级为异步(rpl_semi_sync_master_timeout,默认 10s), 所以部署时要监控这个降级事件,它意味着"此刻数据有丢失风险"。
6.2 主从延迟:一个真实存在的、无法消除的问题
主库写完立刻查从库,可能查不到(因为复制有延迟)。这叫主从延迟,通常在毫秒级, 但在大事务、大批量写入、网络抖动时可能达到秒级甚至分钟级。
三个真实场景,三个解法:
| 场景 | 现象 | 解法 |
|---|---|---|
| 下单后立刻查订单 | 刚下单跳到详情页,显示"订单不存在" | 写后读走主库(同一请求内强制走主),或前端延迟 1 秒再跳转 |
| 支付成功回调 | 回调处理了,但状态查不到 | 用强制主库读 + 幂等重试 |
| 跨机房从库 | 异地从库延迟几百毫秒 | 异地从库只用于只读分析,不承载在线业务 |
千万别做的事
不要为了"解决主从延迟"而把所有读都打到主库——那会瞬间压垮主库。 正确顺序是:① 接受延迟(UI 上做处理)→ ② 精准地把"写后立刻读"的少数请求路由到主库 → ③ 用缓存兜住。
6.3 分库分表:单表撑不住时的必然选择
单表数据量的经验红线(超过就该考虑拆了):
| 数据量 | 现象 |
|---|---|
| < 500 万行 | 正常,索引好就没问题 |
| 500 万 ~ 2000 万 | 查询开始变慢,DDL(改表结构)时间变长 |
| 2000 万 ~ 1 亿 | 明显变慢,DDL 可能锁表几小时 |
| > 1 亿 | 必须拆分,否则维护窗口都无法安排 |
两种拆法:
| 方式 | 做法 | 解决什么 | 不解决什么 |
|---|---|---|---|
| 垂直拆分 | 按业务拆:用户表、订单表、商品表放不同库 | 单库压力、业务解耦 | 单表数据量 |
| 水平拆分 | 按规则拆:order_0000 ~ order_1023 | 单表数据量、单库写入 | 跨分片查询 |
水平拆分的分片键选择(这是最关键的决策):
| 分片键 | 优点 | 缺点 |
|---|---|---|
| 用户 ID | 同一用户的订单在同一分片(查询高效) | 运营按时间查全量订单要扫所有分片 |
| 订单 ID | 分布均匀 | 用户查自己订单要扫所有分片 |
| 用户 ID + 时间(推荐) | 用户维度高效,冷热可分离 | 需要两级路由,复杂 |
常用中间件:ShardingSphere(Apache,最主流)、MyCat(老牌)、 或在应用层用 sharding-jdbc 直接做(无代理,性能好,但每个服务要引依赖)。
分库分表的代价,新人一定要知道
- 跨分片 JOIN 变成不可能 → 只能改成多次查询在应用层拼,或者做冗余字段。
- 分布式事务:跨库写入要保证一致性 → 引入 Seata / 最终一致方案,复杂度陡增。
- 全局唯一 ID:自增 ID 不再唯一 → 改用雪花算法(Snowflake)、号段模式(Leaf)。
- 扩容困难:从 1024 分片扩到 2048 分片要数据迁移,代价巨大。 → 所以分片数要一次规划到位(如提前分 1024 片,未来几年够用)。
- 运维复杂度:DDL 要按分片批量执行、备份要合并、监控要看每个分片。 结论:分库分表是"最后手段",能靠索引优化、读写分离、缓存、归档解决的,绝不上分片。
6.4 缓存:架构里的"性能加速器"
缓存的本质是"用空间换时间",把慢的存储(磁盘数据库)的热数据放到快存储(内存)里。
多级缓存架构(真实公司的做法):
请求 → ① 本地缓存(Caffeine/Guava,进程内,纳秒级,容量小)
│ 未命中
▼
② 分布式缓存(Redis 集群,毫秒级,容量大)
│ 未命中
▼
③ 数据库(MySQL,几十毫秒,容量最大)| 层级 | 代表 | 访问速度 | 特点 |
|---|---|---|---|
| 本地缓存 | Caffeine、Guava Cache | ~100ns | 无网络开销;多实例间不一致 |
| 分布式缓存 | Redis、Tair、Memcached | ~1ms | 一致;有网络开销 |
| 近端缓存 | Redis + 客户端本地副本 | ~10μs | 折中方案(阿里 Tair 的做法) |
缓存三大经典问题(必背,工作中必遇):
| 问题 | 现象 | 原因 | 解法 |
|---|---|---|---|
| 缓存穿透 | 大量请求打不到缓存,全落到数据库 | 查不存在的数据(缓存里也没有) | ① 缓存空值(短 TTL);② 布隆过滤器拦截;③ 参数校验 |
| 缓存击穿 | 某个热 key 失效瞬间,大量请求涌入数据库 | 热 key 过期 | ① 互斥锁(只让一个线程去加载);② 热 key 永不过期 + 后台更新 |
| 缓存雪崩 | 大量 key 同时失效,数据库瞬间被打垮 | 同一时间批量过期 | ① TTL 加随机值(打散);② 多级缓存;③ 熔断降级 |
还有第四、第五个(进阶):
- 缓存与数据库一致性:推荐 Cache Aside + 延迟双删,或订阅 binlog(Canal)异步刷新。 绝对不要用"先更新数据库再更新缓存"——并发下必不一致。
- 热 key / 大 key:单个 key 的 QPS 过高(如秒杀商品)要多副本打散; 单个 key 的 value 过大(如几 MB 的 List)会导致 Redis 阻塞,必须拆分。
Redis 高可用三种形态
| 形态 | 原理 | 优点 | 缺点 | 适用 |
|---|---|---|---|---|
| 主从 + 哨兵 | 哨兵监控主库,故障时自动选一个从库升主 | 部署简单,可用性够 | 单机容量受限于内存,扩容需停机 | 中小规模 |
| Redis Cluster | 数据分 16384 个槽,分布到多个主节点 | 容量和性能可水平扩展 | 运维复杂,跨槽操作受限 | 中大型标配 |
| Codis / 云 Redis | 代理层分片 | 客户端无感 | 多一跳,代理可能成瓶颈 | 云上场景 |
6.5 Elasticsearch:搜索与日志
为什么需要 ES? MySQL 的 LIKE '%关键词%' 无法走索引,几百万行就是全表扫描。 而搜索需要:分词、相关性排序、聚合统计、高亮——这些都不是关系型数据库擅长的。
| 能力 | MySQL | Elasticsearch |
|---|---|---|
| 精确查询 | ✅ 强 | ✅ 可以 |
| 全文检索 | ❌ 慢到不可用 | ✅ 倒排索引,毫秒级 |
| 模糊/前缀搜索 | ❌ | ✅ |
| 聚合统计(类似 GROUP BY 但更灵活) | 一般 | ✅ 强 |
| 相关性排序(匹配度打分) | ❌ | ✅ |
| 事务 | ✅ 强 | ❌ 不支持 |
| 实时性 | 实时 | 准实时(默认 1 秒刷新) |
ES 的两大用途:
- 业务搜索:商品搜索、站内搜索(配合分词器 IK)。
- 日志检索:就是 ELK 里的那个 E(详见第 9 章)。
ES 高可用与两个常见坑
三节点起是 ES 的最低可用配置(集群选主需要多数派)。
- 坑 1:脑裂(Split Brain)。旧版本在节点分区时会分裂成两个集群,都认为自己是主。 7.x 之后用
cluster.initial_master_nodes+ 投票配置基本解决,但节点数要满足过半存活(3 节点最多挂 1 台)。 - 坑 2:分片数不能改。索引的主分片数在创建时就固定了,事后无法修改。 所以创建索引前必须估算数据量和增长,一般规则:单分片 10~50GB。 分片太少 → 无法水平扩展;分片太多 → 元数据开销大,查询要合并几百个分片的结果,反而变慢。
6.6 数据层的容量规划(真实数字)
很多人问"到底多少数据算多"。下面这张表是基于真实生产经验的经验值,可以直接用来判断风险:
| 对象 | 健康区间 | 需要关注 | 危险 | 处理方式 |
|---|---|---|---|---|
| MySQL 单表行数 | < 2000 万 | 2000 万 ~ 5000 万 | > 5000 万 | 归档 / 分表 |
| MySQL 单库大小 | < 500 GB | 500 GB ~ 1 TB | > 1 TB | 垂直拆分 |
| MySQL QPS(单实例) | < 5000 | 5000 ~ 1 万 | > 1 万 | 加从库 / 分片 |
| MySQL 连接数 | < 500 | 500 ~ 1000 | > 1000 | 连接池调优 / ProxySQL |
| Redis 单实例内存 | < 10 GB | 10 GB ~ 20 GB | > 20 GB | Cluster 分片 |
| Redis 单 key 大小 | < 10 KB | 10 KB ~ 100 KB | > 1 MB | 拆分 key |
| Redis 单实例 QPS | < 5 万 | 5 万 ~ 8 万 | > 8 万 | 主从读 / Cluster |
| ES 单分片大小 | 10 ~ 50 GB | 50 ~ 100 GB | > 100 GB | 重新规划分片 |
| ES 集群分片数 | < 1000 | 1000 ~ 3000 | > 3000 | 合并小分片 / 按天索引 |
| Kafka 单分区吞吐 | < 10 MB/s | 10 ~ 50 MB/s | > 50 MB/s | 加分区 |
| Nginx 单机 QPS | < 2 万 | 2 万 ~ 5 万 | > 5 万 | 加节点 / 上 L4 |
这张表的正确用法
它不是为了让你背数字,而是让你在评审时能提出正确的问题。 比如看到"orders 表已经 8000 万行了",你就知道要问: "做过归档吗?查询有没有走索引?要不要按时间分表?" 这比背下来"分表用 ShardingSphere"有用得多——知道什么时候需要,比知道用什么工具更重要。
6.7 数据库高可用的完整形态(含自动切换)
前面讲了"一主两从",但光有主从还不够:主库挂了,谁来把从库升成主? 应用怎么知道新主的 IP?这一节给出完整的生产方案。
方案对比(从简单到复杂):
| 方案 | 原理 | 切换耗时 | 复杂度 | 适用 |
|---|---|---|---|---|
| MHA | Manager 监控主库,故障时提升从库 + 补数据差异 + VIP 漂移 | 10~30 秒 | 中 | 传统自建 MySQL |
| MGR(组复制) | MySQL 官方多主/单主复制,Paxos 协议自动选主 | 秒级 | 高 | MySQL 5.7.17+,金融级 |
| Orchestrator + ProxySQL | 拓扑发现 + 代理层自动改路由 | 秒级 | 高 | 大规模 MySQL 集群 |
| 云 RDS 高可用版 | 云厂商双节点 + 自动切换 | 30 秒~1 分钟 | 低(托管) | 推荐,省心 |
| PXC / MGR 三节点 | 强同步多主 | 秒级 | 高 | 对一致性要求极高 |
完整拓扑(MHA 方案,自建最常见):
图 6-7 MHA 自动主从切换:主库倒了,从库顶上,VIP 漂移,应用无感
切换过程中会发生什么(这是重点):
- MHA Manager 发现主库失联(默认连续 miss 3 次,约 9 秒)。
- 从从库中挑一个数据最新的作为候选主。
- 尝试从宕机主库拉取最后的 binlog(如果主库只是网络隔离而非彻底宕机,能补回差异数据,不丢数据)。
- 提升候选主,其余从库指向新主。
- VIP 漂移到新主(Keepalived 完成 ARP 广播)。
- 应用侧:连接池报错重连,通常 10~30 秒内恢复。
切换期间的三个真实问题
- 应用连接池不会立刻感知。老连接还连着旧 IP,会持续报错直到连接超时。 做法:连接池配置
connectionTestQuery+ 合理超时(如 3 秒)+ 重试机制。 - 切换过程中"双写"风险。如果旧主库只是网络分区(脑裂),它恢复后会认为自己是主, 造成数据冲突。做法:开启
read_only+ MHA 的master_ip_failover脚本强制降级。 - 没做切换演练。很多公司的自动切换从来没成功过,因为配置有细节问题。 做法:每季度在预发环境真实演练一次(用
kill -9模拟主库宕机)。
6.8 Redis 高可用完整拓扑
图 6-8 Redis 两种高可用方案 + 三级缓存的分工
哨兵方案的两个坑
- 应用必须通过哨兵查主地址,不能硬编码 IP。 Java 用
JedisSentinelPool,Spring Boot 配spring.redis.sentinel.master/nodes。 硬编码 IP = 主库切换后你的应用还连在旧主库上(这个错很常见)。 - 哨兵自身也要 ≥3 个。只有 1 个哨兵 → 哨兵挂了就没法切换; 只有 2 个哨兵 → 网络分区时无法达成多数派判断。
第 7 章 中间件全家桶
"中间件"这个词很多人说不清。通俗定义:中间件 = 不直接实现业务、但业务离不开的通用基础软件。 它夹在操作系统和应用之间,为应用提供通用能力。
7.1 消息队列(MQ):异步、解耦、削峰
为什么需要 MQ? 举个最经典的例子——用户下单:
不用 MQ(同步调用):
下单 → 扣库存 → 发优惠券 → 发短信 → 加积分 → 更新推荐 → 返回
↓ ↓ ↓ ↓ ↓
每多一个下游,就多一次等待,任何一环慢/挂,下单就失败
用 MQ(异步解耦):
下单 → 扣库存(同步,必须成功)→ 发消息到 MQ → 返回成功(快!)
↓
优惠券/短信/积分/推荐 各自订阅,异步慢慢消费| 能力 | 说明 | 通俗理解 |
|---|---|---|
| 异步 | 主流程不等下游 | 点餐后拿到号码牌就走,不用站在窗口等 |
| 解耦 | 生产者和消费者互不认识 | 餐厅换了厨师,顾客不用知道 |
| 削峰填谷 | 瞬时流量先进队列,后端按能力消费 | 水坝蓄洪 |
| 最终一致 | 通过重试 + 幂等实现最终一致 | 通知没送到就再送一次 |
| 广播 | 一个消息多个消费者组都收到 | 群发通知 |
主流选型(重要对比):
| 产品 | 语言/生态 | 特点 | 适用 |
|---|---|---|---|
| Kafka | Scala/Java | 吞吐极高(百万/秒)、持久化、可重放 | 日志收集、大数据、流计算 |
| RocketMQ | Java(阿里开源) | 事务消息、延迟消息、顺序消息、消息回溯 | 国内电商交易场景首选 |
| RabbitMQ | Erlang | 协议丰富、路由灵活、社区成熟 | 中小规模、复杂路由 |
| Pulsar | Java | 存算分离、多租户、云原生 | 新一代,学习成本高 |
| 云 MQ(阿里云/腾讯云) | 托管 | 免运维 | 不想自维护时 |
MQ 的四个必须处理的问题
- 消息丢失:生产者要确认(
confirm)、Broker 要持久化(刷盘)、消费者要手动 ACK。 三处任何一处偷懒都会丢消息。 - 消息重复:网络重试必然导致重复投递。解法是消费端幂等—— 用业务唯一键(订单号)+ 去重表 / Redis SETNX 保证"同一消息只处理一次"。 记住:MQ 只能保证"至少一次"(at-least-once),"恰好一次"要靠业务自己实现。
- 消息积压:消费者挂了或变慢了,队列堆积几百万条。 应急处理:临时扩容消费者 → 先把积压消费掉 → 再排查根因(千万别直接清队列)。
- 顺序消息:同一笔订单的"创建/支付/发货"必须按顺序消费。 做法:按订单号 Hash 到同一队列,一个队列一个消费者串行消费。
7.2 注册中心与配置中心:Nacos 是本项目的核心
两个不同的职责,经常被同一个组件承担:
| 职责 | 解决什么问题 | 不用它会怎样 |
|---|---|---|
| 服务注册(Service Registry) | A 服务怎么找到 B 服务的地址 | 地址要硬编码,B 扩容/迁移后 A 全部失效 |
| 配置中心(Config Center) | 配置怎么集中管理、动态生效 | 改一个超时时间要重新打包发版 |
Nacos 同时提供这两种能力,是国内微服务生态(Spring Cloud Alibaba)的默认选择。
图 7-2 Nacos 双能力:服务注册发现 + 配置管理,但它的 MySQL 是最隐蔽的单点
Nacos 部署的三个硬性要求
- 必须 3 节点起。Nacos 用 Raft 做一致性,2 节点反而比 1 节点更差 (无法过半,任何一台挂都影响可用性)。要么 1 台(开发环境),要么 3 台(生产)。
- Nacos 自己的数据库也必须高可用。Nacos 把配置存在 MySQL 里, 这个库挂了 → 所有服务拿不到配置 → 全站起不来。 这是最容易被忽略的单点,很多公司就栽在这里。
- 一定要配鉴权(
nacos.core.auth.enabled=true)。 默认没开鉴权,任何人能进控制台改配置、能注册假服务,是重大安全事故。
配置中心的真实价值(一个具体例子):
| 场景 | 没有配置中心 | 有配置中心 |
|---|---|---|
| 调大数据库连接池 | 改代码 → 提交 → CI → 构建镜像 → 部署 → 重启,30 分钟 | 控制台改一下 → 秒级推送 → 应用热生效 |
| 紧急关闭某功能 | 发版(30 分钟,还可能有风险) | 改一个开关,秒级生效 |
| 不同环境不同配置 | 打包时选 profile,容易打错包 | 按 namespace + group 隔离,天然不会错 |
| 配置变更审计 | 无 | 有版本、有 diff、可一键回滚 |
namespace / group / dataId 三件套(Nacos 的核心概念)
- namespace:最外层隔离,通常用来隔离环境(dev/test/uat/prod 各一个 namespace)。
- group:中层分组,通常按业务线或项目分。
- dataId:具体的一份配置文件(如
ruoyi-gateway-prod.yml)。
定位一份配置 = namespace + group + dataId 三者共同确定。 最常犯的错误是把环境隔离做在 group 而不是 namespace 上—— 这样权限就没法按环境隔离,测试能改到生产的配置。必须用 namespace 隔离环境。
7.3 网关:微服务的统一入口
API 网关(Gateway) 是微服务外的"前台",所有外部请求都从它进。
| 网关职责 | 说明 |
|---|---|
| 路由 | /api/user/** → 用户服务,/api/order/** → 订单服务 |
| 统一鉴权 | 校验 JWT,通过后在 Header 里塞用户 ID 传给下游(下游不用再解 token) |
| 限流 | 按 IP / 用户 / 接口限流(令牌桶、漏桶、滑动窗口) |
| 熔断降级 | 下游服务失败率高时,快速失败,避免雪崩 |
| 灰度路由 | 按 Header/用户标签把部分流量导到新版本 |
| 日志与链路 | 统一记录入口日志,生成 traceId 贯穿全链路 |
| 协议转换 | 外部 HTTP → 内部 Dubbo/gRPC |
主流实现:Spring Cloud Gateway(Java 生态最常用)、Kong、APISIX(国产,性能好)、Nginx + Lua。
网关自身的两个致命问题
- 网关是全局单点。它挂了全站不可用 → 必须集群部署 + 前面挂 L4 LB。
- 网关的降级策略必须明确。当下游服务大面积超时时, 网关要有"快速失败"能力(如 1 秒超时 + 熔断),而不是无限等待, 否则网关线程池被打满,整个网关跟着一起挂(这就是"雪崩"的典型路径)。
7.4 其他常见中间件速查
| 中间件 | 一句话说明 | 典型场景 |
|---|---|---|
| ZooKeeper | 分布式协调(选主、分布式锁、配置) | Kafka 旧版依赖;Dubbo 注册中心 |
| Etcd | 同上的新一代实现,Raft 一致性 | K8s 的元数据存储就靠它 |
| Apollo | 携程开源的配置中心 | 配置管理(比 Nacos 更聚焦配置) |
| Sentinel | 流量控制、熔断降级 | 限流降级(阿里开源) |
| Seata | 分布式事务框架 | AT/TCC/SAGA 模式解决跨库事务 |
| XXL-JOB | 分布式任务调度 | 定时任务(替代 Linux crontab) |
| Canal | 订阅 MySQL binlog | 数据同步到 ES/缓存、跨库同步 |
| SkyWalking | APM 全链路追踪 | 微服务调用链分析(第 9 章详解) |
| Prometheus | 指标采集与时序存储 | 系统/业务监控(第 9 章详解) |
| Kong / APISIX | API 网关 | 统一入口治理 |
| MinIO | 自建对象存储,兼容 S3 协议 | 图片/文件/备份存储 |
| SeaweedFS | 分布式文件系统 | 海量小文件(比 HDFS 更适合) |
| Hadoop HDFS | 大数据存储 | 离线数仓 |
| Flink | 流式计算 | 实时计算、实时风控 |
7.5 支撑平面:项目里其他不可缺的组件
除了上面几类,一家真实公司的"支撑平面"还有这些组件。 它们平时不起眼,但一挂就是全站级事故,因为它们是"被所有人依赖"的东西。
| 组件 | 职责 | 部署形态 | 挂了会怎样 |
|---|---|---|---|
| 统一认证中心(SSO) | 一次登录,全站通行(CAS/OAuth2/LDAP) | 双节点 + Redis | 全站登不上(最高可用等级) |
| 权限中心(RBAC) | 谁能操作什么(菜单、按钮、数据) | 双节点 + 数据库 | 后台功能不可用,可能有越权风险 |
| 分布式任务调度 | 定时任务统一管理与分片(XXL-JOB) | 调度中心双节点 + 执行器 | 对账、清算、报表全部不跑 |
| 统一文件服务 | 上传下载、图片处理、水印 | 多节点 + 对象存储 | 用户传不了图、看不到图 |
| 消息推送中心 | App Push / 短信 / 邮件 / 站内信 | 多节点 + MQ | 用户收不到通知(影响留存) |
| 对账中心 | 与支付渠道核对流水 | 定时任务 + 独立库 | 资金差异无法发现 |
| 风控中心 | 反欺诈、限流、黑名单 | 多节点 + Redis | 被刷、被薅羊毛 |
| 灰度/AB 平台 | 按用户/设备分流实验 | 双节点 + 配置中心 | 无法做新功能灰度 |
| 配置审计/发布平台 | 变更管理与审计 | 双节点 | 变更无记录,出事无法追溯 |
一个真实的教训
某公司把统一认证中心做了集群,但它的 Redis 是单机。 结果 Redis 一挂,认证中心全挂 → 所有系统登不上 → 全公司停摆 40 分钟。 "被依赖最多的组件,必须用最高规格的高可用",这条原则比任何技术细节都重要。 判断标准很简单:"它挂了,有多少系统不能工作?" 依赖越多,规格越高。
第 8 章 存储与文件
8.1 存储的四种形态
| 形态 | 全称 | 通俗理解 | 代表 | 用途 |
|---|---|---|---|---|
| 块存储 | Block Storage | 一块裸硬盘,要自己格式化文件系统 | 云盘、SAN、Ceph RBD、K8s PVC | 数据库、有状态应用 |
| 文件存储 | File Storage / NAS | 网络共享文件夹 | NFS、CephFS、云 NAS | 多台机器共享读写(如上传目录) |
| 对象存储 | Object Storage | 用 HTTP API 存"文件+元数据",无目录层级 | S3、OSS、COS、MinIO | 图片、视频、备份、静态资源 |
| 分布式文件系统 | Distributed FS | 专门为海量文件设计 | HDFS、FastDFS | 大数据、海量小文件 |
怎么选?一句话判断
- 要跑数据库 → 块存储(要求低延迟、高 IOPS)
- 多台机器要同时读写同一批文件 → 文件存储(NAS)
- 存用户上传的图片/视频/附件 → 对象存储(最便宜、最可靠、可 CDN 直连)
- 存海量日志/大数据文件 → HDFS
8.2 对象存储为什么是现代标配
核心优势:
- 容量无限(分布式,加机器就扩容)
- 成本极低(比云盘便宜 5~10 倍)
- 可靠性高(多副本 / 纠删码,标称 11 个 9)
- 可直接对接 CDN(用户上传图片,CDN 直接回源对象存储,不经过你的应用)
- 支持生命周期(30 天后自动转低频存储,1 年后归档,自动降本)
真实用法(重要):
❌ 错误做法:用户上传图片 → 应用服务器 → 保存到本地磁盘 → 应用返回 URL
问题:多台应用服务器磁盘不共享;应用重启磁盘可能丢;无法扩展
✅ 正确做法:用户请求"上传凭证" → 应用返回临时签名 →
用户直接上传到对象存储 → 应用只存 URL 到数据库
优点:不占用应用带宽和磁盘;天然可扩展;可直接接 CDN对象存储的三个坑
- Bucket 权限配错会导致数据泄露。见过太多公司把 Bucket 设成"公共读", 结果全站用户上传的身份证、合同、内部文档被搜索引擎爬到。 默认必须是私有,用临时签名 URL 访问。
- 对象存储不是文件系统。没有真正的目录(
/只是 key 的一部分),rename实际是"复制 + 删除",代价高。 - 最终一致性。虽然 S3 现在标称强一致,但历史上有延迟。 "上传后立刻读"可能 404,业务上要有重试。
第 9 章 三条支撑线:监控 / 日志 / 发布
这一章是整份文档里对运维最核心的部分。 业务线之外,一家公司的技术体系里还有三条"支撑线",它们不直接产生业务价值, 但没有它们,业务线根本不敢上线。
图 9-0 三条支撑线全览:监控 / 收集 / 发布,各自解决一类问题
9.1 监控线:三根支柱(Metrics / Logging / Tracing)
这是可观测性(Observability)的经典三分法,面试必问,工作必用:
| 支柱 | 回答什么问题 | 数据特征 | 代表工具 |
|---|---|---|---|
| 指标 Metrics | "系统整体健康吗?哪里异常?" | 数值、可聚合、采样存储、便宜 | Prometheus、Zabbix、云监控 |
| 日志 Logging | "具体发生了什么?" | 文本、离散事件、量大、贵 | ELK、Loki、SLS |
| 链路 Tracing | "这次请求慢在哪一环?" | 带 traceId 的调用树、采样 | SkyWalking、Jaeger、Zipkin |
一句话记住区别
- Metrics 告诉你"着火了"(CPU 95%、错误率 8%)
- Logging 告诉你"烧的是什么"(异常堆栈、报错内容)
- Tracing 告诉你"从哪儿烧起来的"(哪个服务的哪次调用慢/错)
正确的排查顺序:告警(Metrics)→ 定位服务(Tracing)→ 查具体原因(Logging)。 只有 Metrics 没 Tracing 时会很痛苦:你知道错误率涨了,但不知道是哪个调用链导致的。
9.2 Prometheus + Grafana + Alertmanager(监控线核心)
Prometheus 的工作方式(拉模式 Pull):
图 9-2 指标线:Prometheus 拉取 → 规则判定 → Alertmanager 分发
| 组件 | 作用 | 说明 |
|---|---|---|
| Node Exporter | 采集主机指标 | CPU/内存/磁盘/网络 |
| cAdvisor | 采集容器指标 | 每个容器的 CPU/内存 |
| kube-state-metrics | 采集 K8s 对象状态 | Pod 状态、副本数、重启次数 |
| Prometheus Server | 存储 + 计算告警规则 | 默认保留 15 天,长期存储要接 Thanos/VictoriaMetrics |
| Alertmanager | 告警路由 | 分组(同类告警合并)、抑制(A 挂了不报 B)、静默(维护期) |
| Grafana | 可视化 | 看板、大屏、告警(也可在 Grafana 配告警) |
SRE 的四个黄金指标(记住这四个,监控就有章法了):
| 指标 | 含义 | 对应 Prometheus 查询示例 |
|---|---|---|
| 延迟 Latency | 请求耗时(区分成功和失败的) | histogram_quantile(0.99, ...) P99 |
| 流量 Traffic | 系统负载(QPS) | rate(http_requests_total[5m]) |
| 错误 Errors | 错误率 | rate(http_requests_total{status=~"5.."}[5m]) |
| 饱和度 Saturation | 资源饱和度(CPU/内存/连接池) | container_memory_usage_bytes |
三个最容易配错的告警
- 告警风暴:一个 Redis 挂了,触发 200 个服务告警。 解法:抑制规则(Infrastructure 层告警抑制 Application 层告警)+ 分组。
- 阈值告警太迟钝:CPU > 90% 才报,但你服务慢的时候 CPU 才 60%。 解法:用业务指标(错误率、P99 延迟)而不是只盯资源指标。
- 没有告警分级:所有告警都发钉钉,导致没人看。 解法:P0 电话(立刻处理)、P1 电话/短信(15 分钟内)、P2 钉钉(当天处理)、P3 邮件(周会看)。
9.3 SkyWalking:全链路追踪(微服务排障的命脉)
没有全链路追踪时,排障是这样的: 用户报"下单失败" → 你看订单服务日志没错误 → 看库存服务日志有超时 → 看库存服务调用的商品服务 → 发现商品服务连接池满了 → 商品服务连接池为什么满? → 因为调用它的推荐服务在疯狂重试…… 靠人肉在 7 个服务的日志里串线索,半小时过去了。
有 SkyWalking 时:打开 SkyWalking UI → 输入 traceId → 看到完整调用树, 一眼看到"商品服务 3.2s 超时",再点进去看它的自己的调用链。
图 9-3 链路追踪线:一个请求慢在哪一段,一眼看得出
SkyWalking 的核心概念:
| 概念 | 说明 |
|---|---|
| Agent | 挂在应用上(Java 用 -javaagent,无侵入),采集调用信息 |
| OAP Server | 接收、分析、存储链路数据 |
| Storage | 存储(生产用 ES 或 ClickHouse,测试可用 H2) |
| UI | 查询界面:追踪、拓扑图、指标、告警 |
| TraceId | 一次请求的全局唯一 ID,贯穿所有服务(这是灵魂) |
| Span | 一次调用(一个服务内的一段) |
TraceId 怎么贯穿全链路?三个方案
- 网关生成:网关为每个请求生成 traceId,通过 HTTP Header 传给下游。
- 自动传播:Agent 自动把 traceId 放进 HTTP Header / RPC 上下文 / MQ 消息头。
- 日志打标:把 traceId 打进日志(logback 的 MDC),这样在 ELK 里搜 traceId 就能看到全链路日志。
关键点:日志和链路一定要用同一个 traceId 打通,否则两套系统各自为政,排查效率照样低。
9.4 收集线:ELK 日志平台
为什么需要集中日志?
- 100 个 Pod,每个 Pod 的日志只在自己容器里,重启就没了 → 必须收走
- 排查问题要在 10 个服务的日志里关联搜索 → 必须集中
- 合规要求日志保留 6 个月以上 → 必须归档
ELK 经典架构(Beats → Kafka → Logstash → ES → Kibana):
图 9-4 日志线:生产端 → 缓冲 → 收集 → 存储 → 查询
每个组件的职责:
| 组件 | 职责 | 为什么需要它 |
|---|---|---|
| Filebeat / Fluent Bit | 采集日志文件,轻量(几十 MB) | 部署在每个节点,开销必须小 |
| Kafka | 缓冲 | ES 抖动/重启时日志不丢;削峰(日志量有波峰) |
| Logstash | 解析、清洗、格式化 | Grok 解析非结构化日志成 JSON;过滤敏感信息 |
| Elasticsearch | 存储 + 索引 + 检索 | 毫秒级全文检索 |
| Kibana | 查询界面 | 开发/运维查日志的地方 |
日志平台的四个工程问题
- 成本。日志是最贵的数据(ES 集群磁盘消耗极大)。 必须做的:① 分级(DEBUG 不上生产);② 按天索引 + 生命周期(7 天热、30 天温、180 天冷/归档); ③ 采样(高频重复日志只留样本);④ 字段裁剪(不要整条 JSON 全存)。
- ES 写入压力。日志是"写入多、查询少"的场景。 做法:Logstash 批量写(bulk)、关闭不必要的字段索引、用
refresh_interval调大减少段合并。 - 权限与合规。日志里可能有手机号、密码、token。 做法:Logstash 阶段就做脱敏(
mutate替换字段),而不是事后处理。 - 日志规范。如果每个服务的日志格式都不一样,什么都搜不出来。 做法:统一 JSON 结构化日志 + 统一字段名(
traceId/level/service/timestamp)。 这一条是"日志能不能用"的分水岭,比装什么工具重要得多。
什么时候该用 Loki 而不是 ELK?
Loki(Grafana 出品)只索引标签,不索引内容,存储成本比 ES 低 5~10 倍。 代价是全文检索能力弱(只能先按标签筛选,再 grep)。 选择标准:需要复杂检索/聚合 → ELK;只按服务/时间查日志 → Loki 更省钱。 很多公司是两者并存:核心业务日志 ELK,一般日志 Loki。
9.5 发布线:CI/CD 全链路
这条线就是把前面几份文档讲的东西串起来。放在这里的意义是: 让你看到它在整个架构中的位置——它是一条横切的支撑线,服务于所有环境。
图 9-5 发布线全链路:从代码提交到 Pod 运行,六步,每一步都可审计
四个环境的流水线差异(真实做法):
| 环境 | 触发方式 | 是否需审批 | 副本数 | 验证手段 |
|---|---|---|---|---|
| dev | 每次提交自动 | 否 | 1 | 冒烟测试 |
| test | 自动 / 手动 | 否 | 1~2 | 自动化测试 + 接口测试 |
| uat | 手动触发 | 产品确认 | 接近 prod | 人工验收 + 回归测试 |
| prod | 手动触发 | 必须审批(双人) | 全量 | 灰度观测 + 全链路监控 |
发布线的核心指标(衡量团队工程能力)
| 指标 | 含义 | 优秀水平 |
|---|---|---|
| 发布频率 | 多久发一次 | 每天多次 |
| 变更前置时间 | 从提交到上线 | < 1 小时 |
| 变更失败率 | 发布导致故障的比例 | < 15% |
| 平均恢复时间 MTTR | 故障多久恢复 | < 1 小时 |
这四个指标就是 DORA 指标(Google DevOps 研究), 面试时能说出来,面试官会认为你真的懂 DevOps,而不只是会敲命令。
9.6 三条线的协同:一次故障的完整还原
讲了那么多组件,最重要的是理解它们怎么协同工作。下面用一次真实故障的全过程, 把三条线串起来(这个例子建议反复看,它比任何架构图都更能说明问题):
图 9-6 三条线协同排障时间线:从发现到复盘的完整 5 分钟
反过来,缺了任何一条线会怎样?
| 缺哪条线 | 后果 | 上面的流程会变成 |
|---|---|---|
| 缺监控线 | 靠用户投诉才发现 | 2 小时后才发现(而不是 2 分钟) |
| 缺链路追踪 | 知道商品服务慢,不知道慢在哪 | 在 7 个服务的日志里人肉搜,30 分钟 |
| 缺日志收集 | 只能一台台机器登上去看日志 | Pod 重启后日志丢失,永远查不到 |
| 缺发布线回滚 | 只能手工改 YAML 再重启 | 10 分钟以上,且可能改错 |
"可观测性"不是锦上添花,它就是生产系统的一部分。 一家公司如果把监控和日志当作"以后再补",那它就是在拿用户的耐心当测试环境。
9.7 告警体系设计(别让告警把你淹死)
告警配得不好比不配更糟——因为人会对告警脱敏(Alert Fatigue), 最后所有告警都被忽略,真故障来了也没人看。
告警分级(真实公司的做法):
| 级别 | 定义 | 通知方式 | 响应时间 | 例子 |
|---|---|---|---|---|
| P0 | 全站不可用 / 资金损失 | 电话 + 短信 + 企微 + 值班群 | 立即 | 网关全挂、数据库主库不可用 |
| P1 | 核心功能受损 | 电话 + 短信 | 15 分钟 | 支付成功率跌破 90% |
| P2 | 部分用户受影响 | 企微 / 钉钉 | 2 小时 | 某台机器磁盘 85% |
| P3 | 潜在风险 / 趋势异常 | 邮件 / 日报 | 当天 | 磁盘周增长 20% 需扩容规划 |
告警质量的三条铁律:
| 铁律 | 说明 | 反例 |
|---|---|---|
| 每个告警必须可行动 | 收到告警你知道该做什么 | "CPU 使用率 71%"——然后呢? |
| 每个告警必须有 owner | 有明确的人负责 | 告警发到群里没人认领,最后没人看 |
| 每个告警必须分级 | 不同级别不同处理方式 | 所有告警都发钉钉 → 全部被忽略 |
最常见的三种"垃圾告警",一定要删掉
- 阈值型资源告警:
CPU > 80%。 CPU 80% 不一定是问题(可能是有用负载),CPU 40% 也可能是问题(可能在等锁)。 应该用业务指标(错误率、延迟、饱和度)替代或补充。 - 单点瞬时抖动告警:一次请求超时就告警。 必须加持续时间条件(如"错误率 > 1% 持续 2 分钟"),否则告警风暴。
- 没有抑制关系的告警:数据库挂了,触发 50 个服务的连接失败告警。 必须配抑制规则:基础设施告警触发时,抑制上层应用告警。
第 10 章 安全、容灾与应急
10.1 高可用的等级(先搞清楚你在哪一级)
| 等级 | 可用性 | 年停机时间 | 实现方式 | 成本 |
|---|---|---|---|---|
| 单机 | 99% | 3.65 天 | 一台机器 | 低 |
| 双机/主备 | 99.9% | 8.76 小时 | 主备切换(分钟级) | 2 倍 |
| 集群多副本 | 99.95% | 4.38 小时 | 多节点负载均衡 | 2-3 倍 |
| 多可用区 | 99.99% | 52.6 分钟 | 跨 AZ 部署 | 3-4 倍 |
| 同城双活 | 99.995% | 26.3 分钟 | 两机房同时服务 | 4-6 倍 |
| 异地多活 | 99.999% | 5.26 分钟 | 多地同时服务 | 8 倍以上 |
一个残酷的成本规律
可用性每提高一个 9,成本大约翻一倍。 所以"我们的系统要做到 5 个 9"这句话,背后必须有业务价值支撑(比如支付、金融)。 一个内部审批系统做到 99.9% 就完全够了。 架构设计的第一原则不是"越高可用越好",而是"可用性匹配业务价值"。
10.2 容灾的两个关键指标
| 指标 | 全称 | 含义 | 通俗理解 |
|---|---|---|---|
| RTO | Recovery Time Objective | 恢复时间目标 | 能接受停多久 |
| RPO | Recovery Point Objective | 恢复点目标 | 能接受丢多少数据 |
不同业务的要求差异极大:
| 业务 | RTO | RPO | 说明 |
|---|---|---|---|
| 支付/交易 | < 1 分钟 | 0(不能丢) | 必须同步复制 + 多副本 |
| 核心订单 | < 5 分钟 | < 1 秒 | 半同步 + 自动切换 |
| 商品浏览 | < 30 分钟 | 可回滚 5 分钟 | 异步复制可接受 |
| 日志/报表 | 数小时 | 可丢部分 | 定时备份即可 |
最容易被忽略的:备份不等于容灾
有备份 ≠ 能恢复。 真实事故中,"备份文件损坏""备份恢复了 8 小时""恢复后发现备份是 3 天前的" 是最常见的情况。所以:
- 备份必须定期做恢复演练(每季度至少一次,在隔离环境完整恢复一遍)。
- 备份要异地存放(同机房备份,机房烧了备份也没了)。
- 要监控备份任务是否成功(很多公司是"备份脚本挂了半年没人知道")。
- 备份要防勒索(备份账号不要和业务账号同权限,用不可变存储保留)。
10.3 安全体系的基本盘
| 层面 | 措施 | 说明 |
|---|---|---|
| 网络 | 安全组最小开放、WAF、DDoS 高防、内网隔离 | 只开必需的端口 |
| 主机 | 基线加固、补丁管理、HIDS、防病毒 | 关闭无用服务、禁止 root 直登 |
| 应用 | 代码审计、依赖扫描(SCA)、SAST/DAST | 防 SQL 注入、越权、SSRF |
| 镜像 | 镜像扫描(Trivy)、只允许内网仓库 | 防止引入带漏洞的基础镜像 |
| 数据 | 传输加密(TLS)、存储加密、脱敏、分级分类 | 敏感数据必须加密 |
| 身份 | 统一认证、最小权限、MFA、堡垒机 | 禁止共享账号 |
| 审计 | 操作日志、数据库审计、变更审计 | 出了问题能追责 |
| 合规 | 等保 2.0、个人信息保护法、数据出境 | 法律要求,不是可选项 |
运维最容易犯的 6 个安全错误
- 用 root 直接 SSH 登录生产,且密码是弱密码 → 必须用堡垒机 + 密钥 + MFA。
- 数据库端口对公网开放 → 只允许应用区访问,且要密码 + IP 白名单。
- 密钥、密码写在代码/配置文件里提交到 Git → 用 K8s Secret + Vault,且 Git 提交要有 secret 扫描。
- Nacos / Redis / ES / Docker API 没有鉴权 → 这些都是"裸奔"高发区,能被直接接管服务器。
- 备份和业务用同一个账号 → 被入侵后备份一起被删(勒索软件最爱)。
- 测试环境用生产数据且没有脱敏 → 数据泄露直接违法。
10.4 故障处理的标准动作(SRE 的工作方式)
出故障时,人容易慌。成熟团队有固定流程:
① 发现(监控告警 / 用户反馈)
↓ 5 分钟内
② 定级(P0 全站不可用 / P1 核心功能受损 / P2 部分影响 / P3 轻微)
↓ 立即
③ 止血优先于定位根因 ★★★ 最重要的一条
· 先回滚、先重启、先扩容、先降级、先切流 —— 让服务先恢复
· 千万不要在故障中"顺手改点别的"(会把问题搞得更复杂)
↓
④ 定位(用监控→链路→日志三级定位)
↓
⑤ 修复(修复后验证:指标回落、用户可用)
↓
⑥ 复盘(无指责复盘 Blameless Postmortem)
· 时间线、根因(5 Why)、影响面、改进项(有 owner 有 deadline)
· 目标不是找人背锅,而是让系统更不容易出错"止血优先于定位根因"为什么这么重要
新人最常见的错误:出了故障非要"搞清楚为什么"才动手, 结果服务一直在挂,用户一直在骂,10 分钟过去了。 故障处理的第一目标是恢复服务,不是搞明白原理。回滚是最快、最安全的止血手段——这也是为什么 CI/CD 必须支持一键回滚。 (原理可以在事后复盘时慢慢想,那时候服务已经恢复了,你有充足时间。)
第 11 章 演进路线:小公司怎么长成这个样子
不要一步到位。 这篇文章描述的架构是演进的结果,不是起点。 真实公司是按阶段长起来的,把下面这张表看懂,你就理解了"为什么会有这些组件"。
| 阶段 | 规模 | 架构形态 | 关键技术变化 | 遇到的瓶颈 |
|---|---|---|---|---|
| 第 0 阶段 | 0~1000 DAU | 1 台服务器,Nginx + PHP/Java + MySQL 同机 | 单体应用,手工部署(FTP/scp) | 单点,挂了就全挂 |
| 第 1 阶段 | 1 千~1 万 | 应用与数据库分离;引入 Redis 缓存 | 读写分离;CI 工具(Jenkins) | 数据库压力、发版靠人 |
| 第 2 阶段 | 1 万~10 万 | 应用多实例 + Nginx 负载;主从数据库 | 容器化(Docker);配置中心 | 手工部署易错、环境不一致 |
| 第 3 阶段 | 10 万~100 万 | K8s 集群;微服务拆分;MQ 解耦;ELK 日志 | GitOps、服务治理、监控体系 | 微服务治理复杂、故障定位难 |
| 第 4 阶段 | 100 万~1000 万 | 多可用区、分库分表、Redis 集群、APM 链路追踪 | 灰度发布、全链路压测、容量规划 | 成本飙升、稳定性压力、数据一致性 |
| 第 5 阶段 | 1000 万+ | 异地多活、单元化、混合云、Serverless | 精细化成本治理、混沌工程、SRE 体系 | 组织效率、跨团队协作 |
每个阶段的"必备项"和"可以没有的"
| 阶段 | 必须有 | 明确可以没有 |
|---|---|---|
| 0~1 | 备份、监控(哪怕是 Zabbix) | K8s、MQ、APM |
| 2~3 | CI/CD、容器、集中配置、日志 | 分库分表、异地容灾 |
| 4 | APM、自动化容量管理、灰度 | 单元化 |
| 5 | 混沌工程、成本平台 | —— |
判断标准:当你被某个具体问题反复折磨(比如"发版总要停服"),就引入对应组件解决它。不要为了"架构先进"而引入自己驾驭不了的东西。
11.1 给转行者的学习路径建议
结合这套架构,从哪开始学:
| 顺序 | 学什么 | 对应本文 | 落地方式 |
|---|---|---|---|
| 1 | Linux 基础 + 网络基础 | 第 3、4 章 | 本机装 VM,配通网络 |
| 2 | Nginx 反向代理 + 负载均衡 | 第 4 章 | 2 台 VM 做 upstream |
| 3 | MySQL 主从复制 | 第 6.1 节 | 2 台 VM 搭主从,手动模拟主库宕机 |
| 4 | Redis 主从 + 哨兵 | 第 6.4 节 | 3 台 VM 或 3 个端口 |
| 5 | Docker + K8s 单节点 | 第 5.1 节 | minikube / kubeadm |
| 6 | Jenkins CI 流水线 | 第 9.5 节 | 打通"提交代码 → 自动构建 → 自动部署" |
| 7 | Prometheus + Grafana | 第 9.2 节 | 监控自己的 K8s 和 MySQL |
| 8 | ELK 日志平台 | 第 9.4 节 | 收集 K8s 日志 |
| 9 | Nacos + 微服务部署 | 第 7.2 节 | 部署若依微服务(本系列其他文档有完整流程) |
| 10 | Argo CD GitOps | 第 9.5 节 | 在本系列《Jenkins CI + Argo CD CD》文档里练 |
| 11 | 全链路压测 + 容量规划 | 第 6、10 章 | 用 JMeter 压测自己的服务 |
配套资源:本系列的《若依微服务上 K8s》《Jenkins CI + Argo CD CD》两份文档, 给出了第 5、6、9 步的完整可执行步骤,可以直接照着搭。
第 12 章 附录:术语速查与自检清单
12.1 架构术语速查表
| 缩写 | 全称 | 中文 | 一句话解释 |
|---|---|---|---|
| IDC | Internet Data Center | 自建机房 | 自己租机柜放服务器的机房 |
| VPC | Virtual Private Cloud | 虚拟私有云 | 云上逻辑隔离的私有网络 |
| AZ | Availability Zone | 可用区 | 同一地域内电力和网络独立的机房 |
| Region | Region | 地域 | 地理区域(如华东1、首尔) |
| ELB | Elastic Load Balancer | 弹性负载均衡 | 云上的四层/七层 LB |
| SLB | Server Load Balancer | (阿里云叫法)负载均衡 | 同 ELB |
| CDN | Content Delivery Network | 内容分发网络 | 边缘缓存加速 |
| WAF | Web Application Firewall | Web 应用防火墙 | 防注入/XSS/CC |
| DNS | Domain Name System | 域名系统 | 域名→IP |
| VIP | Virtual IP | 虚拟 IP | 负载均衡对外暴露的浮动 IP |
| L4 / L7 | Layer 4 / Layer 7 | 四层/七层 | 传输层/应用层负载 |
| API GW | API Gateway | API 网关 | 统一入口、鉴权、限流 |
| BFF | Backend For Frontend | 服务于前端的后端 | 为不同端定制的聚合层 |
| RPC | Remote Procedure Call | 远程过程调用 | 服务间调用(Dubbo/gRPC) |
| SLA | Service Level Agreement | 服务等级协议 | 承诺的可用性(如 99.9%) |
| SLO | Service Level Objective | 服务等级目标 | 内部目标,通常严于 SLA |
| SLI | Service Level Indicator | 服务等级指标 | 衡量 SLO 的具体度量 |
| RTO / RPO | — | 恢复时间/恢复点目标 | 能停多久 / 能丢多少 |
| MTTR | Mean Time To Recovery | 平均恢复时间 | 故障恢复耗时 |
| MTBF | Mean Time Between Failures | 平均无故障时间 | 两次故障间隔 |
| QPS / TPS | Queries/Transactions Per Second | 每秒查询/事务数 | 吞吐量指标 |
| P99 / P95 | Percentile | 分位延迟 | 99%/95% 请求快于此值 |
| HA | High Availability | 高可用 | 坏了能自动切 |
| DR | Disaster Recovery | 灾难恢复 | 机房级故障的应对 |
| IaC | Infrastructure as Code | 基础设施即代码 | 用代码管理基础设施 |
| GitOps | — | Git 运维 | 用 Git 作为唯一真相来源驱动部署 |
| PaaS | Platform as a Service | 平台即服务 | 内部研发效能平台 / 云平台 |
| IaaS | Infrastructure as a Service | 基础设施即服务 | 云主机、云网络 |
| SaaS | Software as a Service | 软件即服务 | 直接用的软件 |
12.2 架构自检清单(评审时逐条问)
流量入口
- [ ] 有没有 CDN?静态资源命中率多少?回源率多少?
- [ ] 有没有 WAF?被攻击过吗?防护规则多久更新?
- [ ] LB 是双节点吗?LB 自己挂了怎么办?
- [ ] 七层路由规则变更需要重启吗?会中断连接吗?
应用层
- [ ] 应用是无状态的吗?能不能随时杀掉一个 Pod 不影响用户?
- [ ] readinessProbe / livenessProbe 配了吗?配置正确吗?
- [ ] 有没有资源 limit 和 request?会不会一个 Pod 吃光节点内存?
- [ ] 单点故障有哪些?节点挂了 Pod 能否漂移?
数据层
- [ ] 数据库是一主两从吗?复制的同步模式是什么?延迟多少?
- [ ] 主库挂了谁来做切换?切换要多久?切换期间写请求怎么办?
- [ ] 备份做了吗?恢复演练做过吗? 备份在异地吗?
- [ ] 分库分表的分片键选对了吗?扩容方案想好了吗?
- [ ] Redis 是集群还是单机?挂了缓存穿透会不会打垮数据库?
中间件
- [ ] Nacos 是 3 节点吗?Nacos 自己的数据库高可用吗?开了鉴权吗?
- [ ] MQ 会丢消息吗?消费端幂等做了吗?积压了怎么处理?
- [ ] 配置中心的环境隔离是用 namespace 还是 group?(必须 namespace)
观测
- [ ] 有指标监控吗?告警能到人吗?告警分级了吗?会告警风暴吗?
- [ ] 有链路追踪吗?traceId 贯穿全链路了吗?
- [ ] 日志集中了吗?日志里能搜到 traceId 吗?日志成本多少?
- [ ] 有没有业务监控(而不只是资源监控)?核心接口的成功率知道吗?
发布与应急
- [ ] 发布能一键回滚吗?回滚需要多久?
- [ ] 生产发布需要审批吗?有审计记录吗?
- [ ] 有没有做过故障演练(杀进程、断网、删 Pod)?
- [ ] 有没有应急预案文档?值班表有吗?半夜告警谁接?
12.3 常见"看起来高可用,实际是单点"的陷阱
这是本文最实用的一节。很多架构图看着很美,实际经不起一次故障。
| 陷阱 | 为什么是假的 | 正确做法 |
|---|---|---|
| Nacos 集群了,但 Nacos 用单机 MySQL | MySQL 挂 → Nacos 全挂 → 所有服务拿不到配置 → 全站崩 | Nacos 的库必须一主两从 |
| Redis 主从了,但没配哨兵 | 主库挂了不会自动切换,要人工介入,RTO 几十分钟 | 哨兵 / Cluster 模式 |
| LB 双节点了,但共用一台交换机/一个机柜 | 机柜断电 → 双节点一起挂 | 跨机柜、跨可用区 |
| MySQL 主从了,但 VIP 没有 | 主库挂了切换后,应用不知道新 IP | VIP / 中间件(ProxySQL、MHA) |
| Jenkins 单点 | 挂了就发不了版(虽然不影响线上,但影响抢修) | Jenkins 主备 / 用 K8s 部署可自愈 |
| 只有一台跳板机 | 跳板挂了无法运维(最要命的时刻) | 双跳板 + 证书登录 |
| 监控只有一台 Prometheus | 监控挂了,你就是瞎的 | 双实例 + 远程存储 |
| 日志只有一台 ES | 出故障时日志平台自己先挂 | ES 至少 3 节点 |
| 备份和业务同账号同机房 | 被入侵/断电则备份一起没了 | 异地 + 独立账号 + 不可变存储 |
| "我们有多副本"但都在一个节点 | 节点挂 → 副本全没 | 反亲和性(anti-affinity)强制打散 |
最经典的一幕
故障的时候,恰恰是你最需要监控和跳板机的时候,而它们往往也在这时候挂了。 因为它们和业务系统共享了同一个故障域。 判断一个架构是否真的高可用,有一个非常简单的问题: "如果我现在拔掉任何一个机柜的电源,会发生什么?" 如果答案是"某个支撑系统全挂"——那它就不是高可用。
结语
如果你读到这里,应该已经建立了这样一张地图:
一家中大型互联网公司的技术体系 = 5 个平面(流量/业务/数据/支撑/观测)+ 1 条横切的交付线。
它们的所有复杂度,都在回答两个问题:高可用 和 可扩展。
而对你个人来说,最重要的认知是:
三条给转行者的忠告
- 别追求背下所有组件。 没有人能靠背记住这些。你要掌握的是**"它解决什么问题"**, 遇到具体需求时能想起来"这个场景应该有现成的方案",然后去查。这才是真正的能力。
- 从一条链路完整走通,比看十张架构图有用。 把"用户请求 → 网关 → 服务 → 数据库 → 返回"这一条链路,亲手搭一遍、压一遍、打挂一遍、 恢复一遍。你对架构的理解会超过读一百篇文章。
- 遇到不懂的组件,先问"没有它会怎样"。 这个问题能帮你跳过 90% 的细节,直接抓住本质。
配套阅读(本系列其他文档):
| 文档 | 内容 |
|---|---|
| 《计算机基础概念与术语》 | 本文的前置知识:网络、协议、操作系统、中间件的通俗讲解 |
| 《若依微服务上 K8s》 | 本文第 5、6 章的完整落地步骤 |
| 《Jenkins CI + Argo CD CD》 | 本文第 9.5 章发布线的完整落地步骤 |
| 《客户端 / 服务端类软件发布》 | 本文的补充:有独立客户端的软件怎么发布 |