web-dev-qa-db-fra.com

Docker dans Docker ne peut pas monter de volume

J'utilise un cluster Jenkins où, dans le maître et l'esclave, les deux s'exécutent en tant que conteneurs Docker. 

L'hôte est la dernière version de boot2docker VM sous MacOS.

Pour permettre à Jenkins de pouvoir effectuer un déploiement à l'aide de Docker, j'ai monté le client docker.sock et le client Docker de l'hôte au conteneur Jenkins comme suit: -

docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):/usr/bin/docker -v $Host_JENKINS_DATA_DIRECTORY/jenkins_data:/var/jenkins_home -v $Host_SSH_KEYS_DIRECTORY/.ssh/:/var/jenkins_home/.ssh/ -p 8080:8080 jenkins

Je rencontre des problèmes lors du montage d'un volume sur les conteneurs Docker exécutés à l'intérieur du conteneur Jenkins. Par exemple, si je dois exécuter un autre conteneur à l'intérieur du conteneur Jenkins, je procède comme suit: -

Sudo docker run -v $JENKINS_CONTAINER/deploy.json:/root/deploy.json $CONTAINER_REPO/$CONTAINER_IMAGE 

L'exemple ci-dessus exécute le conteneur, mais le fichier "deploy.json" n'est PAS monté en tant que fichier, mais en tant que "Répertoire". Même si je monte un répertoire en tant que volume, je ne parviens pas à afficher les fichiers du conteneur résultant. 

Est-ce un problème en raison d'autorisations de fichier dues à l'affaire Docker dans l'affaire Docker?

Tous les pointeurs seraient utiles!

Merci!

32
ZephyrPLUSPLUS

Un conteneur Docker dans un conteneur Docker utilise le démon Docker de l'hôte parent et, par conséquent, tous les volumes montés dans le cas "docker-in-docker" sont toujours référencés à partir de l'hôte et non du conteneur. 

Par conséquent, le chemin réel monté à partir du conteneur Jenkins "n'existe pas" dans l'hôte. De ce fait, un nouveau répertoire est créé dans le conteneur "docker-in-docker" qui est vide. La même chose s'applique lorsqu'un répertoire est monté sur un nouveau conteneur Docker dans un conteneur. 

Chose très fondamentale et évidente qui me manquait, mais que je réalisai dès que je tapai la question. 

51
ZephyrPLUSPLUS

Vous pouvez également utiliser des volumes nommés ou des conteneurs de volumes de données. Ainsi, le conteneur à l'intérieur ne doit rien savoir de l'hôte, et le conteneur Jenkins et le conteneur de génération font référence au volume de données de la même manière. 

J'ai essayé de faire quelque chose de similaire à ce que vous faites, sauf avec un agent plutôt qu'avec le maître Jenkins. Le problème était le même en ce que je ne pouvais pas monter l'espace de travail Jenkins dans le conteneur interne. Ce qui a fonctionné pour moi a été d'utiliser l'approche du conteneur de volume de données et les fichiers d'espace de travail étaient visibles à la fois pour le conteneur d'agent et le conteneur interne. Ce que j'ai aimé dans cette approche, c'est que les deux conteneurs référencent le volume de données de la même manière. Monter des répertoires avec un conteneur interne serait délicat, car le conteneur interne doit maintenant connaître quelque chose sur l'hôte sur lequel son conteneur parent est exécuté. 

J'ai un article de blog détaillé sur mon approche ici:

http://damnhandy.com/2016/03/06/creating-containerized-build-environments-with-the-jenkins-pipeline-plugin-and-docker-well-almost/

En plus du code ici:

https://github.com/damnhandy/jenkins-pipeline-docker

Dans mon cas particulier, tout ne fonctionne pas comme je le voudrais en ce qui concerne le plug-in Jenkins Pipeline. Cependant, cela évite que le conteneur interne puisse accéder au répertoire de l'espace de travail Jenkins. 

8

En ce qui concerne votre cas d'utilisation lié à Jenkins, vous pouvez simplement simuler le chemin en créant un lien symbolique sur l'hôte:

ln -s $Host_JENKINS_DATA_DIRECTORY/jenkins_data /var/jenkins_home
8
mperrin

Cela fonctionne également via docker-compose et/ou les volumes nommés. Vous n'avez donc pas besoin de créer un conteneur de données uniquement, mais vous devez tout de même avoir le répertoire vide sur l'hôte.

Configuration de l'hôte

Créez des répertoires côté hôte et définissez les autorisations pour autoriser les conteneurs Docker à accéder à Sudo mkdir -p /var/jenkins_home/{workspace,builds,jobs} && Sudo chown -R 1000 /var/jenkins_home && Sudo chmod -R a+rwx /var/jenkins_home

docker-compose.yml

version: '3.1'
services:
  jenkins:
    build: .
    image: jenkins
    ports:
      - 8080:8080
      - 50000:50000
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - workspace:/var/jenkins_home/workspace/
      # Can also do builds/jobs/etc here and below
  jenkins-lts:
    build:
      context: .
      args:
        versiontag: lts
    image: jenkins:lts
    ports:
      - 8081:8080
      - 50001:50000
