写点什么

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

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

关注

评论

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

Docker的安装及日常命令的使用

Geek_982ff5

6月月更

LabVIEW控制Arduino采集LM35温度传感器数值(基础篇—12)

不脱发的程序猿

单片机 LabVIEW Arduino LIAT 采集LM35温度传感器数值

SPDK对接Ceph性能优化

天翼云开发者社区

开发工具

OceanBase 成为信通院首批可信开源社区、可信开源项目

OceanBase 数据库

中国信通院 OceanBase 开源

LabVIEW控制Arduino采集热敏电阻温度数值(基础篇—13)

不脱发的程序猿

单片机 LabVIEW Arduino LIAT 采集热敏电阻温度数值

LabVIEW控制Arduino采集光敏电阻数值(基础篇—14)

不脱发的程序猿

单片机 LabVIEW Arduino LIAT 采集光敏电阻数值

当我们进行性能优化,我们在优化什么(LightHouse优化实操)

刘悦的技术博客

性能优化 前端 优化 性能优化手册

王者荣耀商城异地多活架构设计

流火

阿里云刘珅孜:云游戏带来的启发——端上创新

阿里云弹性计算

gpu 元宇宙 云游戏

从零开始搭建vue-cli项目

小恺

6月月更

一篇文章带你彻底了解哈希表

武师叔

算法 哈希表 6月月更

架构实战营模块七 作业

库尔斯

架构实战营

利用Python实现自动操作Excel文件

弑着去忘记う

高性能API网关Kong介绍

天翼云开发者社区

都有哪些较好用的项目管理软件?

优秀

项目管理 项目管理软件

云原生训练营大作业

jjn0703

华为云GaussDB首席架构师冯柯:摘取皇冠上的明珠,华为云数据库的创新与探索

华为云开发者联盟

数据库 华为云 GaussDB 国产数据库

企业知识管理体系怎么搭建和运营?

小炮

中国标准走进国际视野,首个零信任国际标准的诞生往事

脑极体

盘点校招面试 HR 可能会问到的问题

宇宙之一粟

面试 校招 6月月更

架构训练 模块七

小马

#架构训练营

移动平台打造新生态 | 助力企业跨业务、一站式、全场景的系统建设

BeeWorks

科创人·神州数码集团CIO沈旸:最佳实践模式正在失灵,开源加速分布式创新

科创人

开始使用DOCKER COMPOSE V2

mengzyou

Docker DevOps Docker-compose

后端解构复习(一)

卢卡多多

技术栈 6月月更

MPLS协议简述

穿过生命散发芬芳

6月月更 MPLS

应用流程挖掘,发现潜在RPA可实施的场景,助力银行优化业务流程

易观分析

RPA

架构实战营模块七作业

天琪实刚亮

动态路由协议之RIP协议,最古老的距离矢量协议!

wljslmz

IP 网络工程师 动态路由 6月月更 路由协议

@全球开发者|首届云原生边缘计算峰会邀您共话

华为云开发者联盟

云原生 边缘计算 kubeedge

Java27岁了——一次争执引起的Java内卷生涯

写代码两年半

javase Java EE 6月月更

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