Knative Service 之流量灰度和版本管理

本篇主要介绍 Knative Serving 的流量灰度,通过一个 rest-api 的例子演示如何创建不同的 Revision、如何在不同的 Revision 之间按照流量比例灰度。

部署 rest-api v1

  • 代码 
    测试之前我们需要写一段 rest-api 的代码,并且还要能够区分不同的版本。下面我基于官方的例子进行了修改,为了使用方便去掉了 github.com/gorilla/mux 依赖,直接使用 Golang 系统包 net/http 替代。这段代码可以通过 RESOURCE 环境变量来区分不同的版本。
package mainimport ("fmt""io/ioutil""log""net/http""net/url""os""flag"
)var resource stringfunc main() {flag.Parse()//router := mux.NewRouter().StrictSlash(true)resource = os.Getenv("RESOURCE")if resource == "" {resource = "NOT SPECIFIED"}root := "/" + resourcepath := root + "/{stockId}"http.HandleFunc("/", Index)http.HandleFunc(root, StockIndex)http.HandleFunc(path, StockPrice)if err := http.ListenAndServe(fmt.Sprintf(":%s", "8080"), nil); err != nil {log.Fatalf("ListenAndServe error:%s ", err.Error())}
}func Index(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Welcome to the %s app! \n", resource)
}func StockIndex(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s ticker not found!, require /%s/{ticker}\n", resource, resource)
}func StockPrice(w http.ResponseWriter, r *http.Request) {stockId := r.URL.Query().Get("stockId")url := url.URL{Scheme: "https",Host:   "api.iextrading.com",Path:   "/1.0/stock/" + stockId + "/price",}log.Print(url)resp, err := http.Get(url.String())if err != nil {fmt.Fprintf(w, "%s not found for ticker : %s \n", resource, stockId)return}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)fmt.Fprintf(w, "%s price for ticker %s is %s\n", resource, stockId, string(body))
}
  • Dockerfile
    创建一个叫做 Dockerfile 的文件,把下面这些内容复制到文件中。执行 docker build --tag registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1 --file ./Dockerfile . 命令即可完成镜像的编译。

你在测试的时候请把 registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1 换成你自己的镜像仓库地址。
编译好镜像以后执行 docker push registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1 把镜像推送到镜像仓库。

FROM registry.cn-hangzhou.aliyuncs.com/knative-sample/golang:1.12 as builderWORKDIR /go/src/github.com/knative-sample/rest-api-go
COPY . .RUN CGO_ENABLED=0 GOOS=linux go build -v -o rest-api-go
FROM registry.cn-hangzhou.aliyuncs.com/knative-sample/alpine-sh:3.9
COPY --from=builder /go/src/github.com/knative-sample/rest-api-go/rest-api-go /rest-api-goCMD ["/rest-api-go"]
  • Service 配置
    镜像已经有了,我们开始部署 Knative Service。把下面的内容保存到 revision-v1.yaml 中,然后执行 kubectl apply -f revision-v1.yaml  即可完成 Knative Service 的部署。
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:name: stock-service-examplenamespace: default
spec:template:metadata:name: stock-service-example-v1spec:containers:- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1env:- name: RESOURCEvalue: v1readinessProbe:httpGet:path: /initialDelaySeconds: 0periodSeconds: 3

首次安装会创建出一个叫做 stock-service-example-v1 的 Revision,并且是把 100% 的流量都打到 stock-service-example-v1 上。

验证 Serving 的各个资源
如下图所示,我们先回顾一下 Serving 涉及到的各种资源。接下来我们分别看一下刚才部署的 revision-v1.yaml 各个资源配置。

  • Knative Service
kubectl get ksvc stock-service-example --output yaml
  • Knative Configuration
kubectl get configuration -l \
"serving.knative.dev/service=stock-service-example" --output yaml
  • Knative Revision
kubectl get revision -l \
"serving.knative.dev/service=stock-service-example" --output yaml
  • Knative Route
kubectl get route -l \
"serving.knative.dev/service=stock-service-example" --output yaml

