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

标签: k8s Istio | 发表时间:2023-11-07 22:11 | 作者:
出处:http://crossoverjie.top/

istio-02.png

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

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

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

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

灰度发布

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

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

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

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

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

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

1     
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Client      
version := r.URL.Query().Get("version")
name := "world"
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
md := metadata.New(map[string]string{
"version": version,
})
ctx = metadata.NewOutgoingContext(ctx, md)
defer cancel()
g, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

// Server
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
md, ok := metadata.FromIncomingContext(ctx)
var version string
if ok {
version = md.Get("version")[0]
} log.Printf("Received: %v, version: %s", in.GetName(), version)
name, _ := os.Hostname()
return &pb.HelloReply{Message: fmt.Sprintf("hostname:%s, in:%s, version:%s", name, in.Name, version)}, nil
}

对 service 分组

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

同时需要将 v1 和 v2 分组:

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

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

使用 标签 app 进行分组

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

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

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

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

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

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

image.png


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

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

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

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

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

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

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

header=100

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

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

1     
2
3
4
5
6
7
8
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"     
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"
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"
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"
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"
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"
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"
Greeting: hostname:k8s-combat-service-v2-5db566fb76-xj7j6, in:world, version:200istio-proxy@k8s-combat-service-v1-5b998dc8c8-hkb72:/$

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

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

  • 超时时间
  • 故障注入
  • 重试
    具体的配置可以参考 Istio 官方文档:
    image.png
    当然在一些云平台也提供了可视化的页面,可以更直观的使用。
    image.png

以上是 阿里云的截图

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

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

#Blog #Istio

相关 [k8s 服务 网格] 推荐:

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

- - crossoverJie's Blog
在上一篇 k8s-服务网格实战-入门Istio中分享了如何安装部署 Istio,同时可以利用 Istio 实现 gRPC 的负载均衡. 今天我们更进一步,深入了解使用 Istio 的功能. 从 Istio 的流量模型中可以看出:Istio 支持管理集群的出入口请求(gateway),同时也支持管理集群内的 mesh 流量,也就是集群内服务之间的请求.

Kong 开源的的服务网格Kuma爬过了K8S这座大山

- -
2019年9月10日,Kong正式宣布开源一款Service Mesh:Kuma. 此消息一出,立即在云原生社区引起反响,各大媒体争相报道. 让我们跟随SDxCentral的总编辑,一起来看看Kong的CTO如何介绍Kuma这款Service Mesh产品以及对于SMI的看法. 关于Kuma的具体功能介绍可以阅读官网博客以及Github.

如何收集K8S容器化部署的服务的日志?

- - 掘金 后端
做开发的同学都知道日志的重要性,日志的种类一般有接口日志、错误日志、关键步骤日志、用户操作日志等. 本文主要详细讲解使用kubernetes容器化部署的服务该如何记录和收集日志. 将想要记录的日志内容输出到stdout或stderr即可(DockerEngine本身具有LogDriver 功能,可通过配置不同的LogDriver将容器的stdout通过DockerEngine写入到日志系统),由DockerEngine将日志写入到日志系统.

CentOS7 安装 K8S

- - 企业架构 - ITeye博客
前提:VirtualBox CentOS7. 物理机IP   192.168.18.8. 虚拟机1IP:192.168.18.100(VMaster master). 虚拟机2IP:192.168.18.101(VServer1 node1). 虚拟机3IP:192.168.18.102(VServer2 node2).

k8s水平扩容

- - Bboysoul's Blog
k8s 的好处就是可以弹性水平扩容和纵向扩容,平时纵向扩容用的不太多,所以今天说说水平扩容,在创建hpa之前你要确定集群中已经安装了metrics-server,我使用的是k3s,直接自带. 首先创建需要的容器,下面是dockerfile. 原理就是当你访问index.php的时候会进行一个循环计算来提高cpu的使用率.

# [k8s] HPA: Horizontal Pod Autoscaling

- - V2EX - 技术
HPA 是 K8S 的一大利器. 通过 HPA, 我们可以让服务的 pod 数量根据特定指标自动增加或减少, 使得在高峰期有足够的资源服务请求, 在低峰期又可以避免占用过多的资源. 同时, 在 SOA 架构下, 我们也习惯通过 HPA 来避免劳心劳力的为每个微服务计算所需的资源.. minReplicas: 允许的最小 pod 数量.

K8S 1.24.0 安装部署

- - Share
在 v1.2x 版本中, Kubernetes 支持的最大节点数为 5000. 更具体地说,我们支持满足以下所有条件的配置:. 每个节点的 pod 数量不超过. Kubernetes v1.20 开始,默认移除 docker 的依赖,如果宿主机上安装了 docker 和 containerd,将优先使用 docker 作为容器运行引擎,如果宿主机上未安装 docker 只安装了 containerd,将使用 containerd 作为容器运行引擎;.

k8s docker集群搭建 - CSDN博客

- -
一、Kubernetes系列之介绍篇.     - 一次构建,到处运行. 2.什么是kubernetes.   首先,他是一个全新的基于容器技术的分布式架构领先方案. Kubernetes(k8s)是Google开源的容器集群管理系统(谷歌内部:Borg). 在Docker技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性.

深入掌握K8S Pod - Yabea - 博客园

- -
K8S configmap介绍. Pod是k8s中最小的调度单元,包含了一个“根容器”和其它用户业务容器. 如果你使用过k8s的话,当然会了解pod的基本使用,但是为了更好的应用,你需要深入了解pod的配置、调度、升级和扩缩容等. pod包含一个或多个相对紧密耦合的容器,处于同一个pod中的容器共享同样的存储空间、IP地址和Port端口.

浅谈 k8s ingress controller 选型 - 知乎

- -
大家好,先简单自我介绍下,我叫厉辉,来自腾讯云. 业余时间比较喜欢开源,现在是Apache APISIX PPMC. 今天我来简单给大家介绍下 K8S Ingress 控制器的选型经验,今天我讲的这些内容需要大家对 K8S 有一定的了解,下面是我的分享. 阅读本文需要熟悉以下基本概念:. 集群:是指容器运行所需云资源的集合,包含了若干台云服务器、负载均衡器等云资源.