在 gitlab-ci 作业中将文件从摇杆容器传递到乳胶容器

Posted

技术标签:

【中文标题】在 gitlab-ci 作业中将文件从摇杆容器传递到乳胶容器【英文标题】:Passing files from a rocker container to a latex container within a gitlab-ci job 【发布时间】:2021-07-10 16:13:54 【问题描述】:

我想使用 Gitlab CI 编译一篇 Latex 文章,如 this answer on tex.stackexchange 中所述(gitlab documentation for artifacts 中显示了类似的 pdf 生成示例)。我使用期刊编辑提供的特殊乳胶模板。我的 Latex 文章包含使用 R 统计软件制作的数据。 R 和 Latex 是两个具有很多依赖关系的大型软件安装,因此我决定使用两个单独的容器进行构建,一个用于使用 R 进行统计分析和可视化,另一个用于将 Latex 文档编译为 pdf。

这里是.gitlab-ci.yml的内容:

knit_rnw_to_tex:
  image: rocker/verse:4.0.0
  script:
  - Rscript -e "knitr::knit('article.Rnw')"
  artifacts:
    paths:
      - figure/

compile_pdf:
  image: aergus/latex
  script:
  - ls figure
  - latexmk -pdf -bibtex -use-make article.tex
  artifacts:
    paths:
      - article.pdf

在 R "rocker" 容器中执行的knit_rnw_to_tex 作业成功,我可以从 gitlab "jobs" 页面下载图形工件。第二个工作compile_pdf的问题是ls figure给我显示一个空文件夹,Latex文章编译失败,因为缺少数字。

应该可以根据this answer 和well explained forum post 使用工件在作业之间传递数据,但它们只使用一个容器来处理不同的作业。在我的情况下它不起作用。可能是因为我使用了两个不同的容器? 另一种解决方案是仅使用rocker/tidyverse 容器并在其中安装latexmk,但apt install latexmk 的安装因未知原因而失败。也许是因为它有上百个依赖项,这对 gitlab-CI 来说太多了? 根据that answer,“dependencies”关键字可能会有所帮助,但是当我使用它时,工件仍然不可用。 如何将工件从一项作业传递到另一项作业? 我应该按照docs.gitlab.com / caching 中的说明使用缓存吗?

【问题讨论】:

你是把它分成两个独立的项目构建还是一个项目和两个阶段? 一个项目和两个阶段在.gitlab-ci.yml 中可见。也许我应该做一个简单的可重现的例子。 【参考方案1】:

这两个不同的图像不是您的问题的原因。工件保存在一个图像中(这似乎有效),然后在另一个图像中恢复。因此,我建议不要构建(和维护)单个映像,因为这里没有必要这样做。

您遇到问题的原因是您缺少通知 gitlab 作业之间依赖关系的构建阶段。因此,我建议您在 .gitlab-ci.yml 中指定阶段及其各自的工作:

stages:
  - do_stats
  - do_compile_pdf

knit_rnw_to_tex:
  stage: do_stats
  image: rocker/verse:4.0.0
  script:
  - Rscript -e "knitr::knit('article.Rnw')"
  artifacts:
    paths:
      - figure/

compile_pdf:
  stage: do_compile_pdf
  image: aergus/latex
  script:
  - ls figure
  - latexmk -pdf -bibtex -use-make article.tex
  artifacts:
    paths:
      - article.pdf

上下文:

默认情况下,如果您添加了相应的规范,之前构建阶段的所有工件都将在以后的阶段中可用。

如果不指定任何阶段,gitlab会将所有作业放入默认的test阶段并并行执行,假设它们是独立的并且不需要彼此的工件。它仍将存储工件,但不会使它们在作业之间可用。这大概是导致您的问题的原因。

至于cache:工件是您在构建阶段之间传递文件的方式。缓存很好,缓存。实际上,它们用于外部包之类的东西,以避免必须多次下载它们,see here。在有多个不同跑步者的情况下,缓存有些不可预测。它们仅用于性能原因,并且使用 cache 在作业之间传递文件而不是使用工件系统是一种巨大的反模式。

编辑:我不确切知道您的knitr 设置是什么,但是如果您从article.Rnw 生成article.tex,那么您可能还需要将其添加到您的artifacts 中。

另外,services 用于测试数据库的 mysql 服务器,或用于构建 docker 映像的 dind (docker in docker) 守护进程。在您的情况下,这应该不是必需的。同样,您应该不需要从默认值更改任何运行器配置(在它们各自的config.toml 中)。

Edit2:我添加了一个 MWE here,它适用于我的 gitlab 设置。

【讨论】:

【参考方案2】:

感谢您的评论,因为我想确定您是如何做到的。示例也会有所帮助,但我现在将是通用的(使用docker)。

要运行多个容器,您需要一个 (The Docker executor)

引用documentation就可以了:

Docker 执行器在与 GitLab CI 一起使用时,连接到 Docker 使用引擎并在单独的隔离容器中运行每个构建 在.gitlab-ci.yml 和中设置的预定义图像 按照config.toml.

工作流程

Docker 执行器将作业分为多个步骤:

