在 2023 年的雲棲大會中,阿里雲服務網格 ASM 推出了《兩全其美:Sidecarless 與 Sidecar 模式融合的服務網格新形態》主題演講,並在演講中展示了一個基於服務網格 ASM 各項能力構建的 DEMO AI 應用。該應用集中展示了 ASM 在模型服務、請求處理、請求路由和安全中心集成單點登錄等各項能力,且這些能力還完全是以 Sidecarless 的形態來實現的。
看完我們的演示,您也許也會想嘗試一下,從零開始構建這樣的一個應用來玩玩吧!當然!我們向您保證,我們能搭出來的東西,您一定也能搭出來。本文就是這樣一篇給各位的入門指引,我們這就開始吧!
01 從零開始搭建一個基於服務網格 ASM 的 AI 應用
1、前提條件
一個 ACK 集羣、一個 ASM 實例以及相關的 istioctl 等工具是一切的根基,我們先來準備一些實驗環境。
- 已創建 ASM 實例,且實例版本在 1.18.0.131 及以上。具體操作,請參見創建 ASM 實例[1]。在創建服務網格頁面配置數據面模式時,選中啓用 Ambient Mesh 模式。
- 已創建 Kubernetes 集羣,且滿足 Kubernetes 集羣及配置要求[2]。關於創建集羣的具體操作,請參見創建 Kubernetes 專有版集羣[3]或創建 Kubernetes 託管版集羣[4]。
- 已添加集羣到 ASM 實例。具體操作,請參見添加集羣到 ASM 實例[5]。
- 已按照實際操作系統及平臺,下載 Istioctl 服務網格調試工具。詳細信息,請參見 Istio[6]。
2、搭建模型推理服務
1)開啓 ASM 的多模型推理服務生態集成能力
對於一個基於 AI 模型推理的應用服務來說,將訓練好的模型快速轉化爲彈性、靈活的模型推理服務無疑是工作的重心之一。
作爲應用感知的下一代雲原生基礎設施,服務網格 ASM 也通過其豐富的生態集成能力、集成了雲原生推理服務框架 KServe(參考 ASM 集成雲原生推理服務框架 KServe[7])、爲 AI 模型推理的服務化提供了一站式解決方案。
在服務網格 ASM 的最新版本中,我們 alpha 階段地引入了模型推理服務集成的多模型服務框架(modelmesh)。在全新的 modelmesh 服務框架之內,不同的模型、其推理將交給多個運行時工作負載來完成。每個運行時支持不同的模型格式;並且可以同時提供多個模型的推理服務。當我們使用 InferenceService 資源定義一個模型後,模型文件將根據模型的格式、動態地加載到對應的運行時工作負載之中。一個運行時可以同時提供多個模型的推理服務。
我們可以通過以下步驟來集成多模型推理服務框架 modelmesh:
1. 在 ASM 實例中創建一個名爲 modelmesh-serving 的全局命名空間(參考管理全局命名空間[8])
2. 要使用這個能力,我們首先使用 kubectl 連接到 ASM 實例(參考通過控制面 kubectl 訪問 Istio 資源[9])
3. 使用以下這個文件,創建 asmkserveconfig.yaml
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMKServeConfig
metadata:
name: default
spec:
enabled: true
multiModel: true
tag: v0.11.0
4. 使用 kubectl 執行以下命令,打開模型推理服務框架集成
kubectl apply -f asmkserveconfig.yaml
執行完此步驟後,我們可以看到 ACK 集羣中出現一個 modelmesh-serving 命名空間,內部包含有模型推理 Servicemodelmesh-serving、以及提供服務的各種運行時工作負載,這就代表模型推理服務已經就緒。
2)準備模型文件,聲明推理服務
模型推理服務框架就緒後,接下來我們需要準備好訓練的模型文件,並將模型加載到運行時工作負載中,成爲可以對外暴露的推理服務。
1. 準備模型
文件機器學習模型在經過訓練後,可以通過各種序列化方式被保存下來(例如:saved_model、pkl 等),模型推理服務器可以加載並利用這些模型文件對外提供訓練好的機器學習模型的推理服務。
在本 DEMO 應用中,我們也需要準備這樣的模型文件。事實上,我們準備了兩個訓練好的模型。這兩個模型分別基於 tensorflow 與 pytorch,其中 pytorch 模型生成的圖片風格固定,而 tensorflow 模型可以抽取圖片風格,進行不同的風格化處理。
模型的獲取也非常簡單,不需要大家去自己訓練了。我們只需要通過 Tensorflow 和 Pytorch 的官方渠道即可獲取了。
- TensorFlow 模型可通過 Tensorflow Hub 獲取,訪問這裏來下載:https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2
- 至於 Pytorch 模型,我們在本例中使用了官方 DEMO 例子中的模型,並將其轉換成了 ONNX 格式。我們可以參考這個教程來下載並轉換模型文件:https://pytorch.org/tutorials/advanced/ONNXLive.html(注意:在轉換成 ONNX 模型的一步,我們是使用了 512*512 的圖片作爲輸入,注意輸入圖片尺寸,這個對 ONNX 格式的模型很重要)。demo 中提供四種固定風格的模型,我們可以任選一款,在我們的 demo 中選擇了 candy 模型。
下載到本地後,我們隨便找個路徑作爲根目錄,新建一個 tensorflow 文件夾和一個 pytorch 文件夾,分別保存兩個模型的文件。我們將兩個模型的模型文件保存成如下的文件夾結構,方便後續操作。
Tensorflow 模型大概長這樣:
Pytorch 模型則是這樣的:
在根目錄運行 ls -R 指令,可以看到如下的文件結構:
$ ls -R
pytorch tensorflow
./pytorch:
style-transfer
./pytorch/style-transfer:
candy.onnx
./tensorflow:
style-transfer
./tensorflow/style-transfer:
saved_model.pb variables
./tensorflow/style-transfer/variables:
variables.data-00000-of-00002 variables.data-00001-of-00002 variables.index
2. 將模型文件加載到 PVC
首先創建一個存儲類,前往容器服務控制檯的 存儲 > 存儲類,創建一個存儲類:
接着創建 PVC,前往容器服務控制檯 存儲 > 存儲聲明,用剛剛創建的存儲類來創建一個存儲聲明 PVC,名字就叫 my-models-pvc。
3.創建一個 pod 用來將模型文件拷貝到 PVC 裏
前往容器服務控制檯的工作負載 > 容器組,點擊“使用 YAML 創建”,並在 YAML 框中輸入以下內容,點擊“創建”來創建一個 pod。
apiVersion: v1
kind: Pod
metadata:
name: "pvc-access"
namespace: modelmesh-serving
spec:
containers:
- name: main
image: ubuntu
command: ["/bin/sh", "-ec", "sleep 10000"]
volumeMounts:
- name: "my-pvc"
mountPath: "/mnt/models"
volumes:
- name: "my-pvc"
persistentVolumeClaim:
claimName: "my-models-pvc"
4. 使用 kubectl cp 將模型文件通過 pod 拷貝進 PVC
首先使用 kubectl 連接至 ACK 集羣(參考獲取集羣 KubeConfig 並通過 kubectl 工具連接集羣[10])。
接下來在剛纔的模型文件根目錄處,打開命令行,運行以下指令:
kubectl cp -n modelmesh-serving tensorflow pvc-access:/mnt/models/
kubectl cp -n modelmesh-serving pytorch pvc-access:/mnt/models/
接下來執行以下命令,確定拷貝已經成功:
kubectl exec -n modelmesh-serving pvc-access -- ls /mnt/models
預期得到以下內容,就說明模型文件已經被拷貝到 PVC 裏了。
pytorch
tensorflow
5. 使用 InferenceService 自定義資源創建模型推理服務
使用以下內容,創建 isvc.yaml 文件
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: tf-style-transfer
namespace: modelmesh-serving
annotations:
serving.kserve.io/deploymentMode: ModelMesh
#serving.kserve.io/secretKey: myoss
spec:
predictor:
model:
modelFormat:
name: tensorflow
storage:
parameters:
type: pvc
name: my-models-pvc
path: tensorflow/style-transfer/
---
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: pt-style-transfer
namespace: modelmesh-serving
annotations:
serving.kserve.io/deploymentMode: ModelMesh
spec:
predictor:
model:
modelFormat:
name: onnx
storage:
parameters:
type: pvc
name: my-models-pvc
path: pytorch/style-transfer/
isvc.yaml 中聲明瞭兩個 InferenceService,分別對應 Tensorflow 和 Pytorch 模型的推理服務聲明。
使用以下命令,在 ACK 集羣中創建模型推理服務。
kubectl apply -f isvc.yaml
我們可以觀察到在集羣中,支持 Tensorflow 和 Pytorch 這兩個模型的運行時工作負責 Pod 被動態擴容拉起,並開始加載對應支持格式的模型。在此 DEMO 示例中,我們用 InferenceService 分別聲明瞭 Tensorflow 和 ONNX 格式的模型文件,因此,可以看到,對應拉起的運行時是 triton-2.x 運行時和 ovms-1.x 運行時。
當運行時啓動與模型加載都完成後,使用 kubectl 獲取 InferenceService,可以看到兩個 InferenceService 也都對應處於就緒狀態:
$ kubectl get isvc -n modelmesh-serving
NAME URL READY PREV LATEST PREVROLLEDOUTREVISION LATESTREADYREVISION AGE
pt-style-transfer grpc://modelmesh-serving.modelmesh-serving:8033 True 11d
tf-style-transfer grpc://modelmesh-serving.modelmesh-serving:8033 True 11d
3)在集羣中部署業務服務
在模型推理服務的前面就是我們的業務服務了,分別是 style-transfer 業務服務和最前方的 AI 應用服務,我們接下來就需要在集羣中部署這些服務以及服務的工作負載。
1. 使用 kubectl 連接到 ACK 集羣,並使用如下命令創建一個命名空間來部署應用
kubectl create namespace apsara-demo
2. 使用以下內容,創建 ai-apps.yaml 文件
apiVersion: v1
kind: ServiceAccount
metadata:
name: ai-backend
namespace: apsara-demo
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: style-transfer
namespace: apsara-demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ai-backend
name: ai-backend
namespace: apsara-demo
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: ai-backend
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: ai-backend
spec:
serviceAccountName: ai-backend
containers:
- image: 'registry.cn-hangzhou.aliyuncs.com/build-test/asm-apsara:g56a99cd1-aliyun'
imagePullPolicy: IfNotPresent
name: ai-backend
ports:
- containerPort: 8000
name: http
protocol: TCP
resources:
requests:
cpu: 250m
memory: 512Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: style-transfer
name: style-transfer-tf
namespace: apsara-demo
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: style-transfer
model-format: tensorflow
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: style-transfer
model-format: tensorflow
spec:
serviceAccountName: style-transfer
containers:
- image: >-
registry.cn-hangzhou.aliyuncs.com/build-test/style-transfer-tf:g78d00b1c-aliyun
imagePullPolicy: IfNotPresent
name: style-transfer-tf
env:
- name: MODEL_SERVER
value: istio-ingressgateway.istio-system.svc.cluster.local:8008
- name: MODEL_NAME
value: tf-style-transfer
ports:
- containerPort: 8000
name: http
protocol: TCP
resources:
requests:
cpu: 250m
memory: 512Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: style-transfer
name: style-transfer-torch
namespace: apsara-demo
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: style-transfer
model-format: pytorch
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: style-transfer
model-format: pytorch
spec:
serviceAccountName: style-transfer
containers:
- image: >-
registry.cn-hangzhou.aliyuncs.com/build-test/style-transfer-torch:g78d00b1c-aliyun
imagePullPolicy: IfNotPresent
name: style-transfer-torch
env:
- name: MODEL_SERVER
value: istio-ingressgateway.istio-system.svc.cluster.local:8008
- name: MODEL_NAME
value: pt-style-transfer
ports:
- containerPort: 8000
name: http
protocol: TCP
resources:
requests:
cpu: 250m
memory: 512Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
labels:
app: ai-backend
name: ai-backend-svc
namespace: apsara-demo
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 8000
selector:
app: ai-backend
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
labels:
app: style-transfer
name: style-transfer
namespace: apsara-demo
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 8000
selector:
app: style-transfer
sessionAffinity: None
type: ClusterIP
3. 使用 kubectl 執行以下命令來部署上方文件中聲明的應用服務
kubectl apply -f ai-apps.yaml
4)創建 ASM 網關、waypoint 網格代理,並部署生效流量規則
部署的最後一部分都有關服務網格,具體來說有以下部分:
- ASM 入口網關。
- 網格 waypoint 代理,它是 Sidecarless 的服務網格能力載體。
- 服務網格流量規則,這些規則將生效到 ASM 網關和 waypoint 代理,保證流量路徑按照我們的設計運行。
1. 部署 ASM 入口網關
我們可參考創建入口網關[11],來創建 ASM 入口網關。我們需要創建兩個 ASM 入口網關,其中一個叫 api-ingresgateway,服務類型爲 LoadBalancer,網關上需要開啓 80 端口;另一個叫 ingressgateway,服務類型爲 ClusterIP,網關上需要開啓 8008 端口。其餘網關配置保持默認即可。
都創建完成後,我們應該可以在 ASM 入口網關頁面看到這樣的顯示:
2. 開啓 apsara-demo 命名空間的 Ambient Mesh 模式
-
- 登錄 ASM 控制檯[12],在左側導航欄,選擇服務網格 > 網格管理。
- 在網格管理頁面,單擊目標實例名稱,然後在左側導航欄,選擇網格實例 > 全局命名空間。
- 在全局命名空間頁面,單擊從 Kubernetes 集羣同步自動注入,選擇數據面 ACK 集羣后單擊確定。
- 在全局命名空間頁面的數據面模式列,單擊 apsara-demo 命名空間對應的切換爲 Ambient Mesh 模式,然後在確認對話框,單擊確定。
3. 部署 waypoint 代理
使用 kubectl 連接到 ACK 集羣,然後使用前提條件中安裝的 istioctl 工具,執行以下指令:
istioctl x waypoint apply --service-account style-transfer -n apsara-demo
執行完成後,我們可以使用 kubectl 列出集羣中的無狀態工作負載。
kubectl get deploy -n apsara-demo
預期輸出:
NAME READY UP-TO-DATE AVAILABLE AGE
ai-backend 1/1 1 1 13d
style-transfer-istio-waypoint 1/1 1 1 13d
style-transfer-tf 1/1 1 1 13d
style-transfer-torch 1/1 1 1 13d
可以看到集羣中除了我們剛纔部署的 AI 應用以及 style-transfer 應用的工作負載外,還增加了一個名爲 style-transfer-istio-waypoint 的工作負載,這就是服務網格的 waypoint 代理,它是以獨立的工作負載方式部署在集羣中的,所提供的所有能力也都是 Sidecarless 的。
4.部署服務網格規則
① 使用以下內容,創建 modelsvc-routing.yaml 文件
# make sure voyage is 1.13.4.13 or higher
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: grpc-gateway
namespace: modelmesh-serving
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*'
port:
name: grpc
number: 8008
protocol: GRPC
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-modelmesh-serving-service
namespace: modelmesh-serving
spec:
gateways:
- grpc-gateway
hosts:
- '*'
http:
- headerToDynamicSubsetKey:
- header: x-model-format-tensorflow
key: model.format.tensorflow
- header: x-model-format-pytorch
key: model.format.pytorch
match:
- port: 8008
name: default
route:
- destination:
host: modelmesh-serving
port:
number: 8033
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: dr-modelmesh-serving-service
namespace: modelmesh-serving
spec:
host: modelmesh-serving-service
trafficPolicy:
loadBalancer:
dynamicSubset:
subsetSelectors:
- keys:
- model.format.tensorflow
- keys:
- model.format.pytorch
---
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMGrpcJsonTranscoder
metadata:
name: grpcjsontranscoder-for-kservepredictv2
namespace: istio-system
spec:
builtinProtoDescriptor: kserve_predict_v2
isGateway: true
portNumber: 8008
workloadSelector:
labels:
istio: ingressgateway
---
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
labels:
asm-system: 'true'
provider: asm
name: grpcjsontranscoder-increasebufferlimit
namespace: istio-system
spec:
configPatches:
- applyTo: LISTENER
match:
context: GATEWAY
listener:
portNumber: 8008
proxy:
proxyVersion: ^1.*
patch:
operation: MERGE
value:
per_connection_buffer_limit_bytes: 100000000
workloadSelector:
labels:
istio: ingressgateway
modelsvc-routing.yaml 中主要包含的是針對集羣中的模型推理服務的流量規則。這主要包含兩部分規則:
針對模型推理服務中不同運行時工作負載的動態子集路由能力高
- 針對 kserve v2 推理接口的 JSON/HTTP - gRPC 請求轉碼能力
我們將在下一個大章節介紹這些能力的細節。
② 使用 kubectl 連接 ASM 實例,執行以下命令,部署 modelsvc-routing 流量規則
kubectl apply -f modelsvc-routing.yaml
③ 使用以下內容,創建 app-routing.yaml 文件
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: ai-app-gateway
namespace: apsara-demo
spec:
selector:
istio: api-ingressgateway
servers:
- hosts:
- '*'
port:
name: http
number: 8000
protocol: HTTP
- hosts:
- '*'
port:
name: http-80
number: 80
protocol: HTTP
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: ai-app-vs
namespace: apsara-demo
spec:
gateways:
- ai-app-gateway
hosts:
- '*'
http:
- route:
- destination:
host: ai-backend-svc
port:
number: 8000
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: style-transfer-vs
namespace: apsara-demo
spec:
hosts:
- style-transfer.apsara-demo.svc.cluster.local
http:
- match:
- headers:
user_class:
exact: premium
route:
- destination:
host: style-transfer.apsara-demo.svc.cluster.local
port:
number: 8000
subset: tensorflow
- route:
- destination:
host: style-transfer.apsara-demo.svc.cluster.local
port:
number: 8000
subset: pytorch
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: style-transfer-dr
namespace: apsara-demo
spec:
host: style-transfer.apsara-demo.svc.cluster.local
subsets:
- labels:
model-format: tensorflow
name: tensorflow
- labels:
model-format: pytorch
name: pytorch
app-routing.yaml 中主要包含的是對 AI 應用服務和 style-transfer 服務的路由規則。其中包括一個對 style-transfer 不同工作負載進行根據用戶身份分流的能力。
④ 使用 kubectl 連接 ASM 實例,執行以下命令,部署 app-routing 流量規則
kubectl apply -f app-routing.yaml
⑤ 將 ASM 網關對接阿里雲 iDaas 應用身份服務,輕鬆實現單點登錄
搭建整個應用的最後一步位於應用的總入口,也就是 ASM 入口網關。在這裏,我們還需要將網關與阿里雲 iDaas 的 OIDC 應用進行對接,對整個應用進行一個單點登錄的配置。
我們可以參考這篇文檔來進行對接的操作:ASM 集成阿里雲 IDaaS 實現網格內應用單點登錄[13]。
值得注意的是,我們使用用戶 jwt claim 中的額外字段 user_type 來完成用戶身份的識別,這需要進行如下操作:
點擊雲身份服務的擴展字段,添加擴展字段(擴展字段名稱和 OIDC 登陸後返回的字段名稱均可以自定義,這裏擴展字段定義爲 user_type,OIDC 登陸後返回字段名稱會在後面定義爲 user_class):
然後編輯用戶信息,爲指定用戶設置該字段:
設置好該字段後,需要配置在 OIDC 登陸成功後,返回該字段。進入 OIDC 應用設置,點擊登錄訪問 tab,點擊“顯示高級配置”。在這裏設置新增一個 OIDC 登陸成功後返回的 key-value 對,key 是 user_type,value 是 user_class 的值。
我們披星戴月我們奮不顧身,終於!我們的 AI 應用搭好了!可以看到,從零開始搭建這樣一套集成了模型推理的業務服務確實不能一步登天,不過服務網格 ASM 在這其中通過一些生態集成的能力,以及完善的 Web UI,將很多步驟進行了簡化。
3、Try it out!
在 ASM 控制檯的網格管理頁面,我們可以直接看到 api-ingressgateway 的服務地址:
整個應用的訪問入口就是 http://{ASM 網關服務地址}/home。用瀏覽器打開它,就可以開始玩我們的 AI 應用了~
02 服務網格如何幫助我們
這個章節會簡要介紹在這個 DEMO 中,服務網格 ASM 開啓了怎樣的一些能力,幫助我們做到更多。也就是我們在雲棲大會中爲大家介紹的內容。
1、針對模型服務運行時的動態子集路由
在 AI 應用的構建中,如何將訓練好的模型轉化爲可靠的推理服務是工作的重心,因此我們首先介紹這個 DEMO 中的模型推理服務。
在模型推理服務的整體框架中,由一個整體的 k8s Service 對外提供所有模型的推理。然而,模型有很多格式種類、如何將類似 sklearn、tensorflow、pytorch 等等不同種類的模型統一成 API 相同的推理服務呢?這就要使用不同的運行時。
在統一的模型推理 Service 之下,不同的模型、其推理將交給多個運行時工作負載來完成。每個運行時支持不同的模型格式;並且可以同時提供多個模型的推理服務。當我們使用 InferenceService 資源定義一個模型後,模型文件將根據模型的格式、動態地加載到對應的運行時工作負載之中。一個運行時可以同時提供多個模型的推理服務。
通過這種方式,能夠實現高彈性、高靈活性、低消耗的模型推理服務部署。
然而這種方式也存在問題,即存在額外的路由代價。由於 k8s Service 的機制,請求發往模型推理服務後,k8s 不會區分請求的模型格式、而是會隨機將請求分發到不同的運行時工作負載,也就無法保證請求能夠正確發往可提供服務的運行時。這就需要在運行時中注入額外的 model-proxy,用來進行額外的路由操作、保證請求的正確響應,在運行時規模增大的情況下會造成消耗和性能問題。
這也正是服務網格的重要價值所在。服務網格通過數據面的網格代理,能夠動態識別模型推理服務內部、支持不同模型格式的運行時。並在推理請求發出時,根據請求元數據尋找匹配的運行時分組,保證請求能夠直接發向正確的運行時,在不額外增加運維成本的同時降低系統路由消耗,這被稱作動態子集路由能力。
要實現動態子集路由能力,我們只需要使用針對服務配置的 DestinationRule 資源與 VirtualService 資源即可。
對運行時的識別主要通過工作負載的標籤,聲明一系列模型格式相關的標籤,服務網格就將以這些標籤爲依據、對運行時進行動態分組。在目標規則 DestinationRule 中,主要聲明瞭一系列的標籤信息,這些標籤將成爲工作負載的分組依據。
在下方的虛擬服務 VirtualService 中,我們可以看到基於標籤動態分組的路由配置。具體來說,服務網格能夠利用請求 header 信息生成請求元數據,元數據包含目標工作負載的標籤信息,可以與工作負載的分組進行匹配。
在這個 DEMO 中,我們將請求中以 x-model-format 開頭的 header 轉換爲請求元數據,並與 DestinationRule 中聲明的工作負載分組進行匹配,找到請求應該發往的分組。
2、Json/http - gRPC 請求轉碼能力
在實現了動態子集路由的網格代理之上,我們還配置了 json to grpc 的轉碼能力。
當前,模型推理服務器大多都只實現了 gRPC 協議的服務,而對於依賴模型推理的業務服務來說,則可能是以 restful 等方式來實現服務之間的相互調用。因此,在業務服務調用模型推理服務時,可能存在協議不兼容、導致難以調用的情況。
通過在服務網格中配置 json to grpc 轉碼能力,原本只能通過 grpc 協議訪問的模型推理服務、現在也可以通過 http 傳輸 json 數據的方式來訪問。
如圖所示,我們只需要聲明 grpc 服務的 proto 描述,集羣中的網格代理將替我們完成 restful 請求中 json 數據到 gRPC 請求體的動態轉換,爲集羣中的服務調用增添更多的靈活性,解決調用協議的兼容問題。
3、基於用戶身份的 Sidecarless 流量路由能力
讓我們將目光投向調用鏈路的前端,針對 AI 應用服務調用 style-transfer 業務服務的這一環,我們也發揮服務網格的能力,實現了基於用戶身份的流量分流。
調用鏈路的上游是集羣中的 style-transfer 業務服務,對於這個業務服務,我們針對 tensorflow 和 pytorch 兩種模型,分別提供了名爲 style-transfer-tf 和 style-transfer-torch 的不同工作負載,負責將下游應用傳入的圖片處理爲模型可以接受的張量、並交給依賴的模型進行推理。而服務網格負責根據用戶身份信息,將下游傳輸的數據交給不同的工作負載。
我們來看相關配置,首先,還是通過目標規則 DestinationRule 將中臺業務服務下不同的工作負載進行分組。接着,虛擬服務 VirtualService 將根據請求中的用戶信息,將流量發往不同的工作負載,用不同的模型對請求進行響應。其中請求的用戶信息則是用戶的 jwt claim,由 OIDC 應用提供。
在本 DEMO 中,服務網格的運用完全基於 Sidecarless 模式,上述能力是通過獨立部署的網格代理 waypoint 實現的,也就是說,這些能力的實現不需要任何業務感知,能夠大大提高服務網格的運維效率。
4、ASM 網關集成 OIDC 應用實現單點登錄能力
最後,在整個調用鏈路的最前端就是作爲流量入口的 ASM 網關。
DEMO 在 ASM 網關上實現了與 OIDC 應用的快速對接來配置單點登錄。本次 DEMO 中使用阿里雲 idaas 應用身份服務。通過將網關與 OIDC 應用進行對接,網關後的應用無需自己實現身份認證、即可對集羣中的應用完成單點登錄並拿到用戶身份。
如圖所示:在服務網格 ASM 中,通過一個完善的 Web 界面即可快速配置與已有 OIDC 應用的對接,這能夠大大降低單點登錄系統的實現與運維成本,提升運維效率。
小結
最後讓我們簡單總結一下。在此次的 DEMO 應用中,服務網格 ASM 針對服務調用鏈路上不同服務的特性以及業務需求,能夠靈活配置不同的流量路由以及流量處理規則,快捷地完成應用的各項運維工作;同時,這些能力的生效也是完全基於 Sidecarless 模式,對業務幾乎無感知,服務網格進一步沉澱爲應用的流量基礎設施。作爲業務入口的 ASM 入口網關,在滿足基礎的路由和安全能力之外,還提供豐富的生態集成、證書管理等增強能力,並都輔以完備的 Web 界面幫助用戶進行快速配置。
大家可以根據自身需求,選擇使用服務網格的相應能力,Let Service Mesh helps you to achieve more!有關更多的產品能力,歡迎參考官方文檔[14]。
相關鏈接:
[1] 創建 ASM 實例
https://help.aliyun.com/zh/asm/user-guide/create-an-asm-instance#task-2370657
[2] Kubernetes 集羣及配置要求
https://help.aliyun.com/zh/asm/user-guide/restrictions-on-use#rwA6T
[3] 創建 Kubernetes 專有版集羣
[4] 創建 Kubernetes 託管版集羣
[5] 添加集羣到 ASM 實例
https://help.aliyun.com/zh/asm/getting-started/add-a-cluster-to-an-asm-instance-1#task-2372122
[6] Istio
https://github.com/istio/istio/releases/tag/1.18.2
[7] ASM 集成雲原生推理服務框架 KServe
[8] 管理全局命名空間
https://help.aliyun.com/zh/asm/user-guide/manage-global-namespaces
[9] 通過控制面 kubectl 訪問 Istio 資源
https://help.aliyun.com/zh/asm/user-guide/use-kubectl-on-the-control-plane-to-access-istio-resources
[10] 獲取集羣 KubeConfig 並通過 kubectl 工具連接集羣
[11] 創建入口網關
https://help.aliyun.com/zh/asm/user-guide/create-an-ingress-gateway?spm=a2c4g.11186623.0.i1
[12] ASM 控制檯
[13] ASM 集成阿里雲 IDaaS 實現網格內應用單點登錄
[14] 官方文檔
https://help.aliyun.com/zh/asm
作者:尹航
本文爲阿里雲原創內容,未經允許不得轉載。