雲原生時代:從 Jenkins 到 Argo Workflows,構建高效 CI Pipeline

阿里云云原生發表於2024-06-05

作者:蔡靖

Argo Workflows

Argo Workflows [ 1] 是用於在 Kubernetes 上編排 Job 的開源的雲原生工作流引擎。可以輕鬆自動化和管理 Kubernetes 上的複雜工作流程。適用於各種場景,包括定時任務、機器學習、ETL 和資料分析、模型訓練、資料流 pipline、CI/CD 等。

Kubernetes Jobs 只提供基礎的任務執行,但是無法定義步驟依賴關係和順序、缺乏工作流模版、沒有視覺化介面,也不支援工作流級別的錯誤處理等,對於批處理、資料處理、科學計算、持續整合等業務場景,Kubernetes Job 無法勝任。

Argo Workflows 作為 CNCF 的畢業專案,已被使用在多種場景,持續整合(CI)是其一個重要應用領域。

CI 與 Jenkins

持續整合和持續部署(CI/CD)是軟體開發生命週期中的重要部分,它允許團隊以敏捷流程開發應用並提高所構建應用程式的質量。持續整合(CI)是面向開發者的自動化流程,經測試、構建等步驟,有助於更頻繁、可靠地將程式碼變更提交到主分支。

Jenkins 作為 CI/CD 領域最常見的解決方案,其具有開源免費、外掛豐富、社群成熟諸多優點,但它仍然存在一些問題,尤其是雲原生大背景的當下:

  • 非 kubernetes 原生;
  • 隨著 pipeline 和外掛的增加,Jenkins 會面臨效能瓶頸;
  • 自動擴充套件能力不足,併發不足,執行時間長,空閒計算浪費成本;
  • 維護成本方面,雖然 Jenkins 的外掛生態系統豐富,但這也可能導致外掛版本不相容、更新不及時或安全漏洞等問題,管理外掛更新和許可權是一個持續的挑戰;
  • 專案隔離/許可權分配方案的缺陷等。

Argo Workflows 與 Jenkins 的對比

相比於 Jenkins,Argo Workflows 有諸多優勢。Argo Workflows 構建在 Kubernetes 之上,使其具有 Kubernetes 經過時間考驗的優勢,其 Autoscaling 和併發等能力,使得 Argo Workflows 可以處理大規模的 pipelins,具有更快的執行速度,和更低的費用/使用成本,讓開發者更加聚焦業務功能和為客戶提供、傳播價值;並且與 Argo 生態的 Argo CD、Argo Rollout、Argo Event 的無縫整合,為 CI 等場景提供更強大的能力。您可以基於 Argo Workflows 來構建更加雲原生、大規模、高效率、低成本的 CI Pipeline。

對比如下:

基於 ACK One Serverless Argo 工作流的 CI Pipeline

ACK One Serverless Argo 工作流

ACK One Serverless Argo 工作流 [ 2] 作為一款完全遵循社群規範的全託管式 Argo Workflows 服務,致力於應對大規模計算密集型作業,透過整合阿里雲 ECI 實現自動擴充套件和極致彈性、按需擴容以最小化成本,透過使用 spot ECI(搶佔式 ECI 例項 [ 3] )可以降低 80% 成本。

CI Pipeline 概述

基於 ACK One Serverless Argo 工作流叢集構建 CI Pipeline,主要使用 BuildKit [ 4] 實現容器映象的構建和推送,並使用 BuildKit Cache [ 5] 加速映象的構建,使用 NAS 來儲存 Go mod cache 加速 go test 和 go build,最終大幅加速 CI Pipeline 流程。

我們將實現的 CI Pipeline 的 ClusterWorkflowTemplate 預置在工作流叢集中(名為 ci-go-v1),其中主要包含 3 個步驟:

  1. Git Clone & Checkout:Clone Git 倉庫,Checkout 到目標分支;並獲取 commit id。

  2. Run Go Test:透過引數控制是否執行,使用 NAS 儲存 Go mod cache 進行加速

  3. Build & Push Image:

    a. 使用 BuildKit 構建和推送容器映象,並使用 BuildKit Cache 中 registry 型別 cache 來加速映象構建;

    b. 映象 tag 預設使用 {container_tag}-{commit_id} 格式,可在提交工作流時透過引數控制是否追加 commit id;

    c. 推送映象的同時,也會推送覆蓋其 latest 映象。

