Jenkins 安装

Jenkins 支持主流的 Linux 发行版系统,同时还支持 macOS、Windows、和 Docker 运行。 具体系统的 Jenkins 安装包可以去官网下载 https://www.jenkins.io/download
作为运行在生产环境,推荐在 CentOS 中安装,目前容器技术也非常流行,在 Docker 中运行 Jenkins 也是不错的选择。接下来将会演示在 CentOS 和 Docker 中运行 Jenkins 服务。
Jenkins 的版本迭代非常快,插件也是每天都有更新。目前 Jenkins 界面也有非常大的改版,我测试对比了一下,只有 2.222.4 及以下的版本界面还是熟悉的样子,2.222.4 之后的版本在 UI 界面上变化非常大,特别是配置管理的功能菜单要花点时间熟悉下。所以我后面的演示操作全是基于 Jenkins 2.222.4 版本,如果各位有兴趣可以尝试用一用最新版的 Jenkins。

Linux 下安装 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 容器部署 Jenkins

采用容器来跑服务有个很大的好处,那就是搞坏了可以快速重新开始,这是我喜欢 Docker 的原因之一。采用 Docker 容器来跑 Jenkins 服务有两种方式:其一是直接使用 docker run 命令;其二就是使用 docker-compose 命令。推荐使用 docker-compose ,因为后期如果要升级 Jenkins 比较方便,更换下 Jenkins 镜像就可以了。下面还是将两种方式都做个记录,各自选择自己喜欢的方式进行实操。
为了让运行在 Docker 容器中的 Jenkins 也能执行 docker 相关命令,我们这里要把宿主机的 dockerdocker.sock 挂载到容器中。当然你也可以在 Jenkins 容器中再安装 Docker 服务,这就是 docker-in-docker 的模式了,docker-in-docker 的坑比较多,留给各位自己去探索吧,我这里就把宿主机的 docker 客户端和 docker daemon 挂载到 Jenkins 容器中。

采用 docker run 的方式运行 Jenkins 容器

# 拉取指定版本 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 <container_id_or_name>
$ docker logs -f jenkins-in-docker --tail=50

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_home 目录的所有权调整下
$ sudo chown -R 1000:1000 volume_dir

容器里面一般都是笔记精简的,能不要的东西就不要,这样可以保证容器镜像尽量安全且体积小,但有时我们确实需要在容器中安装些工具,但是又发现进入容器后默认的用户又没有管理权限,这是就需要用如下命令以管理员身份进入容器,然后在容器中安装工具,进行特殊操作都是可以的。

# 以root身份进入容器,有的容器没有 bash,将 bash 换成 sh 即可

$ docker exec -u 0 -it <container_id_or_name> bash

采用 docker-compose 方式运行 Jenkins 容器

采用 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_network
docker_compose_file=infrastructure-docker-compose.yml

# 输出下docker-compose文件内容
echo "The compose file as below: "
docker-compose -f ${docker_compose_file} config

nw=$(docker network ls | grep ${docker_network} | awk '{print $1}')
if [[ ${nw} ]]; then
  docker-compose -f ${docker_compose_file} --compatibility up --remove-orphans -d
else
  docker network create ${docker_network} && docker-compose -f ${docker_compose_file} --compatibility up --remove-orphans -d
fi

运行 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 端口。
Jenkins 日志和默认密码

输入 jenkins 默认密码,【继续】到下一步。

安装 Jenkins 插件,暂时不知道安装哪些插件的话可以选择默认【安装推进的插件】。

当然你也可以【选择插件来安装】,在里面选择自己想要的插件,每个插件的功能都有介绍。

选好后就点击【安装】,进入插件安装界面,这个界面要等几分钟,如果网络不好的话,有些插件还可能会安装失败,不过失败了也没关系,可以点击界面下的【重试】按钮,重新安装失败的插件。

插件安装好后就开始设置第一个管理员账号。

Jenkins URL 的设置,这里先默认即可,如果后面我们要为 Jenkins 服务设置域名可以重新在 Jenkins 的配置里面重新设置。

这就是 Jenkins 默认的界面了,目前还没有任何任务 Job。

这是管理 Jenkins 的界面,还是熟悉的界面。

如果你安装的 Jenkins 版本是 2.222.4 以上版本,就需要先熟悉下 Jenkins 的管理面板了。
这个版本是 2.263.1,我当时的最新版 Jenkins,下图是进入 Jenkins 的默认界面。

