jenkins

2023-02-20 Views4172字17 min read

jenkins

介绍

Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,起源于Hudson,主要用于持续、自动的构建/测试软件项目、监控外部任务的运行。Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行。通常与版本管理工具(SCM)、构建工具结合使用。常用的版本控制工具有SVN、GIT,构建工具有Maven、Ant、Gradle。

jenkins官网:https://www.jenkins.io/

持续集成CI

持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

通俗来讲持续集成就是软件开发人员提交代码以后自动化构建、发布、测试。

img

持续交付CD

CD(Continuous Delivery, 中文意思持续交付)是在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境(类生产环境)中。比如,我们完成单元测试后,可以把代码部署到连接数据库的Staging环境中更多的测试。如果代码没有问题,可以继续手动部署到生产环境。下图反应的是CI/CD 的大概工作模式。

img

部署

部署方式一般现在就分为二进制部署和docker(k8s)部署。

官网下载:https://www.jenkins.io/download/

二进制包部署

jenkins本身是基于java开发,其二进制包是war文件,运行于tomcat。

1.部署tomcat

2.下载jenkins的war部署包

3.将包放于tomcat的webapps目录下,启动tomcat。默认8080端口访问。

k8s(docker)部署

docker部署

dockeruhb官网镜像地址:https://hub.docker.com/r/jenkins/jenkins

1.拉取镜像 docker pull jenkins/jenkins

2.在本地创建Jenkins-home目录:mkdir -p /home/jenkins_home 用于映射

3.启动Jenkins容器:docker run -d --name jenkins -p 8080:8080 -p 50000:50000 -v /home/jenkins_home:/var/jenkins_home jenkins/jenkins

​ -p 50000:50000 ,这个端口映射要保持一致,否则agent无法启动,agent部分主要作用就是告诉Jenkins,选择哪台节点机器去执行Pipeline代码

4、然后网站上输入IP:8080访问,需要输入密码,复制页面上的路径,在服务器上输入cat 路径查看密码,粘贴到输入框之后下一步就好

​ cat / var/jenkins_home/secrets/initialadminpassword

k8s部署

k8s上面一般有两种部署方式,使用yaml文件部署和helm方式部署。

helm方式

helm方式建议使用官方镜像,这里如果用的是bitnami的镜像则建议替换成官方,我这边使用发现bitnami的jenkins有些系统配置无法持久化。

helm repo add jenkins  https://charts.jenkins.io      
helm pull jenkins  https://charts.jenkins.io    
tar -xvf jenkins-VERSION.tar.gz
cd jenkins
vim values.yaml
#根据需求更改配置
重要的配置包括jenkins密码,svc类型,jenkins持久化存储配置(PVC)
改完以后
helm install jenkins . -n 要部署的名称空间
yaml部署

jenkins.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jks
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jks
  template:
    metadata:
      labels:
        app: jks
    spec:
      containers:
      - name: jks
        image: jenkins/jenkins:2.319.3-centos7-jdk8
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: jks-home
          mountPath: /var/jenkins_home
        - name: time-zone                            #容器内挂载点名称
          mountPath: /etc/localtime              #容器内挂载点路径,可以是文件或目录
      affinity: #这里由于jenkins镜像很大,避免删掉pod重构切换节点又要拉取数据,所以固定其节点。
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname # 标签键名
                operator: In
                values:
                - node2 
      initContainers:
      - args:
        - -c
        - chmod 755 /var/jenkins_home && chown 1000:1000 /var/jenkins_home   ### 这里将上一步所记录的权限属组进行更改
        command:
        - /bin/sh
        image: centos
        imagePullPolicy: IfNotPresent
        name: chauth
        securityContext:
          privileged: true    ###  建议开启特权模式
        volumeMounts:
        - mountPath: /var/jenkins_home      ### initc也要对次数据卷进行挂载
          name: jks-home
      volumes:
      - name: time-zone                              #数据卷名称,需要与容器内挂载点名称一致
        hostPath:
          path: /etc/localtime  
      - name: jks-home
        persistentVolumeClaim:
            claimName: jks-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jks-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Gi
---
apiVersion: v1
kind: Service
metadata:
  name: jks
spec:
  selector:
    app: jks
  type: NodePort
  ports:
    - port: 8080
      targetPort: 8080
      nodePort: 32089

kubectl apply -f jenkins.yaml

部署完后访问jenkins:

image-20230215154224734

这里还是和上面一样,进入容器找到这个文件

