Docker安装Jenkins并支持Maven,Docker,Helm

Docker 安装 Jenkins 很简单, 但是安装完的 Jenkins 并不直接支持 maven, docker 等 CI 常用工具. 特别是 docker 涉及到 docker中运行docker, 也就是 docker in docker 的问题. 本文将示例如何解决.

本示例所主机 ttg12 的环境为:

  • IP: 192.168.31.12,
  • OS: Ubuntu Server 18.04,
  • Docker 版本: 19.03.6.

比较着急的同学可以直接跳到最后一节 四. 方案总结

一. Docker 安装 Jenkins

Jenkins现在也分长期支持版(LTS)和普通支持版, 对于需要线上长期稳定支持的, 最好下载 LTS 版. 当前 (2020.7) jenkins 最新 LTS 版本为 2.235.1-lts.

# 下载镜像
sudo docker pull jenkins/jenkins:2.235.1-lts
# 新建容器并启动
sudo docker run -d -p 8081:8080 -p 50001:50000 \
    -v /data2/jenkins/jenkins_home:/var/jenkins_home \
    -v /etc/localtime:/etc/localtime:ro \
    --restart=always \
    --name dao_jenkins_1 \
    jenkins/jenkins:2.235.1-lts

其中 2 个 Volumn Mapping 说明如下.

  • -v /data2/jenkins/jenkins_home:/var/jenkins_home, docker 中 /var/jenkins_home 是 jenkins 的 $HOME 以及所有配置, 数据存储的地方, 所以必须持久化到本地.
  • -v /etc/localtime:/etc/localtime:ro, 保持 docker 中的时区跟 host 保持一致, 否则日志等时间都使用 UTC+0 时区, 跟中国时间差 8 个小时.

查看容器日志:

sudo docker logs dao_jenkins_1

显示权限错误:

touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?

这是因为 jenkins 在 host 上用户为当前用户 faceless, 而 host 本地目录 /data2/jenkins/jenkins_home 属于 root.

简单的解决方案, 就是将 /data2/jenkins/jenkins_home 的所有者 (owner) 修改为 host 上运行 docker 用户 faceless (uid=1000)

# 修改 faceless 为 host 上 运行 docker 的用户
sudo chown -R faceless /data/jenkins/jenkins_home 
# sudo chown -R faceless /var/run/docker.sock
sudo docker start dao_jenkins_1

然后再重启 jenkins 容器 dao_jenkins_1

sudo docker restart dao_jenkins_1

二. 支持 Docker

我们将 Jenkins 运行在容器中, 而 Jenkins CI 也需要运行 docker 的话, 就会遇到 docker in docker 的问题. 具体请参考: ~jpetazzo/Using Docker-in-Docker for your CI or testing environment? Think twice..

而我们知道, Docker 其实分为两部分: 服务端和客户端. 服务端通过 socket 套接字 或者监听端口接受客户端的命令. 具体可参考官方文档 Configure where the Docker daemon listens for connections.

也就是说如果我们在宿主机上运行了 docker 服务端的话, 我们在容器内可以只安装 docker 客户端, 然后通过 socket套接字 或者 ip+端口 的方式来直接使用宿主的docker 服务. 这样在容器内新建容器, 其实是在宿主机上新建容器.

因为 Docker 服务端和客户端默认使用 socket 套接字进行交互, 所以我们这里也使用 socket套接字 的方案, 即将宿主机的 /var/run/docker.sock 通过映射给 Jenkins 容器.

2.1 让容器使用宿主的docker

按照上面的思路, 我们在创建容器的 docker run 命令中增加如下 3 个参数:

-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker 
-v /etc/docker:/etc/docker \

有关三者的说明

  • -v /var/run/docker.sock:/var/run/docker.sock, 通过映射主机的套接字文件到容器, 让容器内启动 docker 的时候并不是启动容器内的容器(子容器), 而是启动主机上的容器(兄弟容器).
  • -v /usr/bin/docker:/usr/bin/docker, 让容器中直接使用宿主机的 docker 客户端.
  • -v /etc/docker:/etc/docker, 让容器中的 docker 客户端使用宿主机的 docker 配置文件, 包括国内镜像 (mirrors) 和 非ssl安全访问白名单 等配置.

2.2 解决访问宿主 socket 的权限问题

重启容器后, 我们通过 docker exec -it dao_jenkins_1 /bin/bash 命令进入容器, 执行 docker ps 验证 docker 命令是否可正常使用, 结果发现会遇到如下权限问题:

jenkins@f9fd87225ddb:/$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permission denied

我们先看下 host 上 /var/run/docker.sock 的权限:

