写点什么

使用 Flomesh 服务网格进行流量拆分

作者:Flomesh
  • 2023-04-27
    北京
  • 本文字数:3988 字

    阅读完需:约 13 分钟

使用 Flomesh 服务网格进行流量拆分

在微服务的架构中,随着业务迭代的加速和服务数量的增加,每一次服务的更新发布都存在潜在的故障风险,对系统的可靠性带来挑战。如何降低新版本发布对业务的影响,让更新更加可控成为微服务治理的重要工作之一。


通过灰度发布,通过对流量的细粒度控制,可以控制新版本发布影响的范围。比如可以控制新版本只作用于少部分流量,或者只在某些用户中试用,然后边扩大范围边测试,逐步地将新版本覆盖所有流量。


灰度发布的实现,底层实际上是对流量进行拆分。就像负载均衡一样,将流量均衡的拆分到上游实例中。灰度发布则是将这种拆分控制得更加精细,今天就来介绍如何使用服务网格来实现精细的流量拆分。


相比于在微服务 SDK 实现灰度发布,服务网格将该能力卸载到了 sidecar 中,解耦带来的显著优势这里不在过多赘述。

演示

说明


在今天的演示中,我们使用两种应用 curl 以及 Pipy 实现的 httpbin 分别作为客户端和服务端。服务有两个版本 v1 和 v2,通过部署 httpbin-v1httpbin-v2 来模拟版本发布中的新旧两个版本。


可能细心的看客有留意到在演示中经常使用 Pipy 来实现 httpbin 的功能,得益于使用 Pipy 实现的 web 服务可以很容易地定制响应内容,方便观察测试结果。

前置条件

  • Kubernetes 集群

  • kubectl CLI

安装服务网格

下载 osm-edge CLI。


system=$(uname -s | tr [:upper:] [:lower:])arch=$(dpkg --print-architecture)release=v1.3.4curl -L https://github.com/flomesh-io/osm-edge/releases/download/${release}/osm-edge-${release}-${system}-${arch}.tar.gz | tar -vxzf -./${system}-${arch}/osm versioncp ./${system}-${arch}/osm /usr/local/bin/
复制代码


安装服务网格,并等待所有组件成功运行。


osm install --timeout 120s
复制代码

部署示例应用

应用 curlhttpbin 运行在各自的命名空间下,且命名空间通过命令 osm namespace add xxx 交由服务网格纳管。


kubectl create ns httpbinkubectl create ns curlosm namespace add httpbin curl
复制代码


部署 v1 版本的 httpbin,这个版本对所有 HTTP 请求返回 Hi, I am v1!。其他应用访问通过 Service httpbin 来访问 httpbin


kubectl apply -n httpbin -f - <<EOFapiVersion: v1kind: Servicemetadata:  name: httpbinspec:  ports:    - name: pipy      port: 8080      targetPort: 8080      protocol: TCP  selector:    app: pipy---apiVersion: apps/v1kind: Deploymentmetadata:  name: httpbin-v1  labels:    app: pipy    version: v1spec:  replicas: 1  selector:    matchLabels:      app: pipy      version: v1  template:    metadata:      labels:        app: pipy        version: v1    spec:      containers:        - name: pipy          image: flomesh/pipy:latest          ports:            - name: pipy              containerPort: 8080          command:            - pipy            - -e            - |              pipy()              .listen(8080)              .serveHTTP(new Message('Hi, I am v1!\n'))EOF
复制代码


部署 curl 应用。


kubectl apply -n curl -f - <<EOFapiVersion: v1kind: Servicemetadata:  name: curl  labels:    app: curl    service: curlspec:  ports:    - name: http      port: 80  selector:    app: curl---apiVersion: apps/v1kind: Deploymentmetadata:  name: curlspec:  replicas: 1  selector:    matchLabels:      app: curl  template:    metadata:      labels:        app: curl    spec:      containers:      - image: curlimages/curl        imagePullPolicy: IfNotPresent        name: curl        command: ["sleep", "365d"]EOF
复制代码


等待所有应用成功运行。


kubectl wait --for=condition=ready pod --all -A
复制代码


测试应用访问。


curl_client="$(kubectl get pod -n curl -l app=curl -o jsonpath='{.items[0].metadata.name}')"# 这里发送 4 个请求kubectl exec "$curl_client" -n curl -c curl -- curl -s httpbin.httpbin:8080 httpbin.httpbin:8080 httpbin.httpbin:8080 httpbin.httpbin:8080
复制代码


可以看到如下结果。


Hi, I am v1!Hi, I am v1!Hi, I am v1!Hi, I am v1!
复制代码


接下来部署 v2 版本的 httpbin

部署 v2 版本

v2 版本的 httpbin 对所有 HTTP 请求返回 Hi, I am v2!。在部署之前,我们需要设置默认的流量拆分策略,否则通过 Service httpbin 可以访问到新版本的实例。


创建 Service httpbin-v1,其标签选择器相比 Service httpbin 多了 version 标签。当前二者有相同的 endpoints。


kubectl apply -n httpbin -f - <<EOFapiVersion: v1kind: Servicemetadata:  name: httpbin-v1spec:  ports:    - name: pipy      port: 8080      targetPort: 8080      protocol: TCP  selector:    app: pipy    version: v1EOF
复制代码


应用下面的 TrafficSplit 拆分策略,将所有流量都拆分到 httpbin-v1


