Knative 初体验:Serving Hello World
通過前面兩章的學習你已經掌握了很多 Knative 的理論知識,基于這些知識你應該對 Knative 是誰、它來自哪里以及它要做什么有了一定的認識??墒羌幢闳绱四憧赡苓€是會有一種猶抱琵琶半遮面,看不清真容的感覺,這就好比紅娘拿姑娘的 100 張生活照給你看也不如你親自去見一面。按常理出牌,一般到這個階段就該 Hello World 出場了。本篇文章就通過一個 Hello World 和 Knative 來一個“約會”,讓你一睹 Knative 這位白富美的真容。
安裝 Knative
Knative 社區提供的安裝步驟見這里,整個流程大概包含如下三個部分:
- 準備 kubernetes 環境(你可以在阿里云容器服務中快速創建一個 kubernetes 集群?)
- 安裝istio
- 安裝 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/v1alpha1 kind: Service metadata:name: helloworld-gonamespace: default spec:template:metadata:labels:app: helloworld-goannotations:autoscaling.knative.dev/target: "10"spec:containers:- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/simple-app:07env:- name: SIMPLE_MSGvalue: "helloworld-go-07"注意其中?autoscaling.knative.dev/target: "10"?這個 Annotation 是設置每一個 Pod 的可處理并發請求數 10 ,Knative KPA 自動伸縮的時候會根據當前總請求的并發數和?autoscaling.knative.dev/target?自動調整 Pod 的數量,從而達到自動擴縮的目的。更多的策略信息我會在后續的文章中一一介紹。
現在使用 kubectl 命令把 yaml 提交到 Kubernetes 中:
- 部署 helloworld-go
- 查看 helloworld-go pod
到此 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/bashSVC_NAME="helloworld-go" export INGRESSGATEWAY=istio-ingressgateway export 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.domain time curl -H "Host: ${DOMAIN_NAME}" http://${IP_ADDRESS} -v縮容到零
剛剛部署完 Service 的時候 Knative 默認會創建出一個 Pod 提供服務,如果你超過即使秒沒有訪問 helloworld-go 這個服務那么這個 Pod 就會自動刪除,此時就是縮容到零了?,F在看一下 Pod 情況, 你可能會發現沒有 Pod
└─# kubectl get pod -o wide No resources found.現在執行一下?run-test.sh?發起一個請求到 Knative Service
└─# ./run-test.sh IP_ADDRESS: 39.97.31. 219 NAME DOMAIN helloworld-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.775s user 0m0.007s sys 0m0.007s注意?run-test.sh?結果中,這面這一段:
real 0m2.775s user 0m0.007s sys 0m0.007sreal 0m2.775s?意思意思是?curl?請求執行一共消耗了?2.775s?, 也就是說 Knative 從零到 1 擴容 + 啟動容器再到服務響應請求總共消耗了?2.775s?(我之前的測試導致在 Node 上面緩存了鏡像,所以沒有拉鏡像的時間)??梢钥闯鰜磉@個速度還是很快的。
再看一下 pod 數量, 你會發現此時 Pod 自動擴容出來了。并且 Pod 數量為零時發起的請求并沒有拒絕鏈接。
└─# kubectl get pod NAME READY STATUS RESTARTS AGE helloworld-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 添加如下配置
如下所示 這條命令的意思是:
- -z 30s?持續測試 30s
- -c 50?保持每秒 50 個請求
測試結果如下:
└─# hey -z 30s -c 50 "http://helloworld-go.default.example.com/" && kubectl get podsSummary:Total: 30.0407 secsSlowest: 0.1453 secsFastest: 0.0266 secsAverage: 0.0378 secsRequests/sec: 1323.2700Total data: 1113056 bytesSize/request: 28 bytesResponse 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 secs25% in 0.0351 secs50% in 0.0371 secs75% in 0.0407 secs90% in 0.0428 secs95% in 0.0442 secs99% in 0.0495 secsDetails (average, fastest, slowest):DNS+dialup: 0.0001 secs, 0.0266 secs, 0.1453 secsDNS-lookup: 0.0000 secs, 0.0000 secs, 0.0036 secsreq write: 0.0000 secs, 0.0000 secs, 0.0009 secsresp wait: 0.0376 secs, 0.0266 secs, 0.1453 secsresp read: 0.0001 secs, 0.0000 secs, 0.0100 secsStatus code distribution:[200] 39752 responsesNAME READY STATUS RESTARTS AGE helloworld-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 AGE helloworld-go-lq42n-deployment-68ddd64944-nkwpn 2/2 Running 0 77s可以看到實際只有一個 Pod,為什么呢?這是因為雖然并發 50 ,但是每一個請求很快就結束了。看一下剛才測試的結果, 截取核心的一部分展示如下??梢钥吹阶盥囊粋€請求?0.1453?秒就處理完了。而且 99% 的請求 RT 都沒超過?0.0495?秒。
... ...Total: 30.0407 secsSlowest: 0.1453 secsFastest: 0.0266 secsAverage: 0.0378 secsRequests/sec: 1323.2700 ... ... Latency distribution:10% in 0.0330 secs25% in 0.0351 secs50% in 0.0371 secs75% in 0.0407 secs90% in 0.0428 secs95% in 0.0442 secs99% 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 如下:
- 部署 autoscale-go
run-test.sh 中?SVC_NAME?改成??autoscale-go?然后執行 run-test.sh ,如下:
└─# ./run-test.sh IP_ADDRESS: 39.97.31. 219 NAME DOMAIN autoscale-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 intactreal 0m2.999s user 0m0.007s sys 0m0.008s可以看到 autoscale-go 已經可以提供服務了。
使用 hey 命令測試之前需要在本機進行 Host 綁定,對于 autoscale-go 來說要把 autoscale-go 的域名綁定到 Istio Gateway 的 IP 上,/etc/hosts 添加如下配置
- 使用 hey 進行測試:
可以看到此時 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 有更新,如何進行流量灰度?
所有這些疑問我會在后續的一系列文章中都會一一講解到,敬請期待后續的文章。
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的Knative 初体验:Serving Hello World的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里CTO张建锋:明年双11将大规模应用
- 下一篇: 阿里文娱首次公开!AI 如何对爆款内容未