这是 Jenkins 的管理配置界面,对管理配置进行了分类,已经大变样了。

到这里你以为完了,并没有!还有个问题要解决,Jenkins 运行在容器中,并且希望可以正常执行 docker 命令。前面我们将宿主机的 docker 客户端和 docker.sock 挂载到了容器里面,现在在 Jenkins 容器里面执行 docker 命令你可能会遇到如下提示:

$ 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 jenkins
uid=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 bash
bash-4.2$ id jenkins
uid=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 jenkins
uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins),997(docker)

# 退出容器,将jenkins容器重启下
$ docker restart jenkins-in-docker

# 以默认身份进入容器
$ docker exec -it jenkins-in-docker bash
bash-4.2$ 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

# 发现还是不能执行docker命令,我们把docker组的id也调整成和宿主机一样试试
$ docker exec -it -u root jenkins-in-docker bash
[root@e77ed7439afd ~]$ groupmod -g 994 docker
[root@e77ed7439afd ~]$ id jenkins
uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins),994(docker)

# 再重启下jenkins容器,以默认用户进入容器看能否执行docker命令
$ docker exec -it jenkins-in-docker bash
bash-4.2$ docker ps
CONTAINER ID   IMAGE                                 COMMAND                  CREATED       STATUS         PORTS                                              NAMES
e77ed7439afd   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-docker
5f89b1a44f39   mysql:8.0.17                          "docker-entrypoint.s…"   4 weeks ago   Up 2 weeks     0.0.0.0:3306->3306/tcp, 33060/tcp                  mysql-in-docker
87c2dcc88c71   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-docker
bash-4.2$ docker images
REPOSITORY        TAG                   IMAGE ID       CREATED         SIZE
docker            20.10.1-dind          8d54f09a2b77   5 weeks ago     256MB
nginx             1.18.0                05f64a802c26   6 weeks ago     133MB
jenkins/jenkins   2.222.4-lts-centos7   32078832282a   7 months ago    637MB
mysql             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 命令了,其实总结起来就两点:

  • 容器中默认的 jenkins 用户要在 docker 组中。
  • 容器中 docker 组的 id 要保持和宿主机的 docker 组的 id 相同。

OK,到此才算是真正地做完了 jenkins 服务的准备工作,我们将宿主机的 docker 挂载到容器中,并且在尽量不调整宿主机的设置,做到了在容器中可以完美执行 docker 命令。

插件下载加速

Jenkins 插件的官方源在国外,下载速度非常慢,国内也有一些镜像源,但是却做得不彻底,实际上还是会从国外下载。经过我自己测试验证,以及在网上寻找到的资料,有以下的几种方式来加速 Jenkins 插件的下载,但是具体哪种方式有效,各位也可以自己试试看。

  1. 修改默认的配置文件,使其从国内镜像源下载

在 Jenkins 的安装目录下有个updates目录,该目录下有个default.json文件,该文件就是插件镜像源的JSON内容。修改这个文件,将里面的 mirrors.jenkins-ci.org 批量替换成 mirrors.tuna.tsinghua.edu.cn/jenkins 或者你自己知道并且想使用的镜像源,将 www.google.com 替换成 www.baidu.com 即可。

  1. 设置反向代理,将下载源从官方源代理到国内镜像源

主要是将 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;
}
  1. 设置 Jenkins 插件的升级站点

Jenkins 系统管理 → 插件管理 → 高级 → 升级站点,设置国内的镜像源。

  1. 使用 Localization: Chinese (Simplified) 插件

安装 Localization: Chinese (Simplified) 插件后,在 Jenkins 界面的右下角会多出一个【Jenkins 中文社区】按钮。点击进去你会看到 Jenkins 中文社区的介绍和二维码,下边还有个【更新中心镜像设置】,点击下【使用】按钮,然后将 Jenkins 的默认的升级站点 https://updates.jenkins.io/update-center.json 修改为 https://updates.jenkins-zh.cn/update-center.json ,【提交】修改后【立即获取】试试看,没什么问题就可以去【可选插件】里面找几个插件安装看看速度是不是快多了。

经过我自己的测试和验证,目前第4种方式是最有效且没有什么问题的。其他方式都是治标不治本,并且还可能会遇到如下签名校验不过的问题。

关于 Jenkins 镜像源加速站点在使用过程中如果有什么问题,可以参考 Jenkins 中文社区的一些解答 https://community.jenkins-zh.cn/t/jenkins/26

05-15 12:22