kubectl apply -n httpbin -f - <<EOFapiVersion: split.smi-spec.io/v1alpha4kind: TrafficSplitmetadata:  name: httpbin-splitspec:  service: httpbin  backends:  - service: httpbin-v1    weight: 100EOF
复制代码


然后部署新版本。


kubectl apply -n httpbin -f - <<EOFapiVersion: v1kind: Servicemetadata:  name: httpbin-v2spec:  ports:    - name: pipy      port: 8080      targetPort: 8080      protocol: TCP  selector:    app: pipy    version: v2---apiVersion: apps/v1kind: Deploymentmetadata:  name: httpbin-v2  labels:    app: pipy    version: v2spec:  replicas: 1  selector:    matchLabels:      app: pipy      version: v2  template:    metadata:      labels:        app: pipy        version: v2    spec:      containers:        - name: pipy          image: flomesh/pipy:latest          ports:            - name: pipy              containerPort: 8080          command:            - pipy            - -e            - |              pipy()              .listen(8080)              .serveHTTP(new Message('Hi, I am v2!\n'))EOF
复制代码


等待新版本成功运行。


kubectl wait --for=condition=ready pod -n httpbin -l version=v2
复制代码


此时再次发送请求,可以看到处理请求的仍然是 v1 版本的 httpbin


kubectl exec "$curl_client" -n curl -c curl -- curl -s httpbin.httpbin:8080 httpbin.httpbin:8080 httpbin.httpbin:8080 httpbin.httpbin:8080Hi, I am v1!Hi, I am v1!Hi, I am v1!Hi, I am v1!
复制代码

灰度发布

接下来,修改拆分的策略将 25% 的流量拆分到 v2 版本。


kubectl apply -n httpbin -f - <<EOFapiVersion: split.smi-spec.io/v1alpha4kind: TrafficSplitmetadata:  name: httpbin-splitspec:  service: httpbin  backends:  - service: httpbin-v1    weight: 75  - service: httpbin-v2    weight: 25EOF
复制代码


再次发送请求,可以看到有 1/4 的流量是有 v2 版本处理的。


kubectl exec "$curl_client" -n curl -c curl -- curl -s httpbin.httpbin:8080 httpbin.httpbin:8080 httpbin.httpbin:8080 httpbin.httpbin:8080Hi, I am v1!Hi, I am v2!Hi, I am v1!Hi, I am v1!
复制代码

灰度发布进阶


假如 v2 版本中,更新的是 /test 端点的功能。出于风险控制,我们希望只有部分流量访问 v2 版本的 /test 端点,其他端点如 /demo 只访问 v1 版本。


此时我们需要引入另一个资源来对访问 /test 的流量进行定义:定义路由。这里定义了两种路由:


  • httpbin-test:使用动词 GET 访问 /test 的流量。

  • httpbin-all:使用动词 GET 访问 / 的流量,注意这里是前缀匹配。


kubectl apply -n httpbin -f - <<EOFapiVersion: specs.smi-spec.io/v1alpha4kind: HTTPRouteGroupmetadata:  name: httpbin-testspec:  matches:  - name: test    pathRegex: "/test"    methods:    - GET---apiVersion: specs.smi-spec.io/v1alpha4kind: HTTPRouteGroupmetadata:  name: httpbin-allspec:  matches:  - name: test    pathRegex: ".*"    methods:    - GETEOF
复制代码


然后更新流量拆分策略,将路由关联到拆分策略中。同时创建新的策略


kubectl apply -n httpbin -f - <<EOFapiVersion: split.smi-spec.io/v1alpha4kind: TrafficSplitmetadata:  name: httpbin-splitspec:  service: httpbin  matches:  - name: httpbin-test    kind: HTTPRouteGroup  backends:  - service: httpbin-v1    weight: 75  - service: httpbin-v2    weight: 25---apiVersion: split.smi-spec.io/v1alpha4kind: TrafficSplitmetadata:  name: httpbin-allspec:  service: httpbin  matches:  - name: httpbin-all    kind: HTTPRouteGroup  backends:  - service: httpbin-v1    weight: 100EOF
复制代码


此时尝试访问 /test 端点,只有 25% 的流量由新版本处理。


kubectl exec "$curl_client" -n curl -c curl -- curl -s httpbin.httpbin:8080/test httpbin.httpbin:8080/test httpbin.httpbin:8080/test httpbin.httpbin:8080/testHi, I am v1!Hi, I am v2!Hi, I am v1!Hi, I am v1!
复制代码


再请求下 /demo 端点,可以看到全部流量都去到了旧的 v1 版本。


kubectl exec "$curl_client" -n curl -c curl -- curl -s httpbin.httpbin:8080/demo httpbin.httpbin:8080/demo httpbin.httpbin:8080/demo httpbin.httpbin:8080/demoHi, I am v1!Hi, I am v1!Hi, I am v1!Hi, I am v1!
复制代码


符合预期。


引用链接

[1] Pipy: https://github.com/flomesh-io/pipy


发布于: 刚刚阅读数: 3
用户头像

Flomesh

关注

微信订阅号:flomesh 2022-04-07 加入

一站式云原生应用流量管理供应商 官网:https://flomesh.io

评论

发布
暂无评论
使用 Flomesh 服务网格进行流量拆分_微服务_Flomesh_InfoQ写作社区