写点什么

Knative 基本功能深入剖析:Knative Serving 的流量灰度和版本管理

  • 2019-08-12
  • 本文字数:5822 字

    阅读完需:约 19 分钟

Knative 基本功能深入剖析:Knative Serving 的流量灰度和版本管理

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

部署 rest-api v1

  • 代码


测试之前我们需要写一段  rest-api 的代码,并且还要能够区分不同的版本。下面我基于官方的例子进行了修改,为了使用方便去掉了 github.com/gorilla/mux 依赖,直接使用 Golang 系统包  net/http 替代。这段代码可以通过 RESOURCE 环境变量来区分不同的版本。


package main
import ( "fmt" "io/ioutil" "log" "net/http" "net/url" "os"
"flag")
var resource string
func main() { flag.Parse() //router := mux.NewRouter().StrictSlash(true)
resource = os.Getenv("RESOURCE") if resource == "" { resource = "NOT SPECIFIED" }
root := "/" + resource path := 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 builder
WORKDIR /go/src/github.com/knative-sample/rest-api-goCOPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -v -o rest-api-goFROM registry.cn-hangzhou.aliyuncs.com/knative-sample/alpine-sh:3.9COPY --from=builder /go/src/github.com/knative-sample/rest-api-go/rest-api-go /rest-api-go
CMD ["/rest-api-go"]
复制代码


  • Service 配置


镜像已经有了,我们开始部署 Knative Service。把下面的内容保存到 revision-v1.yaml 中,然后执行 kubectl apply -f revision-v1.yaml 即可完成 Knative Service 的部署。


apiVersion: serving.knative.dev/v1alpha1kind: Servicemetadata: name: stock-service-example namespace: defaultspec: template:   metadata:     name: stock-service-example-v1   spec:     containers:     - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1       env:         - name: RESOURCE           value: v1       readinessProbe:         httpGet:           path: /         initialDelaySeconds: 0         periodSeconds: 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/bash
SVC_NAME="stock-service-example"export INGRESSGATEWAY=istio-ingressgatewayexport 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.shWelcome to the v1 app!
复制代码

灰度 50% 的流量到 v2

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


apiVersion: serving.knative.dev/v1alpha1kind: Servicemetadata:  name: stock-service-example  namespace: defaultspec:  template:    metadata:      name: stock-service-example-v2    spec:      containers:      - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1        env:          - name: RESOURCE            value: v2        readinessProbe:          httpGet:            path: /          initialDelaySeconds: 0          periodSeconds: 3  traffic:  - tag: v1    revisionName: stock-service-example-v1    percent: 50  - tag: v2    revisionName: stock-service-example-v2    percent: 50  - tag: latest    latestRevision: true    percent: 0
复制代码


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


└─# ./run-test.shWelcome to the v2 app!└─# ./run-test.shWelcome to the v1 app!└─# ./run-test.shWelcome to the v2 app!└─# ./run-test.shWelcome to the v1 app!
复制代码

提前验证 Revision

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


创建一个 revision-v3.yaml 的文件,内容如下:


apiVersion: serving.knative.dev/v1alpha1kind: Servicemetadata:  name: stock-service-example  namespace: defaultspec:  template:    metadata:      name: stock-service-example-v3    spec:      containers:      - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1        env:          - name: RESOURCE            value: v3        readinessProbe:          httpGet:            path: /          initialDelaySeconds: 0          periodSeconds: 3  traffic:  - tag: v1    revisionName: stock-service-example-v1    percent: 50  - tag: v2    revisionName: stock-service-example-v2    percent: 50  - tag: latest    latestRevision: true    percent: 0
复制代码


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


└─# kubectl get revisionNAME                       SERVICE NAME               GENERATION   READY   REASONstock-service-example-v1   stock-service-example-v1   1            Truestock-service-example-v2   stock-service-example-v2   2            Truestock-service-example-v3   stock-service-example-v3   3            True
复制代码


可以看到现在已经创建出来了三个 Revision 。


此时我们再看一下 stock-service-example 的真实生效:


└─# kubectl get ksvc stock-service-example -o yamlapiVersion: serving.knative.dev/v1beta1kind: Servicemetadata:  annotations:...status:...  traffic:  - latestRevision: false    percent: 50    revisionName: stock-service-example-v1    tag: v1    url: http://v1-stock-service-example.default.example.com  - latestRevision: false    percent: 50    revisionName: stock-service-example-v2    tag: v2    url: http://v2-stock-service-example.default.example.com  - latestRevision: true    percent: 0    revisionName: stock-service-example-v3    tag: latest    url: http://latest-stock-service-example.default.example.com  url: http://stock-service-example.default.example.com
复制代码


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


在 Service 的 status.traffic 配置中可以看到 latest Revision 的配置:


- latestRevision: true    percent: 0    revisionName: stock-service-example-v3    tag: latest    url: http://latest-stock-service-example.default.example.com
复制代码


每一个 Revision 都有一个自己的 URL,所以只需要基于 v3 Revision 的 URL 发起请求就能开始测试了。


我已经写好了一个测试脚本,你可以把下面这段脚本保存在 latest-run-test.sh 文件中,然后执行这个脚本就能直接发起到 latest 版本的请求:


#!/bin/bashexport INGRESSGATEWAY=istio-ingressgatewayexport 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/v1alpha1kind: Servicemetadata:  name: stock-service-example  namespace: defaultspec:  template:    metadata:      name: stock-service-example-v3    spec:      containers:      - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/rest-api-go:v1        env:          - name: RESOURCE            value: v3        readinessProbe:          httpGet:            path: /          initialDelaySeconds: 0          periodSeconds: 3  traffic:  - tag: v1    revisionName: stock-service-example-v1    percent: 40  - tag: v2    revisionName: stock-service-example-v2    percent: 30  - tag: v3    revisionName: stock-service-example-v3    percent: 30  - tag: latest    latestRevision: true    percent: 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.shWelcome to the v1 app!└─# ./run-test.shWelcome to the v2 app!└─# ./run-test.shWelcome to the v1 app!└─# ./run-test.shWelcome to the v2 app!└─# ./run-test.shWelcome 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 灰度的比例并不能真实的代表流量的比例,是一个间接的灰度方法。


作者介绍:


冬岛,阿里云容器平台技术专家,负责阿里云容器平台Knative 相关工作。


相关文章:


《初识 Knative:跨平台的 Serverless 编排框架》


《Knative 初体验:Serving Hello World》


《Knative 初体验:Eventing Hello World》


《Knative 初体验:Build Hello World》


《Knative 初体验:CI/CD 极速入门》


《Knative 基本功能深入剖析:Knative Serving 自动扩缩容 Autoscaler》


2019-08-12 08:427388
用户头像
阿里云容器平台 ACK,企业云原生转型最佳搭档

发布了 43 篇内容, 共 23.6 次阅读, 收获喜欢 81 次。

关注

评论

发布
暂无评论
发现更多内容

智能BI新突破,看思迈特新品背后的第二增长曲线!

ToB行业头条

神策SDK不支持Windows客户端全埋点,怎么实现用户统计分析?

ClkLog

C++客户端 C#客户端 C++客户端用户分析 C#客户端用户分析 windows客户端用户分析

《算力服务产业图谱(2024年)》发布,天翼云两款产品重磅入选!

天翼云开发者社区

云计算 云服务

保定等保测评机构有吗?在哪里?

行云管家

网络安全 等保 堡垒机 保定

是什么拖慢了医疗云桌面的部署进程?

上海锐起科技

海大集团的可观测平台建设实践

巴辉特

监控 可观测性 Prometheus Nightingale Flashcat

工作太忙没时间学习,怎么办?

老张

职场成长 认知 学习成长

荣誉+1 !入选十大科技领先成果!

天翼云开发者社区

云计算 大数据博览会

RPA项目:RPA工作原理及其应用领域

八爪鱼采集器︱RPA机器人

RPA 自动化 RPAxAI

挖掘大模型行业落地潜力,火山引擎发布AI创作者大赛

Geek_2d6073

如何理解RPA的技术架构与原理

八爪鱼采集器︱RPA机器人

RPA 自动化 RPAxAI

类似咸鱼/转转闲置二手商品交易回收商城系统源码定制开发搭建

网站,小程序,APP开发定制

YETUADMIN 野兔二手交易系统 ThinkPHP8

免费试听 | AI自动化副业创收班手把手带你提升副业收益

测试人

软件测试

【行业知识】餐饮业怎么定义?需要堡垒机吗?

行云管家

等保 堡垒机 餐饮

mac电脑可以玩的Red Alert红色警戒:红警2 for Mac合集

你的猪会飞吗

红色警戒 Mac游戏下载 红警游戏下载

小城酒店也要数智化,火山引擎数据飞轮能带去哪些想象?

字节跳动数据平台

大数据 数字化转型 云服务 数据平台 数据飞轮

从零到精通!新手实测ToDesk云电脑、易腾云、顺网云,云端畅玩黑神话悟空、魔兽世界

颜淡慕潇

游戏 云桌面

ps 2022下载:Photoshop 2022 (Win&Mac) 软件直装版下载

你的猪会飞吗

mac软件下载 PS2022下载 ps 2022破解下载

好多kafka难题啊,看看其中的化解之道

王中阳Go

Go kafka 面试 面经

第65期 | GPTSecurity周报

云起无垠

智能未来:低代码与AI如何重塑企业应用开发

天津汇柏科技有限公司

低代码 企业级应用程序开发 AI 人工智能

【论文速读】|RO-SVD:一种用于 AIGC 应用的可重构硬件版权保护框架

云起无垠

在线文库网站程序系统源码/app/小程序开发定制

网站,小程序,APP开发定制

技术分享丨使用 Knowledge Bases for Amazon Bedrock 制作 RAG 工具

伊克罗德信息科技

大数据 AWS Amazon Bedrock

工作 6 年,@Transactional 注解用的一塌糊涂

程序员小富

Java 事务回滚

RPA是如何为企业创造价值的

八爪鱼采集器︱RPA机器人

RPA 自动化 RPAxAI

海外广告投放成功的关键点有哪些?

IPIDEA全球HTTP

【论文速读】| ARVO: 开源软件可重现漏洞的全景图

云起无垠

数据存储“取经路”,HBlock轻松“渡”!

天翼云开发者社区

云计算 存储 天翼云

量化交易丨交易所合约机器人系统开发策略规则/逻辑方案部署

V\TG【ch3nguang】

Knative 基本功能深入剖析:Knative Serving 的流量灰度和版本管理_容器_阿里云容器平台_InfoQ精选文章