写点什么

Knative 初体验:Serving Hello World

  • 2019-06-12
  • 本文字数:7513 字

    阅读完需:约 25 分钟

Knative 初体验:Serving Hello World

本篇文章就通过一个 Hello World 和 Knative 来一个“约会”,让你一睹 Knative 这位白富美的真容。

安装 Knative

Knative 社区提供的安装步骤见这里,整个流程大概包含如下三个部分:



虽然看起来只有三步,但是每一步其实都需要手动做大量的工作,执行一堆命令。另外社区文档提供的 yaml 文件默认使用了大量的 gcr.io 镜像,目前国内无法拉取 gcr.io 镜像。所以这些 yaml 文件在国内不能直接使用,至少需要手动同步 30  多个镜像才行。


不过别着急,阿里云容器服务的应用目录已经有 Knative 的安装包,现在只需要在阿里云容器服务上面点击几下鼠标就能轻轻松松搭建一个 Knative 集群 O ^ ~ ^ O  O ^ ~ ^ O  O ^ ~ ^ O

创建 Kubernetes 集群

阿里云容器服务可以通过管理控制台非常方便地创建 Kubernetes 集群。具体过程可以参考创建Kubernetes集群


容器服务提供了专有集群和托管集群两种类型,如果不知道该怎么选择建议你直接选择托管版的 Kubernetes 集群。托管版无需你自己承担 Kubernetes  Master 组件的管理和运维,你只需要提供 Node 节点即可。

安装 Istio

Knative Serving 运行需要基于 Istio,目前阿里云容器服务 Kubernetes 已提供了一键部署的方式来安装配置 Istio。具体过程可以参考部署Istio


登录 容器服务管理控制台,单击左侧导航栏中的集群,进入集群列表页面。选择所需的集群并单击操作列更多 > 部署 Istio。



根据需要进行配置,然后点击部署按钮。稍等十几秒钟之后,Istio 环境就可以部署完毕。

部署 Istio IngressGateway

在容器服务管理控制台的应用目录中找到 ack-istio-ingressgateway 组件。点击参数标签可以看到默认参数提供了 Istio IngressGateway 的配置项,如果需要定制化参数可以在此进行修改。选择好目标 Kubernetes 集群 然后点击创建按钮即可完成 Ingressgateway 的创建。



在容器服务左侧的容器组页面中选择 Kubernetes 集群和  istio-system  namespace 确认运行状态,如下所示。


部署 Knative CRD

登录容器服务管理控制台,点击左侧的应用目录,在右侧选中 ack-knative-init,如下:



点击创建按钮部署 Knative 初始化所需的内容,包括部署 CRD 等。

部署 Knative Serving

登录容器服务管理控制台,点击左侧的应用目录,在右侧选中 ack-knative-serving,如下:



点击参数, 可以通过修改参数配置进行定制化,默认参数提供了使用 Istio IngressGateway 的配置项,然后点击创建按钮。

Serving Hello World

Serverless 一个核心思想就是按需分配,那么 Knative 是如何实现按需分配的呢?另外在前面的文章中你已经了解到 Knative Serving 在没有流量的时候是可以把 Pod 缩容到零的。接下来就通过一些例子体验一下 Knative 缩容到零和按需自动扩缩容的能力。

部署 helloworld-go 示例

Knative 官方给出了好几种语言的 Helloworld 示例,这些不同的语言其实只是编译镜像的 Dockerfile 有所不同,做好镜像之后的使用方式没什么差异。本例以 go 的 Hello World 为例进行演示。官方给出的例子都是源码,需要编译长镜像才能使用。为了你验证方便我已经提前编译好了一份镜像  registry.cn-hangzhou.aliyuncs.com/knative-sample/simple-app:07 , 你可以直接使用。


首先编写一个 Knative Service 的 yaml 文件 helloworld-go.yaml , 内容如下:


