阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

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

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

关注

评论

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

云手机为什么能避免海外社媒账号被封?

Ogcloud

TikTok 云手机 海外云手机 tiktok云手机 云手机海外版

PHP调用API接口的方法及实现

Noah

保姆级教程:教你UniMRCP对接华为云ASR(Linux版)

华为云开发者联盟

开源 开发 华为云 华为云开发者联盟

如何培养高质量应用型医学人才, “课-训-赛”育人才系列第二场直播活动圆满结束

ModelWhale

人才培养 数据科学 复合型人才 医学 应用型人才

我们所面临的云原生安全问题

德迅云安全杨德俊

使用云手机提升WhatsApp使用体验

Ogcloud

云手机 海外云手机 云手机海外版 国外云手机 电商云手机

数字化与敏捷的关系(第2部分)

ShineScrum捷行

深度|庖丁解InnoDB之Buffer Pool

阿里云瑶池数据库

数据库 云计算 阿里云 polarDB

HashData的湖仓一体思考:非结构化数据支持(Directory Table等)讲解及演示

酷克数据HashData

postgresql AI 湖仓一体

网心科技入选“2023年中国云生态创新应用技术产品”

网心科技

云计算 边缘计算 网心科技

网心科技入选“2023年广东省名优高新技术产品”名单

网心科技

云计算 边缘计算 网心科技

QCA9882, QCA9880, and MT7915 WiFi cards for OpenWrt:What are the difference?

wallyslilly

QCA9880

与鲸同行,智领未来!和鲸科技高校市场渠道招募启动会顺利召开

ModelWhale

人工智能 软件 数据科学 渠道招募 高校市场

有了这 4 款脚本工具, 再也不怕写烂SQL了!!!

Java技术精选

NFTScan 正式上线 Polygon zkEVM NFTScan 浏览器和 NFT API 数据服务

NFT Research

NFT\ NFTScan

云手机:实现便携与安全的双赢

Ogcloud

云手机 海外云手机 云手机海外版 国外云手机 电商云手机

深入探讨iOS开发:从创建第一个iOS程序到纯代码实现全面解析

雪奈椰子

AutoMQ 社区双周精选第九期(2024.03.11~2024.03.22)

AutoMQ

大数据 kafka 云原生 AutoMQ

Go代码优化实用指南

俞凡

golang

macOS Sonoma 14.4.1 (23E224) 正式版发布,ISO、IPSW、PKG 下载

sysin

macos Sonoma macOS Sonoma

青亦学爬虫:根据淘宝天猫商品链接封装淘宝天猫商品详情数据接口

tbapi

淘宝API接口 淘宝商品详情接口 天猫商品详情接口 淘宝数据爬虫 天猫数据爬虫

软件测试学习笔记丨Allure2 失败重试功能应用场景

测试人

软件测试

无人不识又无人不迷糊的this

华为云开发者联盟

Java JavaScript 开发 华为云 华为云开发者联盟

野心、梦想与科幻——浅谈外星殖民与软件工程

博文视点Broadview

那位拿了多个Offer的大佬分享了最新Go面经

王中阳Go

Go 后端 Go 面试题 面经 后端 大厂

数字人交互机:提升社区参与度的智能助手!

青否数字人

How Big Data Works

Jackchang234987

基于 NVIDIA Megatron-Core 的 MoE LLM 实现和训练优化

阿里云大数据AI技术

人工智能 模型训练 大模型 LLM

软件测试学习笔记丨Allure2报告中添加用例支持tags标签

测试人

软件测试

一文带你完整了解Go语言IO基础库

百度Geek说

go语言

AI时代来临我们要如何面对?

小魏写代码

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