faceless@ttg12:~$ ll /var/run/docker.sock
srw-rw---- 1 root docker 0 Jul 16 10:17 /var/run/docker.sock=

可以看到 docker.sock 属于 root 用户 和 docker 组. 映射到容器内的权限为:

jenkins@5affdae1637b:/$ ls -al /var/run/docker.sock
srw-rw---- 1 root 128 0 Jul 16 10:17 /var/run/docker.sock

我们再在容器查看下 jenkins 用户的 user id 和 groupd id:

jenkins@5affdae1637b:/$ id
uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins)

我们可以看到 jenkins 的用户id 为 1000, group id 也为 1000. 因此我们的解决方案也有 2 个:

  • 在宿主机将 socket 文件的所有者改为 user id = 1000 的用户;
  • 在给容器内的用户增加 group id = 128 的权限.

下面分别讲解.

2.2.1 修改宿主机 socket 文件权限设置

将 host 上 docker socket 的拥有者修改为运行 uid=1000 的用户, 或者直接将权限修改为其他人可读写666:

# 修改宿主机上 socket 的 owner 为 id=1000 的用户
sudo chown 1000 /var/run/docker.sock
# 或修改 sock 的权限为 666
sudo chmod 666 /var/run/docker.sock

这个方案无需重启容器, 直接在容器内运行 docker ps 可以看到能输出正常结果.

这个方案是网上大多数文章给出的方案. 但是该方案有一个比较的缺陷, 那就是如果宿主机或者 docker 重启, 会重新创建 docker.sock 文件, 其所有者会被重置为 root 用户, 所以我们又需要再执行上面的命令修改权限.

2.2.2 给予容器 docker 组权限

第二个方案是, 我们给容器内的 jenkins 用户增加 id=128 的组权限. 而正好 docker run 很友好地提供 groupd-add 参数支持该操作.

官方文档 Additional groups

--group-add: Add additional groups to run as
By default, the docker container process runs with the supplementary groups looked up for the specified user. If one wants to add more to that list of groups, then one can use this flag:

$ docker run --rm --group-add audio --group-add nogroup --group-add 777 busybox id

uid=0(root) gid=0(root) groups=10(wheel),29(audio),99(nogroup),777

也就是说我们可一个通过 group-add 参数给容器中的用户通过 group name 或者 group id 添加多个额外的用户组权限, 但是注意: 这个用户组是指容器内的用户组, 其 id 可能跟宿主机上的 id 不一致. 而我们要让容器内的用户拥有 host 的某个 group 权限, 需要通过 id 来赋权.

因此这里我们先看 host 上 docker 组的 id.

faceless@ttg12:~$ cat /etc/group | grep docker
[sudo] password for faceless:
docker:x:128:faceless

可以看到 docker 用户组 id 为 128. 因此我们在创建容器的时候加上 --group-add=128 即可让容器内的 jenkins 用户拥有 /var/run/docker.sock 文件的读写权限:

# 先移除旧容器
sudo docker rm -f dao_jenkins_1
# 重新创建容器
sudo docker run -d -p 8081:8080 -p 50001:50000 \
    -v /data2/jenkins/jenkins_home:/var/jenkins_home \
    -v /etc/localtime:/etc/localtime:ro \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /etc/docker:/etc/docker \
    -v /usr/bin/docker:/usr/bin/docker \
    --restart=always  \
    --group-add=128 \
    --name dao_jenkins_1 \
    jenkins/jenkins:2.235.1-lts

三. 安装其他 CI 工具

3.1 安装和配置 Maven

首先安装 maven:

mkdir -p /opt/ && \
    cd /opt/ && \
    curl -fsSL https://mirror.bit.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz >    /tmp/apache-maven-3.6.3-bin.tar.gz && \
    tar xzf /tmp/apache-maven-3.6.3-bin.tar.gz -C /opt/ && \
    rm /tmp/apache-maven-3.6.3-bin.tar.gz && \
    ln -s /opt/apache-maven-3.6.3/bin/mvn /bin/mvn && \
    ln -s /opt/apache-maven-3.6.3/bin/mvnyjp /bin/mvnyjp && \
    export PATH=/opt/apache-maven-3.6.3/bin:$PATH

然后配置和加速 maven:
1) 在 host 目录 /data2/jenkins/jenkins_home/ (对应 docker 的 /var/jenkins_home/) 中打开或新建 .m2/settings.xml 文件, 添加阿里云镜像:

  
    
      ali-public
      public
      aliyun maven public
      https://maven.aliyun.com/repository/public
    
    
      ali-central
      central
      aliyun maven central
      https://maven.aliyun.com/repository/central
    
        
      aligoogle
      google
      aliyun google
      https://maven.aliyun.com/repository/google
    
        
      alisping
      spring
      aliyun spring
      https://maven.aliyun.com/repository/spring
    
        
      alispringplugin
      spring-plugin
      aliyun spring-plugin
      https://maven.aliyun.com/repository/spring-plugin
    
  