[root@node1 jenkins_test]# kubectl exec -it jks-698b6564fb-5692t -n k3s-test bash 
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "jks" out of: jks, chauth (init)
bash-4.2$ cat /var/jenkins_home/secrets/initialAdminPassword
54c0b40b20f34b4ca7b75840c68883a1
然后填入管理员密码就ok
安装的插件如果不知道可以先选择推荐安装的插件

任务

jenkins主要就是通过执行任务来工作的,左边菜单第一个按钮就是新建item

image-20230215164410954

主要使用的就是自由项目、流水线、多分支流水线,而从任务类型角度来讲主要就分为两种:流水线与非流水线

非流水线(自由风格)

一般任务的步骤分为:推送代码-》触发钩子-》jenkins任务执行-》拉取代码-》构建操作-》构建后操作(一般是通知)

拉取代码

这里从拉取代码开始:

首先在创建freestyle project前需要先下载Git Parameter模块,在系统管理—》插件管理—》可选插件中搜索git parameter,下载并重启jenkins。

新建任务在源码管理中选择git,Repository URL输入git仓库地址,Credentials添加一个全局凭据用于git登录,指定分支中选择要拉取的分支。

构建触发器

构建触发器一般用于在代码仓库推送代码之后,触发触发器然后jenkins开始构建对应的任务。这个需要Generic Webhook Trigger插件,安装完毕重启即可。

webhook触发jenkins任务配置

首先需要在jenkins任务中配置触发器

image-20230216111031873

例如这里的token设置的是test,则触发器url为:http://jenkins/generic-webhook-trigger/invok?token=test

一般代码仓库如gitlab都会在具体项目里面有webhook配置,如下

image-20230216110814851
image-20230216110830102

首先将刚刚设置拿到的触发器url填入这个URL中,然后设置触发的动作,一般push events即可,表示其收到push操作时,则会触发。到这里webhook触发器就配置完成了,当gitlab推送代码时,jenkins会立即执行对应项目的任务。

构建环境

构建环境主要构建前的一些操作,例如构建前删除工作区文件、使用密码文件或文本、如果卡住则终止构建等。这个配置不是必要配置,一般不用对构建环境做配置。

image-20230216112310634

构建

构建最常用的就是执行shell命名,拉取代码以后的编译-》打包推送docker镜像(如果不打包就是二进制部署)-》部署 操作都在这一步完成。

image-20230216112840235

image-20230216112904780

直接在里面执行shell命令或者执行脚本就行了。

构建后操作

构建后常用配置是构建通知,一般常用的是email通知,jenkins默认支持email通知

image-20230216113950123

但现在比较流行用webhook进行通知,jenkins默认不支持这种配置,需要额外下载插件。

企业微信通知

1、在需要发送企业微信通知的群中“添加群机器人”,得到webhook地址

2、在Jenkins系统管理-插件管理-可选插件,搜索"Qy Wechat Notification Plugin"插件,下载并重启Jenkins

3、配置系统配置

​ 输入webhook地址,点击保存

image-20230216130625028

填入webhook即可,其他几项非必填项。

钉钉通知

1、打开钉钉,进入群设置-智能群助手-添加机器人-自定义机器人(填写机器人名称、安全设置、协议)得到webhook

2、在Jenkins系统管理-插件管理-可选插件,搜索“DingTalk”,下载并重启Jenkins

3、配置系统配置

进入系统管理-系统配置-钉钉, 输入webhook地址、id、名称,主要是webhook地址以及钉钉那边机器人是否配置了关键字和加密,如果有需要输入
image-20230216132900385

然后在任务配置中最上面就可以找到钉钉机器人

image-20230216133032694

如果不填写内容则为插件默认的告警模式,如需自定义需要看插件文档。

maven任务

如果是java项目,可以使用maven风格来创建任务。首先需要按Maven Integration plugin插件,安装后重启在创建任务时就可以看到选项

image-20230216133527267

maven风格项目主要是多了一个build选项,可以直接在配置中指定pom.xml进行maven打包,不像自由风格一样所有操作都需要通过shell来执行。这里需要安装maven工具,如果没有则会提示找不到工具,需要在全局工具配置中安装或指定工具目录。

image-20230216133703880

流水线(pipeline)

流水线其实就是通过pipeline的方式来配置任务流程。

pipeline语法

