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 的插件生态系统丰富,但这也可能导致插件版本不兼容、更新不及时或安全漏洞等问题,管理插件更新和权限是一个持续的挑战;
- 项目隔离/权限分配方案的缺陷等。
对比如下:
基于 ACK One Serverless Argo 工作流的 CI Pipeline
ACK One Serverless Argo 工作流 [ 2] 作为一款完全遵循社区规范的全托管式 Argo Workflows 服务,致力于应对大规模计算密集型作业,通过集成阿里云 ECI 实现自动扩展和极致弹性、按需扩容以最小化成本,通过使用 spot ECI 可以降低 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 个步骤:
- Git Clone & Checkout:Clone Git 仓库,Checkout 到目标分支;并获取 commit id。
- Run Go Test:通过参数控制是否运行,使用 NAS 存储 Go mod cache 进行加速
-
Build & Push Image:
a. 使用 BuildKit 构建和推送容器镜像,并使用 BuildKit Cache 中 registry 类型 cache 来加速镜像构建;
b. 镜像 tag 默认使用 {container_tag}-{commit_id} 格式,可在提交工作流时通过参数控制是否追加 commit id;
c. 推送镜像的同时,也会推送覆盖其 latest 镜像。
您可执行以下步骤完成 CI Pipeline 的运行,
- 在工作流集群中准备好 ACR EE 的凭据和 NAS 存储卷
- 基于预置模板启动工作流(workflow)运行 CI Pipeline
预置 CI Pipeline 模板
工作流集群中默认已经预置了名为 ci-go-v1 的工作流模板(ClusterWorkflowTemplate),yaml 如下所示
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
- 登录 ACK One 工作流集群控制台 [ 7]
- 在基础信息,开启工作流控制台(Argo) ,并访问进入页面
- 左侧菜单栏 Cluster Workflow Templates,单击 ci-go-v1 预置模板进入详情页
- 单击+ SUBMIT,在右侧填入您的参数,单击下方+ SUBMIT
参数说明:
执行完以后,可在 Argo UI 的 workflow 详情页查看运行情况,如下所示:
ACK One Serverless Argo 工作流作为全托管的 Argo 工作流服务,可以帮助您实现更大规模、具有更快的运行速度、及更低成本的 CI Pipeline,与 ACK One GitOps (Argo CD)、Argo Event 等事件驱动架构可以构建完整的自动化 CI/CD Pipeline