apiVersion: serving.knative.dev/v1alpha1kind: Servicemetadata:  name: helloworld-go  namespace: defaultspec:  template:    metadata:      labels:        app: helloworld-go      annotations:        autoscaling.knative.dev/target: "10"    spec:      containers:        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/simple-app:07          env:            - name: SIMPLE_MSG              value: "helloworld-go-07"
复制代码


注意其中 autoscaling.knative.dev/target: "10" 这个 Annotation 是设置每一个 Pod 的可处理并发请求数 10 ,Knative KPA 自动伸缩的时候会根据当前总请求的并发数和 autoscaling.knative.dev/target 自动调整 Pod 的数量,从而达到自动扩缩的目的。更多的策略信息我会在后续的文章中一一介绍。


现在使用 kubectl 命令把 yaml 提交到 Kubernetes 中:


  • 部署 helloworld-go


└─# kubectl apply -f helloworld-go.yamlservice.serving.knative.dev/helloworld-go created
复制代码


  • 查看 helloworld-go pod


└─# kubectl get podNAME                                              READY   STATUS    RESTARTS   AGEhelloworld-go-lq6ns-deployment-869cbcc75d-qrln7   2/2     Running   0          6s
复制代码


到此 helloworld-go 已经运行起来了,接下来访问一下  helloworld-go 这个服务吧。

访问 helloworld-go 示例

在访问 helloworld-go 之前我要先来介绍一下在 Knative 模型中流量是怎么进来的。Knative Service 和 Kubernetes 原生的 Deployment 不一样,Knative 不会创建 Loadbalance 的 Service,也不能创建 NodePort 类型的 Service,所以不能通过 SLB 或者 NodePort 访问。只能通过 ClusterIP 访问。而 ClusterIP 是不能直接对外暴露的,所以必须经过 Gateway 才能把用户的流量接入进来。本例就是使用 Istio 的 Gateway 承接 Knative 的南北流量(进和出)。如下图所示是 Knative 模型中流量的转发路径。用户发起的请求首先会打到 Gateway 上面,然后 Istio 通过 VirtualService 再把请求转发到具体的  Revision 上面。当然用户的流量还会经过 Knative 的 queue 容器才能真正转发到业务容器,关于这方面的细节我在后续的文章再进行详细的介绍。



所以想要访问 Knative 的服务首先要获取 Gateway 的 IP 地址,可以通过如下方式获取 Gateway 的 IP:


└─# kubectl get svc istio-ingressgateway --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*].ip}"39.97.31. 219
复制代码


前面也介绍了 Gateway 是通过 VirtualService 来进行流量转发的,这就要求访问者要知道目标服务的名字才行(域名),所以要先获取 helloworld-go 的域名, 注意下面这条命令中的 ${SVC_NAME} 需要替换成 helloworld-go ,这个名字必须要和 Knative Service 的名字一致,因为每一个 Service 都有一个唯一的名字。


└─# kubectl get route ${SVC_NAME} --output jsonpath="{.status.domain}"helloworld-go.default.example.com
复制代码


至此你已经拿到 IP 地址和 Hostname,可以通过 curl 直接发起请求:


└─# curl -H "Host: helloworld-go.default.example.com" "http://39.97.31. 219"<h1>helloworld-go-07-v3</h1>
复制代码


为了方便你进行测试,我提供了一个脚本 run-test.sh,你可以使用此脚本测试你自己的 Service , 你自己在测试的时候把 SVC_NAME 换成自己的 Service Name 就行了。


#!/bin/bash
SVC_NAME="helloworld-go"export INGRESSGATEWAY=istio-ingressgatewayexport IP_ADDRESS=$(kubectl get svc $INGRESSGATEWAY --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*]['ip']}")echo "IP_ADDRESS: ${IP_ADDRESS}"
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.domain}"`
kubectl get ksvc ${SVC_NAME} --output=custom-columns=NAME:.metadata.name,DOMAIN:.status.domaintime curl -H "Host: ${DOMAIN_NAME}" http://${IP_ADDRESS} -v
复制代码

