web-dev-qa-db-fra.com

Définition des arguments de génération pour l'agent Dockerfile à l'aide d'un pipeline déclaratif Jenkins

J'utilise la syntaxe déclarative du pipeline pour faire du travail de CI dans un conteneur Docker.

J'ai remarqué que le plug-in Docker pour Jenkins exécute un conteneur en utilisant l'ID utilisateur et l'ID de groupe de l'utilisateur jenkins dans l'hôte (c'est-à-dire que si l'utilisateur jenkins a l'ID utilisateur 100 et l'ID de groupe 111, il exécuterait le pipeline en créant un conteneur avec la commande docker run -u 100:111 ...).

J'ai eu quelques problèmes avec cela, car le conteneur fonctionnera avec un utilisateur non existant (en particulier, j'ai rencontré des problèmes avec l'utilisateur n'ayant pas de répertoire personnel). J'ai donc pensé à créer un Dockerfile qui recevra l'ID utilisateur et l'ID de groupe comme arguments de construction et créerait un utilisateur jenkins approprié à l'intérieur du conteneur. Le Dockerfile ressemble à ceci:

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

...

L'agent dockerfile a une propriété additionalBuildArgs, donc je peux lire l'ID utilisateur et l'ID de groupe de l'utilisateur jenkins dans l'hôte et les envoyer en tant qu'aguments de construction, mais le problème que j'ai maintenant est qu'il semble qu'il y ait aucun moyen d'exécuter ces commandes dans un pipeline déclaratif avant de spécifier l'agent. Je veux que mon fichier Jenkins soit quelque chose comme ça:

// 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 {
        ..
      }
    }
    ...
  }
}

I existe-t-il un moyen d'y parvenir? J'ai également essayé d'encapsuler la directive pipeline à l'intérieur d'un nœud, mais le pipeline doit être à la racine du fichier.

15
aromero

J'ai vérifié qu'essayer d'attribuer user_id et group_id sans noeud ne fonctionnait pas, comme vous l'avez trouvé, mais cela a fonctionné pour moi d'attribuer ces valeurs et d'y accéder plus tard:

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
      }
    }
  }
}

Espérons que cela fonctionnera également dans votre instruction additionalBuildArgs.

Dans un commentaire, vous avez souligné ce qui est probablement une faille critique avec l'approche qui comprend l'id_utilisateur et l'id_groupe en dehors du pipeline déclaratif avant de l'utiliser pour configurer le dockerfile: l'esclave sur lequel il découvre l'id_utilisateur ne correspondra pas nécessairement avec l'esclave qu'il utilise pour lancer la construction basée sur les dockers. je n'y ai aucun moyen de contourner cela tout en conservant la contrainte déclarative Jenkinsfile.

Vous pouvez garantir un esclave pour toutes les étapes en utilisant une déclaration d'agent globale: pipeline déclaratif Jenkins: quel espace de travail est associé à une étape lorsque l'agent est défini uniquement pour le pipeline?

Mais plusieurs références de nœuds avec la même étiquette ne garantissent pas le même espace de travail: pipeline déclaratif Jenkins: quel espace de travail est associé à une étape lorsque l'agent est défini uniquement pour le pipeline?

13
burnettk

Vous pouvez également ajouter un bloc comme celui-ci:

agent {
    dockerfile {

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

Cela permettra au conteneur d'avoir l'ID d'utilisateur et de groupe correct.

6
John Bresnahan

Vous pouvez également utiliser le paramètre args pour résoudre le problème.
Comme décrit dans Syntaxe du pipeline :

docker accepte également en option un paramètre args qui peut contenir des arguments à passer directement à un appel de docker run.

Cela est également possible lorsque vous utilisez dockerfile au lieu de docker dans la section agent.

J'ai eu le même problème que vous et les lignes suivantes fonctionnent bien pour moi:

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

Je pense que nous avons trouvé un bon moyen de régler ce problème.

Nous avons un déploiement Jenkins qui fonctionne comme une instance de docker, j'ai mappé un volume pour/var/jenkins_home et ajouté le dossier .ssh à /var/jenkins_home/.ssh

Nous exécutons également toutes les versions à l'intérieur des conteneurs Docker, à l'aide de la directive d'agent Dockerfile. Parfois, nous devons accéder à certaines de nos bibliothèques privées composer via git sur ssh.

Nous tirons parti de la mise en cache de l'image Docker en installant le projet deps (composer), ce qui signifie que nous ne reconstruisons les conteneurs de construction que si nos dépôts changent. Cela signifie que nous devons injecter une clé SSH lors de la construction du docker.

Voir ces exemples de fichiers:

projet/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'
      }
    }
  }
}

projet/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.
2
kburnik

si vous avez un accès administrateur à Jenkins, vous pouvez ajouter ces deux approbations de script:

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

dans cet URI: http://${jenkins_Host:port}/jenkins/scriptApproval/

qui vous permettra d'exécuter une commande Shell dans le maître de cette manière:

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