关于linux:Docker中的Docker无法挂载卷

Docker in Docker cannot mount volume

我奔跑在詹金斯在丛集性在主人和奴隶,都是运行在泊坞窗的容器中。 P / < >

"主机也latest boot2docker VM运行在macos。 P / < >

到允许詹金斯到能到执行部署使用泊坞窗,我docker.sock骑警的码头工人和客户端从主机到詹金斯集装箱像这样: P / < >

1
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

我面临的问题,而mounting容的泊坞窗到容器中,是跑在詹金斯的集装箱。例如,如果我需要运行另一个集装箱的集装箱在詹金斯),做以下: P / < >

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

"runs以上的集装箱,但文件"deploy.json"不是个骑警的文件,但而不是年代的"目录"。即使我在山上的目录级的容,我unable到视图的"文件在resulting集装箱。 P / < >

这是一个问题,因为文件permissions由于码头工人在泊坞窗吗? P / < >

任何pointers会useful! P / < >

谢谢! P / < >


Docker容器中的Docker容器使用父主机的Docker守护进程,因此,安装在"Docker in Docker"案例中的任何卷仍然从主机引用,而不是从容器引用。

因此,从Jenkins容器装载的实际路径在主机中"不存在"。因此,在"docker in docker"容器中创建了一个空目录。当一个目录被装载到一个容器中的一个新的Docker容器上时,同样的事情也适用。

很基本很明显的事情我错过了,但我一打出来就意识到了。


关于与Jenkins相关的用例,您可以通过在主机上创建一个符号链接来简单地伪造路径:

1
ln -s $HOST_JENKINS_DATA_DIRECTORY/jenkins_data /var/jenkins_home


另一种方法是使用命名卷或数据卷容器。这样,里面的容器就不需要知道任何关于主机的信息,詹金斯容器和构建容器都以同样的方式引用数据量。

我试过做一些类似于你所做的事情,除了一个代理,而不是使用詹金斯大师。问题是相同的,因为我无法将Jenkins工作区安装在内部容器中。对我有用的是使用数据卷容器方法,代理容器和内部容器都可以看到工作区文件。我喜欢的方法是两个容器以相同的方式引用数据量。用一个内部容器装载目录是很困难的,因为内部容器现在需要知道一些关于其父容器正在运行的主机的信息。

我在这里有关于我的方法的详细博客文章:

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

以及这里的代码:

https://github.com/damhandy/jenkins-pipeline-docker网站

在我的具体案例中,并不是所有的事情都按照我希望的方式运行在Jenkins管道插件方面。但它确实解决了内部容器能够访问Jenkins工作区目录的问题。


这也可以通过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公司

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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/thbkrkrkrkr/jks,其中.groovy脚本预配置jenkins以便在启动时使用),然后您就可以将作业克隆到$jenkins'u home/workspace目录中,并且不应该得到关于丢失文件的错误/etc,因为OST和容器路径将匹配,然后从Docker中的Docker中运行更多的容器也应该有效。

Dockerfile(对于Jenkins和Docker在Docker)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

如果你和我一样,不想和詹金斯的安排搞混,也不想太懒惰,不想经历这些麻烦,这里有一个简单的解决方法,我做了这个为我工作。

步骤1-将以下变量添加到管道的环境部分

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

步骤2-使用以下命令运行容器jenkins pipeline,如下所示。

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

Take note of the double quotes in the above statement, Jenkins will not convert the env variables if the quotes are not formatted properly or single quotes are added instead.

每个变量意味着什么?

  • 绝对工作区是我们在启动Jenkins Docker容器时安装的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

Thus the varible ABSOLUTE_WORKSPACE=/home/ubuntu/volumes/jenkins-data + /workspace

  • job_workspace命令为我们提供代码所在的当前工作区目录。这也是代码库的根目录。只是按照这个答案做参考。

这是怎么工作的?

正如@zephyrplusplus(credits when due)答案中提到的,我们在Jenkins管道中运行的Docker容器的源路径不是当前容器中的路径,而是主机路径。我们在这里所做的就是建造一条通往詹金斯管道的道路。把它装到我们的集装箱里。哇!!

这里有一个小例子来帮助澄清…enter image description here


解决这个问题的一种方法是使用与目的地完全相同的路径挂载一个目录(在挂载Docker套接字的Docker容器中)。然后,当您从该容器中运行容器时,可以使用docker -v将该装载路径中的任何内容装载到新容器中。

举个例子:

1
2
3
4
5
# 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现在安装在主机、中间容器以及目标容器上。由于mount的路径在两个主机上都作为"接近docker-in-docker"容器存在,因此可以按预期使用docker -v

这有点类似于在主机上创建符号链接的建议,但我发现这(至少在我的情况下)是一个更干净的解决方案。只是别忘了事后清理主机上的dir!;)