Knative 基本功能深入剖析: Knative Serving 的流量灰度和版本管理 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
如果想在 V2EX 获得更好的推广效果,欢迎了解 PRO 会员机制:
pro/about
AlibabaSS

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

  •  
  •   AlibabaSS 2019 年 7 月 19 日 1537 次点击
    这是一个创建于 2471 天前的主题,其中的信息可能已经有所发展或是发生改变。

    作者|冬岛 阿里云技术专家

    本篇主要介绍 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-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-go CMD ["/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-example namespace: default spec: 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-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-example namespace: default spec: 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.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-example namespace: default spec: 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 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: 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/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-example namespace: default spec: 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.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 灰度的比例并不能真实的代表流量的比例,是一个间接的灰度方法。

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2993 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 41ms UTC 06:52 PVG 14:52 LAX 23:52 JFK 02:52
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86