为了方便管理容器的启动命令,统一使用 docker-compose
命令配合 docker-compose.yml
文件启动容器。基本使用可以 参考这里。
关于如何在 Docker 中安装 Jenkins,参考这里。
如果只是想看最终的 docker-compose.yml
文件,直接跳到最后一部分。
我的 CentOS7 的内核已经升级为 4.15,版本详情如下:
# uname -a
Linux VM_139_74_centos 4.15.6-1.el7.elrepo.x86_64 #1 SMP Sun Feb 25 20:57:32 EST 2018 x86_64 x86_64 x86_64 GNU/Linux
1. docker: not found
首先,要在 CentOS7 的容器中使用宿主机上的命令,需要在容器中使用 root 用户(也可以以 jenkins 用户运行,但是需要把容器内的 jenkins 用户加入到 docker 组中,通过 grep docker /etc/group
命令查看 docker 组的 GID)。
docker-compose.yml
示例文件如下:
version: '3'
services:
jenkins-compose:
image: jenkins
user: root
ports:
- "8088:8080"
- "50000:50000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /home/demo/jenkins-compose:/var/jenkins_home
其中
user: root
表示在容器中以 root 用户运行/var/run/docker.sock:/var/run/docker.sock
表示 Docker 守护进程监听的 Unix 套接字。要在 Jenkins 容器中使用 docker 命令,则此选项是必需的。
然而,报错了:
/var/jenkins_home/workspace/first@tmp/durable-859cc63c/script.sh: 2: /var/jenkins_home/workspace/first@tmp/durable-859cc63c/script.sh: docker: not found
经过查找资料,要想在容器内使用宿主机的 docker
命令,有两种方法:
- 为 Jenkins 容器添加特权,通过
-v
将宿主机的 docker 命令映射到容器,使其可以直接运行容器所在宿主机上的docker
命令,称为 DooD。 - 在 Jenkins 容器中安装 docker 称为 DioD。但是这样可能会导致莫名其妙的问题,不推荐,原因参考 这篇文章。如果要安装的话,可以参考 这里。
上面的 docker-compose.yml
采用的就是第一种方法,需要通过 -v /usr/bin/docker:/usr/bin/docker
将宿主机的 docker 命令映射到容器。这里冒号前面的 /usr/bin/docker
表示宿主机上的 docker 命令的安装位置,如果你没有使用默认的安装位置,需要替换为你的实际位置。可以通过 which docker
查看 docker 命令的安装位置。
2. docker: Permission denied
修改后的 docker-compose.yml
示例文件如下:
version: '3'
services:
jenkins-compose:
image: jenkins
user: root
ports:
- "8088:8080"
- "50000:50000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
- /home/demo/jenkins-compose:/var/jenkins_home
然而,再次报错:
/var/jenkins_home/workspace/first@tmp/durable-f92a2624/script.sh: 2: /var/jenkins_home/workspace/first@tmp/durable-f92a2624/script.sh: docker: Permission denied
在容器中使用 root 用户时需要给容器 分配 privileged
特权,这样容器中的 root 用户才可以具有和宿主机上 root 用户一样大的权限。可以通过下面的命令查看容器是否具有特权:
docker inspect --format='{{.HostConfig.Privileged}}' 你的容器的 ID
3. 找不到 libltdl.so.7
再次修改后的 docker-compose.yml
示例文件如下:
version: '3'
services:
jenkins-compose:
image: jenkins
privileged: true
user: root
ports:
- "8088:8080"
- "50000:50000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /home/demo/jenkins-compose:/var/jenkins_home
- /usr/bin/docker:/usr/bin/docker
其中
privileged: true
表示当前容器具有特权
这次报错如下:
docker: error while loading shared libraries: libltdl.so.7: cannot open shared object file: No such file or directory
这个错误表示,Jenkins 所在的镜像可以执行 docker 命令了,但是还缺少执行 docker 命令所需的 library。此时有两个选择:
- 将宿主机的 library 映射到容器
- 在容器内安装缺少的 library
因为懒,不想创建中间镜像,同时也是想尽量使用官方的默认镜像,所以选择了方案一。在 CentOS7 上可能也没有安装这个 library,但是没关系,通过 yum install
命令即可轻松安装:
yum install libltdl.so.7
安装好后,查看安装位置,为使用卷做准备:
which libltdl.so.7
我这里的安装位置是:
/usr/lib64/libltdl.so.7
在 Jenkins 镜像中使用这个 library 的位置是 /usr/lib/x86_64-linux-gnu/libltdl.so.7
,通过 -v
映射即可。
4. 完整分析
完整的报错内容如下:
Using sole credentials lihongfeng/****** in realm ‘<svn://111.230.25.113:3690> jenkins demo’
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/demo-pipeline
[Pipeline] {
[Pipeline] sh
[demo-pipeline] Running shell script
+ docker inspect -f . golang:1.10
/var/jenkins_home/workspace/demo-pipeline@tmp/durable-d5484068/script.sh: 2: /var/jenkins_home/workspace/demo-pipeline@tmp/durable-d5484068/script.sh: docker: not found
[Pipeline] sh
[demo-pipeline] Running shell script
+ docker pull golang:1.10
/var/jenkins_home/workspace/demo-pipeline@tmp/durable-3aaa5181/script.sh: 2: /var/jenkins_home/workspace/demo-pipeline@tmp/durable-3aaa5181/script.sh: docker: not found
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
ERROR: script returned exit code 127
Finished: FAILURE
实际上,通过
docker run -p 8080:8080 -p 50000:50000 privildged -v /var/run/docker.sock:/var/run/docker.sock jenkins
命令启动 Jenkins 时,-v /var/run/docker.sock:/var/run/docker.sock
就表示容器可以和 Docker 守护进程通信了,并且 Ubuntu 系列的 Linux 上没有问题(暂时没有在 Ubuntu 上面验证),但在 RedHat 系列(RHEL、CentOS、Fedora 等)的 Linux 上运行 docker 时会因为 SELinux 而报错 docker: not found
。解决方案在 这里。
5. 最终可用的 docker-compose.yml
文件
version: '3'
services:
jenkins-compose:
image: jenkins
privileged: true
user: root
ports:
- "8088:8080"
- "50000:50000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
- /usr/lib64/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7
- /home/demo/jenkins-compose:/var/jenkins_home
其中
image: jenkins
表示使用官方 Jenkins 仓库中的最新版本privileged: true
表示当前容器具有特权user: root
表示在容器内使用 root 用户ports
表示端口映射"8088:8080"
将宿主机的 8088 端口映射到容器的 8080 端口(Jenkins 默认监听 8080 端口)"50000:50000"
只有在其他主机设置了一个或多个基于 JNLP 的 Jenkins 代理程序,而这些代理程序又与 jenkinsci/blueocean 容器(作为主 Jenkins 服务器“Jenkins master”)进行交互时才需要这个配置。
volumes
表示磁盘映射/usr/bin/docker:/usr/bin/docker
表示将宿主机中的 docker 命令映射到容器中/home/demo/jenkins-compose:/var/jenkins_home
表示将宿主机中的/home/demo/jenkins-compose
目录映射到容器中,作为 Jenkins 的工作目录