【问题标题】:Setting build args for dockerfile agent using a Jenkins declarative pipeline使用 Jenkins 声明性管道为 dockerfile 代理设置构建参数
【发布时间】:2017-12-01 23:18:15
【问题描述】:

我正在使用声明性管道语法在 docker 容器内执行一些 CI 工作。

我注意到 Jenkins 的 Docker 插件使用主机中 jenkins 用户的用户 id 和组 id 运行容器(即,如果 jenkins 用户的用户 id 为 100 和组 id 为 111,它将运行管道创建一个带有命令docker run -u 100:111 ...的容器。

我遇到了一些问题,因为容器将与不存在的用户一起运行(特别是我遇到了一些用户没有主目录的问题)。所以我想创建一个 Dockerfile,它将接收用户 ID 和组 ID 作为构建参数,并在容器内创建一个适当的 jenkins 用户。 Dockerfile 如下所示:

FROM ubuntu:trusty
ARG user_id
ARG group_id

# Add jenkins user
RUN groupadd -g ${group_id} jenkins
RUN useradd jenkins -u ${user_id} -g jenkins --shell /bin/bash --create-home
USER jenkins

...

dockerfile 代理有一个additionalBuildArgs 属性,所以我可以读取主机中 jenkins 用户的用户 ID 和组 ID,并将它们作为构建参数发送,但我现在遇到的问题是,似乎有在指定代理之前,无法在声明性管道中执行这些命令。我希望我的 Jenkinsfile 是这样的:

// THIS WON'T WORK
def user_id = sh(returnStdout: true, script: 'id -u').trim()
def group_id = sh(returnStdout: true, script: 'id -g').trim()

pipeline {
  agent {
    dockerfile {
      additionalBuildArgs "--build-arg user_id=${user_id} --build-arg group_id=${group_id}"
    }
  }
  stages {
    stage('Foo') {
      steps {
        ...
      }
    }
    stage('Bar') {
      steps {
        ...
      }
    }
    stage('Baz') {
      steps {
        ..
      }
    }
    ...
  }
}

我有什么方法可以做到这一点?我也尝试将管道指令包装在节点内,但管道需要位于文件的根目录。

【问题讨论】:

  • 我觉得这应该用某种标准模板引擎来完成,比如 jinja2。

标签: docker jenkins dockerfile


【解决方案1】:

我验证了尝试在没有节点的情况下分配 user_id 和 group_id 不起作用,正如您所发现的,但这对我分配这些值并稍后访问它们有用:

def user_id
def group_id
node {
  user_id = sh(returnStdout: true, script: 'id -u').trim()
  group_id = sh(returnStdout: true, script: 'id -g').trim()
}

pipeline {
  agent { label 'docker' }
  stages {
    stage('commit_stage') {
      steps {
        echo 'user_id'
        echo user_id
        echo 'group_id'
        echo group_id
      }
    }
  }
}

希望这些也适用于您的 additionalBuildArgs 声明。

在评论中,您指出了在使用它来配置 dockerfile 之前在声明性管道之外计算 user_id 和 group_id 的方法最有可能存在的一个严重缺陷:它发现 user_id 的从站不一定与它用来启动基于 docker 的构建的从站匹配。在保持声明性 Jenkinsfile 约束的同时,我没有任何办法解决这个问题。

您可以通过使用全局代理声明来保证所有阶段都有一个从站:Jenkins declarative pipeline: What workspace is associated with a stage when the agent is set only for the pipeline?

但具有相同标签的多个节点引用并不能保证相同的工作空间:Jenkins declarative pipeline: What workspace is associated with a stage when the agent is set only for the pipeline?

【讨论】:

  • 谢谢!还没有尝试过,但是如何确保管道与用于提取 uid/gid 的节点在同一节点中运行?
  • 可以肯定的是,声明式是不可能的。更新了详细信息和参考的答案。
【解决方案2】:

如果您对 Jenkins 有管理员权限,您可以添加以下两个脚本批准:

staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods execute java.lang.String
staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods getText java.lang.Process

在这个 URI 中:http://${jenkins_host:port}/jenkins/scriptApproval/

这将允许您以这种方式在 master 中执行 shell 命令:

def user = 'id -u'.execute().text
node {
   echo "Hello World ${user}"
}

【讨论】:

    【解决方案3】:

    你也可以像这样添加一个块:

    agent {
        dockerfile {
    
            args '-v /etc/passwd:/etc/passwd -v /etc/group:/etc/group'
        }
    }
    

    这将允许容器拥有正确的用户和组 ID。

    【讨论】:

    • 当我使用它们时,我无法使用 maven [main] ERROR org.apache.maven.cli.MavenCli - Could not create local repository at /mnt/data/jenkins/.m2/repository 构建
    【解决方案4】:

    您也可以使用 args 参数来解决问题。
    Pipeline Syntax中所述:

    docker 还可以选择接受 args 参数,该参数可能包含要直接传递给 docker run 调用的参数。

    在代理中使用 dockerfile 而不是 docker 时,这也是可能的 部分。

    我遇到了和你一样的问题,以下几行对我来说很好:

           agent { 
                dockerfile { 
                    dir 'Docker/kubernetes-cli' 
                    args '-u 0:0' //Forces Container tu run as User Root                    
                    reuseNode true
                }
            }
    

    【讨论】:

    • 谢谢,我花了 6 个小时修复它。感谢上帝,我找到了你的答案。我曾尝试运行 ansible playbook,但在创建临时 ansible 目录时总是遇到权限问题
    • 如果您使用此解决方案,则从该 docker 容器对工作区所做的任何更改也将“以 root 用户身份”进行。例如。如果您将sh 'touch test-file' 作为流水线步骤运行,您的 Jenkins 工作区将拥有由 root 拥有的测试文件——这可能会在 jenkins 稍后尝试清理工作区时导致问题。
    【解决方案5】:

    我相信我们找到了解决这个问题的好方法。

    我们有一个作为 docker 实例运行的 Jenkins 部署,我已经为 /var/jenkins_home 映射了一个卷,并将 .ssh 文件夹添加到 /var/jenkins_home/.ssh

    我们还使用 dockerfile 代理指令在 docker 容器内运行所有构建。 有时我们需要通过 git over ssh 访问我们的一些私有作曲库。

    我们通过安装项目 deps(composer)来利用 docker 图像缓存,这意味着我们只有在我们的 deps 发生变化时才重建构建容器。这意味着我们需要在 docker build 期间注入一个 SSH 密钥。

    查看这些示例文件:

    项目/Jenkinsfile

    def SSH_KEY
    
    node {
      SSH_KEY = sh(returnStdout: true, script: 'cat /var/jenkins_home/.ssh/id_rsa')
    }
    
    pipeline {
      agent {
        dockerfile {
          filename 'Dockerfile'
          additionalBuildArgs '--build-arg SSH_KEY="' + SSH_KEY + '"'
          reuseNode true
        }
      }
      stages {
        stage('Fetch Deps') {
          steps {
            sh 'mv /home/user/app/vendor vendor'
          }
        }
        stage('Run Unit Tests') {
          steps {
            sh './vendor/bin/phpunit'
          }
        }
      }
    }
    

    项目/Dockerfile

    FROM mycompany/php7.2-common:1.0.2
    
    # Provides the image for building mycompany/project on Jenkins.
    
    WORKDIR /home/user/app
    
    ARG SSH_KEY # should receive a raw SSH private key during build.
    ADD composer.json .
    RUN add-ssh-key "${SSH_KEY}" ~/.ssh/id_rsa && \
        composer install && \
        remove-ssh-keys
    
    # Note: add-ssh-key and remove-ssh-keys are our shell scripts put in
    # the base image to reduce boilerplate for common tasks.
    

    【讨论】:

    • 这是我需要的。谢谢
    【解决方案6】:

    我尝试在 AdditionalBuildArgs 中使用单引号并将 id 命令直接放在 build-args 上,它起作用了

    pipeline {
      agent {
        dockerfile {
          additionalBuildArgs '--build-arg user_id=$(id -u) --build-arg group_id=$(id -g)'
        }
      }
    ...
    

    【讨论】:

      猜你喜欢
      • 2021-01-01
      • 1970-01-01
      • 2017-12-03
      • 2017-10-12
      • 2021-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多