访问 rest-api 服务
我们部署的 Service 名称是: stock-service-example。访问这个 Service 需要获取 Istio Gateway 的 IP,然后使用 stock-service-example Domain 绑定 Host 的方式发起 curl 请求。为了方便测试我写成了一个脚本。创建一个 run-test.sh 文件,把下面这些内容复制到文件内,然后赋予文件可执行权限。执行执行此脚本就能得到测试结果。

#!/bin/bashSVC_NAME="stock-service-example"
export INGRESSGATEWAY=istio-ingressgateway
export GATEWAY_IP=`kubectl get svc $INGRESSGATEWAY --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*]['ip']}"`
export DOMAIN_NAME=`kubectl get route ${SVC_NAME} --output jsonpath="{.status.url}"| awk -F/ '{print $3}'`curl -H "Host: ${DOMAIN_NAME}" http://${GATEWAY_IP}

测试结果:
从下面的命令输出结果可以看到现在返回的是 v1 的信息,说明请求打到 v1 上面了。

└─# ./run-test.sh
Welcome to the v1 app!

灰度 50% 的流量到 v2

修改 Service 创建 v2 revision , 创建一个 revision-v2.yaml 文件,内容如下:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:name: stock-service-examplenamespace: default
spec:template:metadata:name: stock-service-example-v2spec:containers:- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1env:- name: RESOURCEvalue: v2readinessProbe:httpGet:path: /initialDelaySeconds: 0periodSeconds: 3traffic:- tag: v1revisionName: stock-service-example-v1percent: 50- tag: v2revisionName: stock-service-example-v2percent: 50- tag: latestlatestRevision: truepercent: 0

我们对比一下 v1 版本和 v2 版本可以发现,v2 版本的 Service 中增加了 traffic: 的配置。在 traffic 中指定了每一个 Revision。 执行 kubectl apply -f revision-v2.yaml 安装 v2 版本的配置。然后执行测试脚本就能看到现在返回的结果中 v1 和 v2 基本上是各占 50% 的比例。下面这是我真实测试的结果。

└─# ./run-test.sh
Welcome to the v2 app!
└─# ./run-test.sh
Welcome to the v1 app!
└─# ./run-test.sh
Welcome to the v2 app!
└─# ./run-test.sh
Welcome to the v1 app!

提前验证 Revision

上面展示的 v2 的例子,在创建 v2 的时候直接就把流量分发到 v2 ,如果此时 v2 有问题就会导致有 50% 的流量异常。下面我们就展示一下如何在转发流量之前验证新的 revision 服务是否正常。我们再创建一个 v3 版本。
创建一个 revision-v3.yaml 的文件,内容如下:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:name: stock-service-examplenamespace: default
spec:template:metadata:name: stock-service-example-v3spec:containers:- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1env:- name: RESOURCEvalue: v3readinessProbe:httpGet:path: /initialDelaySeconds: 0periodSeconds: 3traffic:- tag: v1revisionName: stock-service-example-v1percent: 50- tag: v2revisionName: stock-service-example-v2percent: 50- tag: latestlatestRevision: truepercent: 0

执行 kubectl apply -f revision-v3.yaml 部署 v3 版本。然后查看一下 Revision 情况:

└─# kubectl get revision
NAME                       SERVICE NAME               GENERATION   READY   REASON
stock-service-example-v1   stock-service-example-v1   1            True
stock-service-example-v2   stock-service-example-v2   2            True
stock-service-example-v3   stock-service-example-v3   3            True

可以看到现在已经创建出来了三个 Revision 。
此时我们再看一下 stock-service-example 的真实生效:

└─# kubectl get ksvc stock-service-example -o yaml
apiVersion: serving.knative.dev/v1beta1
kind: Service
metadata:annotations:
...
status:
...traffic:- latestRevision: falsepercent: 50revisionName: stock-service-example-v1tag: v1url: http://v1-stock-service-example.default.example.com- latestRevision: falsepercent: 50revisionName: stock-service-example-v2tag: v2url: http://v2-stock-service-example.default.example.com- latestRevision: truepercent: 0revisionName: stock-service-example-v3tag: latesturl: http://latest-stock-service-example.default.example.comurl: http://stock-service-example.default.example.com

