简介:《云原生架构白皮书》由阿里云发布,共70页,面向架构师、技术决策者及正在推进数字化转型的企业团队,系统解答云原生是什么、为何选择云原生架构以及如何落地等核心问题。资源为单个PDF文件,压缩包约2.53MB,内容完整、便于随时查阅。白皮书从容器、微服务、Serverless、Service Mesh、DevOps等关键技术切入,梳理云原生架构的定义、原则与典型反模式,并给出ACNA架构设计方法、成熟度模型及企业战略、业务、组织、技术四视角分析。产品层面覆盖容器、微服务、Serverless、消息、云原生数据库与数仓等家族;实践部分收录申通快递、完美日记、特步、中国联通、Timing App等案例,展示传统业务转型、电商零售与Serverless等场景效果,并展望新一代应用编程接口与Serverless趋势。目前已有240人学习,适合作为企业上云与架构演进的参考读本。
1. 云原生架构白皮书(70页).pdf:一份文档怎么变成落地路线图
很多人拿到「云原生架构白皮书(70页).pdf」这类文档,第一反应是收藏,第二反应是转发,第三反应是再也没打开过。我见过太多团队把白皮书当护身符,架构评审会上甩出一句「我们参考了云原生架构白皮书」,底下没人敢追问。问题不在文档,在于没人把它翻译成自己系统里能跑的东西。这份 70 页的体量,恰好卡在一个尴尬位置:比博客系统,比书简略,通读一遍两小时,但读完往往只记得「容器、Kubernetes、微服务、可观测性」几个词。真正值钱的是把它拆成选型判断、迁移顺序和验收指标。这篇笔记面向正在做容器化部署、准备把单体拆成分布式架构、或者被要求「上个云原生」的工程师,讲清楚怎么把一份白皮书读成可执行的改造清单,而不是又一份 PPT 素材。
2. 白皮书里的四层架构:先分清哪些是承诺,哪些是约束
云原生架构白皮书通常不会只讲 Kubernetes,它会把整套体系分成几个层次。读的时候要带着一个判断:这一层是「能力承诺」还是「工程约束」。承诺可以晚点做,约束必须一开始就守。
2.1 基础设施层:容器与 Kubernetes 到底解决了什么
白皮书里基础设施层的关键词是容器、编排、调度。容器资源隔离解决的是「同一台机器上多个服务互不踩脚」的问题,Kubernetes 解决的是「几百个容器怎么摆放、怎么自愈、怎么扩缩」。但这两件事不是免费的。
容器隔离依赖 Linux 的 namespace 和 cgroup。namespace 管「看得见什么」,cgroup 管「用得了多少」。很多线上事故的根因是只设了 requests 没设 limits,或者 limits 设了但没配 QoS,节点内存一紧张,OOM Killer 先杀谁全看运气。白皮书不会写这些细节,但你在落地时必须补上。
# 一个最小可用的 Pod 资源约束示例 apiVersion: v1 kind: Pod metadata: name: demo-app spec: containers: - name: app image: registry.example.com/demo:1.0.0 resources: requests: # 调度依据,决定 Pod 被放到哪个节点 cpu: "250m" # 0.25 核,按实际 P95 用量给 memory: "256Mi" limits: # 硬上限,超过会被 throttle 或 OOM cpu: "500m" memory: "512Mi" readinessProbe: # 就绪探针,没通过就不接流量 httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 10这段 YAML 里,requests 决定调度,limits 决定生死。CPU 超过 limits 会被内核 throttle,表现为延迟毛刺;内存超过 limits 直接 OOM Kill,表现为 Pod 重启。我一般会让 requests 等于 P95 用量,limits 给到 P99 的 1.5 倍,留出突发空间。readinessProbe 的 initialDelaySeconds 要大于应用冷启动时间,否则滚动更新时新 Pod 还没起来就被判定失败,整个发布卡死。
2.2 应用架构层:微服务拆分的三个硬门槛
白皮书会讲微服务架构,但不会告诉你什么时候不该拆。我的血泪经验是:团队少于 10 人、日均请求低于百万、没有独立部署诉求的项目,拆微服务就是给自己找事。拆分的硬门槛有三个。
第一,数据边界是否清晰。如果两个模块共享同一张表且频繁联表查询,拆成两个服务后要么走分布式事务,要么忍受数据不一致。第二,调用链是否可观测。拆完之后一个请求跨五个服务,没有链路追踪就是黑匣子,出问题只能挨个服务翻日志。第三,发布节奏是否真的不同。如果所有模块永远一起发版,拆开只是增加了 CI/CD 的复杂度。
# 用 OpenTelemetry 给服务加链路追踪的最小接入(以 Go 为例) go get go.opentelemetry.io/otel \ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc \ go.opentelemetry.io/otel/sdk/trace # 启动时初始化 TracerProvider,指向 collector 地址 export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 export OTEL_SERVICE_NAME=order-service接入链路追踪不是可选项。没有它,微服务拆分就是盲人摸象。OTEL_EXPORTER_OTLP_ENDPOINT 指向 collector,OTEL_SERVICE_NAME 决定你在 Jaeger 里看到的名字。这两个环境变量配错,数据就进不了后端,排查时你会以为代码没生效。
2.3 可观测性层:日志、指标、追踪的取舍
白皮书会把可观测性写成「三位一体」,但资源有限时要有优先级。我的排序是:指标 > 日志 > 追踪。指标最便宜,Prometheus 拉一个 /metrics 端点就能拿到 QPS、延迟、错误率;日志最贵,量大且难结构化;追踪最复杂,但对定位跨服务问题不可替代。
一个常见误区是把日志当指标用。比如用 grep 统计错误数,量一上来就崩。正确做法是错误计数走 Counter 指标,日志只保留上下文。
# Prometheus 指标暴露的最小示例 from prometheus_client import Counter, Histogram, start_http_server REQUEST_COUNT = Counter('app_requests_total', 'Total requests', ['method', 'endpoint', 'status']) REQUEST_LATENCY = Histogram('app_request_latency_seconds', 'Request latency', ['endpoint']) @REQUEST_LATENCY.labels('/api/order').time() def handle_order(): REQUEST_COUNT.labels('POST', '/api/order', '200').inc() # 业务逻辑 start_http_server(8000) # /metrics 端点Counter 只增不减,适合累计错误数;Histogram 记录分布,适合算 P95/P99。标签 cardinality 要控制,别把 user_id 当标签,否则 Prometheus 内存会被打爆。
2.4 安全与治理层:镜像安全和容器安全不是一回事
镜像安全管的是「镜像里有没有漏洞、有没有多余权限」,容器安全管的是「运行时有没有越权、有没有逃逸」。白皮书往往把两者混在一起讲,落地时要分开做。
镜像侧:基础镜像用 distroless 或 alpine,别用 latest 标签,构建时跑 trivy 扫描。容器侧:禁止 privileged,挂载 readOnlyRootFilesystem,用 securityContext 限制 capabilities。
securityContext: runAsNonRoot: true runAsUser: 1000 readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: ["ALL"]这五行配置能挡掉大部分容器逃逸的常见路径。readOnlyRootFilesystem 会让需要写临时文件的应用报错,解决办法是挂一个 emptyDir 到 /tmp。
3. 把白皮书变成迁移计划:从单体到容器化的五步走
读完架构分层,下一步是排迁移顺序。我一般按「先无状态、后有状态;先边缘、后核心;先灰度、后全量」的原则推进。下面五步是我在多个项目里验证过的顺序。
3.1 第一步:给现有单体做容器化封装
不要一上来就拆微服务。先把单体打成镜像跑起来,验证 CI/CD 和基础监控。这一步的目标是「能跑」,不是「跑得好」。
# 多阶段构建,减小镜像体积 FROM golang:1.21 AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o /app/server ./cmd/server FROM gcr.io/distroless/static:nonroot COPY --from=builder /app/server /server USER nonroot:nonroot ENTRYPOINT ["/server"]多阶段构建把编译环境和运行环境分开,最终镜像只有二进制和必要文件,体积从几百 MB 降到几十 MB。CGO_ENABLED=0 生成静态链接,避免依赖 glibc。distroless 镜像没有 shell,攻击面小,但调试时进不去容器,需要临时换 debug 镜像。
3.2 第二步:配置外置与健康检查补齐
容器化的前提是「配置不 baked 进镜像」。环境变量、配置文件、密钥都要外置。健康检查分 liveness 和 readiness,前者失败重启,后者失败摘流量。
| 探针类型 | 失败后果 | 适用场景 | 常见参数 |
|---|---|---|---|
| livenessProbe | 重启容器 | 死锁、假死 | periodSeconds: 10, failureThreshold: 3 |
| readinessProbe | 摘除流量 | 启动慢、依赖未就绪 | initialDelaySeconds: 10, periodSeconds: 5 |
| startupProbe | 重启容器 | 冷启动极慢 | failureThreshold: 30, periodSeconds: 10 |
startupProbe 是给启动超过 30 秒的应用用的,它通过之前 liveness 和 readiness 都不生效,避免启动期间被误杀。
3.3 第三步:按业务边界拆分服务
拆分顺序建议从「读多写少、依赖少」的模块开始。比如用户查询、商品目录,先拆出去验证链路。核心交易链路放最后,因为回滚成本高。
拆分时用 Strangler Fig 模式:新服务上线后,通过网关把部分流量切过去,观察一段时间再全量。网关层用 Nginx 或 Istio 都行,关键是能按比例切流。
# Nginx 按权重切流到新旧服务 upstream backend { server old-service:8080 weight=90; server new-service:8080 weight=10; }weight 从 10 开始,观察错误率和延迟,没问题再逐步加到 100。回滚就是把权重调回去,秒级生效。
3.4 第四步:数据层拆分与一致性取舍
服务拆了,数据库要不要拆?我的建议是:能晚拆就晚拆。数据库拆分是分布式架构里最痛的部分,涉及数据迁移、双写、一致性校验。
如果必须拆,用「共享数据库、独立 schema」过渡,再逐步迁到独立实例。跨库查询用 API 组合代替 JOIN,跨库事务用 Saga 或本地消息表代替两阶段提交。
-- 本地消息表:保证业务操作和消息发送的最终一致性 CREATE TABLE outbox ( id BIGINT PRIMARY KEY AUTO_INCREMENT, aggregate_id VARCHAR(64) NOT NULL, event_type VARCHAR(64) NOT NULL, payload JSON NOT NULL, status TINYINT DEFAULT 0, -- 0待发送 1已发送 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_status_created (status, created_at) );业务操作和插入 outbox 在同一个本地事务里,然后由后台任务扫描 status=0 的记录发送到消息队列。发送成功更新 status=1。这样避免了「业务成功但消息丢失」或「消息发了但业务回滚」的问题。
3.5 第五步:可观测性补齐与容量压测
迁移完成后,用压测验证容量。工具用 k6 或 wrk,重点看 P99 延迟和错误率随 QPS 的变化曲线。找到拐点,那就是当前架构的容量上限。
# k6 压测脚本示例 k6 run --vus 100 --duration 5m script.js # script.js 核心逻辑 # export default function() { # http.get('http://api.example.com/healthz'); # }vus 是虚拟用户数,duration 是持续时间。压测时同步看 Prometheus 面板,观察 CPU、内存、连接数是否到瓶颈。如果 P99 在某个 QPS 后陡增,说明有资源打满或锁竞争。
4. 落地避坑:五个让云原生改造翻车的常见问题
这一章是我踩过的坑,每条按「现象 → 原因 → 解决」写。你如果正在做容器化部署,大概率会碰到其中至少两个。
4.1 Pod 频繁重启,日志却看不到错误
现象:kubectl get pods 显示 RESTARTS 持续增长,但容器日志最后一行是正常的业务输出。
原因:livenessProbe 配置过激,或者应用启动时初始化了后台线程但主线程阻塞,探针超时被判定失败。另一种可能是内存 limits 设太小,OOM Kill 不会在应用日志里留记录。
解决:先看 kubectl describe pod 的 Last State,如果是 OOMKilled 就调大 memory limits;如果是探针失败,把 initialDelaySeconds 和 timeoutSeconds 调大,或者改用 startupProbe。排查时临时把 livenessProbe 去掉,确认应用本身是否稳定。
4.2 服务间调用超时,但被调服务指标正常
现象:A 服务调 B 服务报 context deadline exceeded,但 B 服务的 QPS、延迟、错误率都正常。
原因:大概率是 DNS 解析或连接池问题。Kubernetes 的 Service DNS 在大量并发新建连接时会成为瓶颈,或者客户端连接池太小,请求排队。
解决:客户端用长连接和连接池,gRPC 设置 keepalive;检查 CoreDNS 的 QPS 和延迟,必要时调大副本数;用 kubectl exec 进 Pod 手动 curl 被调服务,确认网络连通性。
4.3 滚动更新时流量丢失
现象:发布过程中出现少量 502,持续时间几秒到几十秒。
原因:旧 Pod 收到 SIGTERM 后立即停止接受新连接,但此时 Endpoints 还没从 Service 摘除,流量还在往旧 Pod 打。或者 readinessProbe 配置不当,新 Pod 还没就绪就被加入 Endpoints。
解决:应用收到 SIGTERM 后先 sleep 一段时间再退出,给 Endpoints 同步留时间;配置 preStop hook 做优雅停机;readinessProbe 的 initialDelaySeconds 要覆盖冷启动。
lifecycle: preStop: exec: command: ["/bin/sh", "-c", "sleep 10"] terminationGracePeriodSeconds: 30preStop 先执行,sleep 10 秒等 Endpoints 摘除,然后才发 SIGTERM。terminationGracePeriodSeconds 要大于 preStop 加应用停机时间。
4.4 镜像拉取失败,提示权限不足
现象:Pod 一直 ImagePullBackOff,describe 显示 failed to authorize。
原因:imagePullSecrets 没配,或者 Secret 里的凭证过期。私有仓库的 token 通常有有效期。
解决:检查 Secret 是否存在且正确,用 kubectl get secret regcred -o jsonpath='{.data..dockerconfigjson}' | base64 -d 看内容。如果是云厂商的镜像仓库,用对应的 credential helper 自动刷新。
4.5 节点资源充足但 Pod 调度失败
现象:Pod 一直 Pending,describe 显示 0/5 nodes are available: insufficient cpu。
原因:requests 设太大,或者节点上有污点(taint)没有对应容忍(toleration)。也可能是节点被标记为不可调度。
解决:kubectl describe node 看 Allocated resources,确认实际分配;kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints 看污点;如果是 DaemonSet 类工作负载,加 tolerations 容忍 master 污点。
5. 进阶:用白皮书里的成熟度模型给自己打分
白皮书最后一章通常会提成熟度模型,但不会给可操作的评分表。我根据自己的经验整理了一个五级自评表,每季度打一次分,比读十遍文档有用。
| 维度 | L1 初始 | L2 可重复 | L3 标准化 | L4 自动化 | L5 自愈 |
|---|---|---|---|---|---|
| 部署 | 手工 scp | 脚本部署 | CI/CD 流水线 | 蓝绿/金丝雀 | 自动回滚 |
| 配置 | 硬编码 | 配置文件 | 配置中心 | 动态推送 | 自动调优 |
| 可观测 | 无 | 基础监控 | 日志+指标+追踪 | 告警关联 | 异常自愈 |
| 安全 | 无 | 镜像扫描 | 运行时策略 | 零信任 | 自动隔离 |
| 容量 | 拍脑袋 | 压测 | 容量规划 | 弹性伸缩 | 预测扩容 |
打分方法:每个维度选当前最符合的级别,五个维度取最低分作为整体成熟度。比如部署到了 L4 但安全还在 L1,整体就是 L1。短板决定水位。
我自己的习惯是每季度末花半小时填这张表,然后挑一个最低分维度定下季度的改进目标。去年 Q3 我的安全维度卡在 L2,Q4 就集中做了镜像签名和 admission controller,今年 Q1 再看已经到 L3。这种小步迭代比一次性大改造靠谱得多,毕竟线上系统经不起折腾。
还有一个具体技巧:把白皮书里的架构图打印出来贴在工位,每落地一个组件就在图上打个勾。视觉反馈比看文档进度条管用,而且能随时发现「哪些模块被跳过了」。我贴了半年,发现可观测性那部分一直没打勾,才意识到监控告警一直靠人工巡检撑着,后来补了 Prometheus Alertmanager 才算闭环。
希望帮到你。
本文还有配套的精品资源,点击获取