前一篇文章已經介紹了Build的使用方式和原理,本文將緊接上文介紹Serving。
簡而言之,Serving 提供了Serverless應用或函數(Function)的部署能力,並通過 istio 實現服務管理,提供容器擴縮容能力。接管應用的部署和運維,節省開發者在應用部署和運維上消耗的精力。同時,在應用運維方面,Serving提供了根據應用訪問量自動擴縮容的能力,在沒有訪問的時候能把應用實例縮減到0,本文通過將Serving部署到K8s平臺之上試用,以講解這一組件的原理。
Serving部署
安裝istio
安裝Serving之前,我們需要先安裝istio,這裏使用Knative提供的簡易方式安裝。
kubectl apply --filename https://raw.githubusercontent.com/knative/serving/release-0.6/third_party/istio-1.1.3/istio-crds.yaml \
--filename https://raw.githubusercontent.com/knative/serving/release-0.6/third_party/istio-1.1.3/istio.yaml
安裝Serving
Serving可以單獨部署,但是也可以和Build一起部署,我們先來看Serving單獨部署模式:
kubectl apply --selector knative.dev/crd-install=true \
--filename https://github.com/knative/serving/releases/download/v0.6.0/serving.yaml --selector networking.knative.dev/certificate-provider!=cert-manager
執行成功以後,可以通過以下命令查看:
kubectl get pods --namespace knative-serving
當pod全部正常執行後,我們就可以開始使用Serving。
Serving概念和試用
概念
Knative把應用裏的所有能力全都放到統一的CRD資源中管理—Service。這裏的Service與K8s原生用戶訪問的Service不同,這是Knative的自定義資源,管理Knative應用的整個生命週期。
-
Service:service.serving.knative.dev資源管理着工作負載的整個生命週期。它控制其他對象(Route、Configration、Revison)的創建,並確保每次對Service的更新都作用到其他對象。
-
Route: route.serving.knative.dev資源將網絡端點映射到一個或多個Revision。可以通過配置Route實現多種流量管理方式,包括部分流量和命名路由。
-
Configuration:configuration.serving.knative.dev資源保持部署所需的狀態。它提供了代碼和配置之間的清晰分離,並遵循十二要素應用程序方法。修改Configuration將創建新的Revision。
-
Revision:revision.serving.knative.dev資源是對工作負荷所做的每個修改的代碼和配置的時間點快照。修訂是不變的對象,只要有用就可以保留。Revision可以根據進入的流量自動擴縮容。
樣例
現在讓我們來看一個最簡單的Knative Service樣例:
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
name: autoscale-go
namespace: default
spec:
template:
metadata:
annotations:
# Target 10 in-flight-requests per pod.
autoscaling.knative.dev/target: "10"
spec:
containers:
- image: gcr.io/knative-samples/autoscale-go:0.1
可以發現,一個Service相當簡潔,在最簡單的模式下只需要填寫一個鏡像即可,其他都會默認填充。現在把這個樣例創建到集羣中,並查看它自動創建的資源。
kubectl apply --filename https://raw.githubusercontent.com/knative/docs/release-0.6/docs/serving/samples/autoscale-go/service.yaml
kubectl get serving.knative.dev
創建Service之後,自動創建Configuration、Route、Revision、Deployment等資源。
自動擴縮容
接下來測試Service的自動擴縮容功能,在上文例子中,annotations裏面設置了每個Pod的請求併發數爲10。
INGRESSGATEWAY=istio-ingressgateway
export IP_ADDRESS=`kubectl get svc $INGRESSGATEWAY --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*].ip}"`
hey -z 30s -c 50 \
-host "autoscale-go.default.example.com" \
"http://${IP_ADDRESS?}?sleep=100&prime=10000&bloat=5" \
&& kubectl get pods
Summary:
Total: 30.3379 secs
Slowest: 0.7433 secs
Fastest: 0.1672 secs
Average: 0.2778 secs
Requests/sec: 178.7861
Total data: 542038 bytes
Size/request: 99 bytes
Response time histogram:
0.167 [1] \|
0.225 [1462] \|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
0.282 [1303] \|■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.340 [1894] \|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
0.398 [471] \|■■■■■■■■■■
0.455 [159] \|■■■
0.513 [68] \|■
0.570 [18] \|
0.628 [14] \|
0.686 [21] \|
0.743 [13] \|
Latency distribution:
10% in 0.1805 secs
25% in 0.2197 secs
50% in 0.2801 secs
75% in 0.3129 secs
90% in 0.3596 secs
95% in 0.4020 secs
99% in 0.5457 secs
Details (average, fastest, slowest):
DNS+dialup: 0.0007 secs, 0.1672 secs, 0.7433 secs
DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs
req write: 0.0001 secs, 0.0000 secs, 0.0045 secs
resp wait: 0.2766 secs, 0.1669 secs, 0.6633 secs
resp read: 0.0002 secs, 0.0000 secs, 0.0065 secs
Status code distribution:
[200] 5424 responses NAME READY STATUS
RESTARTS AGE
autoscale-go-00001-deployment-78cdc67bf4-2w4sk 3/3 Running 0 26s
autoscale-go-00001-deployment-78cdc67bf4-dd2zb 3/3 Running 0 24s
autoscale-go-00001-deployment-78cdc67bf4-pg55p 3/3 Running 0 18s
autoscale-go-00001-deployment-78cdc67bf4-q8bf9 3/3 Running 0 1m
autoscale-go-00001-deployment-78cdc67bf4-thjbq 3/3 Running 0 26s
以50qps去訪問autoscale-go時,autoscale-go自動從1個實例擴展到5個實例, 剛好和我們設置的併發匹配。
當我們等待一段時間不訪問autoscale-go後再去獲取實例數量,可以看到實例數被縮減到0。
kubectl get pods
NAME READY STATUS RESTARTS AGE
多版本管理
這次,換個應用,我們先創建一個Service:
cat > stock.yaml << EOF
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
name: stock-service-example
namespace: default spec:
template:
metadata:
name: stock-service-example-first
spec:
containers:
- image: ${REPO}/rest-api-go
env:
- name: RESOURCE
value: stock
readinessProbe:
httpGet:
path: /
initialDelaySeconds: 0
periodSeconds: 3
traffic:
- tag: current
revisionName: stock-service-example-first
percent: 100
- tag: latest
latestRevision: true
percent: 0
EOF
kubectl create -f stock.yaml
這個Service配置了流量信息,但是第一次創建時,我們先把所有的流量都導到Revision:stock-service-example-first上。
之後修改Service,另起創建新的Revision,並將一半流量切換到新的Revision。
cat > stock.yaml << EOF
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
name: stock-service-example
namespace: default
spec:
template:
metadata:
name: stock-service-example-second
spec:
containers:
- image: ${REPO}/rest-api-go
env:
- name: RESOURCE
value: share
readinessProbe:
httpGet:
path: /
initialDelaySeconds: 0
periodSeconds: 3
traffic:
- tag: current
revisionName: stock-service-example-first
percent: 50
- tag: candidate
revisionName: stock-service-example-second
percent: 50
- tag: latest
latestRevision: true
percent: 0
EOF
kubectl create -f stock.yaml
接着,我們通過域名訪問Service,多次的結果分別爲Welcome to the share app! 或者 Welcome to the stock app!,比例大致爲各一半。
curl --header "Host: stock-service-example.default.example.com"
http://${INGRESS_IP}
Serving原理
Serving總共有5個組件,其中4個在knative-serving這個namespace下面,是controller 、webhook 、autoscaler、activator這四個組件;還有一個queue, 運行在每個應用的pod裏,作爲pod的sidecar存在。
1. Controller負載Service整個生命週期的管理,涉及、Configuration、Route、Revision等的CURD。
2. Webhook主要負責創建和更新的參數校驗。
3. Autoscaler根據應用的請求併發量對應用擴縮容。
4. Activator 在應用縮容到0後,攔截用戶的請求,通知autoscaler啓動相應應用實例,等待啓動後將請求轉發。
5. Queue負載攔截轉發給Pod的請求,用於統計Pod的請求併發量等,autoscaler會訪問queue獲取相應數據對應用擴縮容。
自動擴縮容
1->n: 任何訪問應用的請求在進入Pod後都會被Queue攔截,統計當前Pod的請求併發數,同時Queue會開放一個metric接口,autoscalor通過訪問該端口去獲取Pod的請求併發量並計算是否需要擴縮容。當需要擴縮容時,autoscalor會通過修改Revision下的deployment的實例個數達到擴縮容的效果。
0->1: 在應用長時間無請求訪問時,實例會縮減到0。這個時候,訪問應用的請求會被轉發到activator,並在請求在轉發到activator之前會被標記請求訪問的Revision信息(由controller修改VirtualService實現)。activator接收到請求後,會將改Revision的併發量加1,並將metric推送給autoscalor,啓動Pod。同時,activator監控Revision的啓動狀態,Revision正常啓動後,將請求轉發給相應的Pod。
當然,在Revision正常啓動後,應用的請求將不會再發送到activator,而且直接發送至應用的Pod(由controller修改VirtualService實現)。
Knative網絡模式
網絡模式分兩個部分,一個爲Service之間的訪問,一個爲外部訪問。
Service之間的訪問:
- istio會解析Knative Service的VirtualService下發給各個Pod的Envoy,當應用通過域名相互訪問時,Envoy會攔截請求直接轉發給相應的Pod。
外部訪問:
-
如果是在集羣外訪問,素喲有的請求入口爲ingressgateway,ingressgateway將請求根據訪問域名轉發到應用。
-
如果是在集羣節點上訪問,每個Knative Service都對應一個k8s Service, 這個Service的後端都爲ingressgateway,ingressgateway會根據訪問域名轉發到應用。
總結
Knative的Serving在istio基礎上,實現應用的簡易部署和多版本管理,把開發人員從應用的部署和運維中真正的解放出來;同時動態擴縮容能力避免了資源冗餘造成的浪費,有效節省應用成本。
相關文章: