Настройка построения args для агента dockerfile с использованием декларативного конвейера Jenkins

Я использую декларативный синтаксис конвейера для выполнения некоторой работы CI внутри контейнера докеров.

Я заметил, что плагин Docker для Jenkins запускает контейнер, используя идентификатор пользователя и идентификатор группы пользователя jenkins на хосте (т.е. если пользователь jenkins имеет идентификатор пользователя 100 и идентификатор группы 111, он будет запускать создание конвейера контейнер с командой docker run -u 100:111 ...).

У меня были некоторые проблемы с этим, так как контейнер будет работать с не существующим пользователем (в частности, я столкнулся с некоторыми проблемами, когда у пользователя отсутствовал домашний каталог). Поэтому я подумал о создании файла Docker, который получит идентификатор пользователя и идентификатор группы в качестве аргументов сборки и создаст правильного пользователя 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 в хосте и отправить их как сборку, но проблема, с которой я сейчас сталкиваюсь, заключается в том, что кажется, что есть никоим образом не выполнять эти команды в декларативном конвейере до указания агента. Я хочу, чтобы мой 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 {
        ..
      }
    }
    ...
  }
}

Есть ли способ добиться этого? Я также попытался обернуть директиву конвейера внутри node, но конвейер должен быть в корне файла.

Ответ 1

Я проверил, что попытка присвоить user_id и group_id без node не работала, как вы нашли, но это помогло мне назначить эти значения и позже получить к ним доступ:

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.

В комментарии вы указали, что наиболее вероятным является критический недостаток подхода, который вычисляет user_id и group_id за пределами декларативного конвейера, прежде чем использовать его для настройки файла docker: ведомый, на котором он обнаруживает user_id, не обязательно совпадают с ведомым, который он использует, чтобы начать сборку на докере. у меня нет никакого способа обойти это, в то же время сохраняя декларативное ограничение Jenkinsfile.

Вы можете гарантировать одно подчиненное для всех этапов, используя объявление глобального агента: декларативный конвейер Jenkins: какое рабочее пространство связано со стадией, когда агент установлен только для конвейера?

Но несколько ссылок node с одним и тем же ярлыком не гарантируют одно и то же рабочее пространство: декларативный конвейер Jenkins: какое рабочее пространство связано со стадией, когда агент установлен только для трубопровод?

Ответ 2

Вы также можете добавить блок следующим образом:

agent {
    dockerfile {

        args '-v /etc/passwd:/etc/passwd -v /etc/group:/etc/group'
    }
}

Это позволит контейнеру иметь правильный идентификатор пользователя и группы.

Ответ 3

Вы также можете использовать параметр args для решения проблемы.
Как описано в Синтаксис трубопровода:

docker также необязательно принимает параметр args, который может содержать аргументы, которые передаются непосредственно вызову запуска docker.

Это также возможно при использовании dockerfile вместо docker в агенте раздел.

У меня была такая же проблема, как и вы, и следующие строки, работающие отлично для меня:

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

Ответ 4

если у вас есть доступ администратора к Jenkins, вы можете добавить эти два script утверждения:

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/

который позволит вам выполнить команду оболочки в главном режиме следующим образом:

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

Ответ 5

Я считаю, что мы нашли хороший способ справиться с этим.

У нас есть развертывание Jenkins, которое запускается как экземпляр докера, я сопоставил том для /var/jenkins_home и добавил папку .ssh в /var/jenkins_home/.ssh

Мы также запускаем все сборки в контейнерах docker, используя директиву агента dockerfile. Иногда нам нужно получить доступ к некоторым из наших частных библиотек композиторов через git over ssh.

Мы используем кэширование образов докера, устанавливая проект deps (composer), что означает, что мы перестраиваем сборочные контейнеры только в том случае, если наши deps меняются. Это означает, что нам нужно ввести ключ 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.