阶段三 · 服务网格与实践

综合实践

一句话总结

本篇将前 8 篇的知识串联成一个端到端实战:用 Go 构建 2 个 gRPC 微服务,部署到 K8s + Istio 环境,完成灰度发布,熔断演练和可观测性验证.

实验总览

实验 覆盖知识点 篇章对应
Lab 1 - 搭建环境 k3d + Istio 安装 K8s 09 / 本篇
Lab 2 - 构建 gRPC 服务 Proto 定义,代码生成,拦截器 02
Lab 3 - Kafka 事件通知 发件箱模式,幂等消费 03
Lab 4 - K8s 部署 Deployment + Service + ConfigMap K8s 03-06
Lab 5 - Istio 灰度发布 VirtualService 权重分割 08
Lab 6 - 熔断演练 DestinationRule outlierDetection 06 / 08
Lab 7 - 可观测性 Prometheus + Kiali + Jaeger 08

Lab 1 - 环境搭建

前置工具

# 安装 k3d(轻量 K8s)
$ curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

# 安装 istioctl
$ curl -L https://istio.io/downloadIstio | sh -
$ export PATH=$PWD/istio-*/bin:$PATH

# 安装 buf(Proto 工具链)
$ go install github.com/bufbuild/buf/cmd/buf@latest

# 安装 grpcurl(gRPC 调试)
$ go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

创建集群 + 安装 Istio

# 创建 3 节点集群,映射 80/443 端口
$ k3d cluster create mesh-lab \
--agents 2 \
-p "80:80@loadbalancer" \
-p "443:443@loadbalancer"

# 安装 Istio(demo profile 包含 Kiali/Jaeger/Prometheus)
$ istioctl install --set profile=demo -y

# 开启 default namespace 自动注入
$ kubectl label namespace default istio-injection=enabled

# 验证
$ kubectl get pods -n istio-system
# 应该看到 istiod,istio-ingressgateway 等 Pod Running

Lab 2 - 构建 gRPC 服务

构建两个服务:order-service(订单)和 user-service(用户).order-service 调用 user-service 获取用户信息.

Proto 定义

// proto/user/v1/user.proto
syntax = "proto3";
package user.v1;
option go_package = "github.com/yourorg/mesh-lab/gen/user/v1;userv1";

service UserService {
rpc GetUser(GetUserRequest) returns (User);
}

message GetUserRequest {
string user_id = 1;
}

message User {
string user_id = 1;
string name = 2;
string email = 3;
}
// proto/order/v1/order.proto
syntax = "proto3";
package order.v1;
option go_package = "github.com/yourorg/mesh-lab/gen/order/v1;orderv1";

service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
rpc GetOrder(GetOrderRequest) returns (Order);
}

message CreateOrderRequest {
string user_id = 1;
string product_id = 2;
int32 quantity = 3;
}

message CreateOrderResponse {
string order_id = 1;
}

message GetOrderRequest {
string order_id = 1;
}

message Order {
string order_id = 1;
string user_id = 2;
string user_name = 3;
string product_id = 4;
int32 quantity = 5;
string status = 6;
}

核心实现要点

// order-service: CreateOrder 调用 user-service
func (s *orderServer) CreateOrder(ctx context.Context, req * orderv1.CreateOrderRequest) (*orderv1.CreateOrderResponse, error) {
// 调用 user-service 验证用户存在
user, err := s.userClient.GetUser(ctx, &userv1.GetUserRequest{UserId: req.UserId})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
}

orderID := uuid.New().String()
s.orders[orderID] = &orderv1.Order{
OrderId: orderID,
UserId: req.UserId,
UserName: user.Name,
ProductId: req.ProductId,
Quantity: req.Quantity,
Status: "created",
}

return &orderv1.CreateOrderResponse{OrderId: orderID}, nil
}

关键配置:

  • user-service 监听 :50051,注册 reflection + health check
  • order-service 监听 :50052,通过环境变量 USER_SERVICE_ADDR 连接 user-service
  • 两个服务都添加 logging + recovery 拦截器

Lab 3 - Kafka 事件通知

实验任务

  1. 在集群中部署单节点 Kafka(可使用 Strimzi Operator 或 Docker Compose 本地模拟)
  2. order-service 创建订单后,通过发件箱模式发送 order.created 事件
  3. 创建一个 notification-consumer 服务,消费事件并打印日志
  4. 验证幂等性:手动重发同一条消息,确认不会重复处理

简化方案(如果不想部署 Kafka 到 K8s):

# 本地用 Docker Compose 跑 Kafka
$ cat docker-compose.kafka.yml
services:
kafka:
image: bitnami/kafka:latest
ports:
- "9092:9092"
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER

Lab 4 - K8s 部署

核心 YAML

# user-service deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: user-service
version: v1
template:
metadata:
labels:
app: user-service
version: v1
spec:
containers:
- name: user-service
image: mesh-lab/user-service:v1
ports:
- containerPort: 50051
livenessProbe:
grpc:
port: 50051
initialDelaySeconds: 5
readinessProbe:
grpc:
port: 50051
initialDelaySeconds: 3
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 50051
targetPort: 50051
name: grpc
# order-service deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
labels:
app: order-service
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: order-service
version: v1
template:
metadata:
labels:
app: order-service
version: v1
spec:
containers:
- name: order-service
image: mesh-lab/order-service:v1
ports:
- containerPort: 50052
env:
- name: USER_SERVICE_ADDR
value: "user-service:50051"
livenessProbe:
grpc:
port: 50052
readinessProbe:
grpc:
port: 50052
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 50052
targetPort: 50052
name: grpc

