内容简介: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
}
},
...
}
]
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Pro JavaScript Techniques
John Resig / Apress / 2006-12-13 / USD 44.99
Pro JavaScript Techniques is the ultimate JavaScript book for the modern web developer. It provides everything you need to know about modern JavaScript, and shows what JavaScript can do for your web s......一起来看看 《Pro JavaScript Techniques》 这本书的介绍吧!