web-dev-qa-db-ja.com

DockerのDockerはボリュームをマウントできません

マスターとスレーブの両方がDockerコンテナーとして実行されているJenkinsクラスターを実行しています。

ホストは最新のboot2docker VM MacOSで実行されています。

JenkinsがDockerを使用して展開を実行できるようにするため、次のようにホストからJenkinsコンテナーにdocker.sockおよびdockerクライアントをマウントしました。

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

Jenkinsコンテナー内で実行されるDockerコンテナーにボリュームをマウントする際に問題に直面しています。たとえば、Jenkinsコンテナー内で別のコンテナーを実行する必要がある場合、次のようにします。

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

上記はコンテナを実行しますが、ファイル「deploy.json」はファイルとしてではなく、「ディレクトリ」としてマウントされます。ディレクトリをボリュームとしてマウントしても、作成されたコンテナ内のファイルを表示できません。

これは、Dockerの場合のDockerによるファイル許可のために問題ですか?

46
ZephyrPLUSPLUS

Dockerコンテナ内のDockerコンテナは、親ホストのDockerデーモンを使用するため、「docker-in-docker」ケースでマウントされたボリュームは、コンテナではなくホストから参照されます。

したがって、Jenkinsコンテナからマウントされた実際のパスは、ホストに「存在しません」。このため、空の「docker-in-docker」コンテナに新しいディレクトリが作成されます。コンテナ内の新しいDockerコンテナにディレクトリがマウントされる場合も同じことが当てはまります。

非常に基本的で明白なことで、私は見逃していましたが、質問を入力するとすぐに実現しました。

68
ZephyrPLUSPLUS

これを回避する別の方法は、名前付きボリュームまたはデータボリュームコンテナを使用することです。この方法では、内部のコンテナはホストについて何も知る必要がなく、Jenkinsコンテナとビルドコンテナの両方が同じ方法でデータボリュームを参照します。

Jenkinsマスターを使用するのではなく、エージェントを使用する以外は、あなたがやっていることと似たようなことをしようとしました。問題は、内部コンテナーにJenkinsワークスペースをマウントできなかったという点で同じでした。私にとってうまくいったのは、データボリュームコンテナアプローチを使用することであり、ワークスペースファイルはエージェントコンテナと内部コンテナの両方に表示されていました。このアプローチで気に入ったのは、両方のコンテナが同じ方法でデータボリュームを参照することです。内側のコンテナは、親コンテナが実行されているホストについて何かを知る必要があるため、内側のコンテナでディレクトリをマウントするのは難しいでしょう。

私のアプローチに関する詳細なブログ投稿はこちらです:

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

ここのコードと同様に:

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

私の特定のケースでは、すべてがJenkins Pipelineプラグインの点で私が望むように機能しているわけではありません。ただし、内部コンテナーがJenkinsワークスペースディレクトリにアクセスできるという問題は解決します。

9

Jenkinsに関連するユースケースについては、ホストにシンボリックリンクを作成することで、単純にパスを偽造できます。

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

これは、docker-composeおよび/または名前付きボリューム。これにより、データのみのコンテナを作成する必要はありませんが、ホストに空のディレクトリが必要です。

ホストのセットアップ

ホスト側のディレクトリを作成し、Dockerコンテナが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/

docker-compose up --build jenkins(これを https://github.com/thbkrkr/jks のようなすぐに実行できるサンプルに組み込んでください。ここで、.groovyスクリプトは起動時にJenkinsを事前に構成します)および次に、ジョブを$ JENKINS_HOME/workspaceディレクトリに複製し、ホストとコンテナのパスが一致するため、ファイルの欠落などに関するエラーを取得しないようにし、Docker-in-Docker内からさらにコンテナを実行します。同様に動作するはずです。

Dockerfile(DockerでDockerを使用するJenkins用)

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

あなたが私のような人で、Jenkins Setupを台無しにしたくない場合や、このようなトラブルをすべて解決するのが面倒な場合は、これを機能させるための簡単な回避策があります。

ステップ1-次の変数をパイプラインの環境セクションに追加します

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

ステップ2-次のコマンドJenkins pipelineでコンテナを実行します。

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

上記のステートメントの二重引用符に注意してください。引用符が適切にフォーマットされていないか、代わりに単一引用符が追加されている場合、Jenkinsはenv変数を変換しません。


各変数の意味は何ですか?

  • ABSOLUTE_WORKSPACEは、Jenkins Docker Containerの起動時にマウントしたJenkinsボリュームのパスです。私の場合、docker runコマンドは次のとおりでした。

    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

したがって、変数ABSOLUTE_WORKSPACE =/home/ubuntu/volumes/jenkins-data +/workspace

  • JOB_WORKSPACEコマンドは、コードが存在する現在のワークスペースディレクトリを提供します。これは、コードベースのルートディレクトリでもあります。 この回答 を参考にしてください。

この仕組みは?

@ZephyrPLUSPLUS(credits where due)の回答で述べたように、Jenkinsパイプラインで実行されているdockerコンテナーのソースパスは、現在のコンテナー内のパスではなく、ホストのパスです。ここで行っているのは、Jenkinsパイプラインが実行されているパスを構築することだけです。そして、コンテナにマウントします。ほら!

ここに明確にするのに役立つ小さなイラストがあります... enter image description here

1
damitj07

この問題を回避する方法は、宛先とまったく同じパスを使用して、ディレクトリを(DockerソケットをマウントしたDockerコンテナ内に)マウントすることです。次に、そのコンテナ内からコンテナを実行すると、docker -vを使用して、そのマウントのパス内にあるものを新しいコンテナにマウントできます。

次の例をご覧ください。

# 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

フォルダー/some/dirがホスト、中間コンテナー、および宛先コンテナー全体にマウントされました。マウントのパスは「ほぼdocker-in-docker」コンテナとして両方のホストに存在するため、docker -vを期待どおりに使用できます。

これは、ホスト上にシンボリックリンクを作成するという提案に似ていますが、これは(少なくとも私の場合は)よりクリーンなソリューションであることがわかりました。後でホスト上のディレクトリをクリーンアップすることを忘れないでください! ;)

0
Toon Lamberigts

Gitlab CIにも同じ問題があります。docker cpを使用してmountのようなことをすることでこれを解決しました

script:
  - docker run --name ${CONTAINER_NAME} ${API_TEST_IMAGE_NAME}
after_script:
  - docker cp ${CONTAINER_NAME}:/code/newman ./
  - docker rm ${CONTAINER_NAME}
0
BohanZhang