部署验证

# 构建镜像并导入 k3d
$ docker build -t mesh-lab/user-service:v1 ./user-service
$ docker build -t mesh-lab/order-service:v1 ./order-service
$ k3d image import mesh-lab/user-service:v1 mesh-lab/order-service:v1 -c mesh-lab

# 部署
$ kubectl apply -f k8s/

# 验证 Pod 有 2 个容器(业务 + istio-proxy)
$ kubectl get pods
# NAME READY STATUS RESTARTS
# order-service-xxx 2/2 Running 0
# user-service-xxx 2/2 Running 0

# 从集群内部测试
$ kubectl run debug --rm -it --image=fullstorydev/grpcurl -- \
grpcurl -plaintext order-service:50052 list

Lab 5 - Istio 灰度发布

实验任务

  1. 构建 user-service v2(修改返回的 name 加前缀 "[v2]")
  2. 部署 v2 Deployment(labels: version: v2)
  3. 创建 DestinationRule 定义 v1/v2 子集
  4. 创建 VirtualService 将 20% 流量路由到 v2
  5. 循环调用验证流量分布比例
  6. 逐步将权重调整为 100% v2
  7. 下线 v1 Deployment
# DestinationRule
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: user-service
spec:
host: user-service
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
# VirtualService: 80/20 分流
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: user-service
spec:
hosts:
- user-service
http:
- route:
- destination:
host: user-service
subset: v1
weight: 80
- destination:
host: user-service
subset: v2
weight: 20
# 验证流量分布(循环调用 100 次统计)
$ for i in $(seq 1 100); do
kubectl exec deploy/order-service -c order-service -- \
grpcurl -plaintext user-service:50051 user.v1.UserService/GetUser \
-d '{"user_id":"u1"}' 2>/dev/null
done | grep -c "\[v2\]"
# 预期约 20 次包含 [v2]

Lab 6 - 熔断演练

实验任务

  1. 配置 user-service 的 DestinationRule 启用 outlierDetection
  2. 修改 user-service 代码:读取环境变量 FAULT_MODE,为 true 时对所有请求返回 codes.Internal 错误(模拟故障)
  3. kubectl set env 让其中一个 Pod 进入故障模式
  4. 观察 Istio 是否自动将故障 Pod 从负载均衡中驱逐
  5. 等待驱逐时间结束后,关闭故障模式,观察 Pod 重新加入
# 启用异常检测
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: user-service
spec:
host: user-service
trafficPolicy:
outlierDetection:
consecutive5xxErrors: 3
interval: 5s
baseEjectionTime: 30s
maxEjectionPercent: 50
subsets:
- name: v1
labels:
version: v1
# 让某个 Pod 进入故障模式
# user-service 代码中需检查此环境变量:
# if os.Getenv("FAULT_MODE") == "true" { return status.Error(codes.Internal, "simulated fault") }
$ kubectl set env deploy/user-service FAULT_MODE=true

# 持续调用观察错误率变化
$ for i in $(seq 1 50); do
kubectl exec deploy/order-service -c order-service -- \
grpcurl -plaintext user-service:50051 user.v1.UserService/GetUser \
-d '{"user_id":"u1"}' 2>&1 | grep -o "OK\|Error"
done
# 前几次可能有错误,之后应该全是 OK(故障 Pod 被驱逐)

Lab 7 - 可观测性验证

实验任务

  1. 打开 Kiali 查看服务拓扑和实时流量
  2. 打开 Jaeger 查看分布式链路追踪
  3. 在 Prometheus 中查询 Istio 标准指标
# 打开各可观测工具面板
$ istioctl dashboard kiali # 服务拓扑 + 流量可视化
$ istioctl dashboard jaeger # 分布式追踪
$ istioctl dashboard prometheus # 指标查询

# Prometheus 常用查询
# 请求量(按服务)
istio_requests_total{destination_service_name="user-service"}

# P99 延迟
histogram_quantile(0.99, rate(istio_request_duration_milliseconds_bucket{destination_service_name="user-service"}[5m]))

# 错误率
sum(rate(istio_requests_total{destination_service_name="user-service",response_code=~"5.."}[5m]))
/
sum(rate(istio_requests_total{destination_service_name="user-service"}[5m]))

环境清理

# 删除集群
$ k3d cluster delete mesh-lab

# 清理 Docker 镜像(可选)
$ docker rmi mesh-lab/user-service:v1 mesh-lab/user-service:v2 mesh-lab/order-service:v1

小结

完整学习路径验证

通过这 7 个 Lab,你已经实践了:Proto 契约定义 → gRPC 服务实现 → 异步事件通知 → K8s 部署 → Istio 灰度发布 → 熔断自愈 → 全链路可观测.
这覆盖了微服务从"写代码"到"跑在生产环境"的完整链路.
接下来可以在工作中逐步将这些实践应用到真实项目中.

快速回顾

  • Lab 1:k3d + Istio demo profile 搭建完整实验环境
  • Lab 2:两个 gRPC 服务互调,验证拦截器和错误处理
  • Lab 3:Kafka 事件通知 + 发件箱 + 幂等消费
  • Lab 4:K8s 部署 + gRPC 健康检查探针 + Sidecar 自动注入
  • Lab 5:VirtualService 权重分流实现金丝雀发布
  • Lab 6:outlierDetection 自动驱逐故障实例
  • Lab 7:Kiali 拓扑 + Jaeger 追踪 + Prometheus 指标