缩容到零

刚刚部署完 Service 的时候 Knative 默认会创建出一个 Pod 提供服务,如果你超过即使秒没有访问 helloworld-go 这个服务那么这个 Pod 就会自动删除,此时就是缩容到零了。现在看一下 Pod 情况, 你可能会发现没有 Pod


└─# kubectl get pod -o wideNo resources found.
复制代码


现在执行一下 run-test.sh 发起一个请求到 Knative Service


└─# ./run-test.shIP_ADDRESS: 39.97.31. 219NAME            DOMAINhelloworld-go   helloworld-go.default.example.com* Rebuilt URL to: http://39.97.31. 219/*   Trying 39.97.31. 219...* TCP_NODELAY set* Connected to 39.97.31. 219 (39.97.31. 219) port 80 (#0)> GET / HTTP/1.1> Host: helloworld-go.default.example.com> User-Agent: curl/7.54.0> Accept: */*>< HTTP/1.1 200 OK< content-length: 28< content-type: text/html; charset=utf-8< date: Mon, 03 Jun 2019 03:47:58 GMT< server: istio-envoy< x-envoy-upstream-service-time: 2681<* Connection #0 to host 39.97.31. 219 left intact<h1>helloworld-go-07-v3</h1>real  0m2.775suser  0m0.007ssys  0m0.007s
复制代码


注意 run-test.sh 结果中,这面这一段:


real  0m2.775suser  0m0.007ssys  0m0.007s
复制代码


real 0m2.775s  意思意思是 curl 请求执行一共消耗了 2.775s , 也就是说 Knative 从零到 1 扩容 + 启动容器再到服务响应请求总共消耗了 2.775s (我之前的测试导致在 Node  上面缓存了镜像,所以没有拉镜像的时间)。可以看出来这个速度还是很快的。


再看一下 pod 数量, 你会发现此时 Pod 自动扩容出来了。并且 Pod 数量为零时发起的请求并没有拒绝链接。


└─# kubectl get podNAME                                              READY   STATUS    RESTARTS   AGEhelloworld-go-p9w6c-deployment-5dfdb6bccb-gjfxj   2/2     Running   0          31s
复制代码

按需分配,自动扩缩

helloworld-go 自动扩容测试


接下来再测试一下 Knative 按需扩容的功能。使用社区提供的 hey 进行测试。hey 有 Windows、Linux 和 Mac 的二进制可以在这里下载


使用这个命令测试之前需要在本机进行 Host 绑定,对于 helloworld-go 来说要把 helloworld-go 的域名绑定到 Istio Gateway 的 IP 上,/etc/hosts 添加如下配置


39.97.31. 219  helloworld-go.default.example.com
复制代码


如下所示 这条命令的意思是:


  • -z 30s 持续测试  30s

  • -c 50 保持每秒 50 个请求


测试结果如下:


└─# hey -z 30s -c 50 "http://helloworld-go.default.example.com/" && kubectl get pods
Summary: Total: 30.0407 secs Slowest: 0.1453 secs Fastest: 0.0266 secs Average: 0.0378 secs Requests/sec: 1323.2700
Total data: 1113056 bytes Size/request: 28 bytes
Response time histogram: 0.027 [1] | 0.038 [23584] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.050 [15839] |■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.062 [255] | 0.074 [30] | 0.086 [28] | 0.098 [14] | 0.110 [0] | 0.122 [0] | 0.133 [0] | 0.145 [1] |

Latency distribution: 10% in 0.0330 secs 25% in 0.0351 secs 50% in 0.0371 secs 75% in 0.0407 secs 90% in 0.0428 secs 95% in 0.0442 secs 99% in 0.0495 secs
Details (average, fastest, slowest): DNS+dialup: 0.0001 secs, 0.0266 secs, 0.1453 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0036 secs req write: 0.0000 secs, 0.0000 secs, 0.0009 secs resp wait: 0.0376 secs, 0.0266 secs, 0.1453 secs resp read: 0.0001 secs, 0.0000 secs, 0.0100 secs
Status code distribution: [200] 39752 responses

