在 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文章编译失败,因为缺少数字。
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 的 sh
、bash
和 pwsh
(自 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_dir
和cache_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