Jenkins CI&CD
Jenkins 版本:2.204.4
kubernetes版本 1.17
需要提前准备应用:Harbor、测试springboot git项目地址
jenkins默认拉取插件地址为外网地址,所以部署后可能出现插件无法下载或者下载很慢的问题,这里是直接使用国内修改版本的镜像。
建议修改镜像地址认证文件(参考https://blog.csdn.net/weixin_40046357/article/details/104489497) 也可以换用国人自己打包好修改的镜像或者war包来进行部署(推荐)
1 2 3 4 5
| cd $JENKINS_HOME/war/WEB-INF/update-center-rootCAs
rm -fr jenkins-update-center-root-ca jenkins-update-center-root-ca.txt
curl 'https://raw.githubusercontent.com/jenkins-zh/docker-zh/master/mirror-adapter.crt' -o mirror-adapter.crt
|
修改文件 vim /var/lib/jenkins/hudson.model.UpdateCenter.xml
替换国内镜像地址https://updates.jenkins-zh.cn/update-center.json
1 Kubernetes 部署 Jenkins
1.1 采用NFS作为jenkins底层存储
Jenkins使用NFS作为底层存储,部署前需要在nfs服务器上创建对应的文件夹,并且确保目录对其它用户有读写权限。
mkdir /data/nfsshare/default/jenkins
1.2 创建 Jenkins 用于存储的 PV、PVC
jenkins-storage.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| apiVersion: v1 kind: PersistentVolume metadata: name: jenkins labels: app: jenkins spec: capacity: storage: 20Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain mountOptions: - hard - nfsvers=4.1 nfs: path: /data/nfsshare/default/jenkins server: 127.0.0.1 --- kind: PersistentVolumeClaim apiVersion: v1 metadata: name: jenkins namespace: default spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi selector: matchLabels: app: jenkins
|
1.3 创建 ServiceAccount & ClusterRoleBinding
jenkins-rbac.yaml 给予jenkins权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| apiVersion: v1 kind: ServiceAccount metadata: name: jenkins-admin namespace: default labels: name: jenkins --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: jenkins-admin labels: name: jenkins subjects: - kind: ServiceAccount name: jenkins-admin namespace: default roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
|
1.4 创建 Service & Deployment
jenkins-deployment.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| apiVersion: v1 kind: Service metadata: name: jenkins namespace: default labels: app: jenkins spec: type: NodePort ports: - name: http port: 8080 targetPort: 8080 - name: jnlp port: 50000 targetPort: 50000 selector: app: jenkins --- apiVersion: apps/v1 kind: Deployment metadata: name: jenkins namespace: default labels: app: jenkins spec: selector: matchLabels: app: jenkins replicas: 1 template: metadata: labels: app: jenkins spec: serviceAccountName: jenkins-admin containers: - name: jenkins image: jenkinszh/jenkins-zh:2.204.4 securityContext: runAsUser: 0 privileged: true ports: - name: http containerPort: 8080 - name: jnlp containerPort: 50000 resources: limits: memory: 2Gi cpu: "2000m" requests: memory: 2Gi cpu: "1000m" env: - name: LIMITS_MEMORY valueFrom: resourceFieldRef: resource: limits.memory divisor: 1Mi - name: "JAVA_OPTS" value: " -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85 -Duser.timezone=Asia/Shanghai " - name: "JENKINS_OPTS" value: "--prefix=/jenkins" volumeMounts: - name: data mountPath: /var/jenkins_home volumes: - name: data persistentVolumeClaim: claimName: jenkins
|
1.5 获取 Jenkins 生成的 Token
在安装 Jenkins 时候,它默认生成一段随机字符串在控制台日志中,用于安装时验证。这里需要获取它输出在控制台中的日志信息,来获取 Token 字符串。
如果日志中没有看到也可以到容器中的/var/jenkins_home/secrets/initialAdminPassword位置文件进行查询
1.6 启动 Jenkins 进行初始化
输入 Kubernetes 集群地址和 Jenkins Service 设置的 NodePort 端口号,访问 Jenkins UI 界面进行初始化
- 安装插件
安装插件,选择推荐安装方式进行安装即可,后续再安装需要的插件。
等待一段时间让jenkins自己进行插件的下载和同步,如果出现错误可以直接进行后续操作,遇到插件无法下载可以从官网下载对应插件的jpi文件,推送到jenkins持久化的plugin目录中,重启jenkins就可以完成手动安装插件。
jenkin插件包地址(https://mutoulazy-public.oss-cn-shenzhen.aliyuncs.com/properties/plugins.rar)
完成设置用户名、密码和设置jenkins地址后,初始化完成
1 2 3 4 5 6 7 8 9 10 11 12 13
| Git: Jenkins 安装中默认安装 Git 插件,所以不需要单独安装。利用 git 工具可以将 github、gitlab 等等的地址下载源码。
Docker: Jenkins 安装中默认安装 Docker 插件,所以不需要单独安装。利用 Docker 插件可以设置 Docker 环境,运行 Docker 命令,配置远程 Docker 仓库凭据等。
Kubernetes: Kubernetes 插件的目的是能够使用 Kubernetes 集群动态配置 Jenkins 代理(使用Kubernetes调度机制来优化负载),运行单个构建,等构建完成后删除该代理。这里我们需要用到这个插件来启动 Jenkins Slave 代理镜像,让代理执行 Jenkins 要执行的 Job。
Kubernetes Cli: Kubernetes Cli 插件作用是在执行 Jenkins Job 时候提供 kubectl 与 Kubernetes 集群交互环境。可以在 Pipeline 或自由式项目中允许执行 kubectl 相关命令。它的主要作用是提供 kubectl 运行环境,当然也可以提供 helm 运行环境。
Config File Provider: Config File Provider 插件作用就是提供在 Jenkins 中存储 properties、xml、json、settings.xml 等信息,可以在执行 Pipeline 过程中可以写入存储的配置。例如,存入一个 Maven 全局 Settings.xml 文件,在执行 Pipeline Job 时候引入该 Settings.xml ,这样 Maven 编译用的就是该全局的 Settings.xml。
Pipeline Utility Steps: 这是一个操作文件的插件,例如读写 json、yaml、pom.xml、Properties 等等。在这里主要用这个插件读取 pom.xml 文件的参数设置,获取变量,方便构建 Docker 镜像。
Git Parameter: 能够与 Git 插件结合使用,动态获取 Git 项目中分支信息,在 Jenkins Job 构建前提供分支选项,来让项目执行人员选择拉取对应分支的代码。
|
2 Jenkins进行配置
配置过程可以参考这个帖子进行配置
http://www.mydlq.club/article/47/#wow8
2.1 导入导出job配置
除了像上面一样进行手动配置外,还可以直接导入已经做好的配置。
Jenkins上,打开Manage Jenkins,打开Jenkins-CLI。
下载jenkins-cli.jar
,按照Jenkins-CLI页面的指引来操作(在jenkins中安全设置任何用户可以做任何事(没有任何限制)):
1 2 3 4
| # 导出一个job java -jar jenkins-cli.jar -s http://192.168.37.131:8080/ get-job myjob > myjob.xml # 导入一个jobs java -jar jenkins-cli.jar -s http://192.168.37.131:8080/ get-job myjob < myjob.xml
|
然后在目标Jenkins上,打开Manage Jenkins,选择Reload Configuration from Disk。
不需要重启目标Jenkins。
2.2 jenkins进行备份与恢复
使用thinbackup插件
https://www.cnblogs.com/paul8339/p/10559248.html
3 流水线脚本
pipeline.groovy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| def label = "jnlp-agent" timeout(time: 900, unit: 'SECONDS') { podTemplate(label: label,cloud: 'kubernetes' ){ node (label) { stage('Git阶段'){ git changelog: true, branch: "${params.GIT_BRANCH}", credentialsId: "${params.GIT_CREADENTIAL}", url: "${GIT_PROJECT_URL}" } stage('Maven阶段'){ container('maven') { configFileProvider([configFile(fileId: "global-maven-settings", targetLocation: "settings.xml")]){ sh "mvn -T 1C clean ${MAVEN_BUILD_OPTION} -Dmaven.test.skip=true --settings settings.xml" } } } stage('读取pom.xml参数阶段'){ pom = readMavenPom file: './pom.xml' appName = "${pom.artifactId}" appVersion = "${pom.version}" } stage('Docker阶段'){ container('docker') { configFileProvider([configFile(fileId: "${params.DOCKER_DOCKERFILE_ID}", targetLocation: "Dockerfile")]){ dockerImageName = "${params.DOCKER_HUB_URL}/${params.DOCKER_HUB_GROUP}/${appName}:${appVersion}" if ("${params.DOCKER_HUB_GROUP}" == '') { dockerImageName = "${params.DOCKER_HUB_URL}/${appName}:${appVersion}" } docker.withRegistry("https://${params.DOCKER_HUB_URL}", "${params.DOCKER_HUB_CREADENTIAL}") { def customImage = docker.build("${dockerImageName}") customImage.push() } } } } stage('Kubernetes 阶段'){ container('kubectl') { withKubeConfig([credentialsId: "${params.KUBERNETES_CREADENTIAL}",serverUrl: "https://kubernetes.default.svc.cluster.local"]) { configFileProvider([configFile(fileId: "${params.KUBERNETES_DEPLOYMENT_ID}", targetLocation: "deployment.yaml")]){ deploy = readFile encoding: "UTF-8", file: "deployment.yaml" deployfile = deploy.replaceAll("#APP_NAME","${appName}") .replaceAll("#APP_REPLICAS","${params.KUBERNETES_APP_REPLICAS}") .replaceAll("#APP_IMAGE_NAME","${dockerImageName}") .replaceAll("#APP_UUID",(new Random().nextInt(100000)).toString()) writeFile encoding: 'UTF-8', file: './deploy.yaml', text: "${deployfile}" sh "cat deploy.yaml" sh "kubectl apply -n ${params.KUBERNETES_NAMESPACE} -f deploy.yaml" } } } } stage('应用启动检查'){ sleep 10 httpRequestUrl = "http://${appName}.${params.KUBERNETES_NAMESPACE}:${params.HTTP_REQUEST_PORT}${params.HTTP_REQUEST_URL}" for(n = 1; n <= "${params.HTTP_REQUEST_NUMBER}".toInteger(); n++){ try{ print "访问服务:${appName} \n" + "访问地址:${httpRequestUrl} \n" + "访问次数:${n}" if(n > 1){ sleep "${params.HTTP_REQUEST_INTERVAL}".toInteger() } result = httpRequest "${httpRequestUrl}" if ("${result.status}" == "200") { print "Http 请求成功,流水线结束" break } }catch(Exception e){ print "监控检测失败,将在 ${params.HTTP_REQUEST_INTERVAL} 秒后将再次检测。" if (n == "${params.HTTP_REQUEST_NUMBER}".toInteger()) { currentBuild.result = "FAILURE" } } } } } } }
|
4 配置项目推送自动触发构建
https://www.cnblogs.com/chenchen-tester/p/10025420.html
5 参考
Jenkins 与 Kubernetes 的 CI 与 CD & Git + Maven + Docker+Kubectl
https://www.cnblogs.com/chenchen-tester/p/10025420.html
https://www.cnblogs.com/paul8339/p/10559248.html