image-20230217104233727
pipeline示例
示例1
pipeline {
	agent any

	stages{
		stage('获取代码') {
			steps {
			
	      checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '4dc04ffd-15bf-46e3-a942-2b58f37bc26b', url: 'git@gitlab.zhangxianwei.com:dev/java.git']]])
			
			}
		}
		
		stage('代码检测'){
			steps {
				echo "sonarqube check...."
			}
		}		
		stage('代码构建'){
			steps {
				echo "maven build....."
			}
		}	
		stage('代码部署'){
			steps {
			    echo "echo sh"
			//	sh -x '/server/scripts/deploy_html_tag.sh'
			}
		}
	}
	
}
java项目构建pipeline示例
pipeline {
    agent {
        label "edge-node" 
    } 
    options {
    buildDiscarder(logRotator(numToKeepStr: '3'))
    disableConcurrentBuilds()
    }
    stages {

        stage('Check'){
            steps {
                sh "mvn sonar:sonar"
            }
        }
        stage('Clean') {
            steps {
               sh "mvn clean"
            }
        }
        stage('Build') {
            steps {
                sh "mvn compile"
            }
        }
        stage('Package') {
            steps {
                sh "mvn package -Dmaven.test.skip=true -P"+env.BRANCH_NAME
            }
        }
        stage('Deploy') {
            steps {
                sh "mvn deploy -Dmaven.test.skip=true -P"+env.BRANCH_NAME
            }
        }

        stage('IN') {
            steps {
                 sh "~/kubectl_update.sh ${env.WORKSPACE} ${env.JOB_NAME}"
            }
        }
    }
     post {
        success {
            dingTalk(
                robot: 'jenkins', 
                type:'MARKDOWN', 
                title: "构建成功: ${JOB_NAME}", 
                text: ["## [${JOB_NAME}](${JOB_URL})",
                       "---",
                       "- 任务: [${BUILD_DISPLAY_NAME}](${BUILD_URL})",
                       "- 状态: <font color=green>成功</font>",
                       "- 持续时间: ${currentBuild.durationString}",	],	
            )
        }
        failure {
            dingTalk(
                robot: 'jenkins', 
                type:'MARKDOWN', 
                title: "构建失败: ${JOB_NAME}", 
                text: ["## [${JOB_NAME}](${JOB_URL})",
                       "---",
                       "- 任务: [${BUILD_DISPLAY_NAME}](${BUILD_URL})",
                       "- 状态: <font color=red>失败</font>",
                       "- 持续时间: ${currentBuild.durationString}",	],		
            ) 
        }
    }
}
前端项目构建pipeline示例
pipeline {
    agent {
        label "edge-node"
    }

    stages {

        stage('Check'){
            steps {
                sh "~/sonar-scanner-4.7.0.2747-linux/bin/sonar-scanner -Dsonar.login=admin -Dsonar.password=finsi0t.c0m" 
            }
        }
        stage('Clean') {
            steps {
               sh "npm cache clean --force"
            }
        }
        stage('Build') {
            steps {
                sh "npm install && npm run build:pro"
            }
        }
        stage('Install') {
            steps {
               sh "~/kubectl_update.sh ${env.WORKSPACE} ${env.JOB_NAME}"
            } 
        }


    }
    options {
        buildDiscarder(logRotator(numToKeepStr: '3'))
        disableConcurrentBuilds()
    }
     post {
        success {
            dingTalk(
                robot: 'jenkins', 
                type:'MARKDOWN', 
                title: "构建成功: ${JOB_NAME}", 
                text: ["## [${JOB_NAME}](${JOB_URL})",
                       "---",
                       "- 任务: [${BUILD_DISPLAY_NAME}](${BUILD_URL})",
                       "- 状态: <font color=green>成功</font>",
                       "- 持续时间: ${currentBuild.durationString}",	],	
            )
        }
        failure {
            dingTalk(
                robot: 'jenkins', 
                type:'MARKDOWN', 
                title: "构建失败: ${JOB_NAME}", 
                text: ["## [${JOB_NAME}](${JOB_URL})",
                       "---",
                       "- 任务: [${BUILD_DISPLAY_NAME}](${BUILD_URL})",
                       "- 状态: <font color=red>失败</font>",
                       "- 持续时间: ${currentBuild.durationString}",	],		
            ) 
        }
    }
}

创建流水线项目

image-20230216152357088
image-20230216152342895
image-20230216153134214

这里pipeline有两种方式,一种是直接在jenkins上面写,还有一种是来自仓库的pipeline文件,如上图。默认脚本文件路径是代码项目根目录下的Jenkinsfile。

下面是我这里gitlab存放的用于测试的jenkinsfile:

image-20230216164244800

jenkinsfile(pipeline)和jenkins流水线配置完成之后就可以执行任务了。需要注意的是这里的git拉取仓库只能写在pipeline中,不能像自由风格和maven风格任务一样在任务配置中设置源码管理(git)。这里钩子触发的配置和上面一样,但构建后通知是写在pipeline中的,下面的多分支流水线会写。

jenkins中的流水线语法生成器