可以看到 v3 Revision 虽然创建出来了,但是因为没有设置 traffic,所以并不会有流量转发。此时你执行多少次 ./run-test.sh 都不会得到 v3 的输出。
在 Service 的 status.traffic 配置中可以看到 latest Revision 的配置:

  - latestRevision: truepercent: 0revisionName: stock-service-example-v3tag: latesturl: http://latest-stock-service-example.default.example.com

每一个 Revision 都有一个自己的 URL,所以只需要基于 v3 Revision 的 URL 发起请求就能开始测试了。
我已经写好了一个测试脚本,你可以把下面这段脚本保存在 latest-run-test.sh 文件中,然后执行这个脚本就能直接发起到 latest 版本的请求:

#!/bin/bash
export INGRESSGATEWAY=istio-ingressgateway
export GATEWAY_IP=`kubectl get svc $INGRESSGATEWAY --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*]['ip']}"`
export DOMAIN_NAME=`kubectl get route ${SVC_NAME} --output jsonpath="{.status.url}"| awk -F/ '{print $3}'`export LAST_DOMAIN=`kubectl get ksvc stock-service-example --output jsonpath="{.status.traffic[?(@.tag=='latest')].url}"| cut -d'/' -f 3`curl -H "Host: ${LAST_DOMAIN}" http://${GATEWAY_IP}

测试 v3 版本如果没问题就可以把流量分发到 v3 版本了。
下面我们再创建一个文件 revision-v3-2.yaml , 内容如下:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:name: stock-service-examplenamespace: default
spec:template:metadata:name: stock-service-example-v3spec:containers:- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1env:- name: RESOURCEvalue: v3readinessProbe:httpGet:path: /initialDelaySeconds: 0periodSeconds: 3traffic:- tag: v1revisionName: stock-service-example-v1percent: 40- tag: v2revisionName: stock-service-example-v2percent: 30- tag: v3revisionName: stock-service-example-v3percent: 30- tag: latestlatestRevision: truepercent: 0

用 vimdiff 看一下 revision-v3.yaml 和 revision-v3-2.yaml 的区别:

revision-v3-2.yaml 增加了到 v3 的流量转发。此时执行 ./run-test.sh 可以看到 v1、v2 和 v3 的比例基本是:4:3:3

└─# ./run-test.sh
Welcome to the v1 app!
└─# ./run-test.sh
Welcome to the v2 app!
└─# ./run-test.sh
Welcome to the v1 app!
└─# ./run-test.sh
Welcome to the v2 app!
└─# ./run-test.sh
Welcome to the v3 app!
... ...

版本回滚

Knative Service 的 Revision 是不能修改的,每次 Service Spec 的更新创建的 Revision 都会保留在 kube-apiserver 中。如果应用发布到某个新版本发现有问题想要回滚到老版本的时候只需要指定相应的 Revision,然后把流量转发过去就行了。

小结

Knative Service 的灰度、回滚都是基于流量的。Workload(Pod) 是根据过来的流量自动创建出来的。所以在 Knative Serving 模型中流量是核心驱动。这和传统的应用发布、灰度模型是有区别的。

假设有一个应用 app1 ,传统的做法首先是设置应用的实例个数( Kubernetes 体系中就是 Pod ),我们假设实例个数是 10 个。如果要进行灰度发布,那么传统的做法就是先发布一个 Pod,此时 v1 和 v2 的分布方式是:v1 的 Pod 9个,v2 的 Pod 1 个。如果要继续扩大灰度范围的话那就是 v2 的 Pod 数量变多,v1 的 Pod 数量变少,但总的 Pod 数量维持 10 个不变。

在 Knative Serving 模型中 Pod 数量永远都是根据流量自适应的,不需要提前指定。在灰度的时候只需要指定流量在不同版本之间的灰度比例即可。每一个 Revision 的实例数都是根据流量的大小自适应,不需要提前指定。

从上面的对比中可以发现 Knative Serving 模型是可以精准的控制灰度影响的范围的,保证只灰度一部分流量。而传统的模型中 Pod 灰度的比例并不能真实的代表流量的比例,是一个间接的灰度方法。


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/518344.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在生产环境中使用 Sentinel