NAME READY STATUS RESTARTS AGEhelloworld-go-lq42n-deployment-68ddd64944-nkwpn 2/2 Running 0 77s
复制代码


回想一下刚才 helloworld-go.yaml 文件配置,已经设置了 autoscaling.knative.dev/target: "10" 这个 Annotation。这表示每一个 Pod 能够接受并发 10 个请求,而刚才并发请求数设置的是 50 所以理论上应该会创建出来 5 个 Pod?,


上面结果中最后一部分,是 kubectl get pods 的结果,如下所示:


NAME                                              READY   STATUS    RESTARTS   AGEhelloworld-go-lq42n-deployment-68ddd64944-nkwpn   2/2     Running   0          77s
复制代码


可以看到实际只有一个 Pod,为什么呢?这是因为虽然并发 50 ,但是每一个请求很快就结束了。看一下刚才测试的结果, 截取核心的一部分展示如下。可以看到最慢的一个请求 0.1453 秒就处理完了。而且 99% 的请求 RT 都没超过 0.0495 秒。


... ...  Total:  30.0407 secs  Slowest:  0.1453 secs  Fastest:  0.0266 secs  Average:  0.0378 secs  Requests/sec:  1323.2700... ... Latency distribution:  10% in 0.0330 secs  25% in 0.0351 secs  50% in 0.0371 secs  75% in 0.0407 secs  90% in 0.0428 secs  95% in 0.0442 secs  99% in 0.0495 secs... ...
复制代码


所以一秒内是可以完整的处理完 50 个请求的,也就不需要扩容了。


再换一个例子,让每一个请求处理的时间拉长一些看看效果。


autoscale-go 自动扩缩测试


如果单个请求处理的太快就不太好展示自动扩缩的效果,那么就让单条请求处理的时间稍微长一些。Knative 官方有一个 Autoscaler 的例子 , 这个例子中每一个请求会进行一些计算,把请求时间拉长,这样就能更容易的测试。你可以直接使用 registry.cn-hangzhou.aliyuncs.com/knative-sample/autoscale-go:0.1 这个镜像进行测试。


  • 编写 Knative Service 文件 autoscale-go.yaml 如下:


└─# cat autoscale-go.yamlapiVersion: serving.knative.dev/v1alpha1kind: Servicemetadata:  name: autoscale-go  namespace: defaultspec:  template:    metadata:      labels:        app: autoscale-go      annotations:        autoscaling.knative.dev/target: "10"    spec:      containers:        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/autoscale-go:0.1
复制代码


  • 部署 autoscale-go


└─# kubectl apply -f autoscale-go.yamlservice.serving.knative.dev/autoscale-go created
复制代码


run-test.shSVC_NAME 改成 autoscale-go 然后执行 run-test.sh ,如下:


└─# ./run-test.shIP_ADDRESS: 39.97.31. 219NAME           DOMAINautoscale-go   autoscale-go.default.example.com* Rebuilt URL to: http://39.97.31. 219/*   Trying 39.97.31. 219...* TCP_NODELAY set* Connected to 39.97.31. 219 (39.97.31. 219) port 80 (#0)> GET / HTTP/1.1> Host: autoscale-go.default.example.com> User-Agent: curl/7.54.0> Accept: */*>< HTTP/1.1 200 OK< content-length: 0< date: Mon, 03 Jun 2019 05:05:38 GMT< server: istio-envoy< x-envoy-upstream-service-time: 2912<* Connection #0 to host 39.97.31. 219 left intact
real 0m2.999suser 0m0.007ssys 0m0.008s
复制代码


可以看到 autoscale-go 已经可以提供服务了。


