8

我在 Docker 中运行 Docker(特别是运行 Jenkins,然后运行 ​​Docker 构建器容器来构建项目映像,然后运行这些容器,然后运行测试容器)。

这是 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

詹金斯工作正常。但是有一个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 最新版本上。

4

3 回答 3

30

After some research, calming down and thinking, I realized that Docker-in-Docker is not really so much "-in-", as it is rather "Docker-next-to-Docker".

The trick to make a container able to run another container is sharing /var/run/docker.sock through a volume: -v /var/run/docker.sock:/var/run/docker.sock

And then the docker client in the container actually calls Docker on the host.

The volume source path (left of :) does not refer to the middle container, but to the host filesystem!

After realizing that, the fix is to make the paths to the Jenkins workspace directory the same in the host filesystem and the Jenkins (middle) container:

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

And voilá! It works. (I created a symlink instead of moving it, seems to work too.)

It is a bit complicated if you're looking at colleague's Mac, because Docker is implemented a bit differently there - it is running in an Alpine Linux based VM but pretending not to. (Not 100 % sure about that.) On Windows, I read that the paths have another layer of abstraction - mapping from C:/somewhere/... to a Linux-like path.

I hope I'll save someone hours of figuring out :)

于 2018-03-16T02:12:08.937 回答
4

Docker cp 的替代解决方案

我面临着同样的问题,即从在 Kubernetes 的 Jenkins 服务器中运行的 Docker 容器中运行的构建中安装卷。当我们使用docker-in-docker, 时dind,我无法以此处建议的任何一种方式安装卷。我仍然不确定原因是什么,但我找到了另一种方法:用于docker cp复制构建工件。

在此处输入图像描述

用于测试的多阶段 Docker 映像

我正在使用以下 Dockerfile 阶段进行单元 + 集成测试。

#
# Build stage to for building the Jar
#
FROM maven:3.2.5-jdk-8 as builder
MAINTAINER marcello.desales@gmail.com

# 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 管道有一个用于运行并行测试(单元 + 集成)的阶段。
  • 我所做的是在一个阶段构建测试映像,并并行运行测试。
  • 我用来docker cp从测试 docker 容器中复制构建工件,该容器可以在命名容器中运行测试后启动。
    • 或者,您可以使用 Jenkins stash 将测试结果带到Post阶段

至此,我用 a 解决了问题,docker run --name test:SHA然后我使用了docker start test:SHAthen 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"
      }
    }
  }
}
于 2018-04-20T19:07:59.263 回答
3

更好的方法是使用Jenkins Docker 插件,让它为您完成所有安装,然后添加-v /var/run/docker.sock:/var/run/docker.sock它的inside函数参数。

例如

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

{
 ...
}
于 2018-03-16T06:38:22.653 回答