我正在运行一个Jenkins集群,在Master和Slave中,它们都作为Docker容器运行。
主机是在MacOS上运行的最新boot2docker VM。
为了让Jenkins能够使用Docker执行部署,我已将docker.sock和docker客户端从主机安装到Jenkins容器,如下所示: -
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容器中运行另一个Container,我会执行以下操作: -
sudo docker run -v $JENKINS_CONTAINER/deploy.json:/root/deploy.json $CONTAINER_REPO/$CONTAINER_IMAGE
上面运行容器,但文件" deploy.json"不是作为文件挂载,而是作为"目录&#34 ;.即使我将目录作为卷安装,我也无法查看生成的容器中的文件。
这是一个问题,因为Docker案例中Docker的文件权限是什么?
任何指针都很有用!
谢谢!
答案 0 :(得分:61)
Docker容器中的Docker容器使用父HOST的Docker守护程序,因此,任何安装在" docker-in-docker" case仍然是从HOST引用的,而不是从Container引用的。
因此,从Jenkins容器"安装的实际路径不存在"在主持人。因此,在" docker-in-docker"中创建了一个新目录。容器是空的。当目录安装到Container内的新Docker容器时,同样适用。
我错过了非常基本和明显的事情,但很快我就输入了问题。
答案 1 :(得分:9)
另一种方法是使用命名卷或数据卷容器。这样,内部容器不必知道有关主机的任何信息,Jenkins容器和构建容器都以相同的方式引用数据卷。
我尝试过类似于你正在做的事情,除了使用代理而不是使用Jenkins主人。问题是相同的,因为我无法在内部容器中安装Jenkins工作区。对我有用的是使用数据卷容器方法,工作区文件对代理容器和内部容器都是可见的。我喜欢这种方法的是两个容器以相同的方式引用数据量。使用内部容器挂载目录会很棘手,因为内部容器现在需要了解其父容器正在运行的主机。
我在这里有关于我的方法的详细博客文章:
以及此处的代码:
https://github.com/damnhandy/jenkins-pipeline-docker
在我的具体案例中,并非所有内容都按照Jenkins Pipeline插件的方式运行。但它确实解决了内部容器能够访问Jenkins工作空间目录的问题。
答案 2 :(得分:8)
关于与Jenkins相关的用例,您可以通过在主机上创建符号链接来伪造路径:
ln -s $HOST_JENKINS_DATA_DIRECTORY/jenkins_data /var/jenkins_home
答案 3 :(得分:3)
这些文章中有很多不错的信息,但我发现他们都不清楚他们指的是哪个容器。因此,我们将3种环境标记为:
我们都知道如何将文件夹从H装入D:以
开头Ddocker run ... -v <path-on-H>:<path-on-D> -v /var/run/docker.sock:/var/run/docker.sock ...
挑战是:您希望path-on-H
在D2中可用。
但是当我们尝试将相同的path-on-D2
装入D2时,我们都被咬了,因为我们从
path-on-H
当您与D共享H上的docker套接字时,然后在D中运行docker命令实际上就是在H上运行它们。的确,如果像这样启动D2,所有的工作(一开始都是意外的,但是当考虑):
docker run ... -v <path-on-D>:<path-on-D2> ...
下一个棘手的问题是,对于我们许多人来说,docker run ... -v <path-on-H>:<path-on-D2> ...
会根据运行它的人而改变。有很多方法可以将数据传递到D,因此它知道将path-on-H
使用什么,但是最简单的方法可能是环境变量。为了使此类var更清晰,我以path-on-H
开头。然后从 H 这样开始D:
DIND_
,然后从 D 像这样开始D2:
docker run ... -v <path-on-H>:<path-on-D> --env DIND_USER_HOME=$HOME \
--env DIND_SOMETHING=blabla -v /var/run/docker.sock:/var/run/docker.sock ...
答案 4 :(得分:2)
这也可以通过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
答案 5 :(得分:1)
我在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}
答案 6 :(得分:0)
如果您像我一样,不想与Jenkins安装程序打交道,或者不想懒得经历所有这些麻烦,那么这里有一个简单的解决方法,我可以使用它解决这个问题。
步骤1 -将以下变量添加到管道的环境部分
environment {
ABSOLUTE_WORKSPACE = "/home/ubuntu/volumes/jenkins-data/workspace"
JOB_WORKSPACE = "\${PWD##*/}"
}
步骤2 -使用以下命令Jenkins管道运行容器,如下所示。
steps {
sh "docker run -v ${ABSOLUTE_WORKSPACE}/${JOB_WORKSPACE}/my/dir/to/mount:/targetPath imageName:tag"
}
请注意上面的语句中的双引号,如果引号格式不正确或添加了单引号,Jenkins将不会转换env变量。
每个变量代表什么?
ABSOLUTE_WORKSPACE是我们在启动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
因此,变量ABSOLUTE_WORKSPACE = / home / ubuntu / volumes / jenkins-data + / workspace
这是如何工作的?
这是非常简单的,正如@ZephyrPLUSPLUS(归因于)所述,在Jenkins管道中运行的Docker容器的源路径不是当前容器中的路径,而是采用的路径是主机的路径。我们在这里所做的只是构建运行Jenkins管道的路径。并将其安装到我们的容器上。瞧!
答案 7 :(得分:0)
您可以解决在环境变量中传递的问题。 示例:
.
├── docker-compose.yml
└── my-volume-dir
└── test.txt
在docker-compose.yml
中version: "3.3"
services:
test:
image: "ubuntu:20.04"
volumes:
- ${REPO_ROOT-.}/my-volume-dir:/my-volume
entrypoint: ls /my-volume
要测试运行
docker run -e REPO_ROOT=${PWD} \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ${PWD}:/my-repo \
-w /my-repo \
docker/compose \
docker-compose up test
您应该在输出中看到:
test_1 | test.txt
答案 8 :(得分:0)
根据@ZephyrPLUSPLUS的描述 这是我设法解决的方法:
vagrant@vagrant:~$ hostname
vagrant
vagrant@vagrant:~$ ls -l /home/vagrant/dir-new/
total 4
-rw-rw-r-- 1 vagrant vagrant 10 Jun 19 11:24 file-new
vagrant@vagrant:~$ cat /home/vagrant/dir-new/file-new
something
vagrant@vagrant:~$ docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock docker /bin/sh
/ # hostname
3947b1f93e61
/ # ls -l /home/vagrant/dir-new/
ls: /home/vagrant/dir-new/: No such file or directory
/ # docker run -it --rm -v /home/vagrant/dir-new:/magic ubuntu /bin/bash
root@3644bfdac636:/# ls -l /magic
total 4
-rw-rw-r-- 1 1000 1000 10 Jun 19 11:24 file-new
root@3644bfdac636:/# cat /magic/file-new
something
root@3644bfdac636:/# exit
/ # hostname
3947b1f93e61
/ # vagrant@vagrant:~$ hostname
vagrant
vagrant@vagrant:~$
因此docker
已安装在Vagrant
的计算机上。让我们称之为vagrant
。您要挂载的目录位于/home/vagrant/dir-new
中的vagrant
中。
它使用主机3947b1f93e61
启动一个容器。请注意,/home/vagrant/dir-new/
未安装3947b1f93e61
。
接下来,我们使用vagrant
中的确切位置(/home/vagrant/dir-new
)作为装载源,并指定所需的任何装载目标,在这种情况下为/magic
。另请注意,/home/vagrant/dir-new
中不存在3947b1f93e61
。
这将启动另一个容器3644bfdac636
。
现在可以从/home/vagrant/dir-new
访问vagrant
中3644bfdac636
中的内容。
我认为是因为docker-in-docker不是孩子,而是兄弟姐妹。并且您指定的路径必须是父路径,而不是兄弟姐妹的路径。因此,无论您进行vagrant
的深度如何,任何挂载仍将引用来自docker-in-docker
的路径。