2) 甚至可以把本地 repository 里面已经下载好的三方库都 copy 到 .m2/repository, 节约下载时间.

3.2 支持 Kubernetes Helm

对 Kubernetes Helm3 的支持比较简单, 直接将宿主机上的 helm 映射到容器中即可. 注意这里针对 helm3, 因为 helm3 中已经移除 tiller, 只需要客户端即可.

先将 k8s master 上的 .kube/config 文件复制到 docker 宿主机 (这里是ttg12) 的 /data2/jenkins/jenkins_home/.kube/ 目录下.

然后在 docker run 命令中增加如下映射, 并重建容器.

-v /usr/local/bin/helm:/bin/helm

3.3 提交修改到新镜像 Image

待 docker-ce 安装完成后, 提交本次更新为新的 image, 后面从这个 image 启动容器:

# 提交修改
sudo docker commit -a "[email protected]" -m "jenkins v2.235.1-lts with support for maven, docker & k8s" dao_jenkins_1 jenkins_with_dockercli:2.235.1-lts

# 停止并移除旧容器
sudo docker rm -f dao_jenkins_1

# 以新image启动新容器
sudo docker run -d -p 8081:8080 -p 50001:50000 \
    -v /data2/jenkins/jenkins_home:/var/jenkins_home \
    -v /etc/localtime:/etc/localtime:ro \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /etc/docker:/etc/docker \
    -v /usr/bin/docker:/usr/bin/docker \
    -v /usr/local/bin/helm:/bin/helm \
    --restart=always  \
    --group-add=128 \
    --name dao_jenkins_1 \
    jenkins_with_dockercli:2.235.1-lts

如果需要, 可以将 image push 到私有的 docker registry. 这里示例 push 到阿里云镜像服务:

docker login --username= registry.cn-hangzhou.aliyuncs.com
docker tag jenkins_with_dockercli:2.235.1-lts registry.cn-hangzhou.aliyuncs.com/faceless/jenkins_with_dockercli:2.235.1-lts
docker push registry.cn-hangzhou.aliyuncs.com/faceless/jenkins_with_dockercli:2.235.1-lts

如果是开发或测试环境, 建议使用 Docker Registry 搭建本地简单镜像服务, 提升效率, 节约时间. 关于如何搭建本地镜像服务, 参考我的另一篇博文 私有化部署极简 Docker Registry.

四. 方案总结

为了方便以后进行扩展, 我把上面的 Jenkins + Maven 的镜像 Dockerfile 上传到了 gitee jenkins-docker-plus. 有兴趣的同学可以直接下载使用, 也欢迎提交新的 Jenkins+XX PR, 一起完善.

最后总结一下前面一步一步得到的方案. Docker 安装 Jenkins 并支持 Maven, Docker, Helm:

  1. 在宿主机上安装 docker-ce, 并配置好国内镜像;
  2. 在宿主机上安装 K8S kubectl 和 helm3, 并完成相关配置.
  3. gitee jenkins-docker-plus下载 jenkins-maven/Dockerfile 文件到宿主机 ~/tmp/jenkins-maven/Dockerfile;
  4. 进入 `~/tmp/jenkins-maven/ 执行 sudo docker build -t jenkins-maven:2.235.1-lts . 构建镜像;
  5. 查看本地 docker 用户组 id: cat /etc/group | grep docker. 以下用 表示 docker 用户组 id.
  6. 配置Volumn映射和用户组等参数, 创建并启动 Jenkins Docker:

    # 注意替换以下变. 本示例中: 
    #  = $HOME/jenkins_home
    #  = 128
    
    mkdir -p 
    sudo docker run -d -p 8081:8080 -p 50001:50000 \
        -v :/var/jenkins_home \
        -v /etc/localtime:/etc/localtime:ro \
        -v /var/run/docker.sock:/var/run/docker.sock \
        -v /etc/docker:/etc/docker \
        -v /usr/bin/docker:/usr/bin/docker \
        -v /usr/local/bin/helm:/bin/helm \
        --restart=always  \
        --group-add= \
        --name dao_jenkins_1 \
        jenkins-maven:2.235.1-lts
  7. 访问 http://:8081, 进入 jenkins, 进行初始化配置.
  8. [可选]参照上面"3.1 安装和配置 Maven"章节, 在宿主机上配置 maven repository 和 .seetings.xml 进行 maven 加速.

五. 附录

参考资料:

你可能感兴趣的:(docker,容器,maven,kubernetes)