Serverless K8s支持Pod粒度的彈性,具有秒級啟動、秒級計費、2000每分鐘的并發等優勢,因此越來越多的用戶開始使用Serverless K8s來運行Argo。本文介紹如何基于ACK集群,使用ECI運行Argo工作流。
搭建Kubernetes+Argo環境
搭建阿里云Serverless K8s集群。
(推薦)創建ACK Serverless集群。具體操作,請參見創建集群。
創建ACK集群,并部署ack-virtual-node組件生成虛擬節點。具體操作,請參見創建Kubernetes托管版集群和通過虛擬節點將Pod調度到ECI上運行。
在Kubernetes集群中部署Argo。
(推薦)安裝ack-workflow組件。具體操作,請參見ack-workflow。
自行部署Argo。具體操作,請參見Argo Quick Start。
安裝Argo命令。具體操作,請參見argo-workflows。
優化基礎資源配置
默認情況下,完成Argo部署后,argo-server和workflow-controller這兩個核心組件并沒有指定對應Pod的resources,這會導致這兩個組件對應Pod的QoS級別較低,在集群資源不足時會出現組件OOM Kill、Pod被驅逐的情況。因此,建議您根據自身集群規模調整上述兩個組件對應Pod的resources,建議其requests或limits設置在2 vCPU,4 GiB內存及以上。
使用OSS作為artifacts倉庫
默認情況下,Argo使用minio作為artifacts倉庫,在生產環境中則需要考慮artifacts倉庫的穩定性,ack-workflow支持使用OSS作為artifacts倉庫。關于如何配置OSS作為artifacts倉庫,請參見Configuring Alibaba Cloud OSS。
配置成功后,您可以使用以下示例創建Wokrflow進行驗證。
將以下內容保存為workflow-oss.yaml。
apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: generateName: artifact-passing- spec: entrypoint: artifact-example templates: - name: artifact-example steps: - - name: generate-artifact template: whalesay - - name: consume-artifact template: print-message arguments: artifacts: # bind message to the hello-art artifact # generated by the generate-artifact step - name: message from: "{{steps.generate-artifact.outputs.artifacts.hello-art}}" - name: whalesay container: image: docker/whalesay:latest command: [sh, -c] args: ["cowsay hello world | tee /tmp/hello_world.txt"] outputs: artifacts: # generate hello-art artifact from /tmp/hello_world.txt # artifacts can be directories as well as files - name: hello-art path: /tmp/hello_world.txt - name: print-message inputs: artifacts: # unpack the message input artifact # and put it at /tmp/message - name: message path: /tmp/message container: image: alpine:latest command: [sh, -c] args: ["cat /tmp/message"]
創建Workflow。
argo -n argo submit workflow-oss.yaml
查看Workflow的執行結果。
argo -n argo list
預期返回:
選擇Executor
Argo創建的每個工作Pod中至少會有以下兩個容器:
main容器
實際的業務容器,真正運行業務邏輯。
wait容器
Argo的系統組件,以Sidecar的形式注入到Pod內。其核心作用如下:
啟動階段
加載main容器依賴的artifacts、inputs。
運行階段
等待main容器退出,Kill關聯的Sidecars容器。
收集main容器的outputs、artifacts,上報main容器狀態
Executor是wait容器訪問和控制main容器的“橋梁”,Argo將其抽象為ContainerRuntimeExecutor,其接口定義如下:
GetFileContents:獲取main容器的輸出參數(outputs/parameters)。
CopyFile:獲取main容器的產物(outputs/artifacts)。
GetOutputStream:獲取main的標準輸出(含標準錯誤)。
Wait:等待main容器退出。
Kill:Kill關聯的Sidecar容器。
ListContainerNames:列舉Pod內容器的名稱列表。
目前Argo已支持多種Executor,其工作原理不同,但都是針對原生K8s架構設計的。由于阿里云Serverless K8s與原生K8s在架構上存在差異,因此需要選擇合適的Executor。建議您選擇Emisarry作為Serverless K8s場景運行Argo的Executor,相關說明如下:
Executor | 說明 |
Emisarry | 通過EmptyDir共享文件的方式實現相關能力,依賴EmptyDir。 由于該Executor僅依賴標準能力EmptyDir,無其他依賴,因此推薦使用該Executor。 |
Kubernetes API | 通過Kubernetes API方式實現相關功能,依賴Kubernetes API但功能不完整。 由于該Executor功能不完整,且在大規模場景中會給K8s控制層面帶來壓力,影響集群規模,因此不推薦使用該Executor。 |
PNS | 基于Pod內的PID共享和chroot實現相關功能,會污染Pod的進程空間,并且依賴特權。 由于Serverless K8s具有更高的安全隔離性,不支持使用特權,因此無法使用該Executor。 |
Docker | 通過Docker CLI實現相關功能,依賴底層容器運行時Docker。 由于Serverless K8s沒有真實節點,不支持訪問節點上的Docker組件,因此無法使用該Executor。 |
Kubelet | 通過Kubelet Client API實現相關功能,依賴Kubernetes底層組件Kubelet。 由于Serverless K8s沒有真實節點,不支持訪問節點上的Kubelet組件,因此無法使用該Executor。 |
調度Argo任務到ECI
ACK Serverless集群默認會將所有Pod都調度到ECI,因此不需要額外配置。ACK集群需要配置后才能將Pod調度到ECI,具體配置方式請參見調度Pod到x86架構的虛擬節點。
以下YAML以配置Label的方式為例:
添加
alibabacloud.com/eci: "true"
的Label:添加相應Label后,Pod會被自動調度到ECI。(可選)指定
{"schedulerName": "eci-scheduler"}
:建議配置,虛擬節點(VK)升級或變更時,WebHook會有短暫不可用,配置后可以避免Pod調度到普通節點。
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: parallelism-limit1-
spec:
entrypoint: parallelism-limit1
parallelism: 10
podSpecPatch: '{"schedulerName": "eci-scheduler"}' #指定調度到ECI
podMetadata:
labels:
alibabacloud.com/eci: "true" #配置Label將Pod調度到ECI
templates:
- name: parallelism-limit1
steps:
- - name: sleep
template: sleep
withSequence:
start: "1"
end: "10"
- name: sleep
container:
image: alpine:latest
command: ["sh", "-c", "sleep 30"]
提升Pod創建成功率
在生產環境中,運行一條Argo工作流通常會涉及到多個計算Pod,工作流中的任意一個Pod失敗都會導致整條工作流失敗。如果Argo工作流的成功率不高,您就需要多次重新運行Argo工作流,這不僅影響任務的執行效率,也會增加計算成本。因此,您需要采取一些策略來提升Pod成功率:
定義Argo工作流時
創建ECI Pod時
配置多可用區,避免因可用區庫存不足導致Pod創建失敗。具體操作,請參見多可用區創建Pod、
指定多規格,避免因特定規格庫存不足導致Pod創建時報。具體操作,請參見多規格創建Pod。
優先使用指定vCPU和內存的方式創建Pod,ECI會自動根據庫存情況匹配符合要求的規格。
使用2 vCPU,4 GiB及以上規格創建Pod,這類規格實例都是獨占的企業級實例,可以確保性能的穩定性。
設置Pod故障處理策略,設置Pod創建失敗后是否嘗試重新創建。具體操作,請參見設置Pod故障處理策略。
配置示例如下:
編輯eci-profile配置多可用區。
kubectl edit -n kube-system cm eci-profile
在
data
中配置vSwitchIds
的值為多個交換機ID:data: ...... vSwitchIds: vsw-2ze23nqzig8inprou****,vsw-2ze94pjtfuj9vaymf**** #指定多個交換機ID來配置多可用區 vpcId: vpc-2zeghwzptn5zii0w7**** ......
創建Pod時使用多個策略提升成功率。
配置
k8s.aliyun.com/eci-use-specs
指定多種規格,本示例指定了三個規格,匹配順序依次為ecs.c6.large
、ecs.c5.large
、2-4Gi
。配置
k8s.aliyun.com/eci-schedule-strategy
設置多可用區調度策略,本示例使用VSwitchRandom
,表示隨機調度。配置
retryStrategy
設置Argo重試策略,本示例設置retryPolicy: "Always"
,表示重試所有失敗的步驟。配置
k8s.aliyun.com/eci-fail-strategy
設置Pod故障處理策略,本示例使用fail-fast
,表示快速失敗。Pod創建失敗后直接報錯。Pod顯示為ProviderFailed狀態,由上層編排決定是否重試,或者把Pod創建調度到普通節點。
apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: generateName: parallelism-limit1- spec: entrypoint: parallelism-limit1 parallelism: 10 podSpecPatch: '{"schedulerName": "eci-scheduler"}' podMetadata: labels: alibabacloud.com/eci: "true" annotations: k8s.aliyun.com/eci-use-specs: "ecs.c6.large,ecs.c5.large,2-4Gi" k8s.aliyun.com/eci-schedule-strategy: "VSwitchRandom" k8s.aliyun.com/eci-fail-strategy: "fail-fast" templates: - name: parallelism-limit1 steps: - - name: sleep template: sleep withSequence: start: "1" end: "10" - name: sleep retryStrategy: limit: "3" retryPolicy: "Always" container: image: alpine:latest command: [sh, -c, "sleep 30"]
優化Pod使用成本
ECI支持多種計費方式,對于不同的業務負載,合理規劃其計費方式可以有效降低計算資源的使用成本。
具體優化成本的方式請參見:
加速Pod創建
啟動Pod時需要先拉取您指定的容器鏡像,但因網絡和容器鏡像大小等因素,鏡像拉取耗時往往成了Pod啟動的主要耗時。為加速ECI Pod的創建速度,ECI提供鏡像緩存功能。您可以預先將需要使用的鏡像制作成鏡像緩存,然后基于該鏡像緩存來創建ECI Pod,以此來避免或者減少鏡像層的下載,從而提升Pod創建速度。
鏡像緩存分為以下兩類:
自動創建:ECI默認已開啟自動創建鏡像緩存功能。創建ECI Pod時,如果沒有完全匹配的鏡像,ECI會自動使用該Pod對應的鏡像制作鏡像緩存。
手動創建:支持通過CRD的方式
執行高并發Argo任務前建議先手動創建鏡像緩存。鏡像緩存完成創建后,指定該鏡像緩存,并將Pod的鏡像拉取策略設置為IfNotPresent,實現Pod在啟動階段無需拉取鏡像,可以加速Pod創建,縮短Argo任務的運行時長,降低運行成本。更多信息,請參見使用ImageCache加速創建Pod。
如果之前已經執行了上文的示例進行測試,則目前ECI已經自動創建了鏡像緩存,您可以登錄彈性容器實例控制臺查看鏡像緩存狀態。基于已有的鏡像緩存,您可以使用以下YAML創建Wokrflow,以此測試Pod啟動速度。
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: parallelism-limit1-
spec:
entrypoint: parallelism-limit1
parallelism: 100
podSpecPatch: '{"schedulerName": "eci-scheduler"}'
podMetadata:
labels:
alibabacloud.com/eci: "true"
annotations:
k8s.aliyun.com/eci-use-specs: "ecs.c6.large,ecs.c5.large,2-4Gi"
k8s.aliyun.com/eci-schedule-strategy: "VSwitchRandom"
k8s.aliyun.com/eci-fail-strategy: "fail-fast"
templates:
- name: parallelism-limit1
steps:
- - name: sleep
template: sleep
withSequence:
start: "1"
end: "100"
- name: sleep
retryStrategy:
limit: "3"
retryPolicy: "Always"
container:
imagePullPolicy: IfNotPresent
image: alpine:latest
command: [sh, -c, "sleep 30"]
創建成功后,從Wokrflow對應的ECI Pod的事件中,可以看到匹配到的鏡像緩存ID,且Pod啟動時跳過了鏡像拉取過程。
加速數據加載
Argo廣泛應用于AI推理領域,計算任務通常需要訪問大量的數據,在目前流行的存算分離的架構中,計算節點加載數據的效率會直接影響整批任務的耗時和成本。如果Argo任務需要大量并發訪問存儲中的數據,存儲的帶寬和性能會成為瓶頸。以OSS為例,當Argo任務并發加載OSS中的數據,OSS Bucket的帶寬達到瓶頸時,Argo任務的每個計算節點都會阻塞在數據加載階段,導致每個計算節點耗時延長,不僅影響計算效率,也會增加計算成本。
對于上述問題,數據加速Fluid可以有效改善這類問題。執行批量計算之前,您可以先創建Fluid Dataset并進行預熱,將OSS中的數據預緩存到少量的緩存節點,然后再啟動并發的Argo任務。使用Fluid后,Argo任務從緩存節點讀取數據,緩存節點可以擴展OSS的帶寬,提升計算節點的數據加載效率,最終提升Argo任務的執行效率,降低Argo任務的運行成本。更多關于Fluid的信息,請參見數據加速Fluid概述。
配置示例如下,以下示例演示100個并發任務從OSS加載10 GB的測試文件并計算MD5。
部署Fluid。
登錄容器服務管理控制臺。
在左側導航欄,選擇市場>應用市場。
找到ack-fluid,單擊對應的卡片。
在ack-fluid頁面,單擊一鍵部署。
在彈出面板選擇目標集群,并完成參數配置,單擊確定。
部署完成后,將自動轉到ack-fluid發布詳情頁面,返回Helm頁面可以看到ack-fluid的狀態為已部署;您也可以通過kubectl命令檢查Fluid是否部署成功。
準備測試數據。
部署Fluid后可以通過Fluid的Dataset實現數據加速。執行后續操作前,需要在OSS Bucket中上傳一個10 GB的測試文件。
生成測試文件。
dd if=/dev/zero of=/test.dat bs=1G count=10
上傳測試文件到OSS Bucket。具體操作,請參見上傳文件。
創建加速數據集。
創建Dataset和JindoRuntime。
kubectl -n argo apply -f dataset.yaml
dataset.yaml的內容示例如下,請根據實際情況替換YAML中的AccessKey和OSS Bucket信息等。
apiVersion: v1 kind: Secret metadata: name: access-key stringData: fs.oss.accessKeyId: *************** # 有權限訪問OSS Bucket的AccessKeyID fs.oss.accessKeySecret: ****************** # 有權限訪問OSS Bucket的AccessKeySecret --- apiVersion: data.fluid.io/v1alpha1 kind: Dataset metadata: name: serverless-data spec: mounts: - mountPoint: oss://oss-bucket-name/ # 您的OSS Bucket路徑 name: demo path: / options: fs.oss.endpoint: oss-cn-shanghai-internal.aliyuncs.com #OSS Bucket對應的Endpoint encryptOptions: - name: fs.oss.accessKeyId valueFrom: secretKeyRef: name: access-key key: fs.oss.accessKeyId - name: fs.oss.accessKeySecret valueFrom: secretKeyRef: name: access-key key: fs.oss.accessKeySecret --- apiVersion: data.fluid.io/v1alpha1 kind: JindoRuntime metadata: name: serverless-data spec: replicas: 10 #創建JindoRuntime緩存節點的數量 podMetadata: annotations: k8s.aliyun.com/eci-use-specs: ecs.g6.2xlarge #指定合適的規格 k8s.aliyun.com/eci-image-cache: "true" labels: alibabacloud.com/eci: "true" worker: podMetadata: annotations: k8s.aliyun.com/eci-use-specs: ecs.g6.2xlarge #指定合適的規格 tieredstore: levels: - mediumtype: MEM #緩存類型。如果指定了本地盤規格,可使用LoadRaid0 volumeType: emptyDir path: /local-storage #緩存路徑 quota: 12Gi #緩存最大容量 high: "0.99" #存儲容量上限 low: "0.99" #存儲容量下限
說明本文使用ECI Pod內存作為數據緩存節點,由于每個ECI Pod都有獨享的VPC網卡,因此帶寬上不會受其他Pod影響。
查看結果。
確認加速數據集的狀態,PHASE為Bound表示創建成功。
kubectl -n argo get dataset
預期返回:
查看Pod信息,可以看到通過加速數據集已經創建了10個JindoRuntime緩存節點。
kubectl -n argo get pods
預期返回:
預熱數據。
加速數據集就緒后,可以創建Dataload觸發數據預熱。
創建Dataload觸發數據預熱。
kubectl -n argo apply -f dataload.yaml
dataload.yaml的內容示例如下
apiVersion: data.fluid.io/v1alpha1 kind: DataLoad metadata: name: serverless-data-warmup namespace: argo spec: dataset: name: serverless-data namespace: argo loadMetadata: true
確認Dataload數據預熱的進度。
kubectl -n argo get dataload
預期返回如下,可以看到雖然測試文件有10 GB,但預熱速度是很快的。
運行Argo工作流。
完成數據預熱后即可并發運行Argo任務,建議配合使用鏡像緩存進行測試。
準備Argo工作流配置文件argo-test.yaml。
argo-test.yaml的內容示例如下:
apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: generateName: parallelism-fluid- spec: entrypoint: parallelism-fluid parallelism: 100 podSpecPatch: '{"terminationGracePeriodSeconds": 0, "schedulerName": "eci-scheduler"}' podMetadata: labels: alibabacloud.com/fluid-sidecar-target: eci alibabacloud.com/eci: "true" annotations: k8s.aliyun.com/eci-use-specs: 8-16Gi templates: - name: parallelism-fluid steps: - - name: domd5sum template: md5sum withSequence: start: "1" end: "100" - name: md5sum container: imagePullPolicy: IfNotPresent image: alpine:latest command: ["sh", "-c", "cp /data/test.dat /test.dat && md5sum test.dat"] volumeMounts: - name: data-vol mountPath: /data volumes: - name: data-vol persistentVolumeClaim: claimName: serverless-data
創建Workflow。
argo -n argo submit argo-test.yaml
查看Workflow執行結果。
argo -n argo list
預期返回:
通過
kubectl get pod -n argo --watch
命令進一步觀察Pod的執行進度,可以看到示例場景的100個Argo任務基本在2~4分鐘左右完成。對于同樣的一組Argo任務,如果不使用數據加速,直接從OSS中加載10 G的測試文件并計算MD5,耗時大概在14~15分鐘左右。
對比可以得出數據加速Fluid既可以提升計算效率,也可以大幅度降低計算成本。