当前位置: 首页 > 知识库问答 >
问题:

如何用带管道的Docker缓存本地Maven库?

宰烈
2023-03-14

我想在Docker容器中运行我的Maven构建。我不想在每次构建时都上传所有依赖项,所以我尝试装载主机的本地Maven存储库,如使用Docker with Pipeline:

为容器缓存数据

[...]

Pipeline支持添加传递给Docker的自定义参数,允许用户指定要挂载的自定义Docker卷,该卷可用于在Pipeline运行之间缓存代理上的数据。以下示例将使用maven容器在Pipeline运行之间缓存~/. m2,从而避免需要为Pipeline的后续运行重新下载依赖项。

pipeline {
    agent {
        docker {
            image 'maven:3-alpine'
            args '-v $HOME/.m2:/root/.m2'
        }
    }
    stages {
        stage('Build') {
            steps {
                sh 'mvn -B'
            }
        }
    }
}

密码

pipeline {
    agent {
        docker { 
            image 'maven:3-alpine' 
            args '-v /home/jenkins/.m2:/root/.m2'
        }    
    }
    stages {
        stage('Build') {
            steps {
                sh 'mvn -B clean verify'
            }
        }
    }   
}

原木

Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on jenkins-docker in /home/jenkins/workspace/Test/Docker Test@2
[Pipeline] {
[Pipeline] sh
+ docker inspect -f . maven:3-alpine
.
[Pipeline] withDockerContainer
jenkins-docker does not seem to be running inside a container
$ docker run -t -d -u 1000:1000 -v /home/jenkins/.m2:/root/.m2 -w "/home/jenkins/workspace/Test/Docker Test@2" -v "/home/jenkins/workspace/Test/Docker Test@2:/home/jenkins/workspace/Test/Docker Test@2:rw,z" -v "/home/jenkins/workspace/Test/Docker Test@2@tmp:/home/jenkins/workspace/Test/Docker Test@2@tmp:rw,z" -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** maven:3-alpine cat

[...]

[DEBUG] Reading global settings from /usr/share/maven/conf/settings.xml
[DEBUG] Reading user settings from ?/.m2/settings.xml
[DEBUG] Reading global toolchains from /usr/share/maven/conf/toolchains.xml
[DEBUG] Reading user toolchains from ?/.m2/toolchains.xml
[DEBUG] Using local repository at /home/jenkins/workspace/Test/Docker Test@2/?/.m2/repository
[DEBUG] Using manager EnhancedLocalRepositoryManager with priority 10.0 for /home/jenkins/workspace/Test/Docker Test@2/?/.m2/repository

问题

构建~/m2目录为空后,没有添加文件/目录。所有文件都添加在/home/jenkins/workspace/Test/DockerTest@2/? /.m2下(Test是文件夹的名称,Docker Test是管道的名称)。

问题是这个目录只用于这个特定的管道,而不用于其他管道,所以我不能与不同的管道/作业共享本地Maven存储库。

还有我的设置。不使用xml,因为它保存在~/m2

是否有任何解决方案可以使用 Docker 与不同的管道共享本地 Maven 存储库和 Maven 设置?

共有3个答案

葛安和
2023-03-14

获取 Jenkins 管道以将 Docker 容器用于 Jenkins 代理,以及用于构建以共享 Maven 本地存储库非常棘手,因为有两个问题需要解决:共享本地存储库文件,以及确保文件具有可用权限。

我创建了一个Docker卷来保存共享文件:

docker volume create maven-cache

然后告诉 Jenkins 将该 Docker 卷挂载到适合每个代理的位置,方法是让它为其 Docker 运行命令提供 --mount 选项。这使得泊坞站卷可用...但归用户所有,而不是运行代理的 jenkins 用户。

解决权限问题的一个复杂之处是,Jenkins将使用Jenkins UID < code > docker run 您的映像,而您无法知道该UID将是什么。正如我在其他地方提到的,您可以使用一些shell脚本和RUN命令为您的代理映像设置< code>jenkins用户名(和< code>docker组名,如果需要的话),来解决这个问题。

您可以通过将sudo添加到Docker映像中,并配置映像以允许jenkinsuser在没有密码的情况下运行 命令来解决权限问题。然后,早期的Jenkins管道步骤可以使用 sudo在共享装载中创建一个合适的目录来存放本地存储库,并将该目录的所有者更改为 Jenkins

最后,您可以设置一个Maven设置文件供Jenkins代理使用,它告诉Maven使用共享的本地存储库。

我的Jenkinsfile如下:

pipeline {
    agent {
        dockerfile {
            filename 'Dockerfile.jenkinsAgent'
            additionalBuildArgs  '--build-arg JENKINSUID=`id -u jenkins` --build-arg JENKINSGID=`id -g jenkins` --build-arg DOCKERGID=`stat -c %g /var/run/docker.sock`'
            args '-v /var/run/docker.sock:/var/run/docker.sock --mount type=volume,source=maven-cache,destination=/var/cache/maven -u jenkins:docker'
       }
    }
    stages {
...
        stage('Prepare') {
            steps {
                sh '[ -d /var/cache/maven/jenkins ] || sudo -n mkdir /var/cache/maven/jenkins'
                sh 'sudo -n chown jenkins /var/cache/maven/jenkins'
...
                sh 'mvn -B -s maven-jenkins-settings.xml clean'
            }
        }

后来使用 Maven 的步骤也说 mvn -B -s maven-jenkins-设置.xml ...

我的Dockerfile。jenkinsAgent如下:

FROM debian:stretch-backports
ARG JENKINSUID
ARG JENKINSGID
ARG DOCKERGID

# Add Docker CE
RUN apt-get -y update && \
 apt-get -y install \
   apt-transport-https \
   ca-certificates \
   curl \
   gnupg \
   lsb-release \
   software-properties-common

RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"

RUN apt-get -y update && \
 apt-get -y install \
   docker-ce \
   docker-ce-cli \
   containerd.io

# Add the build and test tools and libraries
RUN apt-get -y install \
   ... \
   maven \
   sudo \
   ...

# Set up the named users and groups
# Installing docker-ce will already have added a "docker" group,
# but perhaps with the wrong ID.
RUN groupadd -g ${JENKINSGID} jenkins
RUN groupmod -g ${DOCKERGID} docker
RUN useradd -c "Jenkins user" -g ${JENKINSGID} -G ${DOCKERGID} -M -N -u ${JENKINSUID} jenkins
# Allow the build agent to run root commands if it *really* wants to:
RUN echo "jenkins ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers

(如果Jenkins管道本身不运行Docker命令,则可以删除用于安装Docker的run命令,但随后必须groupaddDocker组,而不是groupmod

Jenkins代理的Maven设置文件(< code > Maven-Jenkins-settings . XML )如下所示:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                          https://maven.apache.org/xsd/settings-1.0.0.xsd">
    <localRepository>/var/cache/maven/jenkins</localRepository>
    <interactiveMode>false</interactiveMode>
</settings>
钱朝明
2023-03-14

您可以看到我的答案相关,但使用Gradle配置。

正如你所说,在我的基本映像中,Jenkins 使用用户 1002 运行 Docker html" target="_blank">容器,并且没有用户定义。您必须配置 Maven 变量用户.home 才能将依赖项放在那里。可以通过将 user.home 作为管道中的环境变量包含在JAVA_OPTIONS来执行此操作。还应包括MAVEN_CONFIG

environment {
  JAVA_TOOL_OPTIONS = '-Duser.home=/var/maven'
  SETTINGS = credentials('your-secret-file')
}

并创建一个卷来缓存依赖项:

docker {
    image 'maven:3.3.9-jdk-8-alpine'
    args '-v $HOME:/var/maven'
    reuseNode true
}

更新:忘记告诉您,您可以将settings.xml放在秘密文件中,以便使用“最小暴露原则”来限制Jenkins管道中的凭据暴露。此外,我们正在配置个人凭据,例如,这是我们为每个用户配置Nexus凭据的方式。查看Jenkins留档,了解如何在凭据中上传秘密文件:

sh 'mvn -s $SETTINGS -B clean verify'

UPDATE2:我没有使用声明性管道,所以我的管道看起来像:

            withCredentials([
                 file(credentialsId: 'settings-xml', variable: 'SETTINGS')]) {
                    stage('Deploy') {
                        gitlabCommitStatus(name: 'Deploy') {
                            // Upload the Snapshot artefact
                            sh "mvn -s $SETTINGS clean verify"
                        }
                    }
                }

它似乎也可以用于声明性管道,但我自己没有测试它。

咸弘雅
2023-03-14

我找到了一个解决方法,请参阅本地设置。Jenkins代理未拾取的xml:

这个问题与jenkins用于运行容器的-uuid:gid有关。正如您可能知道的那样,您正在运行的映像只创建了用户root,因此当jenkins传递自己的uid和gid时,没有用户条目,因此没有为其声明$HOME

如果只想独立于用户运行生成,则可以使用以下作为代理:

agent {
        docker {
            image 'maven:3-alpine'
            args '-v $HOME/.m2:/root/.m2:z -u root'
            reuseNode true
        }
}

几个注意事项:

    < li >如果您注意到我正在使用带有< code>z标记的卷,因为我将使用root进行构建,所以我需要告诉docker该卷将在其他容器之间共享,然后阻止来自我的jenkins容器的访问被拒绝(使用用户jenkins而不是root运行) < li >我告诉jenkins重新设置节点,因此使用同一映像的任何其他阶段都将在同一容器上执行(这只是为了加快配置时间)

原木

[DEBUG] Reading global settings from /usr/share/maven/conf/settings.xml
[DEBUG] Reading user settings from /root/.m2/settings.xml
[DEBUG] Reading global toolchains from /usr/share/maven/conf/toolchains.xml
[DEBUG] Reading user toolchains from /root/.m2/toolchains.xml
[DEBUG] Using local repository at /root/.m2/repository
[DEBUG] Using manager EnhancedLocalRepositoryManager with priority 10.0 for /root/.m2/repository

不幸的是,本地存储库/home/jenkins/. m2中的文件现在归用户root而不是用户jenkins所有。这可能会导致其他问题。

 类似资料:
  • Serenity 提供一些缓存抽象和实用功能让你更容易地使用本地缓存。 术语 本地(local) 的意思是指在本地内存中缓存项目(因此没有涉及到序列化)。 当你的应用程序在网站群(web farm) 中部署时,本地缓存可能还不够或者有时合适。我们将在 分布式缓存 章节中讨论该场景。

  • 我正在做一个有大约200MB依赖项的项目,我希望避免由于带宽有限而导致的无用上传。 当我推我的Dockerfile(我一会儿会附上)时,我总是有一个~200MB的上传,即使我没有碰pom.xml: 这个Dockerfile应该生成一个200MB的fatJAR,包括所有依赖项,这就是为什么每次都会发生~200MB的上传。我想要实现的是构建一个包含所有依赖项的层,并“告诉”打包阶段不要将依赖项JAR包

  • 问题内容: 我对Docker的层缓存表现出色感到惊讶,但我也想知道它如何确定是否可以使用缓存的层。 让我们以这些构建步骤为例: 例如,它如何知道可以使用缓存的层,但可以为其创建新层呢? 问题答案: 在Dockerfile最佳实践构建缓存部分中相当详尽地解释了构建缓存过程。 * 从缓存中已存在的基本映像开始,将下一条指令与从该基本映像派生的所有子映像进行比较,以查看是否其中一个是使用完全相同的指令构

  • 我有一个Jenkins管道,我需要在其中登录到两个不同的docker存储库。我知道如何使用以下命令对一个存储库进行身份验证 但不知道如何为超过1次的回购做这件事?

  • 问题内容: 我有一个Jenkins实例,该实例使用从requirements.txt中附带的PyPI包来构建我的项目。但是,与TravisCI构建一样,每次从头开始构建都非常耗时,并且意味着构建要花费> 4-5分钟,这比理想情况要慢得多。 我正在寻找的是一种在本地缓存下载的软件包的方法,因此,当启动具有相同依赖项的构建时,不必从PyPI获取它就可以在本地获取,而当版本发生更改时,它可以获取上游软件

  • dnsmasq如何配置本地的缓存时间过期后重新向指定的DNS(server=)服务器发起请求,以获取该域名的IP地址?我使用两台dnsmasq,一台为dns解析服务,提供域名解析ip,另外一台在/etc/dnsmasq.d/address.config中配置server=该机器ip(那台机器提供DNS服务)。我在提供服务的机器上将A记录改了,dig出来的还是之前的A记录。如何在dnsmasq中配置