一句话总结
本篇将前 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 - 环境搭建
前置工具
$ curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
$ curl -L https://istio.io/downloadIstio | sh - $ export PATH=$PWD/istio-*/bin:$PATH
$ go install github.com/bufbuild/buf/cmd/buf@latest
$ go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
|
创建集群 + 安装 Istio
$ k3d cluster create mesh-lab \ --agents 2 \ -p "80:80@loadbalancer" \ -p "443:443@loadbalancer"
$ istioctl install --set profile=demo -y
$ kubectl label namespace default istio-injection=enabled
$ kubectl get pods -n istio-system
|
Lab 2 - 构建 gRPC 服务
构建两个服务:order-service(订单)和 user-service(用户).order-service 调用 user-service 获取用户信息.
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; }
|
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; }
|
核心实现要点
func (s *orderServer) CreateOrder(ctx context.Context, req * orderv1.CreateOrderRequest) (*orderv1.CreateOrderResponse, error) { 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 事件通知
实验任务
- 在集群中部署单节点 Kafka(可使用 Strimzi Operator 或 Docker Compose 本地模拟)
- order-service 创建订单后,通过发件箱模式发送
order.created 事件
- 创建一个 notification-consumer 服务,消费事件并打印日志
- 验证幂等性:手动重发同一条消息,确认不会重复处理
简化方案(如果不想部署 Kafka 到 K8s):
$ 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
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
|
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
|
部署验证
$ 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/
$ kubectl get pods
$ kubectl run debug --rm -it --image=fullstorydev/grpcurl -- \ grpcurl -plaintext order-service:50052 list
|
Lab 5 - Istio 灰度发布
实验任务
- 构建 user-service v2(修改返回的 name 加前缀 "[v2]")
- 部署 v2 Deployment(labels: version: v2)
- 创建 DestinationRule 定义 v1/v2 子集
- 创建 VirtualService 将 20% 流量路由到 v2
- 循环调用验证流量分布比例
- 逐步将权重调整为 100% v2
- 下线 v1 Deployment
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 ---
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
|
$ 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\]"
|
Lab 6 - 熔断演练
实验任务
- 配置 user-service 的 DestinationRule 启用 outlierDetection
- 修改 user-service 代码:读取环境变量
FAULT_MODE,为 true 时对所有请求返回 codes.Internal 错误(模拟故障)
- 用
kubectl set env 让其中一个 Pod 进入故障模式
- 观察 Istio 是否自动将故障 Pod 从负载均衡中驱逐
- 等待驱逐时间结束后,关闭故障模式,观察 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
|
$ 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
|
Lab 7 - 可观测性验证
实验任务
- 打开 Kiali 查看服务拓扑和实时流量
- 打开 Jaeger 查看分布式链路追踪
- 在 Prometheus 中查询 Istio 标准指标
$ istioctl dashboard kiali $ istioctl dashboard jaeger $ istioctl dashboard prometheus
istio_requests_total{destination_service_name="user-service"}
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 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 指标