天天看點

Jenkins 基礎篇 - 安裝部署

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

相關指令,我們這裡要把主控端的

docker

docker.sock

挂載到容器中。當然你也可以在 Jenkins 容器中再安裝 Docker 服務,這就是

docker-in-docker

的模式了,

docker-in-docker

的坑比較多,留給各位自己去探索吧,我這裡就把主控端的

docker

用戶端和

docker daemon

挂載到 Jenkins 容器中。

注意點:
  1. 要想能執行 docker 指令,就需要将系統使用者加到 docker 組,安裝完 docker 服務這個組就會自動建立。
  2. docker 容器中運作 jenkins,容器裡面是預設使用的 uid=1000 的 jenkins 使用者啟動的 jenkins 服務。
  3. 確定主控端中存在一個系統賬号,可以執行 docker 指令,并且和容器中的 jenkins 使用者的 uid、gid群組的屬性相同。
是以這裡會涉及到調整系統使用者的 id 群組的 id,相關操作和說明,參見後續的操作過程,當然你可以簡單粗暴地将 docker 和 docker.sock 的權限設定為任何賬号都可執行,但是不推薦這樣,可能存在安全風險。

采用 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