您可執行以下步驟完成 CI Pipeline 的執行,詳細步驟請參見最佳實踐 [ 6]

  1. 在工作流叢集中準備好 ACR EE 的憑據和 NAS 儲存卷
  2. 基於預置模板啟動工作流(workflow)執行 CI Pipeline

預置 CI Pipeline 模板

工作流叢集中預設已經預置了名為 ci-go-v1 的工作流模板(ClusterWorkflowTemplate),yaml 如下所示,詳細引數說明請參見最佳實踐 [ 6]

apiVersion: argoproj.io/v1alpha1
kind: ClusterWorkflowTemplate
metadata:
  name: ci-go-v1
spec:
  entrypoint: main
  volumes:
  - name: run-test
    emptyDir: {}
  - name: workdir
    persistentVolumeClaim:
      claimName: pvc-nas
  - name: docker-config
    secret:
      secretName: docker-config
  arguments:
    parameters:
    - name: repo_url
      value: ""
    - name: repo_name
      value: ""
    - name: target_branch
      value: "main"
    - name: container_image
      value: ""
    - name: container_tag
      value: "v1.0.0"
    - name: dockerfile
      value: "./Dockerfile"
    - name: enable_suffix_commitid
      value: "true"
    - name: enable_test
      value: "true"
  templates:
    - name: main
      dag:
        tasks:
          - name: git-checkout-pr
            inline:
              container:
                image: alpine:latest
                command:
                  - sh
                  - -c
                  - |
                    set -eu

                    apk --update add git

                    cd /workdir
                    echo "Start to Clone "{{workflow.parameters.repo_url}}
                    git -C "{{workflow.parameters.repo_name}}" pull || git clone {{workflow.parameters.repo_url}} 
                    cd {{workflow.parameters.repo_name}}

                    echo "Start to Checkout target branch" {{workflow.parameters.target_branch}}
                    git checkout {{workflow.parameters.target_branch}}

                    echo "Get commit id" 
                    git rev-parse --short origin/{{workflow.parameters.target_branch}} > /workdir/{{workflow.parameters.repo_name}}-commitid.txt
                    commitId=$(cat /workdir/{{workflow.parameters.repo_name}}-commitid.txt)
                    echo "Commit id is got: "$commitId

                    echo "Git Clone and Checkout Complete."
                volumeMounts:
                - name: "workdir"
                  mountPath: /workdir
                resources:
                  requests:
                    memory: 1Gi
                    cpu: 1
                activeDeadlineSeconds: 1200
          - name: run-test
            when: "{{workflow.parameters.enable_test}} == true"
            inline: 
              container:
                image: golang:1.22-alpine
                command:
                  - sh
                  - -c
                  - |
                    set -eu

                    if [ ! -d "/workdir/pkg/mod" ]; then
                      mkdir -p /workdir/pkg/mod
                      echo "GOMODCACHE Directory /pkg/mod is created"
                    fi

                    export GOMODCACHE=/workdir/pkg/mod

                    cp -R /workdir/{{workflow.parameters.repo_name}} /test/{{workflow.parameters.repo_name}} 
                    echo "Start Go Test..."

                    cd /test/{{workflow.parameters.repo_name}}
                    go test -v ./...

                    echo "Go Test Complete."
                volumeMounts:
                - name: "workdir"
                  mountPath: /workdir
                - name: run-test
                  mountPath: /test
                resources:
                  requests:
                    memory: 4Gi
                    cpu: 2
              activeDeadlineSeconds: 1200
            depends: git-checkout-pr    
          - name: build-push-image
            inline: 
              container:
                image: moby/buildkit:v0.13.0-rootless
                command:
                  - sh
                  - -c
                  - |         
                    set -eu

                    tag={{workflow.parameters.container_tag}}
                    if [ {{workflow.parameters.enable_suffix_commitid}} == "true" ]
                    then
                      commitId=$(cat /workdir/{{workflow.parameters.repo_name}}-commitid.txt)
                      tag={{workflow.parameters.container_tag}}-$commitId
                    fi

                    echo "Image Tag is: "$tag
                    echo "Start to Build And Push Container Image"

                    cd /workdir/{{workflow.parameters.repo_name}}

                    buildctl-daemonless.sh build \
                    --frontend \
                    dockerfile.v0 \
                    --local \
                    context=. \
                    --local \
                    dockerfile=. \
                    --opt filename={{workflow.parameters.dockerfile}} \
                    build-arg:GOPROXY=http://goproxy.cn,direct \
                    --output \
                    type=image,\"name={{workflow.parameters.container_image}}:${tag},{{workflow.parameters.container_image}}:latest\",push=true,registry.insecure=true \
                    --export-cache mode=max,type=registry,ref={{workflow.parameters.container_image}}:buildcache \
                    --import-cache type=registry,ref={{workflow.parameters.container_image}}:buildcache

                    echo "Build And Push Container Image {{workflow.parameters.container_image}}:${tag} and {{workflow.parameters.container_image}}:latest Complete."
                env:
                  - name: BUILDKITD_FLAGS
                    value: --oci-worker-no-process-sandbox
                  - name: DOCKER_CONFIG
                    value: /.docker
                volumeMounts:
                  - name: workdir
                    mountPath: /workdir
                  - name: docker-config
                    mountPath: /.docker
                securityContext:
                  seccompProfile:
                    type: Unconfined
                  runAsUser: 1000
                  runAsGroup: 1000
                resources:
                  requests:
                    memory: 4Gi
                    cpu: 2
              activeDeadlineSeconds: 1200
            depends: run-test

