云原生与微服务面试题 —— 从容器化到 Service Mesh 的深度问答
覆盖微服务架构、服务发现、API 网关、服务通信(gRPC/MQ)、Docker、Kubernetes、云原生(12-Factor/CNCF)、Service Mesh、可观测性、CI/CD 十大核心主题,30 道高频面试题附架构图解
云原生和微服务是现代后端工程师面试的高频主题——不仅考察你对单个技术的理解,更考察你如何将容器、编排、网关、服务网格等组件组合成一个弹性、可观测、易迭代的系统。
这篇文章的组织思路:每道题先给出”一句话记忆点”,再展开原理和架构思考,最后给出面试回答要点。侧重”为什么这么设计”而非死记硬背。
第一部分:微服务架构基础
Q1:单体架构和微服务架构有什么区别?什么时候该用微服务?
记忆点:单体是所有功能打包成一个进程部署,微服务是按业务能力拆分为多个独立部署的小服务。不要为了微服务而微服务——团队小、业务简单时单体更高效。
| 维度 | 单体架构 | 微服务架构 |
|---|---|---|
| 部署 | 整体打包,一次部署 | 各服务独立部署 |
| 技术栈 | 统一 | 各服务可选不同语言/框架 |
| 扩展 | 整体水平扩展 | 按服务粒度独立扩展 |
| 故障隔离 | 一个模块崩溃全部挂 | 故障隔离在单个服务 |
| 开发效率 | 小团队快,大团队慢 | 大团队并行开发效率高 |
| 运维复杂度 | 低 | 高(需要服务发现、链路追踪等基础设施) |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
单体架构:
┌─────────────────────────────────┐
│ 单体应用 │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │用户模块│ │订单模块│ │支付模块│ │
│ └──┬───┘ └──┬───┘ └──┬───┘ │
│ └────────┴────────┘ │
│ 共享数据库 │
│ ┌──────────┐ │
│ │ MySQL │ │
│ └──────────┘ │
└─────────────────────────────────┘
微服务架构:
┌──────┐ ┌──────┐ ┌──────┐
│用户服务│ │订单服务│ │支付服务│
└──┬───┘ └──┬───┘ └──┬───┘
│ │ │
┌──┴──┐ ┌──┴──┐ ┌──┴──┐
│UserDB│ │OrderDB│ │PayDB│
└─────┘ └──────┘ └─────┘
每个服务有自己的数据库(Database per Service)
什么时候从单体转微服务:
- 团队规模超过 2 个 Pizza Team(>10 人),代码冲突频繁
- 部分模块需要独立扩展(如搜索服务 CPU 密集,订单服务 IO 密集)
- 需要不同技术栈(如 ML 模块用 Python,核心业务用 Java)
- 发布周期需要解耦(A 功能不应等 B 功能一起上线)
面试加分: 微服务不是银弹。它引入了分布式事务、网络延迟、数据一致性、运维复杂度等问题。正确的路径是:先做好单体的模块化,再按需拆分。Martin Fowler 称之为”Monolith First”。
Q2:微服务拆分的原则是什么?怎么确定服务边界?
记忆点:按业务能力(Business Capability)拆分,而非按技术层拆分。每个服务对应一个限界上下文(Bounded Context),拥有独立的数据和生命周期。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
✗ 错误拆分(按技术层):
┌──────────────┐
│ 前端服务 │
├──────────────┤
│ 业务逻辑服务 │ ← 所有业务逻辑混在一起
├──────────────┤
│ 数据访问服务 │
└──────────────┘
✓ 正确拆分(按业务领域):
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│用户域 │ │商品域 │ │订单域 │ │支付域 │
│ │ │ │ │ │ │ │
│API │ │API │ │API │ │API │
│Logic │ │Logic │ │Logic │ │Logic │
│Data │ │Data │ │Data │ │Data │
└──────┘ └──────┘ └──────┘ └──────┘
每个域是自治的垂直切片
拆分原则:
- 单一职责:一个服务只做一件事(用户管理、库存管理)
- 高内聚低耦合:服务内部高度相关,服务之间交互最少
- 数据自治:每个服务管自己的数据,不直接读别人的数据库
- 可独立部署:修改一个服务不需要重新部署其他服务
面试加分: DDD(领域驱动设计)是微服务拆分的最佳理论工具。通过事件风暴(Event Storming)识别领域事件和聚合根,自然就能划出限界上下文。
Q3:微服务之间如何处理分布式事务?
记忆点:微服务中避免使用 2PC(两阶段提交),通常用 Saga 模式——把一个全局事务拆成多个本地事务,每个本地事务有对应的补偿操作。失败时反向执行补偿。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Saga 编排模式(Choreography):
订单服务 库存服务 支付服务
│ │ │
│ 创建订单 │ │
├─── OrderCreated 事件 ──────→│
│ │ │
│ │← 扣减库存 │
│ ├── StockReserved ──→│
│ │ │
│ │ │← 扣款
│ │ ├── PaymentDone
│ │ │
成功:三个本地事务都完成
失败:如果支付失败 → 发 PaymentFailed 事件
→ 库存服务执行补偿(回滚库存)
→ 订单服务执行补偿(取消订单)
Saga 编排模式(Orchestration):
┌────────────┐
│ Saga 编排器 │ ← 中心协调者
└─────┬──────┘
│ 1. 创建订单
├──→ 订单服务
│ 2. 扣减库存
├──→ 库存服务
│ 3. 扣款
├──→ 支付服务
│ 失败时反向补偿
└──→ ...
| 方案 | 一致性 | 复杂度 | 适用场景 |
|---|---|---|---|
| 2PC(两阶段提交) | 强一致性 | 高,阻塞 | 单数据中心、传统数据库 |
| TCC(Try-Confirm-Cancel) | 强一致性 | 高,侵入业务 | 金融、资金场景 |
| Saga | 最终一致性 | 中 | 电商、订单等大多数场景 |
| 本地消息表 | 最终一致性 | 低 | 简单场景 |
面试加分: Saga 的关键挑战是幂等性——补偿操作可能被重复调用,每个参与者必须保证幂等。常用手段:唯一事务 ID + 状态机。
第二部分:服务发现与注册
Q4:什么是服务发现?为什么微服务需要它?
记忆点:微服务实例的 IP 和端口是动态变化的(容器重启、自动扩缩容),不能硬编码地址。服务发现让消费者能动态找到提供者的地址——分为客户端发现和服务端发现两种模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
客户端发现(Client-Side Discovery):
┌──────┐ 1. 查询 ┌────────────┐
│消费者 │ ──────────────→ │ 服务注册中心 │
│ │ ←────────────── │(Eureka/Nacos)│
│ │ 2. 返回实例列表 └────────────┘
│ │ ↑
│ │ 3. 客户端负载均衡 │ 心跳注册
│ │ 选择一个实例 │
│ │ ──→ Provider-1 Provider-1
└──────┘ Provider-2
Provider-3
服务端发现(Server-Side Discovery):
┌──────┐ 1. 请求 ┌───────────┐ 2. 路由 ┌──────────┐
│消费者 │ ────────────→ │ 负载均衡器 │ ──────────→ │Provider-X│
└──────┘ │(Nginx/ALB) │ └──────────┘
└─────┬─────┘
│ 查询注册中心
┌─────┴──────┐
│ 服务注册中心 │
└────────────┘
| 注册中心 | 一致性模型 | 语言生态 | 特点 |
|---|---|---|---|
| Eureka | AP(最终一致) | Java/Spring Cloud | 自我保护机制,停止维护 |
| Nacos | AP + CP 可切换 | Java 为主,多语言 SDK | 同时支持服务发现和配置管理 |
| Consul | CP(Raft) | 多语言 | 内置健康检查、KV 存储、多数据中心 |
| etcd | CP(Raft) | Go/K8s 生态 | K8s 底层存储,轻量级 |
| ZooKeeper | CP(ZAB) | Java | 老牌,Kafka/Hadoop 依赖 |
面试加分: K8s 环境下通常不需要独立的注册中心——K8s Service + CoreDNS 已经实现了服务发现。Pod 通过 service-name.namespace.svc.cluster.local 即可访问目标服务。
Q5:服务注册中心的健康检查机制是怎样的?
记忆点:注册中心通过心跳(Heartbeat)或主动探测(Health Check)判断实例是否存活。心跳超时则剔除实例。Eureka 有自我保护机制——短时间大量实例掉线时不剔除,防止网络抖动导致误删。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
健康检查流程:
Provider ──心跳(每30s)──→ 注册中心
│
超过 90s 未收到心跳?
┌────┴────┐
│ 是 │ 否
标记为不健康 继续
从服务列表剔除
Eureka 自我保护:
最近 15 分钟心跳成功比例 < 85%?
→ 开启自我保护模式
→ 不剔除任何实例(宁可访问到不健康的,也不全部摘除)
→ 网络恢复后自动退出保护模式
三种健康检查方式:
- 客户端心跳(Eureka/Nacos):服务主动上报,注册中心被动接收
- 服务端探测(Consul):注册中心主动调用服务的
/health接口 - TTL 模式:服务定期续约 TTL,过期自动删除(类似 Redis 过期)
Q6:配置中心的作用和实现原理?
记忆点:配置中心把散落在各服务的配置文件集中管理,支持动态更新、灰度发布、环境隔离。核心原理是”推”或”拉”——客户端定期拉取或服务端主动推送配置变更。
1
2
3
4
5
6
7
8
9
10
11
12
13
配置中心架构:
┌──────────────────────────────────┐
│ 配置中心(Nacos/Apollo) │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │dev 环境│ │test环境│ │prod环境│ │
│ └───────┘ └───────┘ └───────┘ │
│ 修改配置 → 推送变更 │
└──────┬─────────┬─────────┬──────┘
│ │ │
┌────┴──┐ ┌───┴───┐ ┌──┴────┐
│服务 A │ │服务 B │ │服务 C │
│热更新 │ │热更新 │ │热更新 │
└───────┘ └───────┘ └───────┘
| 配置中心 | 推/拉模式 | 特点 |
|---|---|---|
| Nacos | 长轮询(Long Polling) | 服务发现+配置一体化 |
| Apollo | 推+拉结合 | 灰度发布、权限管理、审计 |
| Spring Cloud Config | 拉(需配合 Bus 推送) | 基于 Git 存储 |
| etcd/Consul KV | Watch 机制 | 轻量级,适合 K8s |
面试加分: 配置中心的核心挑战是一致性——修改配置后,所有实例什么时候生效?Apollo 的做法是:管理员发布 → 通知 Config Service → 实时推送到客户端(<1s 生效),同时客户端每 5 分钟兜底拉取。
第三部分:API 网关与服务通信
Q7:API 网关的作用是什么?和反向代理有什么区别?
记忆点:API 网关是微服务的统一入口,不仅做反向代理(路由转发),还承担认证鉴权、限流熔断、协议转换、日志监控等横切关注点。反向代理只管转发,网关管”治理”。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
没有网关:
客户端需要知道每个服务的地址
┌──────┐ ──→ 用户服务 :8001
│ 客户端 │ ──→ 订单服务 :8002
│ │ ──→ 支付服务 :8003
└──────┘ ──→ 商品服务 :8004
有网关:
客户端只需要知道网关地址
┌──────┐ ┌──────────────┐ ┌──────────┐
│ 客户端 │────→│ API 网关 │────→│ 用户服务 │
└──────┘ │ │────→│ 订单服务 │
│ • 路由转发 │────→│ 支付服务 │
│ • 认证鉴权 │────→│ 商品服务 │
│ • 限流熔断 │ └──────────┘
│ • 请求聚合 │
│ • 协议转换 │
│ • 日志监控 │
└──────────────┘
| 网关 | 语言 | 特点 |
|---|---|---|
| Kong | Lua/OpenResty | 插件生态丰富,性能高 |
| Spring Cloud Gateway | Java | Spring 生态原生,响应式 |
| APISIX | Lua/OpenResty | 国产,动态路由,Dashboard |
| Envoy | C++ | 云原生标配,Istio 数据面 |
| Nginx + Lua | C/Lua | 轻量,需自行实现治理功能 |
面试加分: BFF(Backend For Frontend)模式——为不同客户端(Web/App/小程序)设置不同的网关层,每个 BFF 聚合后端服务并裁剪数据格式,避免”一个接口适配所有端”。
Q8:gRPC 和 REST 的区别?什么场景用 gRPC?
记忆点:REST 用 HTTP/1.1 + JSON,人类可读但性能一般。gRPC 用 HTTP/2 + Protobuf,二进制序列化性能高,支持双向流式通信。微服务内部通信优先用 gRPC,对外暴露用 REST。
| 维度 | REST | gRPC |
|---|---|---|
| 协议 | HTTP/1.1(也可 HTTP/2) | HTTP/2 |
| 数据格式 | JSON(文本) | Protobuf(二进制) |
| 性能 | 一般 | 高(序列化快 5-10x,体积小 3-5x) |
| 流式通信 | 不支持(需 WebSocket) | 原生支持四种模式 |
| 浏览器支持 | 原生支持 | 需要 gRPC-Web 代理 |
| 可读性 | 高 | 低(二进制) |
| 代码生成 | 手动写 | 自动生成客户端/服务端代码 |
1
2
3
4
5
gRPC 四种通信模式:
1. Unary(一元): 客户端 ──请求──→ 服务端 ──响应──→ 客户端
2. Server Stream: 客户端 ──请求──→ 服务端 ═══多条响应═══→ 客户端
3. Client Stream: 客户端 ═══多条请求═══→ 服务端 ──响应──→ 客户端
4. Bidirectional: 客户端 ═══请求═══⇄═══响应═══ 服务端
适用场景:
- 用 gRPC:微服务间高频内部调用、实时数据流、低延迟要求
- 用 REST:对外 API、第三方集成、浏览器直接调用、简单 CRUD
面试加分: Protobuf 的向前/向后兼容性很好——新增字段用新编号,旧客户端忽略未知字段,新客户端对缺失字段用默认值。这在微服务独立部署时非常重要。
Q9:消息队列在微服务中扮演什么角色?
记忆点:消息队列实现服务间的异步解耦——生产者不需要等消费者处理完,削峰填谷应对流量突增,还能保证消息不丢失实现最终一致性。核心场景:异步处理、解耦、削峰。
1
2
3
4
5
6
7
8
9
10
11
12
同步调用的问题:
用户下单 → 订单服务 → 同步调用库存服务 → 同步调用支付服务 → 同步调用通知服务
总延迟 = 50ms + 80ms + 200ms + 100ms = 430ms
任一服务挂掉,整个链路失败
异步消息队列:
用户下单 → 订单服务 → 发消息到 MQ → 返回用户(50ms)
│
┌─────────┼─────────┐
↓ ↓ ↓
库存服务 支付服务 通知服务
异步消费 异步消费 异步消费
| 消息队列 | 吞吐量 | 延迟 | 特点 |
|---|---|---|---|
| Kafka | 极高(百万/s) | ms 级 | 日志、大数据、事件流 |
| RocketMQ | 高(十万/s) | ms 级 | 事务消息、延时消息、电商场景 |
| RabbitMQ | 中(万/s) | μs 级 | 路由灵活、协议丰富、中小规模 |
| Pulsar | 极高 | ms 级 | 存算分离、多租户、云原生 |
消息可靠性三板斧:
- 生产者确认:消息写入 Broker 后返回 ACK
- Broker 持久化:消息落盘 + 副本同步
- 消费者手动 ACK:处理完再确认,失败重试
面试加分: 消息队列要防止重复消费——网络抖动可能导致同一条消息被投递多次。解决方案:消费者做幂等处理(数据库唯一键、Redis 去重、状态机)。
第四部分:容器化与 Docker
Q10:Docker 的核心概念是什么?容器和虚拟机有什么区别?
记忆点:Docker 容器是进程级别的隔离,共享宿主机内核,启动秒级、体积 MB 级。虚拟机是操作系统级别的隔离,有独立内核,启动分钟级、体积 GB 级。容器 = 应用 + 依赖,虚拟机 = 应用 + 依赖 + 完整 OS。
1
2
3
4
5
6
7
8
9
10
11
12
13
虚拟机 vs 容器:
┌─────────────────┐ ┌─────────────────┐
│ App A │ App B │ │ App A │ App B │
├─────────┤────────┤ ├────────┤─────────┤
│ Bins/Libs│Bins/Libs│ │Bins/Libs│Bins/Libs│
├─────────┤────────┤ ├────────┴─────────┤
│ Guest OS│Guest OS│ │ Docker Engine │
├─────────┴────────┤ ├──────────────────┤
│ Hypervisor │ │ Host OS │
├──────────────────┤ ├──────────────────┤
│ Hardware │ │ Hardware │
└──────────────────┘ └──────────────────┘
虚拟机 容器
Docker 三大核心概念:
- 镜像(Image):只读模板,分层存储(UnionFS),类比”类”
- 容器(Container):镜像的运行实例,可写层,类比”对象”
- 仓库(Registry):存储和分发镜像的服务(Docker Hub、Harbor)
Docker 的隔离原理:
- Namespace:隔离进程 ID、网络、文件系统、用户等(看不到别的容器)
- Cgroups:限制 CPU、内存、IO 资源(不能用超)
- UnionFS:分层文件系统,镜像层共享 + 容器层可写
Q11:Dockerfile 的最佳实践有哪些?
记忆点:减少镜像层数、利用构建缓存、多阶段构建减小体积、不用 root 运行、用 .dockerignore 排除无关文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ✗ 不好的 Dockerfile
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get install -y pip
COPY . /app
RUN pip install -r requirements.txt
CMD ["python3", "app.py"]
# 问题:层太多、基础镜像太大、用了 latest 标签、root 运行
# ✓ 优化后的 Dockerfile(多阶段构建)
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
RUN useradd -r appuser
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY . .
USER appuser
EXPOSE 8080
CMD ["python3", "app.py"]
核心原则:
- 基础镜像用 slim/alpine:减小体积(从 ~1GB 到 ~100MB)
- 多阶段构建:编译阶段和运行阶段分离,最终镜像不含编译工具
- 利用缓存:把不常变的层(如依赖安装)放前面,常变的(如代码)放后面
- 不用 root:创建专用用户运行应用
- 固定版本:不用
latest标签,用具体版本号
面试加分: Docker 镜像的分层机制让多个容器可以共享底层镜像层(如 python:3.12-slim),只有顶部的可写层是各容器独立的,大幅节省磁盘和部署时间。
Q12:Docker 网络模式有哪些?
记忆点:四种网络模式——bridge(默认,容器通过虚拟网桥通信)、host(共享宿主机网络)、none(无网络)、overlay(跨主机通信,Swarm/K8s 用)。微服务本地开发用 bridge + docker-compose,生产用 overlay 或 K8s 网络。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Bridge 模式(默认):
┌─────────── 宿主机 ───────────────┐
│ │
│ ┌────────┐ ┌────────┐ │
│ │容器 A │ │容器 B │ │
│ │172.17.0.2│ │172.17.0.3│ │
│ └────┬───┘ └────┬───┘ │
│ │ │ │
│ ┌────┴─────────────┴────┐ │
│ │ docker0 bridge │ │
│ │ 172.17.0.1 │ │
│ └───────────┬───────────┘ │
│ │ NAT │
│ ┌────┴────┐ │
│ │ eth0 │ │
└─────────┴─────────┴─────────────┘
| 模式 | 隔离性 | 性能 | 适用场景 |
|---|---|---|---|
| bridge | 高 | 有 NAT 开销 | 默认,单机多容器 |
| host | 无 | 最高(无 NAT) | 对网络性能敏感的应用 |
| none | 完全隔离 | - | 安全敏感的批处理 |
| overlay | 高 | 有封装开销 | 多主机集群通信 |
第五部分:Kubernetes 核心概念
Q13:Kubernetes 的整体架构是怎样的?
记忆点:K8s 是 Master-Worker 架构。Master(控制面)负责调度决策,包含 API Server、Scheduler、Controller Manager、etcd。Worker(数据面)运行业务容器,包含 kubelet、kube-proxy、容器运行时。所有组件通过 API Server 通信。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
K8s 架构:
┌────────────────── Master Node(控制面)──────────────────┐
│ │
│ ┌────────────┐ ┌───────────┐ ┌──────────────────┐ │
│ │ API Server │ │ Scheduler │ │Controller Manager│ │
│ │ 集群入口 │ │ 调度 Pod │ │ 维护期望状态 │ │
│ └─────┬──────┘ └─────┬─────┘ └────────┬─────────┘ │
│ │ │ │ │
│ └───────────────┼──────────────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ etcd │ │
│ │ 集群状态存储│ │
│ └───────────┘ │
└──────────────────────────────────────────────────────────┘
│ │
┌──────────┴──── Worker Node 1 ──┐ ┌┴──── Worker Node 2 ──┐
│ ┌─────────┐ ┌────────────┐ │ │ │
│ │ kubelet │ │ kube-proxy │ │ │ (同样的组件) │
│ │管理 Pod │ │网络代理/LB │ │ │ │
│ └────┬────┘ └────────────┘ │ └───────────────────────┘
│ │ │
│ ┌────┴──────────────────┐ │
│ │ Pod Pod Pod │ │
│ │ ┌───┐ ┌───┐ ┌───┐ │ │
│ │ │app│ │app│ │app│ │ │
│ │ └───┘ └───┘ └───┘ │ │
│ └───────────────────────┘ │
└───────────────────────────────┘
各组件职责:
- API Server:集群的唯一入口,所有操作都经过它(RESTful API)
- etcd:分布式 KV 存储,保存集群所有状态数据
- Scheduler:决定 Pod 调度到哪个 Node(根据资源、亲和性等)
- Controller Manager:确保实际状态 = 期望状态(如 Deployment 维持副本数)
- kubelet:Node 上的代理,负责启停 Pod、健康检查、上报状态
- kube-proxy:维护 Node 上的网络规则,实现 Service 的负载均衡
Q14:Pod 是什么?为什么不直接管理容器?
记忆点:Pod 是 K8s 最小调度单元,包含一个或多个共享网络和存储的容器。不直接管理容器因为有些应用需要紧密协作的辅助容器(如日志收集 Sidecar),它们必须在同一个 Node、共享 localhost 和 Volume。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Pod 内部结构:
┌────────────── Pod ──────────────┐
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 主容器 │ │ Sidecar │ │
│ │ (app) │ │ (日志收集) │ │
│ │ :8080 │ │ :9090 │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ 共享 Network Namespace │
│ (同一个 IP, 通过 localhost 通信) │
│ │
│ 共享 Volume │
│ ┌───────────────────────┐ │
│ │ /var/log/app │ │
│ └───────────────────────┘ │
│ │
│ Pause 容器(维持网络命名空间) │
└─────────────────────────────────┘
Pod 的生命周期: Pending → Running → Succeeded/Failed
常见 Sidecar 模式:
- 日志收集:Filebeat/Fluentd 收集应用日志
- 代理:Envoy/Istio Sidecar 处理流量
- 配置热加载:监听 ConfigMap 变化并通知主容器
面试加分: Pod 中所有容器共享同一个 Network Namespace(通过 Pause 容器实现),所以容器间用 localhost:port 通信,对外则共享一个 Pod IP。
Q15:Deployment、Service、Ingress 分别是什么?
记忆点:Deployment 管”应用怎么跑”(副本数、滚动更新),Service 管”怎么找到应用”(稳定的虚拟 IP + 负载均衡),Ingress 管”外部怎么访问”(域名路由 + HTTPS 终止)。三者组合:Ingress → Service → Deployment → Pod。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
流量路径:
用户请求 api.example.com/users
│
┌──────┴──────┐
│ Ingress │ ← 域名路由 + TLS
│ /users → ? │
└──────┬──────┘
│
┌──────┴──────┐
│ Service │ ← ClusterIP: 10.96.0.100
│ user-svc │ 负载均衡到后端 Pod
└──────┬──────┘
┌────┼────┐
↓ ↓ ↓
┌───┐┌───┐┌───┐
│Pod││Pod││Pod│ ← Deployment 管理 3 个副本
└───┘└───┘└───┘
Deployment 的核心功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3 # 维持 3 个副本
strategy:
type: RollingUpdate # 滚动更新
rollingUpdate:
maxUnavailable: 1 # 更新时最多 1 个不可用
maxSurge: 1 # 更新时最多多 1 个
selector:
matchLabels:
app: user-service
template:
spec:
containers:
- name: user
image: user-service:v2.0
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Service 的四种类型:
| 类型 | 作用 | 场景 |
|---|---|---|
| ClusterIP | 集群内部虚拟 IP(默认) | 服务间内部调用 |
| NodePort | 在每个 Node 上开放端口(30000-32767) | 开发测试 |
| LoadBalancer | 创建云厂商的外部负载均衡器 | 生产环境对外暴露 |
| ExternalName | 映射到外部 DNS 名称 | 访问集群外部服务 |
面试加分: Ingress 本身只是一组路由规则,需要 Ingress Controller(如 Nginx Ingress、Traefik)来实际执行。可以实现基于路径和域名的路由、TLS 终止、灰度发布等。
Q16:K8s 的滚动更新和回滚是怎么实现的?
记忆点:Deployment 通过创建新 ReplicaSet 并逐步增加新 Pod、减少旧 Pod 来实现滚动更新。每次更新都会保留旧的 ReplicaSet(默认保留 10 个),回滚就是把副本数切回旧 ReplicaSet。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
滚动更新过程(replicas=3, maxSurge=1, maxUnavailable=1):
阶段1:创建新 ReplicaSet,启动 1 个新 Pod
旧 RS: [v1] [v1] [v1] 新 RS: [v2]
可用: 3 → 满足 maxUnavailable=1
阶段2:终止 1 个旧 Pod
旧 RS: [v1] [v1] [ ] 新 RS: [v2]
可用: 3
阶段3:启动 1 个新 Pod + 终止 1 个旧 Pod
旧 RS: [v1] [ ] [ ] 新 RS: [v2] [v2]
可用: 3
阶段4:启动 1 个新 Pod + 终止最后旧 Pod
旧 RS: [ ] [ ] [ ] 新 RS: [v2] [v2] [v2]
完成!旧 RS 保留(replicas=0)用于回滚
回滚命令:
1
2
3
4
5
6
7
8
# 查看历史版本
kubectl rollout history deployment/user-service
# 回滚到上一个版本
kubectl rollout undo deployment/user-service
# 回滚到指定版本
kubectl rollout undo deployment/user-service --to-revision=2
面试加分: 生产环境可以配合 readinessProbe 确保新 Pod 健康后才接收流量,避免滚动更新期间把流量路由到还没准备好的 Pod。
Q17:K8s 的资源调度机制是怎样的?
记忆点:Scheduler 分两步——过滤(Filter)排除不满足条件的 Node,打分(Score)在剩余 Node 中选最优。常用调度策略:资源请求/限制、节点亲和性(Node Affinity)、Pod 反亲和性(Anti-Affinity)、污点容忍(Taint/Toleration)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
调度流程:
新 Pod 创建 → 进入调度队列
│
┌────┴────── 过滤阶段 ──────┐
│ Node-A: CPU 不足 ✗ │
│ Node-B: 满足条件 ✓ │
│ Node-C: 满足条件 ✓ │
│ Node-D: 有污点不容忍 ✗ │
└────┬──────────────────────┘
│
┌────┴────── 打分阶段 ──────┐
│ Node-B: 资源均衡 80分 │
│ Node-C: 亲和性匹配 90分 │ ← 选 Node-C
└────┬──────────────────────┘
│
绑定 Pod → Node-C
核心调度策略:
- requests/limits:保证最小资源和上限,超出 limits 会被 OOM Kill
- nodeSelector:简单的标签匹配调度(如
disk: ssd) - Node Affinity:更灵活的节点亲和规则(软/硬约束)
- Pod Anti-Affinity:同一服务的 Pod 分散到不同 Node(高可用)
- Taint/Toleration:Node 打污点驱逐 Pod,特定 Pod 可容忍污点(如 GPU 节点只跑 ML 任务)
第六部分:云原生理念
Q18:什么是云原生?CNCF 是什么?
记忆点:云原生是一套方法论——利用容器、微服务、服务网格、不可变基础设施、声明式 API 等技术,构建弹性、可观测、易变更的分布式系统。CNCF(Cloud Native Computing Foundation)是云原生的标准制定者和项目孵化器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CNCF 云原生全景图(核心层):
┌────────────────────────────────────────────────┐
│ 应用定义与开发 │
│ Helm │ Kustomize │ Operator Framework │
├────────────────────────────────────────────────┤
│ 编排与管理 │
│ Kubernetes │ Scheduling │ Service Discovery │
├────────────────────────────────────────────────┤
│ 运行时 │
│ containerd │ CRI-O │ gVisor │ Kata │
├────────────────────────────────────────────────┤
│ 配置与安全 │
│ Vault │ OPA │ Cert-Manager │ SPIFFE │
├────────────────────────────────────────────────┤
│ 可观测与分析 │
│ Prometheus │ Grafana │ Jaeger │ OpenTelemetry│
├────────────────────────────────────────────────┤
│ 服务网格 │
│ Istio │ Linkerd │ Envoy │
└────────────────────────────────────────────────┘
云原生的四大支柱:
- 容器化:统一打包和运行环境
- 动态编排:K8s 自动调度、扩缩容、自愈
- 微服务化:按业务拆分,独立迭代
- DevOps + CI/CD:自动化交付流水线
Q19:12-Factor App 是什么?
记忆点:12-Factor 是 Heroku 总结的 12 条 SaaS 应用开发最佳实践。核心思想:应用与基础设施解耦,配置与代码分离,无状态设计,适合在云平台上运行。
| 因素 | 原则 | 要点 |
|---|---|---|
| 1. Codebase | 一份代码,多处部署 | 一个 repo 对应一个应用 |
| 2. Dependencies | 显式声明依赖 | package.json / go.mod / requirements.txt |
| 3. Config | 配置存环境变量 | 不在代码中硬编码数据库地址、密钥 |
| 4. Backing Services | 后端服务当附加资源 | 数据库/MQ/缓存通过 URL 连接,可随时替换 |
| 5. Build/Release/Run | 严格分离构建、发布、运行 | CI 构建 → 打镜像 → 部署运行 |
| 6. Processes | 无状态进程 | 不在本地存会话,用 Redis/DB 存状态 |
| 7. Port Binding | 端口绑定导出服务 | 应用自带 HTTP 服务器,不依赖外部容器 |
| 8. Concurrency | 通过进程模型扩展 | 水平扩展多实例而非垂直加配置 |
| 9. Disposability | 快速启动,优雅停止 | 秒级启动,SIGTERM 时清理资源 |
| 10. Dev/Prod Parity | 开发与生产尽量一致 | Docker + docker-compose 保证环境一致 |
| 11. Logs | 日志当事件流 | 输出到 stdout,由平台收集(不写本地文件) |
| 12. Admin Processes | 管理任务当一次性进程 | 数据库迁移用 kubectl exec 或 Job |
面试加分: 12-Factor 是 2011 年提出的,后来有人补充了 3 条形成”15-Factor”:API First、Telemetry(可观测性)、Authentication(安全)。
Q20:不可变基础设施(Immutable Infrastructure)是什么意思?
记忆点:不可变基础设施 = 不修改已部署的实例,而是用新镜像创建新实例替换旧实例。类比:不是修补旧衣服(Mutable),而是直接换新衣服(Immutable)。Docker 镜像 + K8s 滚动更新就是这个理念的实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
可变基础设施(传统):
服务器 A(已运行 3 个月)
→ SSH 上去打补丁
→ 手动改配置
→ 安装新依赖
→ 每台服务器状态都不一样(Configuration Drift)
不可变基础设施(云原生):
1. 修改代码/配置
2. 构建新镜像 v2.1
3. K8s 用新镜像滚动更新
4. 旧 Pod 销毁,新 Pod 运行
→ 所有实例状态一致,可重复创建
好处:
- 消除环境漂移:没有”我机器上能跑”的问题
- 可回滚:回到上个版本就是切回旧镜像
- 可审计:每次变更都有镜像版本记录
第七部分:Service Mesh
Q21:什么是 Service Mesh?它解决了什么问题?
记忆点:Service Mesh(服务网格)是处理服务间通信的基础设施层——把原本在应用代码中的负载均衡、熔断、重试、认证、可观测等逻辑下沉到 Sidecar 代理。应用只关心业务逻辑,通信治理交给 Mesh。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
没有 Service Mesh:每个服务自己处理通信逻辑
┌─────────────────────┐ ┌─────────────────────┐
│ 服务 A │ │ 服务 B │
│ ┌─────────────────┐ │ │ ┌─────────────────┐ │
│ │ 业务逻辑 │ │ │ │ 业务逻辑 │ │
│ ├─────────────────┤ │ │ ├─────────────────┤ │
│ │ 重试 │ 熔断 │ TLS│ │ ──→ │ │ 重试 │ 熔断 │ TLS│ │
│ │ 负载均衡 │ 监控 │ │ │ │ 负载均衡 │ 监控 │ │
│ └─────────────────┘ │ │ └─────────────────┘ │
└─────────────────────┘ └─────────────────────┘
问题:每个服务都要集成 SDK,语言绑定,升级困难
有 Service Mesh:通信逻辑在 Sidecar 中
┌───────────────────────────┐ ┌───────────────────────────┐
│ Pod A │ │ Pod B │
│ ┌──────────┐ ┌──────────┐ │ │ ┌──────────┐ ┌──────────┐ │
│ │ 服务 A │ │ Envoy │ │ ──→ │ │ Envoy │ │ 服务 B │ │
│ │ 纯业务 │ │ Sidecar │ │ │ │ Sidecar │ │ 纯业务 │ │
│ │ 逻辑 │ │ 重试/熔断 │ │ │ │ TLS/监控 │ │ 逻辑 │ │
│ └──────────┘ │ TLS/监控 │ │ │ │ 重试/熔断 │ └──────────┘ │
│ └──────────┘ │ │ └──────────┘ │
└───────────────────────────┘ └───────────────────────────┘
应用代码零侵入,治理能力由平台统一管理
Service Mesh 的核心能力:
- 流量管理:负载均衡、灰度发布、流量镜像、故障注入
- 安全:mTLS 双向认证、细粒度访问控制
- 可观测:自动生成请求指标、分布式追踪、访问日志
Q22:Istio 的架构是怎样的?
记忆点:Istio 分为数据面(Data Plane)和控制面(Control Plane)。数据面是每个 Pod 中注入的 Envoy Sidecar,拦截所有出入流量。控制面是 istiod,统一管理配置下发、证书颁发、服务发现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Istio 架构:
┌──────────────────── 控制面 ────────────────────┐
│ │
│ ┌───────────────── istiod ─────────────────┐ │
│ │ Pilot │ Citadel │ Galley │ │
│ │ 流量规则 │ 证书管理 │ 配置验证 │ │
│ │ 服务发现 │ mTLS │ 配置分发 │ │
│ └──────────────────────────────────────────┘ │
│ │ xDS 协议下发配置 │
└──────────────────────┼─────────────────────────┘
│
┌──────────────────── 数据面 ────────────────────┐
│ │ │
│ ┌─── Pod A ────┐ │ ┌─── Pod B ────┐ │
│ │ App │ Envoy │←──┘──→│ Envoy │ App │ │
│ └──────┴───────┘ └───────┴──────┘ │
│ ↕ 所有流量经 Envoy ↕ │
└────────────────────────────────────────────────┘
Istio 核心资源:
- VirtualService:定义路由规则(如 90% 流量到 v1,10% 到 v2)
- DestinationRule:定义目标策略(负载均衡算法、连接池、熔断)
- Gateway:配置入口网关(类似 Ingress 但更强大)
- PeerAuthentication:配置 mTLS 策略
Q23:Service Mesh 的 Sidecar 模式有什么问题?有替代方案吗?
记忆点:Sidecar 的主要问题是性能开销(每次请求多两跳代理)和资源消耗(每个 Pod 多一个 Envoy 容器占 ~50MB 内存)。替代方案是 Sidecar-less 模式——Cilium 用 eBPF 在内核层实现,Istio Ambient Mesh 用共享的 ztunnel 节点代理替代 per-Pod Sidecar。
1
2
3
4
5
6
7
8
9
10
11
12
Sidecar 模式 vs Sidecar-less 模式:
Sidecar(传统):
Pod 1: [App] ↔ [Envoy] 每个 Pod 一个 Sidecar
Pod 2: [App] ↔ [Envoy] 延迟 +2ms,内存 +50MB/Pod
Pod 3: [App] ↔ [Envoy]
Ambient Mesh(Sidecar-less):
Node: [ztunnel] ← 每个 Node 一个共享代理
Pod 1: [App] ──→ ztunnel ──→ Pod 2: [App]
Pod 3: [App] ──↗
内存开销大幅减少,延迟更低
| 方案 | 延迟开销 | 资源开销 | 功能完整性 | 成熟度 |
|---|---|---|---|---|
| Istio + Envoy Sidecar | 中(~2ms) | 高 | 最完整 | 生产就绪 |
| Istio Ambient Mesh | 低 | 低 | 逐步完善 | 正在成熟 |
| Cilium(eBPF) | 极低 | 极低 | L4 完整,L7 部分 | 生产就绪 |
| Linkerd | 低 | 中 | 够用 | 生产就绪,轻量 |
面试加分: eBPF(Extended Berkeley Packet Filter)是近年来最热的底层技术之一——它允许在 Linux 内核中安全运行沙盒程序,无需修改内核代码。Cilium 利用 eBPF 在内核态实现网络策略、负载均衡和可观测性,完全绕过用户态代理。
第八部分:可观测性
Q24:可观测性的三大支柱是什么?
记忆点:可观测性(Observability)= 日志(Logs)+ 指标(Metrics)+ 链路追踪(Traces)。日志告诉你”发生了什么”,指标告诉你”系统状态如何”,链路追踪告诉你”请求走了哪条路”。三者结合才能快速定位问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
可观测性三大支柱:
┌─────────────────────────────────────────────┐
│ 可观测性平台 │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Logs │ │ Metrics │ │ Traces │ │
│ │ 日志 │ │ 指标 │ │ 链路追踪 │ │
│ │ │ │ │ │ │ │
│ │ 离散事件 │ │ 聚合数值 │ │ 请求链路 │ │
│ │ 排查细节 │ │ 趋势告警 │ │ 跨服务追踪 │ │
│ │ │ │ │ │ │ │
│ │ ELK Stack │ │Prometheus │ │ Jaeger │ │
│ │ Loki │ │ Grafana │ │ Zipkin │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │
│ OpenTelemetry(统一采集标准) │
└─────────────────────────────────────────────┘
| 支柱 | 数据类型 | 典型工具 | 用途 |
|---|---|---|---|
| 日志 | 文本事件流 | ELK、Loki、Fluentd | 错误排查、审计 |
| 指标 | 时序数值 | Prometheus、Grafana | 监控告警、趋势分析 |
| 链路追踪 | 请求调用链 | Jaeger、Zipkin、Tempo | 延迟分析、依赖排查 |
面试加分: OpenTelemetry(OTel)正在成为可观测性的事实标准——它统一了 Logs、Metrics、Traces 的 SDK 和协议,一次埋点即可导出到任意后端(Prometheus、Jaeger、Datadog 等),避免厂商锁定。
Q25:Prometheus + Grafana 的监控架构是怎样的?
记忆点:Prometheus 是拉模式(Pull)的时序数据库——定期从目标端点抓取指标。Grafana 是可视化面板。完整链路:应用暴露 /metrics → Prometheus 拉取存储 → Grafana 展示 → AlertManager 告警。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Prometheus 监控架构:
┌──────────────┐
│ Grafana │
│ 可视化面板 │
└──────┬───────┘
│ PromQL 查询
┌──────┴───────┐
│ Prometheus │
│ 时序数据库 │
│ 定期拉取指标 │
└──┬───┬───┬───┘
Pull │ │ │
┌───────────────────┘ │ └──────────────┐
↓ ↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│ 服务 A │ │ 服务 B │ │ Node │
│ /metrics │ │ /metrics │ │ Exporter │
│ http_req │ │ http_req │ │ cpu/mem │
│ _total │ │ _duration │ │ disk/net │
└───────────┘ └───────────┘ └───────────┘
┌──────────────┐
│ AlertManager │
│ 告警路由 │
│ → Slack │
│ → PagerDuty │
│ → 邮件 │
└──────────────┘
四种指标类型:
- Counter(计数器):只增不减,如请求总数
http_requests_total - Gauge(仪表盘):可增可减,如当前内存使用
memory_usage_bytes - Histogram(直方图):分布统计,如请求延迟 P50/P95/P99
- Summary(摘要):类似 Histogram,客户端计算分位数
关键监控指标(RED 方法):
- Rate:请求速率(QPS)
- Errors:错误率(5xx 比例)
- Duration:延迟分布(P99 < 200ms)
Q26:分布式链路追踪的原理是什么?
记忆点:请求进入系统时生成全局唯一的 Trace ID,每经过一个服务生成一个 Span(包含服务名、耗时、状态)。所有 Span 通过 Trace ID 串联成一棵调用树。传递方式:HTTP Header 或 gRPC Metadata。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
一次请求的链路追踪:
Trace ID: abc-123
┌───────────────────────────────────────────────────┐
│ Span A: API Gateway (总耗时 350ms) │
│ ┌───────────────────────────────────┐ │
│ │ Span B: 用户服务 (50ms) │ │
│ └───────────────────────────────────┘ │
│ ┌─────────────────────────────────────────┐ │
│ │ Span C: 订单服务 (200ms) │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Span D: 数据库查询 (80ms)│ │ │
│ │ └──────────────────────┘ │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ Span E: 缓存查询 (5ms) │ │ │
│ │ └────────────────────────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ ┌───────────────────┐ │
│ │ Span F: 通知服务 (30ms)│ │
│ └───────────────────┘ │
└───────────────────────────────────────────────────┘
上下文传递:
服务 A → HTTP Header: traceparent: 00-abc123-span_a-01
服务 B 收到 Header → 创建子 Span,parent = span_a
核心概念:
- Trace:一次完整请求的调用链
- Span:调用链中的一个操作单元(一个 RPC 调用、一次 DB 查询)
- Context Propagation:跨服务传递 Trace ID 和 Span ID
面试加分: 采样策略很重要——全量采集链路数据开销太大。常用策略:头部采样(请求入口决定是否采集,如 1% 采样率)、尾部采样(请求完成后根据是否出错/慢来决定是否保留)。
第九部分:CI/CD 与 DevOps
Q27:CI/CD 的核心概念是什么?
记忆点:CI(持续集成)= 频繁合并代码 + 自动构建测试。CD 有两层含义——持续交付(Continuous Delivery)= 自动部署到预发环境,人工审批上线;持续部署(Continuous Deployment)= 全自动部署到生产,无需人工干预。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CI/CD 流水线:
开发者 Push 代码
│
┌────┴──── CI(持续集成)────────────────────┐
│ 1. 代码拉取 │
│ 2. 依赖安装 │
│ 3. 代码检查(Lint) │
│ 4. 单元测试 │
│ 5. 构建镜像 │
│ 6. 集成测试 │
└────┬──────────────────────────────────────┘
│ 通过
┌────┴──── CD(持续交付/部署)────────────────┐
│ 7. 推送镜像到 Registry │
│ 8. 部署到 Staging 环境 │
│ 9. 自动化 E2E 测试 │
│ 10. [人工审批] 或 自动 │
│ 11. 部署到 Production │
│ 12. 健康检查 + 监控 │
└────────────────────────────────────────────┘
| CI/CD 工具 | 特点 |
|---|---|
| GitHub Actions | 与 GitHub 深度集成,YAML 配置 |
| GitLab CI | GitLab 内置,功能全面 |
| Jenkins | 老牌,插件丰富,自托管 |
| ArgoCD | K8s 原生,GitOps 模式 |
| Tekton | K8s 原生,Pipeline as Code |
Q28:什么是 GitOps?和传统 CI/CD 有什么区别?
记忆点:GitOps = 用 Git 仓库作为基础设施和应用配置的唯一真实来源(Single Source of Truth)。所有变更通过 Git PR 触发,ArgoCD 等工具自动同步 Git 中的声明式配置到 K8s 集群。核心原则:声明式 + 版本化 + 自动同步。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
传统 CI/CD(Push 模式):
开发者 → Push 代码 → CI 构建 → CD 脚本 Push 到集群
问题:CD 脚本有集群权限,安全风险高
GitOps(Pull 模式):
┌────────┐ PR ┌──────────┐ Watch ┌──────────┐
│开发者 │ ────→ │ Git Repo │ ←────── │ ArgoCD │
│修改 YAML│ │ 声明式配置 │ │ 集群内运行 │
└────────┘ └──────────┘ └────┬─────┘
│ Sync
┌──────┴──────┐
│ K8s 集群 │
│ 自动同步状态 │
└─────────────┘
Git 中的状态 = 集群中的实际状态(drift detection)
GitOps 的好处:
- 可审计:所有变更都有 Git 提交记录
- 可回滚:
git revert即可回滚基础设施 - 安全:CI 不需要集群权限,ArgoCD 在集群内部拉取
- 一致性:自动检测并修复配置漂移
Q29:蓝绿部署、金丝雀发布、灰度发布有什么区别?
记忆点:蓝绿部署 = 两套完整环境一键切换。金丝雀发布 = 先给小比例用户用新版本,逐步扩大。灰度发布 = 金丝雀的增强版,按用户特征(地域、设备、用户 ID)精确控制流量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
蓝绿部署:
┌──────────┐ ┌──────────┐
│ 蓝(v1) │ ←── │ 负载均衡 │ 100% 流量走蓝
│ 当前版本 │ └──────────┘
└──────────┘
┌──────────┐
│ 绿(v2) │ 新版本待命
│ 新版本 │
└──────────┘
切换后:
┌──────────┐
│ 蓝(v1) │ 旧版本待命(可快速回滚)
└──────────┘
┌──────────┐ ┌──────────┐
│ 绿(v2) │ ←── │ 负载均衡 │ 100% 流量走绿
└──────────┘ └──────────┘
金丝雀发布:
┌──────────┐ ┌──────────┐
│ v1(95%) │ ←── │ 流量分发 │ ──→ 95% 用户
└──────────┘ └──────────┘
┌──────────┐ │
│ v2(5%) │ ←───────┘ 5% 用户试用新版本
└──────────┘
监控正常 → 逐步增加到 10% → 50% → 100%
发现问题 → 立即回退到 0%
| 策略 | 资源成本 | 回滚速度 | 风险控制 | 适用场景 |
|---|---|---|---|---|
| 蓝绿部署 | 高(双倍资源) | 秒级 | 全量切换 | 核心服务、需要快速回滚 |
| 金丝雀发布 | 低 | 秒级 | 精细 | 大规模服务、渐进式发布 |
| 滚动更新 | 低 | 分钟级 | 中等 | K8s 默认,日常发布 |
第十部分:综合实战
Q30:如果让你从零设计一个云原生微服务架构,你会怎么做?
记忆点:不要一开始就全套上——从单体开始,识别瓶颈再拆分。架构设计遵循”演进式架构”,每引入一个组件都要有明确的问题要解决。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
演进路径:
Phase 1: 单体 + Docker
┌─────────────┐
│ 单体应用 │ ← Docker 容器化,CI/CD 自动部署
│ PostgreSQL │
└─────────────┘
Phase 2: 拆分核心服务
┌──────┐ ┌──────┐ ┌──────┐
│用户 │ │订单 │ │支付 │ ← 按 DDD 拆分
└──┬───┘ └──┬───┘ └──┬───┘
│ REST/gRPC │ MQ │
┌──┴─────────────┴──────┴──┐
│ API Gateway │ ← 统一入口
└───────────────────────────┘
Phase 3: 上 Kubernetes
┌────────────────────────────────┐
│ K8s Cluster │
│ Deployment + Service + Ingress│
│ HPA 自动扩缩容 │
│ ConfigMap/Secret 配置管理 │
└────────────────────────────────┘
Phase 4: 可观测性 + 治理
┌────────────────────────────────┐
│ Prometheus + Grafana(监控) │
│ ELK/Loki(日志) │
│ Jaeger(链路追踪) │
│ Istio/Cilium(Service Mesh) │
└────────────────────────────────┘
Phase 5: 成熟期
GitOps(ArgoCD)
混沌工程(Chaos Monkey)
多集群/多云部署
面试回答框架:
- 业务理解:先搞清楚业务域和流量规模
- 技术选型:根据团队技术栈和规模选择合适的工具
- 渐进式演进:不要过度设计,每一步都解决当前最痛的问题
- 可观测性优先:没有监控的微服务是灾难
- 自动化一切:CI/CD、基础设施即代码、自动扩缩容
面试加分: 能说出”微服务不是目标,而是手段”的候选人通常更受青睐。真正的目标是组织效率——让多个团队能独立、快速、安全地交付价值。如果一个 5 人团队维护 3 个服务就够了,就不要拆成 30 个。
补充一:高频遗漏题——弹性设计与容错模式
Q31:熔断器(Circuit Breaker)模式是怎么工作的?
记忆点:熔断器有三个状态——Closed(正常放行)→ 当错误率超过阈值 → Open(直接拒绝,快速失败)→ 等待超时 → Half-Open(放少量请求试探)→ 成功则恢复 Closed,失败则回到 Open。类比家里的电路跳闸。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
熔断器状态机:
成功率恢复
┌─────────────────────────┐
│ ↓
┌──────┐ 错误率>阈值 ┌──────┐ 超时后试探 ┌───────────┐
│Closed│ ───────────→ │ Open │ ──────────→ │ Half-Open │
│ 正常 │ │ 熔断 │ │ 半开试探 │
└──────┘ └──────┘ └───────────┘
↑ │
└─────────── 试探请求成功 ──────────────────────┘
试探请求失败 → 回到 Open
实际例子(用户服务调用积分服务):
正常:用户服务 → 积分服务(200 OK) → Closed
异常:积分服务连续 5 次 5xx → Open(直接返回降级响应)
30s 后:放 1 个请求试试 → Half-Open
成功:恢复正常调用 → Closed
失败:继续熔断 → Open
常用框架:
| 框架 | 语言 | 特点 |
|---|---|---|
| Resilience4j | Java | 轻量级,函数式 API,Spring Boot 集成 |
| Sentinel | Java | 阿里开源,限流+熔断+降级一体 |
| Hystrix | Java | Netflix 开源,已停止维护,用 Resilience4j 替代 |
| Polly | .NET | .NET 生态的弹性库 |
| Istio | 任意语言 | Service Mesh 层面实现,无需改代码 |
Q32:限流的常用算法有哪些?
记忆点:四种主流限流算法——固定窗口(简单但有临界突刺)、滑动窗口(平滑但内存大)、漏桶(恒定速率,削峰)、令牌桶(允许突发,最常用)。面试记住令牌桶 = “攒令牌,花令牌”。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
令牌桶算法(Token Bucket):
┌─────────────────┐
│ 令牌生成器 │ ← 每秒往桶里放 N 个令牌
│ (恒定速率) │
└────────┬────────┘
↓
┌─────────────────┐
│ 令牌桶(容量M) │ ← 桶满了就不放了
│ [●][●][●][●][ ] │
└────────┬────────┘
↓ 每个请求消耗 1 个令牌
请求来了 → 桶里有令牌?
├── 有:通过,取走一个令牌
└── 没有:拒绝(返回 429 Too Many Requests)
优点:允许一定程度的突发流量(桶里攒了令牌就能突发用掉)
漏桶 vs 令牌桶:
漏桶:请求进桶,恒定速率流出 → 严格匀速,不允许突发
令牌桶:令牌恒定速率生成,请求需要令牌 → 允许突发(桶满了有 M 个令牌可以一次用完)
面试口诀: “漏桶匀速不突发,令牌突发能攒下”
Q33:K8s 的健康检查探针有哪些?
记忆点:三种探针——Liveness(活不活?死了就重启)、Readiness(准备好没?没好就不给流量)、Startup(启动完没?没完就等着别杀)。记忆口诀:”活不活,好没好,启动了没有”。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 三种探针配置示例
containers:
- name: app
livenessProbe: # 活不活?失败 → 重启容器
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3 # 连续 3 次失败才判定为死
readinessProbe: # 好没好?失败 → 从 Service 摘除,不接流量
httpGet:
path: /ready
port: 8080
periodSeconds: 5
startupProbe: # 启动完没?没完就不检查 liveness
httpGet: # 适合启动慢的应用(如 Java)
path: /healthz
port: 8080
failureThreshold: 30 # 最多等 30 × 10 = 300s
periodSeconds: 10
1
2
3
4
5
6
7
8
9
10
11
12
13
探针决策流程:
Pod 启动
│
├── startupProbe 检查中... (不检查 liveness/readiness)
│ └── 成功 → 开始检查 liveness 和 readiness
│ └── 失败超过阈值 → 杀掉重启
│
├── livenessProbe: 容器死了吗?
│ └── 失败 → kubelet 重启容器
│
└── readinessProbe: 能接流量吗?
└── 失败 → 从 Service Endpoints 摘除(不接新流量)
└── 恢复 → 重新加入 Endpoints
面试常考陷阱: “如果只配了 livenessProbe 没配 readinessProbe 会怎样?” → 容器还没完全启动就被加入 Service 接收流量,导致请求 503。所以两个都要配。
Q34:K8s HPA 自动扩缩容是怎么工作的?
记忆点:HPA(Horizontal Pod Autoscaler)根据指标(CPU/内存/自定义指标)自动调整 Pod 副本数。每 15 秒检查一次指标,当 CPU 利用率超过目标值就扩容,低于就缩容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-service
minReplicas: 2 # 最少 2 个
maxReplicas: 20 # 最多 20 个
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU 平均利用率超 70% 就扩容
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 缩容等 5 分钟(防抖)
1
2
3
4
5
6
7
8
9
10
11
12
HPA 工作流程:
Metrics Server 采集 Pod 指标
│
HPA Controller(每 15s 检查一次)
│
当前 CPU 平均利用率 = 85%,目标 = 70%
│
期望副本数 = ceil(当前副本 × 85/70) = ceil(3 × 1.21) = 4
│
调整 Deployment replicas: 3 → 4
│
Scheduler 调度新 Pod → Node
补充二:K8s 故障排查实战速查
面试最爱问:”你遇到过什么生产问题?怎么排查的?” 以下是最常见的 K8s 故障场景和排查路径:
场景 1:Pod 状态 CrashLoopBackOff
1
2
3
4
5
6
7
8
9
10
# 排查路径:
kubectl describe pod <pod-name> # 看 Events 和 Last State
kubectl logs <pod-name> # 看应用日志
kubectl logs <pod-name> --previous # 看上次崩溃的日志(重点!)
# 常见原因:
# 1. 应用启动失败(配置错误、缺少环境变量)
# 2. OOM Killed(内存超过 limits)→ describe 里看 Reason: OOMKilled
# 3. livenessProbe 误杀(探针路径写错、超时太短)
# 4. 启动命令错误(CMD/ENTRYPOINT 写错)
场景 2:Pod 状态 Pending
1
2
3
4
5
6
7
8
kubectl describe pod <pod-name> # 看 Events 里的调度失败原因
# 常见原因:
# 1. 资源不足 → "Insufficient cpu/memory"
# → 扩容 Node 或调整 requests
# 2. 节点亲和性不匹配 → "didn't match node selector"
# 3. PVC 绑定失败 → "unbound PersistentVolumeClaims"
# 4. 所有节点有 Taint 且 Pod 没有 Toleration
场景 3:Service 访问不通
1
2
3
4
5
6
7
8
9
10
# 排查路径(从外到内):
kubectl get svc <svc-name> # 检查 Service 是否存在
kubectl get endpoints <svc-name> # 检查后端 Pod 列表是否为空!
kubectl get pods -l app=<label> # Pod 的 label 是否和 Service selector 匹配
kubectl exec -it <pod> -- curl localhost:8080 # Pod 内部能不能访问自己
# 最常见原因:
# 1. endpoints 为空 → Pod label 和 Service selector 不匹配(拼写错误!)
# 2. Pod 的 readinessProbe 失败 → 被摘除出 endpoints
# 3. 网络策略(NetworkPolicy)阻断了流量
场景 4:应用延迟突然升高
1
2
3
4
5
6
7
8
9
# 排查路径:
# 1. 看监控 → Grafana 看 QPS、延迟 P99、错误率(RED 三板斧)
# 2. 看链路追踪 → Jaeger 找慢在哪个 Span
# 3. 看资源 → kubectl top pods(CPU/内存是否打满)
# 4. 看日志 → 是不是数据库慢查询、GC 停顿、连接池耗尽
# 5. 看网络 → 是不是 DNS 解析慢、跨 AZ 延迟
# 常见原因排名:
# 数据库慢查询 > 下游服务超时 > GC 停顿 > 连接池/线程池满 > 网络问题
补充三:5 分钟动手实验速记
纸上得来终觉浅,这些命令建议在本地 Docker/Minikube 上实际跑一遍,比看十遍文档都管用。
实验 1:Docker 镜像分层体验(5 分钟)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 创建一个简单的 Dockerfile
cat > Dockerfile <<'EOF'
FROM alpine:3.19
RUN echo "layer 1" > /file1.txt
RUN echo "layer 2" > /file2.txt
RUN echo "layer 3" > /file3.txt
CMD ["cat", "/file1.txt", "/file2.txt", "/file3.txt"]
EOF
# 2. 构建并查看层
docker build -t layer-demo .
docker history layer-demo # ← 看每一层的大小和命令
# 3. 修改最后一行 RUN,重新 build,观察缓存命中
# 你会发现前两层秒过(Using cache),只有最后一层重新构建
# → 这就是"把不常变的放前面"的原理
实验 2:K8s 滚动更新+回滚(10 分钟,需要 Minikube/Kind)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 创建 Deployment
kubectl create deployment nginx-demo --image=nginx:1.24 --replicas=3
# 2. 观察滚动更新(开两个终端)
# 终端 1:实时观察 Pod 变化
kubectl get pods -w
# 终端 2:触发更新
kubectl set image deployment/nginx-demo nginx=nginx:1.25
# 3. 查看更新历史
kubectl rollout history deployment/nginx-demo
# 4. 回滚
kubectl rollout undo deployment/nginx-demo
# 5. 感受 → 你会亲眼看到旧 Pod 逐个 Terminating,新 Pod 逐个 Running
实验 3:熔断器效果体验(5 分钟,用 curl 模拟)
1
2
3
4
5
6
7
8
9
10
11
12
13
# 概念模拟(不需要装框架):
# 假设你调用一个服务,连续 5 次超时
# 没有熔断器:
for i in {1..20}; do
curl --max-time 3 http://slow-service/api # 每次都等 3 秒才超时
done
# → 20 次请求,总共等了 60 秒,线程全部阻塞
# 有熔断器(伪代码逻辑):
# 前 5 次:正常调用,3 秒超时 → 触发熔断
# 第 6-20 次:直接返回降级响应(<1ms),不再调用下游
# → 保护了调用方,避免级联故障(雪崩效应)
实验 4:Prometheus 指标体验(3 分钟)
1
2
3
4
5
6
7
8
9
10
11
12
# 不需要装 Prometheus,先理解 /metrics 端点长什么样
# 任意一个暴露 metrics 的应用(如 Node Exporter):
curl http://localhost:9100/metrics | head -20
# 你会看到类似:
# # HELP node_cpu_seconds_total Seconds the CPUs spent in each mode.
# # TYPE node_cpu_seconds_total counter
# node_cpu_seconds_total{cpu="0",mode="idle"} 123456.78
# node_cpu_seconds_total{cpu="0",mode="system"} 1234.56
#
# 这就是 Prometheus 的数据格式:metric_name{labels} value
# Counter 只增不减,Gauge 可增可减
补充四:面试速记口诀与记忆锚点
面试前 30 分钟快速过一遍这些口诀,覆盖 80% 的高频考点。
微服务篇
| 口诀 | 对应知识点 |
|---|---|
| “先单体,后拆分” | Monolith First,别上来就微服务 |
| “按业务切,不按技术切” | 服务拆分原则:Business Capability + DDD Bounded Context |
| “自己的数据自己管” | Database per Service,服务间不共享数据库 |
| “Saga 补偿,幂等是关键” | 分布式事务用 Saga,每个参与者必须幂等 |
| “同步用 gRPC,异步用 MQ” | 内部高频调用 gRPC,解耦削峰用消息队列 |
Kubernetes 篇
| 口诀 | 对应知识点 |
|---|---|
| “API Server 是唯一入口” | 所有组件通过 API Server 通信,etcd 只有 API Server 能访问 |
| “Deployment 管跑,Service 管找,Ingress 管进” | 三大核心资源的职责 |
| “活不活,好没好,启动了没有” | livenessProbe / readinessProbe / startupProbe |
| “过滤打分选节点” | Scheduler 调度流程:Filter → Score → Bind |
| “声明式,非命令式” | K8s 的核心理念:你声明期望状态,Controller 负责达成 |
云原生篇
| 口诀 | 对应知识点 |
|---|---|
| “配置放环境变量,日志打 stdout” | 12-Factor 中最实用的两条 |
| “不修旧的,换新的” | 不可变基础设施 |
| “Git 是唯一真相” | GitOps 核心原则 |
| “数据面干活,控制面指挥” | Service Mesh / K8s / Istio 的通用架构模式 |
可观测性篇
| 口诀 | 对应知识点 |
|---|---|
| “日志看细节,指标看趋势,链路看瓶颈” | 可观测性三大支柱的分工 |
| “RED = 速率+错误+延迟” | 服务监控的黄金指标 |
| “Trace ID 串全链,Span 管单段” | 分布式链路追踪的核心概念 |
| “拉模式 Prometheus,推模式 Push Gateway” | Prometheus 的数据采集方式 |
部署发布篇
| 口诀 | 对应知识点 |
|---|---|
| “蓝绿一键切,金丝雀慢慢放” | 两种发布策略的核心区别 |
| “漏桶匀速不突发,令牌突发能攒下” | 限流算法:漏桶 vs 令牌桶 |
| “电路跳闸三状态:闭合-断开-半开” | 熔断器模式:Closed → Open → Half-Open |
补充五:面试真题模拟与回答框架
面试官不只问”是什么”,更爱问”你怎么选”、”遇到过什么问题”。以下是开放题的回答框架。
模拟题 1:”你们的微服务是怎么做服务治理的?”
回答框架(STAR 法):
1
2
3
4
5
6
7
8
9
10
11
Situation: 我们有 30+ 个微服务,Java 为主,部署在 K8s 上。
Task: 需要解决服务间调用的超时、重试、熔断、限流问题。
Action:
1. 初期用 Spring Cloud(Feign + Ribbon + Hystrix)
2. Hystrix 停止维护后迁移到 Resilience4j
3. 后来上了 Istio Service Mesh,把治理逻辑从代码下沉到基础设施
→ 好处:新增的 Go/Python 服务不用集成 Java SDK
4. 限流在网关层用 Sentinel 做,熔断在 Mesh 层做
Result:
→ 服务间 P99 延迟从 500ms 降到 200ms(熔断避免了级联超时)
→ 故障恢复时间从 30 分钟降到 5 分钟(自动熔断+降级)
模拟题 2:”生产环境出现大量 5xx,你怎么排查?”
回答框架(由外到内,逐层定位):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Step 1: 看监控大盘
→ Grafana 看是全局还是单个服务?从什么时间开始?
→ 是否和某次发布时间吻合?
Step 2: 定位到具体服务
→ 链路追踪(Jaeger)找到出错的 Span
→ 看是服务自身报错还是下游依赖报错
Step 3: 看日志
→ Kibana/Loki 搜索该服务的 ERROR 日志
→ 关键词:OOM、Connection refused、Timeout、NullPointer
Step 4: 看资源
→ kubectl top pods / kubectl describe pod
→ CPU 打满?内存 OOM?Pod 重启了?
Step 5: 常见根因(按概率排序)
1. 刚发布了有 bug 的版本 → 回滚
2. 数据库连接池耗尽 → 检查慢查询
3. 下游服务挂了导致级联 → 检查熔断是否生效
4. 配置变更出错 → 检查配置中心最近的变更
5. 流量突增超过限流阈值 → 检查 HPA 是否生效
模拟题 3:”如果让你选 Kafka 还是 RabbitMQ?”
回答框架(场景驱动选型):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
不能一概而论,取决于场景:
选 Kafka 的场景:
✓ 日志收集、事件流(高吞吐,百万/s)
✓ 大数据管道(对接 Flink/Spark)
✓ 需要消息回溯(重新消费历史消息)
✓ 顺序保证(同一 Partition 内有序)
选 RabbitMQ 的场景:
✓ 复杂路由需求(Topic、Fanout、Header Exchange)
✓ 低延迟(μs 级 vs Kafka 的 ms 级)
✓ 中小规模,团队更熟悉 AMQP 协议
✓ 需要消息优先级、延时消息等高级功能
一句话总结:
Kafka = 高速公路(吞吐大,但路线固定)
RabbitMQ = 城市路网(灵活路由,但承载量有限)