文章目录一、安装zookeeper1. linux环境2. windows环境2. 安装并启动zkui二、编译打包2.1. 拉取项目2.2. 启动2.3. 登录 sentinel2.4. 登录zkui2.5. 重启Sentinel2.6. 删除Sentinel的流控规则三、将客户端和zk打通3.1. 引入依赖3.2. 配置3.3. 启动springboot3.4. sentinel控制台…

JavaScript-浏览器控制台使用

基本语法 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!-- JavaScript 严格区分大小写 --><script>// 1. 定义变量 变量类型 变量名 变量值var num 1;num …

看完就入门系列!吞吐量、消息持久化、负载均衡和持久化、伸缩性…… 你真的了解 Kafka 了吗?...

作者| liuhehe123来源| CSDN博客 责编| Carol出品| CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;封图| CSDN下载于视觉中国 无论是已经接触过 Kafka 还是刚入坑的小伙伴&#xff0c;都应该时不时回头了解一下 Kafka &#xff0c;有时候会有不少新收获。今天这份…

Alibaba Cloud Linux 2 开源后又有什么新动作?

阿里妹导读&#xff1a;2019 年 4 月&#xff0c;Alibaba Cloud Linux 2 (Aliyun Linux 2) 正式开源。时至今日&#xff0c;已经走过三个月的里程。在这段时间内&#xff0c;这个刚诞生不久的为阿里云 ECS 环境定制优化的 Linux 操作系统发行版的装机量稳步上升。本文将重点介绍…

一站式数据采集存储的利器:阿里云InfluxDB®️数据采集服务

背景 随着时序数据的飞速增长&#xff0c;时序数据库不仅需要解决系统的稳定性和性能问题&#xff0c;还需实现数据从采集到分析的链路打通&#xff0c;才能让时序数据真正产生价值。在时序数据采集领域&#xff0c;一直缺少自动化的采集工具。虽然用户可以使用一些开源的采集…

Serverless 风起云涌,为什么阿里、微软、AWS 纷纷拥抱开源 OAM?

作者 | 张磊&#xff0c;阿里云原生应用平台高级技术专家邓洪超&#xff0c;阿里云技术专家责编 | 唐小引头图 | CSDN 下载自东方 IC出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;Serverless 这个词第一次被是 2012 年一篇名为《Why The Future of Software and …

K8S从懵圈到熟练 - 我们为什么会删除不了集群的命名空间?

阿里云售后技术团队的同学&#xff0c;每天都在处理各式各样千奇百怪的线上问题。常见的有&#xff0c;网络连接失败&#xff0c;服务器宕机&#xff0c;性能不达标&#xff0c;请求响应慢等。但如果要评选&#xff0c;什么问题看起来微不足道事实上却足以让人绞尽脑汁&#xf…

为什么技术人一定要懂点“可信计算”?

阿里妹导读&#xff1a;可信计算&#xff08;TrustedComputing&#xff0c;简称TC&#xff09;是一项由TCG(可信计算组)推动和开发的技术。可信的核心目标之一是保证系统和应用的完整性&#xff0c;从而确定系统或软件运行在设计目标期望的可信状态。可信和安全是相辅相成的&am…

很用心的为你写了 9 道 MySQL 面试题,建议收藏!

来源 | Java 建设者责编| Carol封图| CSDN下载于视觉中国 MySQL 也是作者本人正在学习的部分&#xff0c;后面会多输出 MySQL 的文章贡献给大家&#xff0c;毕竟 MySQL 涉及到数据存储、锁、磁盘寻道、分页等操作系统概念&#xff0c;而且互联网对 MySQL 的注重程度是不言而喻的…

OpenTelemetry-可观察性的新时代

有幸在2019KubeCon上海站听到Steve Flanders关于OpenTelemetry的演讲&#xff0c;之前Ops领域两个网红项目OpenTracing和OpenCensus终于走到了一起&#xff0c;可观察性统一的标准化已经扬帆起航。 这篇文章旨在抛砖引玉&#xff0c;希望能够和更多的同学一起交流可观察性相关的…

