webdevqa.jp.net

DockerのDocker-ボリュームが機能しない:第1レベルのコンテナーにファイルがいっぱい、第2層に空

DockerでDockerを実行しています(具体的には、Jenkinsを実行し、Docker Builderコンテナーを実行してプロジェクトイメージをビルドし、次にこれらを実行してからテストコンテナーを実行します)。

これは、jenkinsイメージが構築および開始される方法です。

docker build --tag bb/ci-jenkins .
mkdir $PWD/volumes/
docker run -d --network=Host  \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /usr/bin/docker:/usr/bin/docker \
  -v $PWD/volumes/jenkins_home:/var/jenkins_home \
  --name ci-jenkins bb/ci-jenkins

Jenkinsは正常に動作します。しかし、これを実行するJenkinsfileベースのジョブがあります。

docker run -i --rm -v /var/jenkins_home/workspace/forkMV_jenkins-VOLTRON-3057-KQXKVJNXOU4DGSUG3P27IR3QEDHJ6K7HPDEZYN7W6HCOTCH3QO3Q:/tmp/build collab/collab-services-api-mvn-builder:2a074614 mvn -B -T 2C install

そして、これはエラーで終わります:

指定した目標を実行するにはプロジェクトが必要ですが、このディレクトリ(/ tmp/build)にPOMがありません。

私がする時 docker exec -it shコンテナに、/tmp/buildは空です。しかし、私がJenkinsコンテナにいるとき、パス/var/jenkins_home/...QO3Q/が存在し、すべてのファイルがチェックアウトされて準備されたワークスペースが含まれています。

だから私は疑問に思います---Dockerはどのようにしてボリュームをうまくマウントし、それからそれは空になりますか?*

さらに紛らわしいのは、この設定はMacの同僚にとってはうまくいくということです。 Linux、Ubuntu 17.10、Docker最新を使用しています。

6
Ondra Žižka

いくつかの調査、落ち着き、そして考えた後、Docker-in-Dockerは本当にそれほど「-in-」ではなく、むしろ「Docker-next-to-Docker」であることに気付きました。

コンテナが別のコンテナを実行できるようにする秘訣は、ボリュームを介して/var/run/docker.sockを共有することです:-v /var/run/docker.sock:/var/run/docker.sock

そして、コンテナ内のdockerクライアントは、実際にはホスト上のDockerを呼び出します。

ボリュームソースパス(:の左側)は、中央のコンテナではなく、ホストファイルシステムを参照しています。

それを認識した後の修正は、Jenkins workspaceディレクトリへのパスをホストファイルシステムとJenkins(中央)コンテナで同じにすることです。

docker run -d --network=Host  \
   ...
   -v /var/jenkins_home:/var/jenkins_home

そしてボイラ!できます。 (移動する代わりにシンボリックリンクを作成しましたが、機能しているようです。)

同僚のMacを見ると、Dockerの実装が少し異なるため、少し複雑です。AlpineLinuxベースのVMで実行されていますが、そうではないふりをしています。(100%ではありません)それについては確かです。)Windowsでは、パスに別の抽象化レイヤーがあることを読みました。C:/somewhere/...からLinuxのようなパスへのマッピングです。

私は誰かが理解する時間を節約できることを願っています:)

20
Ondra Žižka

Dockercpを使用した代替ソリューション

KubernetesのJenkinsサーバーで実行されているDockerコンテナで実行されているビルドからボリュームをマウントするという同じ問題に直面していました。 docker-in-dockerdindを使用しているため、ここで提案した方法でボリュームをマウントできませんでした。理由はまだわかりませんが、別の方法を見つけました。docker cpを使用してビルドアーティファクトをコピーします。

enter image description here

テスト用のマルチステージDockerイメージ

ユニット+統合テストに次のDockerfileステージを使用しています。

#
# Build stage to for building the Jar
#
FROM maven:3.2.5-jdk-8 as builder
MAINTAINER [email protected]

# Only copy the necessary to pull only the dependencies from registry
ADD ./pom.xml /opt/build/pom.xml
# As some entries in pom.xml refers to the settings, let's keep it same
ADD ./settings.xml /opt/build/settings.xml

WORKDIR  /opt/build/

# Prepare by downloading dependencies
RUN mvn -s settings.xml -B -e -C -T 1C org.Apache.maven.plugins:maven-dependency-plugin:3.0.2:go-offline

# Run the full packaging after copying the source
ADD ./src /opt/build/src
RUN mvn -s settings.xml install -P embedded -Dmaven.test.skip=true -B -e -o -T 1C verify

# Building only this stage can be done with the --target builder switch
# 1. Build: docker build -t config-builder --target builder .
# When running this first stage image, just verify the unit tests
# Overriden them by removing the "!" for integration tests
# 2. docker run --rm -ti config-builder mvn -s settings.xml -Dtest="*IT,*IntegrationTest" test
CMD mvn -s settings.xml -Dtest="!*IT,!*IntegrationTest" -P jacoco test

Jenkinsパイプラインテスト用

  • 私のJenkinsパイプラインには、並列テスト(Unit + Integration)を実行するためのステージがあります。
  • 私がやっていることは、テストイメージをステージで構築し、テストを並行して実行することです。
  • docker cpを使用して、名前付きコンテナーでテストを実行した後に開始できるテストDockerコンテナー内からビルドアーティファクトをコピーします。
    • または、Jenkins stashを使用して、テスト結果をPostステージに運ぶこともできます。

この時点で、docker run --name test:SHAで問題を解決し、次にdocker start test:SHA、次にdocker cp test:SHA:/path .を使用します。ここで、.は現在のワークスペースディレクトリであり、これは私たちと同じです。現在のディレクトリにマウントされたDockerボリュームが必要です。

stage('Build Test Image') {
  steps {
    script {
      currentBuild.displayName = "Test Image"
      currentBuild.description = "Building the docker image for running the test cases"
    }
    echo "Building docker image for tests from build stage ${env.GIT_COMMIT}"
    sh "docker build -t tests:${env.GIT_COMMIT} -f ${paas.build.docker.dockerfile.runtime} --target builder ."
  }
}

stage('Tests Execution') {
  parallel {
    stage('Execute Unit Tests') {
      steps {
        script {
          currentBuild.displayName = "Unit Tests"
          currentBuild.description = "Running the unit tests cases"
        }
        sh "docker run --name tests-${env.GIT_COMMIT} tests:${env.GIT_COMMIT}"
        sh "docker start tests-${env.GIT_COMMIT}"
        sh "docker cp tests-${env.GIT_COMMIT}:/opt/build/target ."

        // https://jenkins.io/doc/book/pipeline/jenkinsfile/#advanced-scripted-pipeline#using-multiple-agents
        stash includes: '**/target/*', name: 'build'
      }
    }
    stage('Execute Integration Tests') {
      when {
        expression { paas.integrationEnabled == true }
      }
      steps {
        script {
          currentBuild.displayName = "Integration Tests"
          currentBuild.description = "Running the Integration tests cases"
        }
        sh "docker run --rm tests:${env.GIT_COMMIT} mvn -s settings.xml -Dtest=\"*IT,*IntegrationTest\" -P jacoco test"
      }
    }
  }
}
4

より良いアプローチは、 Jenkins Dockerプラグイン を使用して、すべてのマウントを実行させ、inside関数の引数に-v /var/run/docker.sock:/var/run/docker.sockを追加することです。

例えば。

docker.build("bb/ci-jenkins")
docker.image("bb/ci-jenkins").inside('-v /var/run/docker.sock:/var/run/docker.sock')

{
 ...
}
2
yorammi