云原生时代:从 Jenkins 到 Argo Workflows,构建高效 CI Pipeline

作者:蔡靖

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*

相关推荐
勇往直前plus41 分钟前
centos7利docker compose 快速部署 Elasticsearch + Kibana
elasticsearch·docker·jenkins
奥尔特星云大使10 小时前
《系统规划与管理师教程(第2版)》方法篇 第10章 云原生系统规划 知识点总结
云原生·软考·高级·系规
熙客11 小时前
Kubernetes是如何保证有状态应用数据安全和快速恢复的
mysql·云原生·容器·kubernetes
pursue.dreams12 小时前
Ubuntu安装Jenkins完整教程
linux·ubuntu·jenkins
城南花开时12 小时前
CICD工具,Jenkins or Tekton or Arbess一文全面对比评测
jenkins·tekton
非典型代码12 小时前
Jenkins发不出邮件
运维·servlet·jenkins
Wang's Blog12 小时前
Nestjs框架: 微服务项目工程结构优化与构建方案
微服务·云原生·架构·nestjs
似水流年 光阴已逝14 小时前
Kubernetes Pod 基本原理:全面详解
云原生·容器·kubernetes·pod
阿里云云原生14 小时前
评估工程正成为下一轮 Agent 演进的重点
云原生
wangruofeng14 小时前
为 CI/CD 装上“眼睛”:App 包大小监控的实践
ci/cd·架构