【QCon】精华内容上线92%,全面覆盖“人工智能+”的典型案例!>>> 了解详情
写点什么

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

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

关注

评论

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

Map-Reduce 思想在 ABAP 编程中的一个实际应用案例

Jerry Wang

mapreduce abap CRM系统 企业级应用 3月月更

企业帮助中心的搭建步骤

小炮

帮助中心

资产管理系统开发解决方案

低代码小观

企业管理 资产管理 CRM系统 企业管理软件

全新 OceanBase 社区版开发者中心 ODC 核心功能解读

OceanBase 数据库

oceanbase OceanBase 开源 OceanBase 社区版

详解4种微服务框架接入Istio方案

华为云开发者联盟

微服务 k8s istio 服务治理 微服务框架

2022年济南正规等保测评公司名单(排名不分先后)

行云管家

等保 等保测评 等保2.0 济南

两小时,掌握四个数字化工具!

明道云

Apifox才是最强Postman替代品,看看国产软件到底有对牛!

Liam

后端 Postman API swagger java开发工具

【面试-性能测试工程师】如何在项目中练手性能测试,莫慌

测试猿温大大

面试 性能测试 测试工程师

HSC推出「万物生长计划」 赋能虎符交易所HOO新应用场景

区块链前沿News

Hoo 虎符交易所 虎符智能链

ZEGO 自研客户端配置管理系统 —— 云控

ZEGO即构

后台开发 客户端配置 音视频架构

国内首届DataOps+MLOps meetup回顾

星策开源社区

人工智能 机器学习 DevOps Meetup MLOps

恒源云(Gpushare)_【存储优化】/hy-tmp可以扩/缩容啦

恒源云

云计算 存储 tmp

测试开发【Mock平台】01开篇:平台设计和整体规划

MegaQi

测试开发 测试平台开发教程 测试干货

零基础学编程?从这本豆瓣评分9.2的入门级神作开始

图灵社区

Python 零基础

抓到Netty一个隐藏很深的内存泄露Bug | 详解Recycler对象池的精妙设计与实现

bin的技术小屋

中间件 池化技术 java netty 内存池

【直播回顾】OpenHarmony知识赋能第四期第二课——GPIO驱动开发

OpenHarmony开发者

OpenHarmony GPIO 驱动开发

数据对接 - 大屏云极简使用手册

shulinwu

可视化 数据可视化 大屏可视化 数据可视化控件 大屏

OceanBase 存储引擎详解

OceanBase 数据库

鸿蒙开发必备书籍【收藏】

坚果

鸿蒙 3月月更

OceanBase 在线体验环境,现已上线!

OceanBase 数据库

Linux性能优化—内存实战篇

Linux服务器开发

性能优化 内存管理 Linux服务器开发 Linux内核 内核源码

【模拟面试-半年实习】项目经验不突出,如何让面试官「眼前一亮」

测试猿温大大

面试 电商 测试工程师

【模拟面试-4年实习】工作4年业务做的不深入,如何突破

测试猿温大大

面试 测试工程师

【三级等保】三级等保服务费用一年大概要多少?一年需要测评一次嘛?

行云管家

网络安全 等保 等级保护 三级等保

Meetup预告| AIOps指标相关算法体系分享

云智慧AIOps社区

机器学习 大数据 算法 AIOPS 智能运维

抖音获客,抖音SEO询盘系统源码开发,思路分享,开发者掏心窝的说......

yunluohd168

短视频获客 抖音获客系统源码 大数据获客 抖音SEO获客源码

并发异步编程之争:协程(asyncio)到底需不需要加锁?(线程/协程安全/挂起/主动切换)Python3

刘悦的技术博客

多线程 协程 Python3 协程原理

一个 测试岗 面了 30 多人,不能再真实了...

六十七点五

软件测试 面试题 自动化测试 经验总结 测试工程师

【模拟面试-2年测试工程师】「脱产1年」如何能拿下offer

测试猿温大大

一站式运维管理工具平台 OCP 到底有多好用,看这篇文章就够了!

OceanBase 数据库

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