本文共 11088 字,大约阅读时间需要 36 分钟。
Jenkins 支持主流的 Linux 发行版系统,同时还支持 macOS、Windows、和 Docker 运行。 具体系统的 Jenkins 安装包可以去官网下载 。
作为运行在生产环境,推荐在 CentOS 中安装,目前容器技术也非常流行,在 Docker 中运行 Jenkins 也是不错的选择。接下来将会演示在 CentOS 和 Docker 中运行 Jenkins 服务。Jenkins 的版本迭代非常快,插件也是每天都有更新。目前 Jenkins 界面也有非常大的改版,我测试对比了一下,只有 2.222.4 及以下的版本界面还是熟悉的样子,2.222.4 之后的版本在 UI 界面上变化非常大,特别是配置管理的功能菜单要花点时间熟悉下。所以我后面的演示操作全是基于 Jenkins 2.222.4 版本,如果各位有兴趣可以尝试用一用最新版的 Jenkins。直接在 Linux 中安装 Jenkins 需要做以下的几件事,参见详细步骤:
1、Jenkins 运行时需要安装 Git 和 JDK# 安装最新版的 git 和 JDK 1.8$ sudo yum install git java-1.8.0-openjdk
2、设置 Jenkins 源并安装 Jenkins
# 安装指定版本的 Jenkins$ wget https://pkg.jenkins.io/redhat-stable/jenkins-2.222.4-1.1.noarch.rpm$ sudo rpm -ivh jenkins-2.222.4-1.1.noarch.rpm# 安装最新版本的 Jenkins$ sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo$ sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key$ sudo yum install jenkins -y# 将 Jenkins 加入开机启动项$ sudo systemctl start jenkins$ sudo systemctl enable jenkins$ sudo systemctl daemon-reload
3、允许 Jenkins 通过 Linux 防火墙
# Jenkins 默认运行在 8080 端口,允许8080端口通过防火墙$ sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent$ sudo firewall-cmd --zone=public --add-service=http --permanent$ sudo firewall-cmd --reload# 或者直接关闭防火墙$ sudo firewall-cmd --state # 查看防火墙状态$ sudo systemctl stop firewalld.service # 停止防火墙$ sudo systemctl disable firewalld.service # 禁用防火墙开机启动$ sudo systemctl enable firewalld.service # 起用防火墙开机启动$ sudo systemctl start firewalld.service # 禁用防火墙开机启动
采用容器来跑服务有个很大的好处,那就是搞坏了可以快速重新开始,这是我喜欢 Docker 的原因之一。采用 Docker 容器来跑 Jenkins 服务有两种方式:其一是直接使用 docker run
命令;其二就是使用 docker-compose
命令。推荐使用 docker-compose
,因为后期如果要升级 Jenkins 比较方便,更换下 Jenkins 镜像就可以了。下面还是将两种方式都做个记录,各自选择自己喜欢的方式进行实操。
docker
相关命令,我们这里要把宿主机的 docker
和 docker.sock
挂载到容器中。当然你也可以在 Jenkins 容器中再安装 Docker 服务,这就是 docker-in-docker
的模式了,docker-in-docker
的坑比较多,留给各位自己去探索吧,我这里就把宿主机的 docker
客户端和 docker daemon
挂载到 Jenkins 容器中。 注意点:
- 要想能执行 docker 命令,就需要将系统用户加到 docker 组,安装完 docker 服务这个组就会自动创建。
- docker 容器中运行 jenkins,容器里面是默认使用的 uid=1000 的 jenkins 用户启动的 jenkins 服务。
- 确保宿主机中存在一个系统账号,可以执行 docker 命令,并且和容器中的 jenkins 用户的 uid、gid和组的属性相同。
所以这里会涉及到调整系统用户的 id 和组的 id,相关操作和说明,参见后续的操作过程,当然你可以简单粗暴地将 docker 和 docker.sock 的权限设置为任何账号都可执行,但是不推荐这样,可能存在安全风险。
# 拉取指定版本 Jenkins 容器$ docker pull jenkins/jenkins:2.222.4-lts-centos7# 创建一个属于 Jenkins 容器的 docker 网络(可选,如果没创建,后面就不要指定网络)$ docker network create jenkins_network# 运行 jenkins 容器$ docker run --name jenkins-in-docker \ --network jenkins_network \ --publish 8080:8080 --publish 50000:50000 \ --volume jenkins_home:/var/jenkins_home \ --volume /usr/bin/docker:/usr/bin/docker \ --volume /var/run/docker.sock:/var/run/docker.sock \ jenkins/jenkins:2.222.4-lts-centos7
docker run
中的命令参数设置不做过多解释,不明白的可以自己去先熟悉下 docker 常用命令。这里单独说明下 --volume
数据卷的设置,该参数可以挂载宿主机的文件到容器,也可以创建新的数据卷,新的数据卷由 docker 容器管理,如果你是将 jenkins_home 目录挂载到宿主机,可能还会遇到 Jenkins 容器启动失败的问题。
# 看看容器日志 docker logs -f$ docker logs -f jenkins-in-docker --tail=50touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission deniedCan not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?# 如果看到是上面的内容,那就是目前权限的问题,需要把映射到宿主机的 jenkins_home 目录的所有权调整下$ sudo chown -R 1000:1000 volume_dir
容器里面一般都是笔记精简的,能不要的东西就不要,这样可以保证容器镜像尽量安全且体积小,但有时我们确实需要在容器中安装些工具,但是又发现进入容器后默认的用户又没有管理权限,这是就需要用如下命令以管理员身份进入容器,然后在容器中安装工具,进行特殊操作都是可以的。
# 以root身份进入容器,有的容器没有 bash,将 bash 换成 sh 即可$ docker exec -u 0 -itbash
采用 docker-compose 方式运行 Jenkins 容器,首先请确保自己系统可以执行 docker-compose 命令,如果还没安装 docker-compose 可翻看前面安装 docker 的章节。
新建一个目录,在下面创建 infrastructure-docker-compose.yml、startup-infrastructure-docker-compose.sh 两个文件。docker-compose 配置文件如下 infrastructure-docker-compose.yml :
version: '3.7'services: jenkins-in-docker: image: jenkins/jenkins:2.222.4-lts-centos7 container_name: jenkins-in-docker privileged: true restart: unless-stopped ports: - 8080:8080 - 50000:50000 environment: JAVA_OPTS: "-Xmx2048m -Xms1024m -Djava.security.egd=file:/dev/./urandom -Duser.timezone=Asia/Shanghai -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8" volumes: - /usr/bin/docker:/usr/bin/docker - /var/run/docker.sock:/var/run/docker.sock - ./docker_data/jenkins/2.222.4/jenkins_home:/var/jenkins_home - ./docker_data/jenkins/2.222.4/jenkins_home_bak:/var/jenkins_home_bak networks: - infrastructure_network deploy: resources: limits: memory: 4096M cpus: '2'networks: infrastructure_network: external: true
下面就是启动 docker-compose 运行 jenkisn 服务,我这里专门写了个 shell 脚本,在演示的过程中有时需要重复的敲命令,写在 shell 脚本中直接运行就行,省去重复敲命令的繁琐。
shell 脚本 startup-infrastructure-docker-compose.sh :#!/usr/bin/env bash# 设置docker网络名称和docker-compose文件docker_network=infrastructure_networkdocker_compose_file=infrastructure-docker-compose.yml# 输出下docker-compose文件内容echo "The compose file as below: "docker-compose -f ${docker_compose_file} confignw=$(docker network ls | grep ${docker_network} | awk '{print $1}')if [[ ${nw} ]]; then docker-compose -f ${docker_compose_file} --compatibility up --remove-orphans -delse docker network create ${docker_network} && docker-compose -f ${docker_compose_file} --compatibility up --remove-orphans -dfi
运行 shell 脚本,启动 Jenkins 服务:
# 设置shell脚本可执行$ chmod +x startup-infrastructure-docker-compose.sh# 运行shell脚本$ ./startup-infrastructure-docker-compose.sh
同样你可能还是会遇到 jenkins_home 目录权限问题,解决方式还是如下,调整目前所有权:
$ chown -R 1000:1000 docker_data/jenkins$ ./startup-infrastructure-docker-compose.sh
没什么意外的话等个1分钟,就可以访问 Jenkins 服务了,或者看到 jenkins 日志 Jenkins is fully up and running
也说明 jenkins 已经启动好了。浏览器访问 http://host_ip:8080,如果访问不了,确保自己防火墙开放了 8080 端口。
$ 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.24/containers/json: dial unix /var/run/docker.sock: connect: permission denied
这就很纳闷儿了,我宿主机上有个 jenkins 用户,并且 uid 也是 1000,为什么在 jenkins 容器中还是不能执行 docker 相关命令呢?我的排查过程如下:
先看看我在宿主机上的用户信息:$ id jenkinsuid=1000(jenkins) gid=1000(jenkins) 组=1000(jenkins),10(wheel),994(docker) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
可以看到 jenkins 这个用户的 uid、gid和所属组的信息,我们再看看容器中 jenkins 的 uid、gid 信息:
$ docker exec -it jenkins-in-docker bashbash-4.2$ id jenkinsuid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins)
发现 Jenkins 容器默认的 jenkins 用户没有在 docker 组中,处理方式如下:
# 以 root 身份进入容器$ docker exec -it -u root jenkins-in-docker bash# 看看用户和组信息[root@e77ed7439afd ~]$ cat /etc/group ⋮jenkins:x:1000:docker:x:997: ⋮# 发现docker组存在,但是宿主机docker组的id=994,容器中docker组的id=997# 我们先将jenkins用户加入到docker组中试试[root@e77ed7439afd ~]$ usermod -aG docker jenkins[root@e77ed7439afd ~]$ id jenkinsuid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins),997(docker)# 退出容器,将jenkins容器重启下$ docker restart jenkins-in-docker# 以默认身份进入容器$ docker exec -it jenkins-in-docker bashbash-4.2$ docker psGot permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json: dial unix /var/run/docker.sock: connect: permission denied# 发现还是不能执行docker命令,我们把docker组的id也调整成和宿主机一样试试$ docker exec -it -u root jenkins-in-docker bash[root@e77ed7439afd ~]$ groupmod -g 994 docker[root@e77ed7439afd ~]$ id jenkinsuid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins),994(docker)# 再重启下jenkins容器,以默认用户进入容器看能否执行docker命令$ docker exec -it jenkins-in-docker bashbash-4.2$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESe77ed7439afd jenkins/jenkins:2.222.4-lts-centos7 "/sbin/tini -- /usr/…" 2 weeks ago Up 4 seconds 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp jenkins-in-docker5f89b1a44f39 mysql:8.0.17 "docker-entrypoint.s…" 4 weeks ago Up 2 weeks 0.0.0.0:3306->3306/tcp, 33060/tcp mysql-in-docker87c2dcc88c71 nginx:1.18.0 "/docker-entrypoint.…" 4 weeks ago Up 2 weeks 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp nginx-in-dockerbash-4.2$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEdocker 20.10.1-dind 8d54f09a2b77 5 weeks ago 256MBnginx 1.18.0 05f64a802c26 6 weeks ago 133MBjenkins/jenkins 2.222.4-lts-centos7 32078832282a 7 months ago 637MBmysql 8.0.17 b8fd9553f1f0 16 months ago 445MB# 如果容器中没有docker用户组,直接创建一个docker并设置docker组ID与宿主机docker组ID相同,然后将jenkins用户加入docker组$ groupadd -g 994 docker && usermod -aG docker jenkins
现在发现在容器中可以正常执行 docker 命令了,其实总结起来就两点:
OK,到此才算是真正地做完了 jenkins 服务的准备工作,我们将宿主机的 docker 挂载到容器中,并且在尽量不调整宿主机的设置,做到了在容器中可以完美执行 docker 命令。
Jenkins 插件的官方源在国外,下载速度非常慢,国内也有一些镜像源,但是却做得不彻底,实际上还是会从国外下载。经过我自己测试验证,以及在网上寻找到的资料,有以下的几种方式来加速 Jenkins 插件的下载,但是具体哪种方式有效,各位也可以自己试试看。
在 Jenkins 的安装目录下有个updates目录,该目录下有个default.json文件,该文件就是插件镜像源的JSON内容。修改这个文件,将里面的 mirrors.jenkins-ci.org
批量替换成 mirrors.tuna.tsinghua.edu.cn/jenkins
或者你自己知道并且想使用的镜像源,将 www.google.com
替换成 www.baidu.com
即可。
主要是将 Jenkins 插件下载地址代理到清华等国内的 Jenkins 插件下载地址。在 Jenkins 机器上安装 Nginx 服务,然后添加一条hosts记录:127.0.0.1 updates.jenkins-ci.org
然后修改 Nginx 的配置文件:
rewrite ^/download/plugins/(.*)$ https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/$1? last;# 或者添加一条 location 配置location /download/plugins { proxy_next_upstream http_502 http_504 error timeout invalid_header; proxy_set_header Host mirrors.tuna.tsinghua.edu.cn; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; rewrite /download/plugins(.*) /jenkins/plugins/$1 break; proxy_pass https://mirrors.tuna.tsinghua.edu.cn;}
Jenkins 系统管理 → 插件管理 → 高级 → 升级站点,设置国内的镜像源。
安装 Localization: Chinese (Simplified) 插件后,在 Jenkins 界面的右下角会多出一个【Jenkins 中文社区】按钮。点击进去你会看到 Jenkins 中文社区的介绍和二维码,下边还有个【更新中心镜像设置】,点击下【使用】按钮,然后将 Jenkins 的默认的升级站点 修改为 ,【提交】修改后【立即获取】试试看,没什么问题就可以去【可选插件】里面找几个插件安装看看速度是不是快多了。
经过我自己的测试和验证,目前第4种方式是最有效且没有什么问题的。其他方式都是治标不治本,并且还可能会遇到如下签名校验不过的问题。
关于 Jenkins 镜像源加速站点在使用过程中如果有什么问题,可以参考 Jenkins 中文社区的一些解答转载地址:http://cpwaz.baihongyu.com/