准备:创建并启动服务。 作业前:克隆、恢复缓存和下载先前阶段的工件。这是在一个特殊的 Docker 映像上运行的。 工作:用户构建。这是在用户提供的 Docker 映像上运行的。 作业后:创建缓存,将工件上传到 GitLab。这是在一个特殊的 Docker 映像上运行的。

您的config.toml 可能如下所示:

[runners.docker]
  image = "rocker/verse:4.0.0"
  builds_dir = /home/builds/rocker

[[runners.docker.services]]
  name = "aergus/latex"
  alias = "latex"

从上面的链接文档:

image 关键字

image 关键字是本地 Docker 引擎中存在的 Docker 镜像的名称(列出所有带有 docker 镜像的镜像)或可以在 Docker Hub 中找到的任何镜像。有关镜像和 Docker Hub 的更多信息,请阅读 Docker 基础文档。

简而言之,我们指的是 Docker 映像,它将用于创建一个容器,您的构建将在该容器上运行。

如果你不指定namespace,Docker 暗示包含所有官方镜像的库。这就是为什么你会在 .gitlab-ci.yml 和 config.toml 中多次看到库部分被省略的原因。例如,您可以定义一个像image: ruby:2.6 这样的图像,它是图像的快捷方式:library/ruby:2.6

然后,对于每个 Docker 镜像,都有标签,表示镜像的版本。这些是在图像名称后用冒号 (:) 定义的。例如,对于 Ruby,您可以在 docker hub 看到支持的标签。如果您不指定标签(如image: ruby),则暗示最新。

您选择通过image 指令运行构建的image 必须在其操作系统PATH 中有一个工作shell。受支持的 shell 是用于 Linux 的 shbashpwsh(自 13.9 起),以及用于 Windows 的 PowerShell。 GitLab Runner 无法使用底层 OS 系统调用(例如 exec)执行命令。

services 关键字

services 关键字定义了另一个在构建期间运行的 Docker 映像,并链接到 image 关键字定义的 Docker 映像。这允许您在构建期间访问服务映像。

service 映像可以运行任何应用程序,但最常见的用例是运行数据库容器,例如 mysql。使用现有映像并将其作为附加容器运行比每次构建项目时都安装 mysql 更容易、更快捷。

您可以在CI services examples的相关文档中看到一些广泛使用的服务示例。

如果需要,您可以为每个服务分配一个alias

至于你的问题:

应该可以使用工件在作业之间传递数据 根据这个答案和这个解释清楚的论坛帖子,但是 他们只使用一个容器来完成不同的工作。它在我的不起作用 案子。可能是因为我使用了两个不同的容器?

构建和缓存存储(来自文档)

默认情况下,Docker 执行器将所有构建存储在 /builds/<namespace>/<project-name> 中,并将所有缓存存储在 /cache 中(在容器内)。您可以通过在config.toml[[runners]] 部分下定义builds_dircache_dir 选项来覆盖/builds/cache 目录。这将修改数据在容器内的存储位置。

如果您修改/cache 存储路径,您还需要确保通过在volumes = ["/my/cache/"]config.toml[runners.docker] 部分下定义该目录来将该目录标记为持久化。

builds_dir -> 构建存储在所选执行程序上下文中的目录的绝对路径。例如,本地、Docker 或 SSH。

The [[runners]] section documentation

您可能已经注意到,我已将您的toml 文件中的build_dir 自定义为/home/builds/rocker,请将其调整为您自己的路径。

如何将工件从一项作业传递到另一项作业?

您可以使用build_dir 指令。第二种选择是使用Job Artifacts API。

我应该按照 docs.gitlab.com/caching 中的说明使用缓存吗?

是的,您应该使用cache 来存储项目依赖项。优点是您只需从 Internet 获取一次依赖项,然后后续运行会快得多,因为它们可以跳过此步骤。 Artifacts 用于在构建阶段之间共享结果。

我希望现在更清楚,我已经为您指明了正确的方向。

【讨论】:

谢谢,services 关键字听起来很有用。我希望latex 容器提供编译Latex 文档的服务。只需弄清楚如何将latexmk 命令发送到该服务。 @PaulRougieux 你可以这么简单。在您的.gitlab-ci.yml 中设置command,例如:services: - name: aergus/latex command: ["/usr/bin/latexmk", "run"] 谢谢,但我需要将数据发送到该服务。抱歉,Docker 容器之间的网络仍然让我感到困惑。我现在很忙,但我会在本周末之前尝试制作一个可重复的示例。 @PaulRougieux 去过那里,所以我完全理解这一点。慢慢来。

以上是关于在 gitlab-ci 作业中将文件从摇杆容器传递到乳胶容器的主要内容,如果未能解决你的问题,请参考以下文章

如何将存储库中的文件复制到用于作业的 Docker 容器中,在 gitlab-ci.yml

如何在 GitLab CI 的管道中将变量的值从一个作业传递到下一个作业?

Gitlab-CI:测试作业失败

GitLab CI:我的测试作业没有拾取mysql容器

如何排除 gitlab-ci.yml 更改触发作业

从 gitlab-ci dir 中获取所有工件并使其成为工件