使用 hey 命令测试之前需要在本机进行 Host 绑定,对于 autoscale-go 来说要把 autoscale-go 的域名绑定到 Istio Gateway 的 IP 上,/etc/hosts 添加如下配置


39.97.31. 219  autoscale-go.default.example.com
复制代码


  • 使用 hey 进行测试:


└─# hey -z 30s -c 50 "http://autoscale-go.default.example.com?sleep=100&prime=10000&bloat=5" && kubectl get pods
Summary: Total: 30.1443 secs Slowest: 6.0173 secs Fastest: 0.1285 secs Average: 0.1717 secs Requests/sec: 290.4364
Total data: 875284 bytes Size/request: 99 bytes
Response time histogram: 0.128 [1] | 0.717 [8704] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 1.306 [0] | 1.895 [0] | 2.484 [0] | 3.073 [0] | 3.662 [0] | 4.251 [0] | 4.840 [0] | 5.428 [0] | 6.017 [50] |

Latency distribution: 10% in 0.1329 secs 25% in 0.1356 secs 50% in 0.1383 secs 75% in 0.1413 secs 90% in 0.1435 secs 95% in 0.1450 secs 99% in 0.1574 secs
Details (average, fastest, slowest): DNS+dialup: 0.0002 secs, 0.1285 secs, 6.0173 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0036 secs req write: 0.0000 secs, 0.0000 secs, 0.0011 secs resp wait: 0.1713 secs, 0.1283 secs, 5.9780 secs resp read: 0.0001 secs, 0.0000 secs, 0.0066 secs
Status code distribution: [200] 8755 responses

NAME READY STATUS RESTARTS AGEautoscale-go-zqcm2-deployment-6cf67b4545-2f2ck 2/2 Running 0 28sautoscale-go-zqcm2-deployment-6cf67b4545-4xc9s 2/2 Running 0 26sautoscale-go-zqcm2-deployment-6cf67b4545-6wt8r 2/2 Running 0 28sautoscale-go-zqcm2-deployment-6cf67b4545-hdbnc 2/2 Running 0 30sautoscale-go-zqcm2-deployment-6cf67b4545-w9pm7 2/2 Running 0 28s
复制代码


可以看到此时 Knative 自动扩容出来了 5 个 Pod 处理请求。

总结

至此你已经完成了和 Knative Serving 的首次约会,也看到了这位白富美的真容。通过本篇文章你应该掌握


以下几点:


  • 在阿里云容器服务上面快速搭建 Knative 集群的方法

  • 理解 Knative 从零到一的含义,并且能够基于 helloworld-go 例子演示这个过程

  • 理解 Knative 按需扩缩容的含义,并且能够基于 autoscale-go 例子演示这个过程

  • 理解 Knative 按需扩容的原理,按需扩容不单单是用户发起 50 个并发、每一个 Pod 最多能够并发处理 10 个请求就一定需要创建  5  个 Pod 出来。如果请求的处理时间很短,一个 Pod 就能满足的情况下 Knative 是不会做无用的扩容的

Next

通过前面的例子相信你已经对  Knative Serving 有了更深刻的理解。但是你可能也会产生很多疑问:


  • 这里面只是展示了 Serving 的 Hello World,Eventing、Build 能不能也来一些这样的例子?

  • 为什么上面例子的域名都是 xx.example.com, 我能不能换成自己的域名?

  • Knative 流量转发的细节是怎样的?queue 的作用是什么?

  • 在前面的例子中,容器都是监听 8080 端口的,如果容器监听的不是 8080 端口那么需要怎么配置自定义的端口?

  • Knative 默认扩缩容的策略是怎样的?

  • hey 这个压测工具虽然展示了 Knative 自动扩容的过程,但是还不够直观、还有没有更好的工具来验证并发数、服务响应时间和 Knative 自动扩容 Pod 之间的关系?

  • 如果 Service 有更新,如何进行流量灰度?


