All Products
Search
Document Center

Container Service for Kubernetes:Build a CI pipeline for a Golang project using a workflow cluster

Last Updated:Mar 26, 2026

ACK One workflow clusters run hosted Argo Workflows with built-in elasticity and auto scaling. This topic shows you how to set up a Continuous Integration (CI) pipeline that clones a Go repository, runs tests, builds a container image, and pushes it to a registry — with two layers of persistent caching to keep each run fast.

image

How it works

The pipeline uses a predefined ClusterWorkflowTemplate named ci-go-v1. Each run executes three steps as a directed acyclic graph (DAG):

  1. Git clone and checkout — clones the repository, checks out the target branch, and writes the short commit ID to a shared file.

  2. Go test — runs go test -v ./... against the cloned source. The Go module cache is stored in the /pkg/mod directory on a NAS file system. Modules downloaded in one run are reused in the next, so go test and go build skip re-downloading dependencies.

  3. Build and push image — uses buildctl-daemonless.sh (BuildKit) to build the image and push it to Container Registry Enterprise Edition (ACR EE). BuildKit caches image layers in the registry under the :buildcache tag. Unchanged layers are not rebuilt on subsequent runs.

Both caches persist across pipeline runs: the Go module cache on NAS is shared across all tasks within a single run, and the BuildKit layer cache lives in the registry between runs.

Prerequisites

Before you begin, ensure that you have:

Step 1: Create registry credentials in the workflow cluster

BuildKit pulls and pushes images using a Secret named docker-config. Create it in the namespace where you plan to submit pipelines.

Important

The Secret and the NAS volume (created in Step 2) must belong to the same namespace as the pipeline you submit.

  1. Configure access credentials for your ACR EE instance. See Configure access credentials.

  2. Run the following command to create the Secret. Replace $repositoryDomain, $username, and $password with your ACR EE domain and credentials.

    kubectl create secret generic docker-config \
      --from-literal="config.json={\"auths\": {\"$repositoryDomain\": {\"auth\": \"$(echo -n $username:$password|base64)\"}}}"

Step 2: Mount a NAS volume to the workflow cluster

The NAS volume serves two purposes:

  • Data sharing between tasks: the cloned repository and commit ID file are written here by the git-checkout-pr task and read by downstream tasks.

  • Go module cache persistence: the cache in /pkg/mod is reused across pipeline runs. The first run downloads modules; subsequent runs skip that step.

NAS uses ReadWriteMany access mode so all pipeline tasks can read and write the volume concurrently.

  1. Get the mount target address of your NAS file system. See Manage mount targets.

  2. Apply the following manifest to create a persistent volume (PV) and persistent volume claim (PVC) in the workflow cluster. Replace <your nas filesystem mount point address> with your actual mount target address. For more information, see Use NAS volumes.

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv-nas
      labels:
        alicloud-pvname: pv-nas
    spec:
      capacity:
        storage: 100Gi
      accessModes:
        - ReadWriteMany          # Required: all pipeline tasks read and write this volume concurrently
      csi:
        driver: nasplugin.csi.alibabacloud.com
        volumeHandle: pv-nas
        volumeAttributes:
          server: <your nas filesystem mount point address>
          path: "/"
      mountOptions:
      - nolock,tcp,noresvport
      - vers=3
    ---
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: pvc-nas             # The predefined template references this name — do not change it
    spec:
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 100Gi
      selector:
        matchLabels:
          alicloud-pvname: pv-nas

Step 3: Submit a pipeline

The predefined ci-go-v1 template is available in all workflow clusters. Use the Argo workbench or the Argo CLI to submit a pipeline run.

The examples below use a public Git repository. For a private repository, add a step that clones using stored credentials before the git clone and checkout step.

Submit from the Argo workbench

  1. Log on to the ACK One console. In the left-side navigation pane, choose the workflow cluster you created.

  2. On the Cluster Information page, click the Basic Information tab. In the Common Operations section, click Workflow Console (Argo).

  3. In the Argo workbench, click Cluster Workflow Templates in the left-side navigation pane, then click the template named ci-go-v1.

    image

  4. Click + SUBMIT in the upper-right corner. Configure the parameters in the panel that appears (see Template parameters for descriptions), then click + SUBMIT.

    image

  5. You can view the status of the pipeline on the Workflows details page.

    image

Submit using the Argo CLI

  1. Create a file named workflow.yaml with the following content. Update the parameter values to match your environment.

    apiVersion: argoproj.io/v1alpha1
    kind: Workflow
    metadata:
      generateName: ci-go-v1-
      labels:
        workflows.argoproj.io/workflow-template: ackone-ci
    spec:
      arguments:
        parameters:
        - name: repo_url
          value: https://github.com/ivan-cai/echo-server.git
        - name: repo_name
          value: echo-server
        - name: target_branch
          value: main
        - name: container_image
          value: "test-registry.cn-hongkong.cr.aliyuncs.com/acs/echo-server"
        - name: container_tag
          value: "v1.0.0"
        - name: dockerfile
          value: ./Dockerfile
        - name: enable_suffix_commitid
          value: "true"
        - name: enable_test
          value: "true"
      workflowTemplateRef:
        name: ci-go-v1
        clusterScope: true
  2. Submit the pipeline:

    argo submit workflow.yaml

Template parameters

The ci-go-v1 template accepts the following parameters.

Parameter Description Default Example
repo_url URL of the Git repository https://github.com/ivan-cai/echo-server.git
repo_name Repository name (used as the local directory name) echo-server
target_branch Branch to check out main main
container_image Image to build and push. Format: <ACR EE domain>/<namespace>/<repository> test-registry.cn-hongkong.cr.aliyuncs.com/acs/echo-server
container_tag Base image tag v1.0.0 v1.0.0
dockerfile Relative path to the Dockerfile from the repository root ./Dockerfile ./Dockerfile
enable_suffix_commitid Append the short commit ID to the image tag, producing <container_tag>-<commit_id>. Set to false to use the base tag only. true true
enable_test Run the Go test step. Set to false to skip testing and go straight to build. true true

Predefined template reference

The full template is provided below for customization. Key sections are annotated.

apiVersion: argoproj.io/v1alpha1
kind: ClusterWorkflowTemplate
metadata:
  name: ci-go-v1
spec:
  entrypoint: main
  volumes:
  - name: run-test
    emptyDir: {}          # Temporary volume scoped to the run-test task
  - name: workdir
    persistentVolumeClaim:
      claimName: pvc-nas  # NAS volume: shared across all tasks, persists Go module cache
  - name: docker-config
    secret:
      secretName: docker-config  # Registry credentials created in Step 1
  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 --track origin/{{workflow.parameters.target_branch}} || git checkout {{workflow.parameters.target_branch}}
                    git pull

                    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"  # Skipped when enable_test=false
            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  # Point Go to the NAS-backed module cache

                    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  # e.g. v1.0.0-abc1234
                    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 \  # Save layer cache to registry
                    --import-cache type=registry,ref={{workflow.parameters.container_image}}:buildcache            # Reuse layer cache from previous runs

                    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

What's next

  • Customize ci-go-v1 — copy the template and modify steps (for example, add a lint step or change the Go version) to create your own ClusterWorkflowTemplate.

  • Trigger pipelines automatically — integrate argo submit with your Git platform's webhook to run the pipeline on every push or pull request.

References