内容简介:knative在service里面实现了serverless的功能,其中最重要的莫过于按需来启动服务,并基于流量来弹性伸缩。在社区的文档里面找到这样一张架构设计图,先初略理解一下,详细介绍一下其流量转发流程。这里说的模型,其实就是knative的service里面关于CRD的定义。其中声明在外的莫属
knative在service里面实现了serverless的功能,其中最重要的莫过于按需来启动服务,并基于流量来弹性伸缩。在社区的文档里面找到这样一张架构设计图,先初略理解一下,详细介绍一下其流量转发流程。
模型抽象
这里说的模型,其实就是knative的service里面关于CRD的定义。其中声明在外的莫属 service
、 configuration
、 revision
以及 route
。但是要真正搞明白整个模型的原作原理,理解清楚所有CRD的关联关系是非常有必要的。
上图展示了从 service
衍生出来的所有CRD的血缘关系,下面简述其流程和个CRD的作用。
- 当用户创建一个knative的service的时候,其controller会对应创建出
configuration
和route
;这一块较简单,因为service的spec里面其实是包含了对configuration和traffic的定义的。 - 花开两支,
configuration
一方面基于配置创建对应的revision
;另一方面,route
除了创建externalService类型的service
用于将流量指向istio网关外,同时还创建了clusterIngress
(作用很重要)。 -
clusterIngress
是对各种可用于knative流量入口组件的抽象。对于底层是istio的环境,networkController会将clusterIngress资源转化为istio的virtualService
配置,从而提供将外部流量转发到集群内部的功能。 - 对于
revision
来讲,其controller一方面基于资源描述创建出deployment
、imageCache
等资源;另一方面,为了提供serverless功能,controller还对应创建了podAutoScaler
。需要知道的是podAutoScaler
有两种实现,分别对应kpa和hpa。 - autoscaler controller又实现了基于
podAutoScaler
创建对应的sks
(serverlessService)。如果策略是基于kpa的,就需要一套监控流量和并发请求量的机制,于是又创建了private和public的service
专用于访问实例上监控组件sidecar的端口。
代码实现
上面的分析,具体的代码实现流程见下图。
流量转发
当外部流量需要访问内部服务时,其流量的转发流程如何?接下来分小节介绍主要的流量转发逻辑。
流量入口
服务映射
之前在将CRD资源的时候有提到 clusterIngress
资源,以及对应的的ExternalService类型的 service
,这里是一个简单的实例。
[root@k8s-master knative]# kc describe svc autoscale-go Name: autoscale-go Namespace: default Labels: serving.knative.dev/route=autoscale-go Annotations: <none> Selector: <none> Type: ExternalName IP: External Name: istio-ingressgateway.istio-system.svc.cluster.local Session Affinity: None Events: <none>
该service的作用是,当请求 autoscale-go.default.example.com
时(cluster名字换成环境的cluster name),DNS会直接返回external name istio-ingressgateway.istio-system.svc.cluster.local
作为响应。相当于就将对autoscale-go服务的访问,重定向到了istio的ingressgateway上。而Istio上早已按照 clusterIngress
的要求,配置好了流量转发规则。
转发规则
serverlessservice有两种模式, proxy
和 service
。其中 proxy
会将流量转发到activator上,而 service
则会将流量转发到对应的后端实例上真正处理业务。
我们先假设此时后端的deployment并没有启动起来,或者是很久没有请求流量,pod已经被autoscaler出于节约资源消耗的目的干掉了,即serverlessservice处于 proxy
模式。此时istio的配置如下。
[root@k8s-master ~]# istioctl pc listener istio-ingressgateway-67cbb7f6c6-bqv2h.istio-system ADDRESS PORT TYPE 0.0.0.0 80 HTTP 0.0.0.0 15090 HTTP [root@k8s-master ~]# istioctl pc route istio-ingressgateway-67cbb7f6c6-bqv2h.istio-system -o json [ { "name": "http.80", "virtualHosts": [ { "name": "autoscale-go.default.example.com:80", "domains": [ "autoscale-go.default.example.com", "autoscale-go.default.example.com:80" ], "routes": [ { "match": { "prefix": "/", "headers": [ { "name": ":authority", "regexMatch": "^autoscale-go\\.default(?::\\d{1,5})?$" } ] }, "route": { "cluster": "outbound|80||autoscale-go-52f52.default.svc.cluster.local", "timeout": "600s", "retryPolicy": { "retryOn": "connect-failure,refused-stream,unavailable,cancelled,resource-exhausted,retriable-status-codes", "numRetries": 3, "perTryTimeout": "600s", "retryHostPredicate": [ { "name": "envoy.retry_host_predicates.previous_hosts" } ], "hostSelectionRetryMaxAttempts": "3", "retriableStatusCodes": [ 503 ] }, "maxGrpcTimeout": "600s" }, ... } ] } ] } ]
查看EDS的配置信息,发现其对应的路由为IP 10.244.0.234
的8012端口。
[root@k8s-master ~]# istioctl pc endpoint istio-ingressgateway-67cbb7f6c6-bqv2h.istio-system -o json { ... "name": "outbound|80||autoscale-go-52f52.default.svc.cluster.local", "addedViaApi": true, "hostStatuses": [ { "address": { "socketAddress": { "address": "10.244.0.234", "portValue": 8012 } }, ... } ] }
在来看此时该IP是activator对应的pod IP地址。
[root@k8s-master ~]# kc get pod -o wide --all-namespaces | grep 10.244.0.234 knative-serving activator-5b7d897458-xv4tp 1/1 Running 0 132m 10.244.0.234 k8s-master <none> <none>
查看service上面的流量以及对应的转发目标变为activator。
[root@k8s-master knative]# kc get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE alertmanager-operated ClusterIP None <none> 9093/TCP,6783/TCP 7d2h autoscale-go ExternalName <none> istio-ingressgateway.istio-system.svc.cluster.local <none> 148m autoscale-go-52f52 ClusterIP 10.108.134.58 <none> 80/TCP 148m autoscale-go-52f52-metrics ClusterIP 10.109.214.47 <none> 9090/TCP 148m autoscale-go-52f52-priv ClusterIP 10.106.251.157 <none> 80/TCP 148m kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 66d prometheus-operated ClusterIP None <none> 9090/TCP 7d2h [root@k8s-master knative]# kc get ep NAME ENDPOINTS AGE autoscale-go-52f52 10.244.0.234:9090,10.244.0.234:8012,10.244.0.234:8013 148m autoscale-go-52f52-metrics <none> 148m autoscale-go-52f52-priv <none> 148m
所以,我们可以判断:当很久没有流量请求的时候,serverlessservice会切换到 proxy
模式,其autoscaler逻辑会将pod全干掉;而此时,为了检测到外部的流量请求,Istio将流量转发到activator上面。
弹性伸缩
通过上面的分析,相信大家对serverlessservice的弹性伸缩都有了一个直观的了解。接下来该部分通过两个小节介绍弹性伸缩的工作原理。
弹性伸缩分为 hpa
和 kpa
:
hpa kpa
触发器
前面提到的activator就是触发器,它的作用就是为那些几乎没有流量访问的服务充当看门人。显然这个看门人并不是只为某个VIP客户服务,而是为大家一起服务的。那activator都做了些什么?当流量到了的时候,它又是如何通知正处于放空状态的服务实例的呢?
作用
- 当service的实例数为0的时候,activator代替service接收流量,统计流量和并发数量,并通过websocket上报到autoscaler服务。
- 当service的实例启动起来后,activator通过获取revision和serverlessservice来找到对应的service,并探测该service的服务是否已经可以访问,一旦准备好,就发送流量。
- 通过一系列的handler链来打印日志,记录trace信息,限速,零时存储请求内容,响应probe和healthcheck等。
核心工作流程
- 作为代理看门人,activator收到外部访问service的请求后,第一件事请就是去除报文中的header信息,按照revision name作为key产生一条event。
- 如果从来没有改key的event,就可以判断这是到某一个客户的第一条请求,此时看门人需要立刻通过websocket上报到autoscaler(autoscaler的逻辑会创建service的实例)。
- 当然,activator并非只做这些,它还记录日志,trace信息等,最重要的一点,activator会基于报文header中的信息来知道其所请求的revision以及sks。
- 通过查询sks的privateService来找到该service的healthCheck probe地址,探测其服务是否ready。一旦probe成功就会将缓存的报文发送到目标服务。
代码实现
其代码住逻辑分为三块,也对应三个goroutine,分别是:
- 监控上报
- 监控数据统计
- 报文处理(包含多个处理链)
弹性伸缩逻辑
在触发器的流程中,我们提到当触发器发现某个报文是对某个revision的第一个请求时,会通过websocket上报到autoscaler controller。接下来,我们看看autoscaler的处理流程。
功能
- autoscalr的核心作用就是做弹性伸缩的决策,为了做决策,它需要实现对上报监控指标的采集,这又分为两种形式。
- 通过websocket上报来收集activator的报文请求指标;
- 通过定时pull的形势来从metrics service获取queue中统计的指标;
- 基于metric指标来做弹性伸缩决策并下发。
代码实现
由于这里重点讲流量,该代码基本是按照功能来实现,这里就不再做讲解。
这里重点讲一下autoscaler将service的pod拉起来了之后的动作,通过代码里面可以看到, ks.applyScale
最后会走到 c.reconcileSKS
。这里其实是更改serverlessservice的模式,从 proxy
切换到 service
,而接下来的又做了什么?请参考service controller的逻辑。
当pod启动起来之后,对应的private Service会基于selector而选中新起来的pod,从而监控流量将从pod的8012端口采集。
流量监控
上面提到,当pod启动之后,autoscaler会从pod的8012端口采集监控指标。这里的监控指标是如何产生的?应用需要关注吗?这些问题在这一节来解答。
作用
首先,knative中的revision在创建deploy的时候,会为其自动加入一个sidecar,这个container就是queue-proxy;在整个平台中,queue-proxy的作用非常重要。
- 基于一层反向代理,收集访问业务container的流量情况,对外暴露9090(即:metrics的访问端口)
- 在反向代理上,基于配置的并发数来限制外部访问的请求速率;
- 对外提供对主容器进行healthcheck以及drain的接口(admin的端口:8022),(业务代理端口: http1:8012,http2:8013);
代码实现
实验
autoscaler通过周期性的抓取每一个业务容器对应queue-proxy的metrics来感知实时流量情况的。当外部往service发包的时候,在pod的queue container网卡上抓包可以发现有很多的prometheus metrics的报文,collector拉取指标的频率为每秒4次,具体指标见下图的抓包细节:
在报文里面发现来收集metrics的IP地址为 10.244.0.235
, 通过在k8s里面查询,发现该IP为autoscaler的地址。
[root@k8s-master ~]# kc get pod -o wide --all-namespaces | grep 10.244.0.235 knative-serving autoscaler-74f47bfff8-7znzv 1/1 Running 0 15d 10.244.0.235 k8s-master <none> <none>
当服务启动后,各服务的具体内容如下:
[root@k8s-master ~]# kc get serverlessservice NAME SERVICENAME PRIVATESERVICENAME READY REASON autoscale-go-52f52 autoscale-go-52f52 autoscale-go-52f52-priv True [root@k8s-master ~]# kc get revisions NAME SERVICE NAME GENERATION READY REASON autoscale-go-52f52 autoscale-go-52f52 1 True [root@k8s-master ~]# kc get deploy NAME READY UP-TO-DATE AVAILABLE AGE autoscale-go-52f52-deployment 1/1 1 1 141m [root@k8s-master ~]# kc get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE autoscale-go ExternalName <none> istio-ingressgateway.istio-system.svc.cluster.local <none> 142m autoscale-go-52f52 ClusterIP 10.108.134.58 <none> 80/TCP 142m autoscale-go-52f52-metrics ClusterIP 10.109.214.47 <none> 9090/TCP 142m autoscale-go-52f52-priv ClusterIP 10.106.251.157 <none> 80/TCP 142m [root@k8s-master ~]# kc get ep NAME ENDPOINTS AGE alertmanager-operated <none> 7d2h autoscale-go-52f52 10.244.0.18:8012 142m autoscale-go-52f52-metrics 10.244.0.18:9090 142m autoscale-go-52f52-priv 10.244.0.18:8012 142m kubernetes 10.200.204.76:6443 66d prometheus-operated <none> 7d2h [root@k8s-master ~]# istioctl ps NAME CDS LDS EDS RDS PILOT VERSION istio-ingressgateway-67cbb7f6c6-bqv2h.istio-system SYNCED SYNCED SYNCED (100%) SYNCED istio-pilot-75984f55cc-5brpc 1.1.3 [root@k8s-master ~]# istioctl pc endpoint istio-ingressgateway-67cbb7f6c6-bqv2h.istio-system -o json { "name": "outbound|80||autoscale-go-52f52.default.svc.cluster.local", "addedViaApi": true, "hostStatuses": [ { "address": { "socketAddress": { "address": "10.244.0.18", "portValue": 8012 } }, ... } ] }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。