写点什么

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:427463
用户头像
阿里云容器平台 ACK,企业云原生转型最佳搭档

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

关注

评论

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

线性稳压电源和开关电源,到底有什么区别?

元器件秋姐

电源电路 元器件采购 元器件电商 元器件知识 华秋商城

聚焦年轻消费人群 百草味入围最受“Z世代”喜爱品牌TOP10

E科讯

Kotlin变量的空安全(Null Safety)

子不语Any

android kotlin 11月月更

Spring Boot框架下实现Excel服务端导入导出

葡萄城技术团队

前端 Excel 控件

10 分钟部署一个 API 工具

Scar 

开源 Serverless 接口测试 Vercel

【Redis】Redis AOF持久化

石臻臻的杂货铺

redis 11月月更

Wallys/Introduction of DR9074 series network card/qcn9074/qcn9072/qcn9024/industrial M.2 card

wallysSK

QCN9074 QCN9024 QCN9072

一路同行:开发者与华为云的2022

脑极体

10月月更获奖名单出炉啦!快来看看你上榜没有!

InfoQ写作社区官方

热门活动

直播CDN调度技术关键挑战与架构设计

京东科技开发者

架构 系统架构 直播 CDN加速 CDN技术

扒去Spring事件监听机制的外衣,竟然是观察者模式

程序知音

Java spring ssm Java后端 后端技术

Redis6.0新特性、剖析线程模型(单线程和多线程)

C++后台开发

redis 多线程 后端开发 单线程 C++开发

2 分钟,教你用 Serverless 每天给女朋友自动发土味情话

阿里巴巴云原生

阿里云 Serverless 云原生

首批!腾讯云通过金融开源技术服务能力评估

腾源会

开源

云原生网关的可观测性体系实践

阿里巴巴云原生

阿里云 云原生 云原生微服务

MASA MAUI Plugin (六)集成个推,实现本地消息推送[Android] 篇

MASA技术团队

blazor MASA MAUI Xamarin MASA Blazor

JavaScript刷LeetCode拿offer-树的遍历

js2030code

JavaScript LeetCode

鼎茂科技获得阿里云首批产品生态集成认证,携手阿里云共建新合作

阿里巴巴云原生

阿里云 云原生 加速器 企业招募

JavaScript刷LeetCode拿offer-二叉树层序遍历篇

Geek_07a724

JavaScript LeetCode

java Apache poi 对word doc文件进行读写操作

@下一站

编程 原创 word Java core 11月月更

以用户为导向,vivo打造安全新范式

极客天地

8分钟带你学会SpringBoot整合Redis来实现缓存技术

好程序员IT教育

redis Spring Boot

Docgeni 2.0 发布,开启自动化

PingCode研发中心

vue和react的区别有哪些,哪个好

千锋IT教育

【倒计时4天】杭州专场-单元测试产品体验样板间,等你加入!

TRaaS

用javascript分类刷leetcode3.动态规划(图文视频讲解)

js2030code

JavaScript LeetCode

java企业级开发中常见的注入方式

千锋IT教育

大数据 Hadoop 的五大优势

千锋IT教育

居民消费价格指数变化新鲜出炉,这类商品同比涨幅最大

前嗅大数据

数据分析 数据采集 居民消费 居民消费指数

【Go电商实战05】结合项目解答使用Go中间件遇到的问题:中间件的概念和应用

王中阳Go

golang 学习方法 中间件 11月月更 电商实战

前端leetcde算法面试套路之二叉树

js2030code

JavaScript LeetCode

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