我如何在 gitlab-ci 中同时运行循环?

Posted

技术标签:

【中文标题】我如何在 gitlab-ci 中同时运行循环?【英文标题】:how do i run loops simultaneously in gitlab-ci? 【发布时间】:2022-01-08 12:06:27 【问题描述】:

我的 gitlab-ci 中有以下脚本,并且希望同时运行循环,有人知道这样做的好方法吗?让它们同时运行

注意该作业是一项手动作业,我正在寻找单击一个按钮以循环遍历 bash 脚本中的所有包,如下所示

    when: manual
    script:
        - |-
            for PACKAGE in name1 name2; do
                export IMAGE="$CI_REGISTRY/$GITLAB_REPO/$PACKAGE:$BUILD_TAG"
                docker build -t $IMAGE -f $PACKAGE/Dockerfile .
                docker push $IMAGE
            done

目前它首先为name1 运行,然后在完成之后运行name2。由于没有依赖关系,我希望同时运行两者

这是我从 SO => (https://unix.stackexchange.com/a/216475/138406) 上的答案中尝试的结果

    when: manual
    script:
        - |-
            task()
                export IMAGE="$CI_REGISTRY/$GITLAB_REPO/$1:$BUILD_TAG"
                docker build -t $IMAGE -f $1/Dockerfile .
                docker push $IMAGE         
            
            for PACKAGE in name1 name2; do
                task "$PACKAGE" &
            done

这适用于常规 bash 脚本,但是当我将它与 gitlab-ci 一起使用时,它没有按预期运行,甚至没有运行任何命令,只是立即成功完成工作

有谁愿意帮助解决问题所在以及如何解决这个问题?

【问题讨论】:

研究“如何在 bash 中同时运行...”,你会得到相同的答案。 如果你知道答案,可以在下面发布答案吗? 【参考方案1】:

为了实现您的用例,我建议您“并行化”构建使用多个专用 GitLab-CI 作业,而不是使用多个 bash 作业单个 GitLab-CI 作业。

概念验证:

stages:
  - push

.push-template:
  stage: push
  image: docker:latest
  services:
    - docker:dind
  variables:
    IMAGE: "$CI_REGISTRY/$GITLAB_REPO/$PACKAGE:$BUILD_TAG"
    # where $PACKAGE should be provided by the child jobs...
  before_script: |
    if [ -z "$PACKAGE" ]; then
      echo 'Error: variable PACKAGE is undefined' >&2
      false
    fi
    # just for completeness, this may be required:
    echo "$CI_JOB_TOKEN" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
  script:
    - docker build -t "$IMAGE" -f "$PACKAGE/Dockerfile" .
    - docker push "$IMAGE"
    - docker logout "$CI_REGISTRY" || true

push-name1:
  extends: .push-template
  variables:
    PACKAGE: name1

push-name2:
  extends: .push-template
  variables:
    PACKAGE: name2

有关extends 关键字的详细信息,请参阅.gitlab-ci.yml reference manual。

【讨论】:

这种方法的问题是我失去了单击一个按钮来运行该作业,现在这将是 2 个作业..这是一项手动作业,仅供参考...所以如果可以在 bash 中完成,这就是我所追求的 好的,您能否更具体地说明您的约束并在您的问题中添加详细信息?顺便说一句,我绝对相信在 bash 中不可能做到这一点,因为例如你可能在 gitlab.com 上有 $(nproc) = 1,如果你明白我的意思……而 GitLab CI 将通过使用多个作业让你拥有更大的并行度。 when: manual 是唯一的约束..这是自托管的 gitlab ......所以不是 gitlab.com 好的,但您不能将多个工作标记为when: manual 吗?这样,您将在您认为合适的时候运行这几个作业。 (在doc 中,我没有看到任何阻止从事多项体力工作的因素)。 但也许您对拥有多个手动作业感兴趣,并且能够通过仅在按钮上单击一次 来运行这些 n 个作业? (不在 n 按钮上)【参考方案2】:

马上就成功了

这就是在后台运行的意思——这意味着主进程将立即继续。您必须等待后台进程完成。

    - |-
        task()
            export IMAGE="$CI_REGISTRY/$GITLAB_REPO/$1:$BUILD_TAG"
            docker build -t $IMAGE -f $1/Dockerfile .
            docker push $IMAGE         
        
        for PACKAGE in name1 name2; do
            task "$PACKAGE" &
        done
        wait

但这不会捕获任何错误,这将导致问题未被发现。您将不得不收集 pid 并单独等待:

...
        childs=""
        for package in name1 name2; do
            task "$package" &
            childs="$childs $!"
        done
        for pid in $childs; do
            if ! wait "$pid"; then
                echo "Process with pid=$pid failed"
                kill $childs
                wait
                exit 1
            fi
        done

但无论如何,这很麻烦,而且是在重新发明***。安装 GNU xargs(或者更好的 parallel)并确保你的 docker 容器有 bash shell。然后,只需导出函数并使用 xargs 在子进程中运行它:

...
export -f task
printf "%s\n" name1 name2 | xargs -P0 -d '\n' bash -xeuo pipefail -c 'func "$@"' --

您可能想研究https://man7.org/linux/man-pages/man1/xargs.1.html 甚至https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html。

当然,不要在 .gitlab-ci yaml 文件中编写长脚本 - 将其全部移至专用脚本文件,以便您可以在本地测试运行。用 shellcheck 检查你的脚本。无论如何 - 使用docker-compose 也可能更简单。

无论如何,这都很奇怪,当然无论如何我都不会那样做。 Gitlab-runner 已经是为您提供并行化的工具 - 它在同一阶段并行运行多个作业。只需运行两个任务。

 .todo:
    script:
        export IMAGE="$CI_REGISTRY/$GITLAB_REPO/$CI_JOB_NAME:$BUILD_TAG"
        docker build -t $IMAGE -f $CI_JOB_NAME/Dockerfile .
        docker push $IMAGE   

 name1:
    extends: .todo
 name2:
    extends: .todo

这种方法将为您的管道提供关于哪些特定任务失败的可见指示,因此您无需滚动查看两个并行运行的进程的不可读日志。一份工作,一项任务。

【讨论】:

+1 以获得这个全面的答案(另请参阅mine 以查看更多关于 GitLab-CI 多作业解决方案的“样板”:) - BTW @KamilCuk 似乎有错字? ($$CI_JOB_NAME$CI_JOB_NAME)

以上是关于我如何在 gitlab-ci 中同时运行循环?的主要内容,如果未能解决你的问题,请参考以下文章

Gitlab-ci:如果 MR 存在则触发 merge_request 分离管道,如果不存在则触发源分支管道。这两条管道不应同时运行

gitlab-ci.yaml:在多行 python docker 镜像中运行 python

获取使用 .gitlab-ci.yml 运行的 Windows Docker 容器

构建后的测试将在 gitlab-ci 上的新环境中运行

如何在 Gitlab-CI 上安装 Python

如何在不中断 tkinter 主循环的情况下运行一个函数,同时将该函数的信息发送到我的主循环中的小部件?