JavaScript-严格检查模式

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!--前提&#xff1a; IDEA需要设置支持ES6 语法use strict; 必须写在JavaScript 的第一行严格检查模式&#xff0c;预防JavaS…

我们为什么需要 SpringBoot?

作者 | 阿文&#xff0c;责编 | 郭芮头图 | CSDN 下载自东方IC出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;任何先进技术的产生都不是凭空出现的&#xff0c;SpringBoot 也不例外&#xff0c;SpringBoot 是基于Spring 的基础上产生的。总所周知&#xff0c;Spri…

高德网络定位之“移动WiFi识别”

导读 随着时代的发展&#xff0c;近10年来位置产业蓬勃发展&#xff0c;定位能力逐渐从低精度走向高精度&#xff0c;从部分场景走向泛在定位。设备和场景的丰富&#xff0c;使得定位技术和能力也不断的优化更新。定位能力包括GNSS、DR&#xff08;航迹推算&#xff09;、MM&a…

JavaScript-字符串

字符串 正常字符串使用单引号 或者 双引号 包裹注意转义字符 \ \ \n \t \u4e2d \u#### Unicode字符 \x41 Ascll字符多行字符串编写 // tab 上面 esc下面的引号 let msg helloworld你好 console.log(msg)模板字符串 let name ht let age 5; let msg hello${name}你…

Alibaba Sentinel规则持久化-拉模式-手把手教程【基于文件】

文章目录一、拉模式架构二、原理简述三、编写3.1 加依赖3.2 写代码3.3 配置四、优缺点分析五、你可能会有的疑问六、参考文档七、案例测试7.1. 添加流控规则7.2. 服务停止7.3. 重新启动服务7.4. 调用接口7.5. 查看流控规则本文实现基于拉模式的Alibaba Sentinel规则持久化。一、…

开发者说:Seata 0.7.0 版本,你 get 'Metrics' 技能了吗?

从用户的视角来感受一个开源项目的成长&#xff0c;是我们推出「开发者说」专栏的初衷&#xff0c;即在开发者进行开源项目选型时&#xff0c;提供更为立体的项目信息。专栏所有内容均来自作者原创/投稿&#xff0c;本文是「开发者说」的第9篇&#xff0c;作者郑扬勇&#xff0…

面试时遇到「看门狗」脖子上挂着「时间轮」,我就问你怕不怕?

来源 | Why技术封图 | CSDN 下载于视觉中国之前写了一篇文章&#xff0c;有一个小节中写到这样一段话&#xff1a;于是就有读者来问了&#xff1a;老哥&#xff0c;看门狗介绍一下呗。面试的时候被问到了&#xff0c;没有回答上来。听到这个问题我脑海里首先浮现出了几个问题&…

海量结构化数据存储技术揭秘:Tablestore存储和索引引擎详解

前言 表格存储Tablestore是阿里云自研的面向海量结构化数据存储的Serverless NoSQL多模型数据库。Tablestore在阿里云官网上有各种文档介绍&#xff0c;也发布了很多场景案例文章&#xff0c;这些文章收录在这个合集中《表格存储Tablestore权威指南》。值得一提的是&#xff0…

JavaScript-数组

数组 Array可以包含任意的数据类型 var arr [1,2,3,4,5,6] arr[0] // 取值 arr[0] 3 // 赋值长度 arr.length arr.length 10 // 长度是可变的注意&#xff1a;假如给arr.length 赋值&#xff0c;数组大小就会发生变化。如果赋值过小&#xff0c;元素就会丢失 获取指定字…

Alibaba Sentinel规则持久化-推模式-手把手教程【基于Nacos】

前面&#xff0c;已经为Sentinel实现了 基于拉模式的规则持久化 &#xff0c;本文来实现基于 推模式的规则持久化。 文章目录一、推模式架构图二、原理简述三、微服务改造3.1. 加依赖3.2. 添加配置四、Sentinel控制台改造五、编译 & 启动六、测试测试1&#xff1a;测试2&am…