本文選自 《Knative 雲原生應用開發指南》 。
通過前面兩章的學習你已經掌握了很多 Knative 的理論知識,基于這些知識你應該對 Knative 是誰、它來自哪裡以及它要做什麼有了一定的認識。可是即便如此你可能還是會有一種猶抱琵琶半遮面,看不清真容的感覺,這就好比紅娘拿姑娘的 100 張生活照給你看也不如你親自去見一面。按常理出牌,一般到這個階段就該 Hello World 出場了。本篇文章就通過一個 Hello World 和 Knative 來一個“約會”,讓你一睹 Knative 這位白富美的真容。
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/v1
kind: Service
metadata:
name: helloworld-go
spec:
template:
metadata:
labels:
app: helloworld-go
annotations:
autoscaling.knative.dev/target: "10"
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:160e4dc8
ports:
- name: http1
containerPort: 8080
env:
- name: TARGET
value: "World"
注意其中
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.yaml
service.serving.knative.dev/helloworld-go created
- 檢視 helloworld-go pod
└─# kubectl get pod
NAME READY STATUS RESTARTS AGE
helloworld-go-7n4dm-deployment-65cb6d9bf-9njqx 2/2 Running 0 8s
到此 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.106.232.122
前面也介紹了 Gateway 是通過 VirtualService 來進行流量轉發的,這就要求通路者要知道目标服務的名字才行(域名),是以要先擷取 helloworld-go 的域名, 注意下面這條指令中的
${SVC_NAME}
需要替換成
helloworld-go
,這個名字必須要和 Knative Service 的名字一緻,因為每一個 Service 都有一個唯一的名字。
└─# kubectl get route ${SVC_NAME} --output jsonpath="{.status.domain}"
helloworld-go.default.knative.kuberun.com
至此你已經拿到 IP 位址和 Hostname,可以通過 curl 直接發起請求:
└─# curl -H "Host: helloworld-go.default.knative.kuberun.com" "http://39.106.232.122"
Hello World!
縮容到零
剛剛部署完 Service 的時候 Knative 預設會建立出一個 Pod 提供服務,如果你超過 90 秒沒有通路 helloworld-go 這個服務那麼這個 Pod 就會自動删除,此時就是縮容到零了。現在看一下 Pod 情況, 你可能會發現沒有 Pod
└─# kubectl get pod -o wide
No resources found.
└─# time curl -H "Host: helloworld-go.default.knative.kuberun.com" "http://39.106.232.122"
Hello World!
real 0m2.775s
user 0m0.007s
sys 0m0.007s
注意結果中,這面這一段:
real 0m2.775s
user 0m0.007s
sys 0m0.007s
real 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
按需配置設定,自動擴縮
接下來再測試一下 Knative 按需擴容的功能。使用社群提供的 hey 進行測試。hey 有 Windows、Linux 和 Mac 的二進制可以在
這裡下載下傳使用這個指令測試之前需要在本機進行 Host 綁定,對于 helloworld-go 來說要把 helloworld-go 的域名綁定到 Istio Gateway 的 IP 上,/etc/hosts 添加如下配置
39.106.232.122 helloworld-go.default.knative.kuberun.com
如下所示 這條指令的意思是:
-
持續測試 30s-z 30s
-
保持每秒 50 個請求-c 50
測試結果如下:
└─# hey -z 30s -c 50 "http://helloworld-go.default.knative.kuberun.com/" && kubectl get pods
Summary:
Total: 30.0388 secs
Slowest: 2.6178 secs
Fastest: 0.0276 secs
Average: 0.0434 secs
Requests/sec: 1152.3116
Total data: 449982 bytes
Size/request: 13 bytes
Response time histogram:
0.028 [1] |
0.287 [34559] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
0.546 [4] |
0.805 [0] |
1.064 [0] |
1.323 [0] |
1.582 [0] |
1.841 [0] |
2.100 [0] |
2.359 [0] |
2.618 [50] |
Latency distribution:
10% in 0.0320 secs
25% in 0.0346 secs
50% in 0.0380 secs
75% in 0.0424 secs
90% in 0.0480 secs
95% in 0.0532 secs
99% in 0.0775 secs
Details (average, fastest, slowest):
DNS+dialup: 0.0001 secs, 0.0276 secs, 2.6178 secs
DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0185 secs
req write: 0.0000 secs, 0.0000 secs, 0.0023 secs
resp wait: 0.0431 secs, 0.0273 secs, 2.5646 secs
resp read: 0.0001 secs, 0.0000 secs, 0.0195 secs
Status code distribution:
[200] 34614 responses
NAME READY STATUS RESTARTS AGE
helloworld-go-7n4dm-deployment-65cb6d9bf-8x6b5 2/2 Running 0 29s
helloworld-go-7n4dm-deployment-65cb6d9bf-dvrm5 2/2 Running 0 27s
helloworld-go-7n4dm-deployment-65cb6d9bf-f7db8 2/2 Running 0 30s
helloworld-go-7n4dm-deployment-65cb6d9bf-fsn2z 2/2 Running 0 29s
helloworld-go-7n4dm-deployment-65cb6d9bf-rmrh2 2/2 Running 0 29s
回想一下剛才 helloworld-go.yaml 檔案配置,已經設定了
autoscaling.knative.dev/target: "10"
這個 Annotation。這表示每一個 Pod 能夠接受并發 10 個請求,而剛才并發請求數設定的是 50 是以理論上應該會建立出來 5 個 Pod?,
上面結果中最後一部分,是
kubectl get pods
的結果,如下所示:
NAME READY STATUS RESTARTS AGE
helloworld-go-7n4dm-deployment-65cb6d9bf-8x6b5 2/2 Running 0 29s
helloworld-go-7n4dm-deployment-65cb6d9bf-dvrm5 2/2 Running 0 27s
helloworld-go-7n4dm-deployment-65cb6d9bf-f7db8 2/2 Running 0 30s
helloworld-go-7n4dm-deployment-65cb6d9bf-fsn2z 2/2 Running 0 29s
helloworld-go-7n4dm-deployment-65cb6d9bf-rmrh2 2/2 Running 0 29s
可以看到此時 Knative 自動擴容出來了 5 個 Pod 處理請求。
總結
至此你已經完成了和 Knative Serving 的首次約會,也看到了這位白富美的真容。通過本篇文章你應該掌握
以下幾點:
- 了解 Knative 從零到一的含義,并且能夠基于 helloworld-go 例子示範這個過程
- 了解 Knative 按需擴縮容的含義,并且能夠基于 autoscale-go 例子示範這個過程
- 了解 Knative KPA 按需擴容的原理
參考文檔
“ 阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”