QCon北京「鸿蒙专场」火热来袭!即刻报名,与创新同行~ 了解详情
写点什么

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

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

关注

评论

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

软件测试--缺陷报告

测试人生路

软件测试

再谈自研开源Kube-OVN, 设计思路及实现原理

York

灵雀云 Kubernetes k8s Kube-OVN

Kube-OVN 0.6.0 发布,支持 IPv6、流量镜像及更多功能

York

灵雀云 Kubernetes k8s Kube-OVN

灵雀云开源项目 Kube-OVN 亮相开源基础设施峰会

York

灵雀云 Kubernetes k8s Kube-OVN

同城快递系统-大作业

三板斧

极客时间架构师一期

电商后链路如何做好私域运营?

Linkflow

客户数据平台 CDP 客户画像

一份阿里Java学习路线出现“病毒式”传播,导致44人秋招同时拿到offer

Java架构师迁哥

“58同城”架构师分享:联盟广告平台架构及实践

Java架构师迁哥

“持证”就能上岗 京东绿色内推招聘通道开启

京东科技开发者

云计算 大数据 程序人生

剖析JDK:强引用、软引用、弱引用、虚引用有何区别?

后台技术汇

28天写作

2021年的28天写作,从微信视频号开始

赵新龙

法庭上的CTO 28天写作

Yarn RM写ZNode超数据量限制bug修复

kwang

大数据 zookeeper YARN

Python进阶系列文章汇总

Kaito

Python 爬虫

OVS 设计与实现阅读笔记,五年前的这篇论文里这些问题已经明了

York

Kubernetes k8s Kube-OVN

容器网络方案调研:都是网络插件,Kube-OVN凭啥脱颖而出?

York

灵雀云 Kubernetes k8s Kube-OVN

技术实录 | 灵雀云基于 OVN 的 Kubernetes 网络架构解析

York

灵雀云 Kubernetes k8s Kube-OVN

SpringCloud从入门到精通02---支付模块01

Felix

大作业一:架构设计方案评审

Nick~毓

Kube-OVN再更新! v0.8.0 支持网关高可用以及网络监控集成

York

灵雀云 Kubernetes k8s Kube-OVN

Kube-OVN v0.9.0更新,网络可视化和控制平面稳定性提升

York

灵雀云 Kubernetes k8s Kube-OVN

代码整洁之道

田维常

代码

SpringCloud从入门到精通01---父项目创建

Felix

SpringCloud Eureka 高可用架构

灵雀云Kube-OVN:基于OVN的开源Kubernetes网络实践

York

灵雀云 Kubernetes k8s Kube-OVN

Kube-OVN 0.5.0 发布,支持 NetworkPolicy、用户自定义网卡和MTU

York

灵雀云 Kubernetes k8s Kube-OVN

架构师训练营技术知识点

三板斧

架构师训练营第 1 期

中国特色新基建可视化,工程监控画面还能这么美?你绝对没见过

一只数据鲸鱼

物联网 新基建 数据可视化 绿色工业

一个20年技术老兵的 2020 年度技术总结

万俊峰Kevin

微服务 go-zero Go 语言

教小师妹快速入门Maven,嘿嘿嘿...

田维常

maven

Kube-OVN v 0.7.0 发布,IPAM、子网和安全功能增强

York

灵雀云 Kubernetes k8s Kube-OVN

腾讯大佬亲自总结出“Java知识地图+学习路线”从点到面一应俱全!不看血亏

比伯

Java 编程 架构 程序人生 计算机

2021年阿里巴巴面试参考指南泰山版开源(Java版)

Java架构追梦

Java 架构 面试 分布式 微服务

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