volumes:
  workspace:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /var/jenkins_home/workspace/

Lorsque vous docker-compose up --build jenkins (vous voudrez peut-être incorporer cela dans un exemple prêt à l'emploi, tel que https://github.com/thbkrkr/jks où les scripts .groovy préconfigurent Jenkins pour qu'il soit utile au démarrage) vos travaux peuvent être clonés dans le répertoire $ JENKINS_HOME/workspace et ne doivent pas contenir d'erreurs concernant les fichiers manquants/etc car les chemins de l'hôte et du conteneur vont correspondre, puis l'exécution de conteneurs supplémentaires à partir de Docker-in-Docker devrait fonctionner ainsi que.

Dockerfile (pour Jenkins avec Docker dans Docker)

ARG versiontag=latest
FROM jenkins/jenkins:${versiontag}

ENV Java_OPTS="-Djenkins.install.runSetupWizard=false"

COPY jenkins_config/config.xml /usr/share/jenkins/ref/config.xml.override
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt

USER root
RUN curl -L http://get.docker.io | bash && \
    usermod -aG docker jenkins
# Since the above takes a while make any other root changes below this line
# eg `RUN apt update && apt install -y curl`
# drop back to the regular jenkins user - good practice
USER jenkins
EXPOSE 8080
2
dragon788

Un moyen de contourner ce problème consiste à monter un répertoire (à l'intérieur de votre conteneur de menu fixe dans lequel vous avez monté votre socket de menu fixe) en utilisant exactement le même chemin pour sa destination. Ensuite, lorsque vous exécutez un conteneur à partir de ce conteneur, vous pouvez monter n'importe quel élément du chemin de ce montage dans le nouveau conteneur à l'aide de docker -v.

Prenons cet exemple:

# Spin up your container from which you will use docker
docker run -v /some/dir:/some/dir -v /var/run/docker.sock:/var/run.docker.sock docker:latest

# Now spin up a container from within this container
docker run -v /some/dir:/usr/src/app $CONTAINER_IMAGE

Le dossier /some/dir est maintenant monté sur votre hôte, le conteneur intermédiaire ainsi que votre conteneur de destination. Étant donné que le chemin du montage existe sur l'hôte en tant que conteneur "near docker-in-docker", vous pouvez utiliser docker -v comme prévu.

Cela ressemble un peu à la suggestion de créer un lien symbolique sur l'hôte, mais j'ai trouvé ceci (du moins dans mon cas), une solution plus propre. N'oubliez pas de nettoyer le répertoire de l'hôte après! ;)

0
Toon Lamberigts

Si vous êtes comme moi et que vous ne voulez pas vous mêler de la configuration de Jenkins ou que vous êtes trop paresseux pour résoudre tous ces problèmes, voici une solution simple que j'ai mise en place pour que cela fonctionne pour moi. 

Step 1 - Ajout des variables suivantes à la section d'environnement du pipeline

environment {
    ABSOLUTE_WORKSPACE = "/home/ubuntu/volumes/jenkins-data/workspace" 
    JOB_WORKSPACE = "\${PWD##*/}"
}

Step 2 - Exécutez votre conteneur avec la commande suivante Jenkins pipeline comme suit.

    steps {
        sh "docker run -v ${ABSOLUTE_WORKSPACE}/${JOB_WORKSPACE}/my/dir/to/mount:/targetPath imageName:tag"
    }

Prenez note des guillemets doubles dans l'instruction ci-dessus, Jenkins ne convertira pas les variables env si les guillemets ne sont pas correctement formatés ou si des guillemets simples sont ajoutés à la place.


Que signifie chaque variable?

  • ABSOLUTE_WORKSPACE est le chemin de notre volume Jenkins que nous avions monté lors du démarrage de Jenkins Docker Container. Dans mon cas, la commande d'exécution du menu fixe était la suivante. 

    Sudo docker run \ -p 80:8080 \ -v /home/ubuntu/volumes/jenkins-data:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ -d -t jenkinsci/blueocean

Ainsi, la variable ABSOLUTE_WORKSPACE =/home/ubuntu/volumes/jenkins-data +/workspace

  • La commande JOB_WORKSPACE nous indique le répertoire actuel de l’espace de travail où réside votre code. C'est aussi le répertoire racine de votre base de code. Je viens de suivre cette réponse pour référence.

Comment cela marche-t-il ?

C'est très simple, comme mentionné dans la réponse de @ZephyrPLUSPLUS (crédits où il convient), le chemin source de notre conteneur docker exécuté dans le pipeline Jenkins n'est pas le chemin du conteneur actuel, mais plutôt le chemin pris par l'hôte. Tout ce que nous faisons ici est de construire le chemin où notre pipeline Jenkins est exploité. Et le monter sur notre conteneur. Voila !!

Voici une petite illustration pour aider à clarifier ...  enter image description here

0
damitj07