在控制檯執行 CI Pipeline

  1. 登入 ACK One 工作流叢集控制檯 [ 7]
  2. 基礎資訊,開啟工作流控制檯(Argo) ,並訪問進入頁面
  3. 左側選單欄 Cluster Workflow Templates,單擊 ci-go-v1 預置模板進入詳情頁
  4. 單擊+ SUBMIT,在右側填入您的引數,單擊下方+ SUBMIT

引數說明:

執行完以後,可在 Argo UI 的 workflow 詳情頁檢視執行情況,如下所示:

總結

ACK One Serverless Argo 工作流作為全託管的 Argo 工作流服務,可以幫助您實現更大規模、具有更快的執行速度、及更低成本的 CI Pipeline,與 ACK One GitOps [ 8] (Argo CD)、Argo Event 等事件驅動架構可以構建完整的自動化 CI/CD Pipeline。

歡迎加入 ACK One 客戶交流釘釘與我們一同交流。(釘釘群號:35688562

相關連結:

[1] Argo Workflows

https://argoproj.github.io/argo-workflows/

[2] ACK One Serverless Argo 工作流**

https://help.aliyun.com/zh/ack/distributed-cloud-container-platform-for-kubernetes/user-guide/overview-12

[3] 搶佔式 ECI 例項

https://help.aliyun.com/zh/eci/use-cases/run-jobs-on-a-preemptible-instance?spm=a2c4g.11186623.0.i7

[4] BuildKit

https://github.com/moby/buildkit

[5] BuildKit Cache

https://github.com/moby/buildkit?tab=readme-ov-file#cache

[6] 最佳實踐

https://help.aliyun.com/zh/ack/distributed-cloud-container-platform-for-kubernetes/use-cases/building-a-ci-pipeline-of-golang-project-based-on-workflow-cluster

[7] ACK One 工作流叢集控制檯

https://account.aliyun.com/login/login.htm?oauth_callback=https%3A%2F%2Fcs.console.aliyun.com%2Fone%3Fspm%3Da2c4g.11186623.0.0.555018e1SiD2lC#/argowf/cluster/detail

[8] ACK One GitOps

https://help.aliyun.com/zh/ack/distributed-cloud-container-platform-for-kubernetes/user-guide/gitops-overview

相關文章