マスターとスレーブの両方が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によるファイル許可のために問題ですか?
Dockerコンテナ内のDockerコンテナは、親ホストのDockerデーモンを使用するため、「docker-in-docker」ケースでマウントされたボリュームは、コンテナではなくホストから参照されます。
したがって、Jenkinsコンテナからマウントされた実際のパスは、ホストに「存在しません」。このため、空の「docker-in-docker」コンテナに新しいディレクトリが作成されます。コンテナ内の新しいDockerコンテナにディレクトリがマウントされる場合も同じことが当てはまります。
非常に基本的で明白なことで、私は見逃していましたが、質問を入力するとすぐに実現しました。
これを回避する別の方法は、名前付きボリュームまたはデータボリュームコンテナを使用することです。この方法では、内部のコンテナはホストについて何も知る必要がなく、Jenkinsコンテナとビルドコンテナの両方が同じ方法でデータボリュームを参照します。
Jenkinsマスターを使用するのではなく、エージェントを使用する以外は、あなたがやっていることと似たようなことをしようとしました。問題は、内部コンテナーにJenkinsワークスペースをマウントできなかったという点で同じでした。私にとってうまくいったのは、データボリュームコンテナアプローチを使用することであり、ワークスペースファイルはエージェントコンテナと内部コンテナの両方に表示されていました。このアプローチで気に入ったのは、両方のコンテナが同じ方法でデータボリュームを参照することです。内側のコンテナは、親コンテナが実行されているホストについて何かを知る必要があるため、内側のコンテナでディレクトリをマウントするのは難しいでしょう。
私のアプローチに関する詳細なブログ投稿はこちらです:
ここのコードと同様に:
https://github.com/damnhandy/jenkins-pipeline-docker
私の特定のケースでは、すべてがJenkins Pipelineプラグインの点で私が望むように機能しているわけではありません。ただし、内部コンテナーがJenkinsワークスペースディレクトリにアクセスできるという問題は解決します。
Jenkinsに関連するユースケースについては、ホストにシンボリックリンクを作成することで、単純にパスを偽造できます。
ln -s $Host_JENKINS_DATA_DIRECTORY/jenkins_data /var/jenkins_home
これは、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
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内からさらにコンテナを実行します。同様に動作するはずです。
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
あなたが私のような人で、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
この仕組みは?
@ZephyrPLUSPLUS(credits where due)の回答で述べたように、Jenkinsパイプラインで実行されているdockerコンテナーのソースパスは、現在のコンテナー内のパスではなく、ホストのパスです。ここで行っているのは、Jenkinsパイプラインが実行されているパスを構築することだけです。そして、コンテナにマウントします。ほら!
この問題を回避する方法は、宛先とまったく同じパスを使用して、ディレクトリを(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
を期待どおりに使用できます。
これは、ホスト上にシンボリックリンクを作成するという提案に似ていますが、これは(少なくとも私の場合は)よりクリーンなソリューションであることがわかりました。後でホスト上のディレクトリをクリーンアップすることを忘れないでください! ;)
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}