如果不熟悉流水线语法可以在配置任务中选择流水线语法,跳转到语法生成器。

image-20230217104911232
image-20230217104951115

例如我这里生成一个git拉取代码的pipeline片段

image-20230217105413932

多分支流水线

多分支流水线和流水线不同的地方是多分支流水线可以直接对一个项目多个分支进行任务。在实际中,需要多分支同时进行开发。如果每个分支都创建一个Jenkins项目,比较多余。一般企业生产中也会采用这种方式。

img

新建任务中选择多分支流水线

file
分支源配置

填入git项目地址以及登录凭据,添加行为clean before/after checkout 用于清理未跟踪的文件

image-20230217101740905
build configuration

如果是仓库根目录下的jenkinsfile就用默认即可,需要注意的是项目下需要执行jenkins任务的分支都需要有jenkinsfile,每个分支的jenkinsfile是独立的。

image-20230217101839957
扫描 多分支流水线 触发器

多分支流水线钩子触发器需要下载插件:Multibranch Scan Webhook Trigger。

安装完以后在扫描 多分支流水线 触发器中选择scan by webhook,输入trigger token。例如这里输入的是test,则gitlab那边设置触发钩子配置的url就应该是

http(s)😕/JENKINS_URL/multibranch-webhook-trigger/invoke?token=test

image-20230217103028959

配置完保存以后会自动开始扫描多分支

image-20230217103434365

由于我这里有两个分支mian、master配置了jenkinsfile,则这两个分支可以构建任务

image-20230217103520226
image-20230217103531928

多分支和钩子触发器都配置完毕后,在git仓库那边对对应分支进行push操作,这边就会自动开始构建任务

安全

安全配置位于:系统配置-》安全全局配置

凭据

这个上面在git拉取时已经用到过了,就不详细讲述了。就是存放密钥或者账号密码的模块,在系统管理-》凭据管理中可以配置(配置任务时也可以直接添加凭据)。

安全域

安全域主要是定义jenkins用户的数据源,默认是 Jenkins自带的用户数据库。

配置路径:jenkins设置-》全局安全配置-安全域

这个一般使用默认项就行,如果有ldap配置需求也可以选择ldap(用户数据使用ldap的数据库)。

授权策略

登录用户可以做任何事

授权策略默认是“登录用户可以做任何事“,表示登录用户皆为最高权限,其中有一个子选项是”匿名用户具有可读权限“,一般会配合子选项进行使用。(登录的是管理员,不登陆就只有只读权限)

任何用户可以做任何事(没有任何限制)

简明知意,所有用户(包括匿名用户)都有最高权限。一般不用

安全矩阵和项目矩阵授权策略

根据用户或用户组授予对应权限,可以精确到具体模块权限,但一般不会使用,因为有一个更好的模块Role-Based Strategy作为替代。

Role-Based Strategy

角色授权模块,也是最常用的策略,需要先下载模块Role-based Authorization Strategy

应用Role-Based Strategy以后,选择 系统管理-》安全-》Manage and Assign Roles

管理角色

位于系统管理-》安全-》Manage and Assign Roles-》管理角色

管理角色主要的功能就是创建一个角色,并对于角色授予权限,然后将角色赋予用户或用户组

Global roles

全局管理,可以对用户或组授予权限(输入用户 add以后通过选择打勾授予对应模块权限,然后保存生效)。image-20230220153651762

image-20230220153700562
Item roles

通过项目进行授权,可以使用正则匹配。例如我这里是授予有英文字母a-z的项目权限

image-20230220160917798
Node roles

Node roles在Jenkins集群环境下使用,配置不同结点的Jenkins权限。(目前没使用过,如后期有使用会补上这一块内容)

分配角色

分配角色也是三大块:Global roles、Item roles、Node roles。分配主要就是添加用户、用户组然后勾选上面创建好的角色,此时用户、用户组则拥有了该角色的权限。

备注:jenkins默认没有组选项,只能通过active directory或者ldap对接jenkins用户数据来使用用户组管理。

其他问题

k8s环境中可能出现的问题

时区

时区默认肯定不是国内的,最简便的解决方法是通过挂载主机(k8s hostpath方法)的时区文件(/etc/localtime)解决。或者dockerfile用jenkins作为基础镜像,再加上时区修改的配置也可以。

中文乱码

这个是不是k8s都有可能出现这个问题,主要是主机的字符集格式需要修改为中文utf-8。如果是k8s的话需要dockerfile重新打一下镜像,改成中文utf-8,或者每次启动时安装utf-8中文并配置(一般jenkins镜像没有中文utf-8)

EOF