所有这些疑问我会在后续的一系列文章中都会一一讲解到,敬请期待后续的文章。


2019-06-12 08:008784
用户头像
阿里云容器平台 ACK,企业云原生转型最佳搭档

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

关注

评论

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

书单 | “阿里云数字新基建”系列丛书全家福来啦!

博文视点Broadview

Serverless 让我们的运维更轻松

领创集团Advance Intelligence Group

#Serverless

什么是代码加密?基于云效 Codeup的代码仓库加密是如何实现的

阿里云云效

云计算 阿里云 代码管理 Codeup 代码加密

用css制作旋转的立方体

云智慧AIOps社区

CSS 前端 大前端 3D css特效

【愚公系列】2022年04月 二十三种设计模式(零)-简单工厂模式(Simple Factory Pattern)

愚公搬代码

4月月更

TiDB 在连锁快餐企业丨海量交易与实时分析的应用探索

TiDB 社区干货传送门

2022年中国茶饮产业洞察

易观分析

茶饮 产业洞察

豆瓣评分9.0,时隔6年,“Linux命令行圣经”新版终于来了!

图灵教育

Linux shell脚本编程

当 API 成为服务,我们将连接一切!

鼎道智联

《数字经济全景白皮书》Z世代用户洞察篇(2)重磅发布!

易观分析

Z世代

组合式应用新利器?SaaS新时代事件网格如何解决集成标准化问题

华为云开发者联盟

Serverless 无服务器 事件网格 组合式应用

GPU底层技术、全球市场格局分析(中)

Finovy Cloud

人工智能 云计算 云服务器 GPU服务器 GPU算力

增长270%!PWA 在 Chrome 平台迎来井喷式增长

鼎道智联

轻量级兼顾本地体验,PWA应用到底有多卷?

鼎道智联

【数据安全与流通专题】星环科技春季新品发布周

星环科技

图数据库|正反向边的最终一致性——TOSS 介绍

NebulaGraph

图数据库 知识图谱

NFT数字藏品交易平台系统开发搭建

薇電13242772558

NFT 数字藏品

项目中如何优雅的消除if-else

Rubble

4月日更 4月月更

什么时候需要使用CRM系统?

低代码小观

CRM 客户关系管理 低代码开发 CRM系统 客户关系管理系统

LSM-Tree - LevelDb了解和实现

懒时小窝

LSM树 LSM-Tree

火山引擎 MARS X 今日头条 | 大型App高效协同开发,实现研发流程自动化

字节跳动终端技术

今日头条 字节跳动 研发 火山引擎MARS

TASKCTL 调度设计器作业属性的应用

敏捷调度TASKCTL

分布式 元数据区 ETL 自动化运维 任务调度器

大咖说·智篆商业|颜杰华:数字经济时代,不确定性中的确定性

大咖说

阿里巴巴 数字经济数字时代 数智化 存量时代

华为云物联网高级攻城狮的4年配置中心实践分享

华为云开发者联盟

开源 DevOps 配置中心 Apollo 业务配置

基于LAXCUS开发分布式视频转码应用

LAXCUS分布式操作系统

分布式计算 分布式应用 视频转码

阿里云代码托管平台,不限容量,免费使用

阿里云云效

云计算 阿里云 代码管理 代码托管 阿里云代码托管

TASKCTL 变量的函数表达式运算

敏捷调度TASKCTL

批量任务 调度引擎 ETL 自动化运维 调度任务

Android C/C++层hook和java层hook原理以及比较

云智慧AIOps社区

Java android 开发技能 hook

人人都可以学会的产品手册制作方法

小炮

产品宣传手册

MySQL事务并发带来的问题以及其解决方案分析

乌龟哥哥

4月月更

HLP分词后的文本如何在web端高亮显示

lo

前端 4月月更

Knative 初体验:Serving Hello World_云原生_阿里云容器平台_InfoQ精选文章