2. 微服务在Google Cloud上的落地形态
把微服务架构落到 Google Cloud 上,最先要回答的不是“用哪个产品”,而是“每个服务应该以什么形态运行”。GKE、Cloud Run、App Engine 三者都能承载微服务,但它们对“服务边界”的约束方式完全不同:GKE 把调度、网络、扩缩容的控制权交给团队,Cloud Run 把运行单元收敛到容器加请求,App Engine 则把运行单元收敛到应用版本与实例类别。理解这三种落地形态,才能判断一个服务该拆到哪里、以什么粒度部署、按什么信号扩缩容。
三种落地形态的本质差异
GKE 是托管的 Kubernetes 控制平面。你提交的是 Pod、Deployment、Service、Ingress 等对象,GKE 负责控制平面可用性、节点池升级和集群自动扩缩容。一个微服务通常对应一个 Deployment,副本数、资源 requests/limits、就绪探针、HPA 指标都由你定义。它的优势是表达力最强:Sidecar、Service Mesh、NetworkPolicy、StatefulSet、Job 都能用;代价是你要理解集群、节点、网络与配额。
Cloud Run 是请求驱动的无服务器容器平台。你提交一个容器镜像,声明 CPU、内存、并发数、最小/最大实例数,平台按请求量自动伸缩,缩到零时不产生实例费用。它天然适合 HTTP/gRPC 微服务,尤其是流量波动大、单请求处理时间短的服务。它的约束也很明确:实例是无状态的,本地磁盘是内存文件系统,长连接和后台线程要谨慎使用,请求超时和并发模型决定了服务拆分方式。
App Engine 是更早的应用托管形态,分标准环境和柔性环境。标准环境按语言运行时部署,扩缩容由实例类别、自动扩缩容配置和流量拆分控制;柔性环境则运行 Docker 容器,更接近 Cloud Run 的模型。App Engine 的强项是版本管理与流量拆分:你可以把 10% 流量切到新版本做金丝雀,也可以按 IP 或 Cookie 做 A/B 测试。它更适合“应用版本”粒度,而不是“细粒度微服务”粒度。
服务拆分粒度:先看运行单元,再看边界
在 GKE 上,拆分粒度可以很细。一个订单服务可以拆成 order-api、order-worker、order-scheduler 三个 Deployment,分别用 HPA 按 QPS、队列长度、定时任务并发扩缩容。它们之间通过 Service 或 Istio VirtualService 通信,可以给 order-api 配 3 个副本、order-worker 配 2~20 个副本。这种细粒度拆分的前提是团队能维护 YAML、能看懂 Pod 事件、能处理节点资源不足。如果团队没有 Kubernetes 运维能力,细拆会变成负担。
在 Cloud Run 上,拆分粒度通常按“请求边界”来定。一个服务如果同时处理同步查询和异步批处理,就应该拆成两个 Cloud Run 服务:查询服务设 concurrency=80、min instances=1,保证冷启动不影响用户体验;批处理服务设 concurrency=1、max instances=10,避免同时拉起太多实例打爆下游数据库。Cloud Run 的并发模型决定了:单实例并发越高,单请求越要轻量;如果单请求要跑 30 秒以上,就要考虑拆成任务队列加 worker,而不是硬扛在请求线程里。
在 App Engine 上,拆分粒度往往停留在“应用版本”层面。一个 App Engine 应用可以包含多个服务(service),每个服务有多个版本(version),版本之间可以切流量。例如把 default 服务作为主 API,把 worker 服务作为后台任务,把 admin 服务作为管理后台。但 App Engine 标准环境的运行时约束较强,不适合把每个微服务都拆成一个独立应用;更常见的做法是把一组相关功能放在一个服务里,用版本做灰度,而不是用服务做细粒度拆分。
部署模型与流量治理的对应关系
三种形态的部署模型差异,直接决定了流量治理方式。GKE 的部署是“声明式对象 + 控制器循环”:你提交 Deployment,ReplicaSet 保证副本数,Service 提供稳定 VIP,Ingress 或 Gateway 暴露入口。灰度发布通常靠两个 Deployment 加 Service 权重,或者用 Istio 的 VirtualService 按 Header 分流。它的流量治理能力最强,但配置也最复杂。
Cloud Run 的部署是“镜像 + 修订版本”:每次部署生成一个 revision,流量可以 100% 切到新 revision,也可以按百分比切分。例如把 5% 流量切到新 revision,观察 10 分钟错误率和 P95 延迟,再逐步放大到 100%。如果新 revision 异常,把流量切回旧 revision 即可,不需要重新部署。这种模型适合快速迭代的微服务,但要注意 revision 之间的兼容性:数据库 schema 变更必须向前兼容,否则回滚会失败。
App Engine 的部署是“版本 + 流量拆分”:每个版本是一个独立可寻址单元,你可以用 gcloud app services set-traffic 把 90% 流量给 v1、10% 给 v2,也可以按 IP 或 Cookie 做定向分流。它的灰度能力很成熟,但版本数量过多会增加管理成本。对于微服务架构,App Engine 更适合作为“边缘应用”或“单体应用”的托管形态,而不是细粒度微服务的主要载体。
选择判断:从服务特征反推落地形态
实际选型时,可以按服务特征反推。第一,看是否有状态:需要本地磁盘、长连接、StatefulSet 的服务,优先 GKE;无状态 HTTP/gRPC 服务,优先 Cloud Run。第二,看流量形态:流量突发且不可预测、希望缩到零的服务,Cloud Run 更合适;流量平稳、需要预留容量的服务,GKE 的节点池更可控。第三,看团队能力:有 Kubernetes 经验的团队可以用 GKE 做细粒度治理;运维人力有限的团队用 Cloud Run 能显著降低负担。第四,看发布策略:需要按版本做复杂流量拆分的,App Engine 或 Cloud Run revision 都能满足;需要 Mesh 级治理的,GKE 加 Istio 更合适。
部署配置示例:同一服务的两种落地写法
以“订单查询服务”为例,如果部署到 GKE,核心配置如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-query
spec:
replicas: 3
selector:
matchLabels:
app: order-query
template:
metadata:
labels:
app: order-query
spec:
containers:
- name: app
image: gcr.io/demo/order-query:1.4.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-query-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-query
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65
如果部署到 Cloud Run,同一服务的核心配置如下:
gcloud run deploy order-query \
--image=gcr.io/demo/order-query:1.4.0 \
--region=asia-east1 \
--cpu=1 \
--memory=512Mi \
--concurrency=80 \
--min-instances=1 \
--max-instances=20 \
--timeout=30s \
--set-env-vars=DB_HOST=10.0.0.5,DB_NAME=orders \
--allow-unauthenticated
两段配置的差异很直观:GKE 需要显式声明副本数、资源 requests/limits、探针和 HPA 指标;Cloud Run 只需要声明并发、实例上下限和超时。GKE 的扩缩容基于 CPU 利用率,Cloud Run 的扩缩容基于并发请求数。GKE 的副本数下限是 3,意味着即使没有流量也有 3 个 Pod 在运行;Cloud Run 的 min-instances=1 只保留 1 个实例,缩零时甚至可以为 0。这些差异会直接影响成本模型和容量规划。
结果观察与判断依据
部署完成后,判断服务是否健康,三种形态的观察点不同。GKE 要看 kubectl get pods 的 Ready 状态、HPA 的当前副本数与目标副本数、节点池的分配率;如果 Pod 一直 Pending,通常是节点资源不足或亲和性配置过严。Cloud Run 要看 revision 的流量分配、实例数曲线、请求延迟 P50/P95/P99 和错误率;如果实例数频繁在 0 和 1 之间抖动,说明 min-instances 设得太低或冷启动太慢。App Engine 要看版本的实例数、QPS、延迟和错误率,以及流量拆分是否按预期生效。
一个可操作的判断标准是:如果服务在流量低谷时仍需要保留大量常驻实例,GKE 的固定成本会偏高;如果服务在流量高峰时频繁触发冷启动,Cloud Run 的 min-instances 需要上调;如果服务需要按版本做精细灰度,App Engine 或 Cloud Run revision 的流量拆分能力更直接。选型不是一次性的,而是随着服务特征变化不断调整的过程。