• k8s-服务网格实战-配置 Mesh(灰度发布)


    ef0ac36f6d11e80d75e0debf8fc5beae.png

    istio-02.png

    在上一篇 k8s-服务网格实战-入门Istio中分享了如何安装部署 Istio,同时可以利用 Istio 实现 gRPC 的负载均衡。

    今天我们更进一步,深入了解使用 Istio 的功能。365008ec03d49d03837ebe2d1fefe869.png从 Istio 的流量模型中可以看出:Istio 支持管理集群的出入口请求(gateway),同时也支持管理集群内的 mesh 流量,也就是集群内服务之间的请求。

    本次先讲解集群内部的请求,配合实现以下两个功能:

    • 灰度发布(对指定的请求分别路由到不同的 service 中)

    • 配置 service 的请求权重

    灰度发布

    在开始之前会部署两个 deployment 和一个 service,同时这两个 deployment 所关联的 Pod 分别对应了两个不同的 label,由于在灰度的时候进行分组。3bc43919b4916fb3a7b9eafe09fd30fb.png

    使用这个 yaml 会部署所需要的 deploymentservice

    kubectl apply -f https://raw.githubusercontent.com/crossoverJie/k8s-combat/main/deployment/istio-mesh.yaml

    首先设想下什么情况下我们需要灰度发布,一般是某个重大功能的测试,只对部分进入内测的用户开放入口。

    假设我们做的是一个 App,我们可以对拿到了内测包用户的所有请求头中加入一个版本号。

    比如 version=200 表示新版本,version=100 表示老版本。同时在服务端会将这个版本号打印出来,用于区分请求是否进入了预期的 Pod。

    1. // Client 
    2. version := r.URL.Query().Get("version")  
    3. name := "world"  
    4. ctx, cancel := context.WithTimeout(context.Background(), time.Second)  
    5. md := metadata.New(map[string]string{  
    6.     "version": version,  
    7. })  
    8. ctx = metadata.NewOutgoingContext(ctx, md)  
    9. defer cancel()  
    10. g, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
    11. // Server
    12. func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {  
    13.     md, ok := metadata.FromIncomingContext(ctx)  
    14.     var version string  
    15.     if ok {  
    16.        version = md.Get("version")[0]  
    17.     }    log.Printf("Received: %v, version: %s", in.GetName(), version)  
    18.     name, _ := os.Hostname()  
    19.     return &pb.HelloReply{Message: fmt.Sprintf("hostname:%s, in:%s, version:%s", name, in.Name, version)}, nil  
    20. }

    对 service 分组

    进行灰度测试时往往需要新增部署一个灰度服务,这里我们称为 v2(也就是上图中的 Pod2)。

    同时需要将 v1 和 v2 分组:

    1. apiVersion: networking.istio.io/v1alpha3  
    2. kind: DestinationRule  
    3. metadata:  
    4.   name: k8s-combat-service-ds  
    5. spec:  
    6.   host: k8s-combat-service-istio-mesh  
    7.   subsets:  
    8.     - name: v1  
    9.       labels:  
    10.         app: k8s-combat-service-v1  
    11.     - name: v2  
    12.       labels:  
    13.         app: k8s-combat-service-v2

    这里我们使用 Istio 的 DestinationRule 定义 subset,也就是将我们的 service 下的 Pod 分为 v1/v2。

    使用 标签 app 进行分组

    注意这里的 host: k8s-combat-service-istio-mesh 通常配置的是 service 名称。

    1. apiVersion: v1  
    2. kind: Service  
    3. metadata:  
    4.   name: k8s-combat-service-istio-mesh  
    5. spec:  
    6.   selector:  
    7.     appId: "12345"  
    8.   type: ClusterIP  
    9.   ports:  
    10.     - port: 8081  
    11.       targetPort: 8081  
    12.       name: app  
    13.     - name: grpc  
    14.       port: 50051  
    15.       targetPort: 50051

    也就是这里 service 的名称,同时也支持配置为 host: k8s-combat-service-istio-mesh.default.svc.cluster.local,如果使用的简写Istio 会根据当前指定的 namespace 进行解析。

    Istio 更推荐使用全限定名替代我们这里的简写,从而避免误操作。

    当然我们也可以在 DestinationRule 中配置负载均衡的策略,这里我们先略过:

    1. apiVersion: networking.istio.io/v1alpha3  
    2. kind: DestinationRule  
    3. metadata:  
    4.   name: k8s-combat-service-ds  
    5. spec:  
    6.   host: k8s-combat-service-istio-mesh 
    7.   trafficPolicy:
    8.     loadBalancer:
    9.       simple: ROUND_ROBIN
    6691c8db8e3f9bd7a9dfe6ca34d514a7.png
    image.png

    这样我们就定义好了两个分组:

    • v1:app: k8s-combat-service-v1

    • v2:app: k8s-combat-service-v2

    之后就可以配置路由规则将流量分别指定到两个不同的组中,这里我们使用 VirtualService 进行配置。

    1. apiVersion: networking.istio.io/v1alpha3  
    2. kind: VirtualService  
    3. metadata:  
    4.   name: k8s-combat-service-vs  
    5. spec:  
    6.   gateways:  
    7.     - mesh  
    8.   hosts:  
    9.     - k8s-combat-service-istio-mesh # match this host
    10. http:  
    11.   - name: v1  
    12.     match:  
    13.       - headers:  
    14.           version:  
    15.             exact: '100'  
    16.     route:  
    17.       - destination:  
    18.           host: k8s-combat-service-istio-mesh  
    19.           subset: v1  
    20.   - name: v2  
    21.     match:  
    22.       - headers:  
    23.           version:  
    24.             exact: '200'  
    25.     route:  
    26.       - destination:  
    27.           host: k8s-combat-service-istio-mesh  
    28.           subset: v2  
    29.   - name: default  
    30.     route:  
    31.       - destination:  
    32.           host: k8s-combat-service-istio-mesh  
    33.           subset: v1

    这个规则很简单,会检测 http 协议的 header 中的 version 字段值,如果为 100 这路由到 subset=v1 这个分组的 Pod 中,同理为 200 时则路由到 subset=v2 这个分组的 Pod 中。

    当没有匹配到 header 时则进入默认的 subset:v1

    gRPC 也是基于 http 协议,它的 metadata 也就对应了 http 协议中的 header

    header=100

    1. Greeting: hostname:k8s-combat-service-v1-5b998dc8c8-hkb72, in:world, version:100istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=100"
    2. Greeting: hostname:k8s-combat-service-v1-5b998dc8c8-hkb72, in:world, version:100istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=100"
    3. Greeting: hostname:k8s-combat-service-v1-5b998dc8c8-hkb72, in:world, version:100istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=100"
    4. Greeting: hostname:k8s-combat-service-v1-5b998dc8c8-hkb72, in:world, version:100istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=100"

    header=200

    1. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    2. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    3. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    4. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    5. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    6. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    7. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"

    根据以上的上面的测试请求来看,只要我们请求头里带上指定的 version 就会被路由到指定的 Pod 中。

    利用这个特性我们就可以在灰度验证的时候单独发一个灰度版本的 Deployment,同时配合客户端指定版本就可以实现灰度功能了。

    配置权重

    同样基于 VirtualService 我们还可以对不同的 subset 分组进行权重配置。

    1. apiVersion: networking.istio.io/v1alpha3  
    2. kind: VirtualService  
    3. metadata:  
    4.   name: k8s-combat-service-vs  
    5. spec:  
    6.   gateways:  
    7.     - mesh  
    8.   hosts:  
    9.     - k8s-combat-service-istio-mesh # match this host  
    10.   http:  
    11.     - match:  
    12.         - uri:  
    13.             exact: /helloworld.Greeter/SayHello  
    14.       route:  
    15.         - destination:  
    16.             host: k8s-combat-service-istio-mesh  
    17.             subset: v1  
    18.           weight: 10  
    19.         - destination:  
    20.             host: k8s-combat-service-istio-mesh  
    21.             subset: v2  
    22.           weight: 90  
    23.       timeout: 5000ms

    这里演示的是针对 SayHello 接口进行权重配置(当然还有多种匹配规则),90% 的流量会进入 v2 这个 subset,也就是在 k8s-combat-service-istio-mesh service 下的 app: k8s-combat-service-v2 Pod。

    1. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    2. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    3. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    4. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    5. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    6. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    7. Greeting: hostname:k8s-combat-service-**v1**-5b998dc8c8-hkb72, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$ curl "http://127.0.0.1:8081/grpc_client?name=k8s-combat-service-istio-mesh&version=200"
    8. Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$

    经过测试会发现大部分的请求都会按照我们的预期进入 v2 这个分组。

    当然除之外之外我们还可以:

    • 超时时间

    • 故障注入

    • 重试 具体的配置可以参考 Istio 官方文档:84498f7f03a05883805d4411fa1981fb.png当然在一些云平台也提供了可视化的页面,可以更直观的使用。69ee1d8c141c990641f87eefe3f2cdc7.png

    以上是 阿里云的截图

    但他们的管理的资源都偏 kubernetes,一般是由运维或者是 DevOps 来配置,不方便开发使用,所以还需要一个介于云厂商和开发者之间的管理发布平台,可以由开发者以项目维度管理维护这些功能。

    本文的所有源码在这里可以访问:https://github.com/crossoverJie/k8s-combat

    PS:最近也在更新视频号,也会有一些技术干货,动动小手帮主播点播关注9264d338970fa31d7133e991fd35c847.jpeg

    747000e50e51d8093325bf4ceefbec40.jpeg

    往期推荐

    技术阅读周刊第第四期

    k8s-服务网格实战-入门Istio

    如何优雅重启 kubernetes 的 Pod

    在 kubernetes 环境中实现 gRPC 负载均衡

    67d83078a886b42a1a4b79e839d88aff.gif

    点分享

    c711d7a511121c3b6b358a0c86c45e6c.gif

    点收藏

    cd05130171f186f6a378df1904377882.gif

    点点赞

    b52d71a09377c61c5c97529fa8fede87.gif

    点在看

  • 相关阅读:
    计算机毕业设计Java文档资料管理系统(源码+系统+mysql数据库+Lw文档)
    CRM项目 - 心得
    【kali-漏洞利用】(3.1)Metasploitable 操作系统:安装,使用
    Jenkins自动化部署(虚拟机部署)
    Cmake
    算法村开篇
    当未指定且存在多个构造器,实例化对象时Spring如何选择?
    LeetCode经典算法题 no.118杨辉三角
    机器学习基础之《回归与聚类算法(5)—分类的评估方法》
    C/C++编程新手容易犯的10种编程错误
  • 原文地址:https://blog.csdn.net/qq_18661793/article/details/134279369