Kubernetes: k8s DevOps篇-CICD
- TAGS: Kubernetes
DevOps篇-CICD
什么是 DevOps 和 CICD
DevOps 简介
Gdevops:http://dbaplus.cn/
DevOps 是 Development 和 Operations 的组合,也就是开发和运维的简写。是一种重视研发人员(Dev)、运维人员(Ops)之间沟通合作的文化、协作和整合。通过自动化"软件交付"和"架构变更"的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。
开发、技术运营、QA、OPS 部门之间沟通、协作与整合,通过一系列自动化工具来完成软件的生命周期管理。
DevOps 四大平台:代码托管(gitlab/svn)、项目管理(jira)、运维平台(腾讯蓝鲸/开源平台)、持续交付(Jenkins/gitlab)
什么是 DevOps
一体化:
- Dev
- DevOps
- 研发运维一体化,持续集成,持续交付
- 研发
- 开发流程高效、稳定、快速、交付结果可预期
- DevOps
自动化:
- Ops
- 运维
- 容器监控,自动化运维,降低维护成本
- 运维
敏捷化:
- QA
- 质量
- 持续进行自动化测试,提升交付质量
- 质量
为什么要推广 DevOps
DevOps 强调团队协作、相互协助、持续发展,然而传统的模式是开发人员只顾开发 程序,运维只负责基础环境管理和代码部署及监控等,其并不是为了一个共同的目标 而共同实现最终的目的,而 DevOps 则实现团队作战,即无论是开发、运维还是测试, 都为了最终的代码发布、持续部署和业务稳定而付出各自的努力,从而实现产品设计、 开发、测试和部署的良性循环,实现产品的最终持续交付。
CICD 概念
CI/CD 是一种通过在应用开发阶段引入自动化工具,来频繁向客户交付应用的方法,主要针对在集成新代码时所引发的问题,CICD 是 DevOps 中尤为重要的一个环节。
CI/CD 主要分为了三个方面:
- 持续集成(CI: Continuous Integration)
- 持续交付(CD: Continuous Delivery)
- 持续部署(CD: continuous deployment)
持续集成(CI:Continuous Integration)
CICD 中的 CI 指持续集成,它属于开发人员的自动化流程。
持续集成可以帮助开发人员更加频繁地将代码更改合并到共享分支和主干中。
持续交付(CD:Continuous Delivery)
在持续交付中,每个阶段都涉及测试自动化和代码发布自动化。在流程结束时,运维团队可以快速、轻松地将应用部署到生产环境中。
持续交付的目标是拥有一个可以随时部署到生产环境的代码库。
持续部署(CD-continuous deployment)
对于一个成熟的 CICD 管道来说,最后的阶段是持续部署。作为持续交付(自动将生产就绪型构建版本发布到代码存储库)的延伸,持续部署可以自动将应用发布到生产环境中。
持续部署意味着开发人员对应用的更改在最后几分钟内就能生效,这更加便于持续接收和整合用户反馈。
Jenkins
什么是流水线
把复杂的工作拆分为单独的任务。
提交、编译、部署是主要的流水线,其中还包括代码检查、安全。
pipline 语法概述
Pipeline支持两种语法:
- Declarative Pipeline(声明式pipeline,在pipeline2.5中引入,结构化方式)
- Scripted Pipeline(脚本式pipeline)
- 需要了解 groovy 语法
两者都支持建立连续输送的Pipeline。
声明式Pipeline是后续Open Blue Ocean所支持类型,建议使用声明式Pipeline的方式进行编写,从jenkins社区动向看,很明显这种语法结构会是未来的趋势。
Declarative Pipeline(声明式流水线)
声明式流水线-结构
pipeline { //在任何可用的代理上执行Pipeline [必须] agent any // 选项[可选] options {} // 工具[可选] tools{} // 触发器[可选] triggers{} //参数化变量[可选] parameters {} // 环境变量[可选] environment{} // 工作空间[必须] stages { // 工作空间的描述[必须] stage('Example') { // 工具[可选] tools{} // 交互式提示[可选] input{} // 条件判断[可选] when{} // 工作空间中执行步骤[必须] steps { // 执行命令 echo 'Hello World' sh """echo hello' // 脚本[可选] script{} } } // 执行结果状态 [可选] post{} } // 执行结果状态 [可选] post{} } // 函数定义 def myFunc(myArgs) { 脚本语法 }
在声明式流水线中有效的基本语句和表达式遵循与 Groovy的语法同样的规则, 有以下例外:
- 声明式pipeline可以内嵌脚本式pipeline
- 有效的声明式pipeline必须包含在一个 `pipeline{}`块中
- 块只能包含节段Sections,指令Directives,步骤Steps或者赋值语句组成
pipeline 块:
- 节段(Sections): 通常包括一个或多个指令或步骤
- agent: [必须] 代理执行环境,范围:pipeline block and each stage block.
- post: [可选] 执行结果状态,范围:pipeline block and each stage block.
- stages: [必须] 工作空间,包含多个stage指令,范围:pipeline block
- stage:执行过程(相当于一个阶段),比如 Build、Test、Deploy,但是这个名字根据实际情况进行定义
- steps: [必须] 工作空间中执行步骤,范围:stage block.
- 指令(Directives):
- environment: [可选] 环境变量 范围:pipeline block and stage directives
- options: [可选] 选项,也可以由插件提供,如options { timestamps() },范围:pipeline block and stage block
- parameters:[可选] 触发流水线时的参数列表,范围:pipeline block
- triggers:[可选] 触发器,流水线被重新触发的自动化方法,范围:pipeline block
- stage:[必须] 工作空间的描述,范围:stages section
- tools:[可选] 工具,范围:Inside the pipeline block or a stage block
- input: [可选] 交互式提示,范围:stage block
- when:[可选] 下一步执行判断,范围:stage block
- 步骤(steps): 在steps中可执行脚本式pipeline,如script{}
- 注释:
//
- 引号的运用
echo 中
'单行命令', '''多行命令''': 原样输出 "", """""": 有变量会转译
sh 中
'单行命令', '''多行命令''': 原样输出 "", """""": 有变量会转译
注意 : 变量的定义加引号,单双引号都可。
environment { var1='abc' var2='d \nf' }
变量引用
${var}
变量中存在空格或换行,在引用时加双引号转译
sh 'echo "${var1} ${var2}"' sh "echo \"${var1} \n${var2}\""
script {}中定义的变量,用双引号或者3双引号引用
stages { stage('Hello') { steps { script{ var3="hi" tag = sh(script: "date +'%Y%m%d_%H%M%S'", returnStdout: true).trim() } sh "echo ${var3}" } } }
语法参考-段Sections
- agent
Agent表示整个流水线或特定阶段中的步骤和命令执行的位置,该部分必须在pipeline块的顶层被定义,也可以在stage中再次定义,但是stage级别是可选的。
any
在任何可用的代理上执行流水线,配置语法:
pipeline { agent any }
none
表示该Pipeline脚本没有全局的agent配置。当顶层的agent配置为none时, 每个stage部分都需要包含它自己的agent。配置语法
pipeline { agent none stages { stage('Stage For Build'){ agent any } } }
label
以节点标签形式选择某个具体的节点执行Pipeline命令,例如:agent { label 'my-defined-label' }。节点需要提前配置标签。
pipeline { agent none stages { stage('Stage For Build'){ agent { label 'role-master' } steps { echo "role-master" } } } }
node
和label配置类似,只不过是可以添加一些额外的配置,比如customWorkspace(设置默认工作目录)
pipeline { agent none stages { stage('Stage For Build'){ agent { node { label 'role-master' customWorkspace "/data" } } steps { sh "echo role-master > 1.txt" } } } }
dockerfile
使用从源码中包含的Dockerfile所构建的容器执行流水线或stage。此时对应的agent写法如下
agent { dockerfile { filename 'Dockerfile.build' //dockerfile文件名称 dir 'build' //执行构建镜像的工作目录 label 'role-master' //执行的node节点,标签选择 additionalBuildArgs '--build-arg version=1.0.2' //构建参数 } }
agent 中选项 reuseNode 在 label 指定是docker 时有用,值为 true 多个 docker 可共享同工作目录
docker
相当于dockerfile,可以直接使用docker字段指定外部镜像即可,可以省去构建的时间。比如使用maven镜像进行打包,同时可以指定args
agent{ docker{ image '192.168.10.15/kubernetes/alpine:latest' //镜像地址 label 'role-master' //执行的节点,标签选择 args '-v /tmp:/tmp' //启动镜像的参数 } }
kubernetes
需要部署kubernetes相关的插件,官方文档:https://github.com/jenkinsci/kubernetes-plugin/
Jenkins 也支持使用 Kubernetes 创建 Slave,也就是常说的动态 Slave。配置示例如下
- cloud: Configure Clouds的名称,指定到其中一个k8s
- slaveConnectTimeout: 连接超时时间
- yaml: pod定义文件,jnlp容器的配置必须有配置无需改变,其余containerd根据自己情况指定
- workspaceVolume:持久化jenkins的工作目录。
- persistentVolumeClaimWorkspaceVolume:挂载已有pvc。
- 配置示例
docker 示例
pipeline { agent none stages { stage('Example Build') { agent { docker 'maven:3-alpine' } steps { echo 'Hello, Maven' sh 'mvn --version' } } stage('Example Test') { agent { docker 'openjdk:8-jre' } steps { echo 'Hello, JDK' sh 'java -version' } } } }
kubernetes 示例
比如定义三个容器的pod
- jnlp 负责与 jenkins master 通信
- build 负责执行构建命令
- kubectl 负责执行 kubernetes 命令
在 setps 中可能通过 containers 字段,选择在某个容器执行命令:
pipeline { agent { kubernetes { cloud 'kubernetes' slaveConnectTimeout 1200 workspaceVolume emptyDirWorkspaceVolume() yaml ''' kind: Pod metadata: name: jenkins-agent spec: containers: - args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\'] image: '192.168.10.15/kubernetes/jnlp:alpine' name: jnlp imagePullPolicy: IfNotPresent - command: - "cat" image: "192.168.10.15/kubernetes/maven:latest" imagePullPolicy: "IfNotPresent" name: "build" tty: true # 保持镜像不要退出,避免频繁删除创建 - command: - "cat" image: "192.168.10.15/kubernetes/kubectl:apline" imagePullPolicy: "IfNotPresent" name: "kubectl" tty: true restartPolicy: Never ''' } } environment { MY_KUBECONFIG = credentials('kubernetes-cluster') } stages { stage('Bulding') { steps { container(name: 'build') { sh """ mvn clean install """ } } } stage('Deploy') { steps { container(name: 'kubectl') { sh """ kubectl get pod -A --kubeconfig $MY_KUBECONFIG """ } } } } }
- post
post 一般用于流水线结束后的进一步处理,比如错误通知等。Post 可以针对流水线不同的结果做出不同的处理,就像开发程序的错误处理,比如 Python 语言的 try catch。
post 可以定义在 Pipeline 或 stage 中,目前支持以下条件:
- always:无论 Pipeline 或 stage 的完成状态如何,都允许运行该 post 中定义的指令;
- changed:只有当前 Pipeline 或 stage 的完成状态与它之前的运行不同时,才允许在该 post 部分运行该步骤;
- fixed:当本次 Pipeline 或 stage 成功,且上一次构建是失败或不稳定时,允许运行该 post 中定义的指令;
- regression:当本次 Pipeline 或 stage 的状态为失败、不稳定或终止,且上一次构建的 状态为成功时,允许运行该 post 中定义的指令;
- failure:只有当前 Pipeline 或 stage 的完成状态为失败(failure),才允许在 post 部分运行该步骤,通常这时在 Web 界面中显示为红色
- success:当前状态为成功(success),执行 post 步骤,通常在 Web 界面中显示为蓝色 或绿色
- unstable:当前状态为不稳定(unstable),执行 post 步骤,通常由于测试失败或代码 违规等造成,在 Web 界面中显示为黄色
- aborted:当前状态为终止(aborted),执行该 post 步骤,通常由于流水线被手动终止触发,这时在 Web 界面中显示为灰色;
- unsuccessful:当前状态不是 success 时,执行该 post 步骤;
- cleanup:无论 pipeline 或 stage 的完成状态如何,都允许运行该 post 中定义的指令。 和 always 的区别在于,cleanup 会在其它执行之后执行。
- stages
- steps
语法参考-指令Directives
- 指令(Directives):
- environment: [可选] 环境变量 范围:pipeline block and stage directives
- options: [可选] 选项,也可以由插件提供,如options { timestamps() },范围:pipeline block and stage block
- parameters:[可选] 触发流水线时的参数列表,范围:pipeline block
- triggers:[可选] 触发器,流水线被重新触发的自动化方法,范围:pipeline block
- stage:[必须] 工作空间的描述,范围:stages section
- tools:[可选] 工具,范围:Inside the pipeline block or a stage block
- input: [可选] 交互式提示,范围:stage block
- when:[可选] 下一步执行判断,范围:stage block
- environment
Environment 主要用于在流水线中配置的一些环境变量,根据配置的位置决定环境变量的作用域。可以定义在 pipeline 中作为全局变量,也可以配置在 stage 中作为该 stage 的环境变量。
该指令支持一个特殊的方法 credentials(),该方法可用于在 Jenkins 环境中通过标识符访问预定义的凭证。对于类型为 Secret Text 的凭证,credentials()可以将该 Secret 中的文本内容赋值给环境变量。对于类型为标准的账号密码型的凭证,指定的环境变量为 username 和 password,并且也会定义两个额外的环境变量,分别为MYVARNAME_USR和MYVARNAME_PSW。
- options
- buildDiscarder : 保留多少个流水线的构建记录
- disableConcurrentBuilds:禁止流水线并行执行,防止并行流水线同时访问共享资源导致流水线失败。
- disableResume :如果控制器重启,禁止流水线自动恢复。
- newContainerPerStage:agent 为 docker 或 dockerfile 时,每个阶段将在同一个节点的新容器中运行,而不是所有的阶段都在同一个容器中运行。
- quietPeriod:流水线静默期,也就是触发流水线后等待一会在执行。
- retry:流水线失败后重试次数。
- timeout:设置流水线的超时时间,超过流水线时间,job 会自动终止。如果不加unit参数默认为1分。
- timestamps:为控制台输出时间戳。
定义在pipeline中:
pipeline { agent any options { timeout(time: 1, unit: 'HOURS') //超时时间1小时,如果不加unit参数默认为1分 timestamps() //所有输出每行都会打印时间戳 buildDiscarder(logRotator(numToKeepStr: '3')) //保留三个历史构建版本 quietPeriod(10) //注意手动触发的构建不生效 retry(3) //流水线失败后重试次数 } stages { stage('env1') { steps { sh "env" sleep 2 } } stage('env2') { steps { sh "env" } } } }
定义在stage中: Option除了写在Pipeline顶层,还可以写在stage中,但是写在stage中的option仅支持retry、 timeout、timestamps,或者是和 stage 相关的声明式选项,比如 skipDefaultCheckout。处于stage级别的options写法如下
pipeline { agent any stages { stage('env1') { options { //定义在这里这对这个stage生效 timeout(time: 2, unit: 'SECONDS') //超时时间2秒 timestamps() //所有输出每行都会打印时间戳 retry(3) //流水线失败后重试次数 } steps { sh "env && sleep 2" } } stage('env2') { steps { sh "env" } } } }
- parameters
Parameters提供了一个用户在触发流水线时应该提供的参数列表,这些用户指定参数的值可以通过params对象提供给流水线的step(步骤)。只能定义在pipeline顶层。
目前支持的参数类型如下:
- string:字符串类型的参数。
- text:文本型参数,一般用于定义多行文本内容的变量。
- booleanParam:布尔型参数。
- choice:选择型参数,一般用于给定几个可选的值,然后选择其中一个进行赋值。
- password:密码型变量,一般用于定义敏感型变量,在 Jenkins 控制台会输出为*。
插件Parameters:
- imageTag:镜像 tag,需要安装 Image Tag Parameter 插件后使用
- gitParameter:获取 git 仓库分支,需要安装 Git Parameter 插件后使用
pipeline { agent any parameters { string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '1') //执行构建时需要手动配置字符串类型参数,之后赋值给变量 text(name: 'DEPLOY_TEXT', defaultValue: 'One\nTwo\nThree\n', description: '2') //执行构建时需要提供文本参数,之后赋值给变量 booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '3') //布尔型参数 choice(name: 'CHOICES', choices: ['one', 'two', 'three'], description: '4') //选择形式列表参数 password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'A secret password') //密码类型参数,会进行加密 imageTag(name: 'DOCKER_IMAGE', description: '', image: 'kubernetes/kubectl', filter: '.*', defaultTag: '', registry: 'https://192.168.10.15', credentialId: 'harbor-account', tagOrder: 'NATURAL') //获取镜像名称与tag gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH') } //获取git仓库分支列表,必须有git引用 stages { stage('env1') { steps { sh "env" } } stage('git') { steps { git branch: "$BRANCH", credentialsId: 'gitlab-key', url: '[email protected]:root/env.git' //使用gitParameter,必须有这个 } } } }
注意 不建议在 pipline 用,第一次构建后才能显示。修改后必须要构建一次才更新。
- triggers
在 Pipeline 中可以用 triggers 实现自动触发流水线执行任务,可以通过 Webhook、Cron、 pollSCM 和 upstream 等方式触发流水线。
cron
定时构建假如某个流水线构建的时间比较长,或者某个流水线需要定期在某个时间段执行构建,可以 使用 cron 配置触发器,比如周一到周五每隔四个小时执行一次
pipeline { agent any triggers { cron('H */4 * * 1-5') //周一到周五每隔四个小时执行一次 cron('H/12 * * * *') //每隔12分钟执行一次 cron('H * * * *') //每隔1小时执行一次 } stages { stage('Example') { steps { echo 'Hello World' } } } }
注意:
- H 的意思不是 HOURS 的意思,而是 Hash 的缩写。主要为了解决多个流水线在同一时间同时运行带来的系统负载压力。
使用 cron 字段可以定期执行流水线,如果代码更新想要重新触发流水线,可以使用 pollSCM 字段:
pipeline { agent any triggers { cronpollSCM('H */4 * * 1-5') //周一到周五每隔四个小时执行一次 } stages { stage('Example') { steps { echo 'Hello World' } } } }
upstream
Upstream可以根据上游 job 的执行结果决定是否触发该流水线。比如当 job1 或 job2 执行成功时触发该流水线
目前支持的状态有 SUCCESS、UNSTABLE、FAILURE、NOT_BUILT、ABORTED 等。
pipeline { agent any triggers { upstream(upstreamProjects: 'env', threshold: hudson.model.Result.SUCCESS) //当env构建成功时构建这个流水线,多个用 , 逗号分隔 } stages { stage('Example') { steps { echo 'Hello World' } } } }
- input
Input 字段可以实现在流水线中进行交互式操作,比如选择要部署的环境、是否继续执行某个阶段等。
配置Input支持以下选项:
- message:必选,需要用户进行 input 的提示信息,比如:“是否发布到生产环境?”;
- id:可选,input 的标识符,默认为 stage 的名称;
- ok:可选,确认按钮的显示信息,比如:“确定”、“允许”;
- submitter:可选,允许提交 input 操作的用户或组的名称,如果为空,任何登录用户均可提交input;
- parameters:提供一个参数列表供 input 使用。
假如需要配置一个提示消息为“还继续么”、确认按钮为“继续”、提供一个 PERSON 的变量的参数,并且只能由登录用户为 alice 和 bob 提交的 input 流水线:
pipeline { agent any stages { stage('Example') { input { message "还继续么?" ok "继续" submitter "alice,bob" parameters { string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?') } } steps { echo "Hello, ${PERSON}, nice to meet you." } } } }
- when
When 指令允许流水线根据给定的条件决定是否应该执行该 stage,when 指令必须包含至少 一个条件。如果 when 包含多个条件,所有的子条件必须都返回 True,stage 才能执行。
When 也可以结合 not、allOf、anyOf 语法达到更灵活的条件匹配。
目前比较常用的内置条件如下
- branch:当正在构建的分支与给定的分支匹配时,执行这个stage。注意,branch只适用于多分支流水线
- changelog:匹配提交的changeLog决定是否构建,例如:`when { changelog '.*^\\[DEPENDENCY\\] .+$' }`
- environment:当指定的环境变量和给定的变量匹配时,执行这个 stage,例如:`when { environment name: 'DEPLOY_TO', value: 'production' }`
- equals:当期望值和实际值相同时,执行这个 stage,例如:`when { equals expected: 2, actual: currentBuild.number };`
- expression:当指定的 Groovy 表达式评估为 True,执行这个 stage,例如:`when { expression { return params.DEBUG_BUILD } };`
- tag:如果 TAG_NAME 的值和给定的条件匹配,执行这个 stage,例如:`when { tag "release-" };`
- not:当嵌套条件出现错误时,执行这个 stage,必须包含一个条件,例如:`when { not { branch 'master' } };`
- allOf:当所有的嵌套条件都正确时,执行这个 stage,必须包含至少一个条件,例如: `when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } };`
- anyOf:当至少有一个嵌套条件为 True 时,执行这个 stage,例如:`when { anyOf { branch 'master'; branch 'staging' } }`。
示例:当分支为main且DEPLOY_TO变量的值为main时执行Example Deploy步骤
pipeline { agent any environment { DEPLOY_TO = "main" } stages { stage('Example Build') { steps { echo 'Hello World' } } stage('Example Deploy') { when { branch 'main' //多分支流水线,分支为 main 才会执行。 environment name: 'DEPLOY_TO', value: 'main' } steps { echo 'Deploying' } } } }
默认情况下,如果定义了某个 stage 的 agent,在进入该 stage 的 agent 后,该stage的when 条件才会被评估,但是可以通过一些选项更改此选项。比如在进入stage的agent前评估when, 可以使用beforeAgent,当when为true时才进行该stage。
目前支持的前置条件如下
- beforeAgent:如果 beforeAgent为true,则会先评估when条件。在when条件为true时,才会进入该stage
- beforeInput:如果beforeInput为true,则会先评估when条件。在when条件为true时,才会进入到input阶段;
- beforeOptions:如果beforeInput为true,则会先评估when条件。在when条件为true时,才会进入到options阶段;
- beforeOptions优先级大于beforeInput大于beforeAgent
pipeline { agent none stages { stage('Example Build') { steps { echo 'Hello World' } } stage('Example Deploy') { when { beforeAgent true branch 'main' } steps { echo 'Deploying' } } } }
Parallel 并行执行
场景:
- 代码检查
在声明式流水线中可以使用 Parallel 字段,即可很方便的实现并发构建,比如对分支 A、B、 C 进行并行处理
pipeline { agent any stages { stage('Non-Parallel Stage') { steps { echo 'This stage will be executed first.' } } stage('Parallel Stage') { failFast true //表示其中只要有一个分支构建执行失败,就直接推出不等待其他分支构建 parallel { stage('Branch A') { steps { echo "On Branch A" } } stage('Branch B') { steps { echo "On Branch B" } } stage('Branch C') { stages { stage('Nested 1') { steps { echo "In stage Nested 1 within Branch C" } } stage('Nested 2') { steps { echo "In stage Nested 2 within Branch C" } } } } } } } }
常用语法
可以给node打上标签,在jenkins机器管理中找到。
实际上`agent { label 'jdk8' }`是 `agent { node { label 'jdk8' } }` 的简写
- 构建历史
options { buildDiscarder(logRotator(daysToKeepStr: '7')) }
daysToKeep: 构建记录将保存的天数 numToKeep: 最多此数目的构建记录将被保存 artifactDaysToKeep: 比此早的发布包将被删除,但构建的日志、操作历史、报告等将被保留 artifactNumToKeep: 最多此数目的构建将保留他们的发布包
- 构建前清理工作空间
https://plugins.jenkins.io/ws-cleanup/
cleanWs()
pipeline { agent any stages { stage('Hello') { steps { cleanWs() sh "echo hello >>a.log" } } } }
- parameters参数化构建的参数
parameters指令提供用户在触发Pipeline时的参数列表。这些参数值通过该params对象可用于Pipeline步骤
目前只支持[booleanParam, choice, credentials, file, text, password, run, string]这几种参数类型
parameters { choice(name:'GIT_ADDR',choices:'[email protected]:chinese/chinese-parent-server.git',description:'') string(name:'GIT_BRANCH',defaultValue:'',description:'') } stages { stage('Build') { steps { timestamps { // pull git code git branch:"${GIT_BRANCH}",url:"${GIT_ADDR}" } } } }
- 归档文件
https://www.jenkins.io/doc/pipeline/steps/core/
// 归档 archiveArtifacts 'target/*.jar'
- 构建名
buildName "${ENV}--${PROJECT}--${BUILD_NUMBER}" 或者 currentBuild.displayName = "${namespace} ${service_name} ${tags}"
staging-wallet-2 pfgw-service-admin 20220126_164557
可用插件语法
- User Build Vars获取构建用户
https://plugins.jenkins.io/build-user-vars-plugin/
pipeline { agent any stages { stage('test') { steps { wrap([$class: 'BuildUser']) { BUILD_USER = "${env.BUILD_USER}" } } } } }
- Git Parameter选择git分支
选择git分支 https://plugins.jenkins.io/git-parameter/
pipeline { agent any parameters { choice(name:'GIT_ADDR',choices:'[email protected]:chinese/chinese-parent-server.git',description:'') gitParameter branchFilter: 'origin/(.*)', defaultValue: 'master', name: 'GIT_BRANCH', type: 'PT_BRANCH', listSize: '3', selectedValue: 'DEFAULT', sortMode: 'ASCENDING_SMART' } stages { stage('Build') { steps { timestamps { // pull git code git branch:"${GIT_BRANCH}",url:"${GIT_ADDR}" } } } } }
重要的!如果您需要使用其他类型(除分支之外)参数,您必须在结帐时使用 git
- Timestamper构建任务执行时间
https://plugins.jenkins.io/timestamper/
stage('Build') { steps { timestamps { git branch:"${GIT_BRANCH}",url:"${GIT_ADDR}" } } }
- AnsiColor输出颜色
https://plugins.jenkins.io/timestamper/
stage('Deploy') { steps { timestamps { // ansiColor 输出颜色 ansiColor('xterm') { sh """ export ANSIBLE_FORCE_COLOR=true ansible-playbook /etc/ansible/01deploy.yml -e "WORKSPACE=${WORKSPACE} \ SERVICE_HOSTS=${ServiceName}-${ENV} \ SERVICE_DIR=${SERVICE_DIR} \ SERVICE_NAME=${ServiceName} \ PORT=${PORT} \ TYPE=${TYPE} \ COMMAND='${COMMAND}'" """ } } } }
- DingTalk钉钉通知
https://plugins.jenkins.io/dingding-notifications
(新版本需要在"系统设置中配置钉钉robotid")
pipeline { agent any parameters { booleanParam(name:'dingding',defaultValue:false,description:'') // dingTalk 1.9版本 // string(name:'dingToken',defaultValue:'',description:'') } environment { // 钉钉robotId robotId = "5e808e23-40ed-4828-8ec6-111864d6cc41" } stages { stage('开始构建通知'){ dingSend("开始构建") // 清理工作空间 cleanWs() } } post { // 成功通知 success {dingSend("构建成功")} // 失败通知 failure {dingSend("构建失败")} } } def dingSend(dMsg) { // sh 'echo start'; timestamps { if (params.dingding) { // dingTalk 1.9版本 // dingTalk accessToken: "https://oapi.dingtalk.com/robot/send?access_token=${dingToken}", jenkinsUrl: "${env.BUILD_URL}", message: "${dMsg} branch: ${GIT_BRANCH} by ${BUILD_USER} email: ${BUILD_USER_EMAIL}", notifyPeople: "${notifyPeople}" // dingTalk 2.3版本 dingtalk ( robot: "${robotId}", type: 'ACTION_CARD', at:[], atAll: true, title: "${env.JOB_BASE_NAME} ${dMsg}", text:[ "**** [${env.JOB_BASE_NAME} ${dMsg}](${env.JOB_URL})", " 1. 任务: [#${env.BUILD_NUMBER}](${env.BUILD_URL})", " 2. 状态:${dMsg}", " 3. 持续时间: ${currentBuild.durationString}", " 4. ${dMsg} branch: ${GIT_BRANCH} by ${BUILD_USER} email: ${BUILD_USER_EMAIL}" ], messageUrl: "${env.BUILD_URL}", picUrl:"", singleTitle:'', btns: [ [ title: '构建记录', actionUrl: "${env.BUILD_URL}" ], [ title: '控制台', actionUrl: "${env.JENKINS_URL}" ] ], btnLayout: 'H', hideAvatar: true ) } } }
- 构建描述
Build Name and Description Setter构建描述
buildDescription("Branch: ${GIT_BRANCH} Committer: ${BUILD_USER}")
Scripted Pipeline(脚本式流水线)
//Jenkinsfile (Scripted Pipeline) node { stage('Build') { echo 'Build' } stage('Test') { echo 'Test' } stage('Deploy') { echo 'Deploy' } }
- node:在任何可用的代理上执行流水线或它的任何阶段,也可以指定到具体的节点
- stage:和声明式的含义一致,定义流水线的阶段。Stage 块在脚本化流水线语法中是可选的,然而在脚本化流水线中实现 stage 块,可以清楚地在Jenkins UI界面中显示每个stage的任务子集。
group/french/french-node.groovy
node('tpln-jnlp') { // 定义全局变量 if (ENV == 'qa') { K8S_NAMESPACES='french-qa' DOCKER_IMAGE_NAMESPACES='french-qa' KUBECONFIG='/root/.kube/config-qa-bj' } if (ENV == 'prod') { timeout(time: 5, unit: 'MINUTES'){ input message: '是否发布或回滚prod', ok: '确认', submitter: 'guoyangchan,zhengxin' } K8S_NAMESPACES='french-prod' DOCKER_IMAGE_NAMESPACES='french-prod' KUBECONFIG='/root/.kube/config' } // 发布代码 if (PROJECT == 'release') { withCredentials([usernamePassword(credentialsId: 'docker_image', passwordVariable: 'password', usernameVariable: 'username')]) { sh "docker login -u ${username} -p ${password} tope365-registry-vpc.cn-beijing.cr.aliyuncs.com" } // 定义Build name stage('Initialization') { buildName "${ENV}--${PROJECT}--${BUILD_NUMBER}" } // Pull仓库代码 stage('git-clone') { echo "发布分支:${GIT_BRANCH}" git branch: "${GIT_BRANCH}", credentialsId: 'devops_git', url: '[email protected]:${GIT_GROUP}/${GIT_NAME}.git' } // 编译代码 stage('node-build') { sh "npm install && npm run ${ENV}" } // 编译Docker image stage('docker-build') { // 判断是否存在start目录并编译Docker sh "docker build -t tope365-registry-vpc.cn-beijing.cr.aliyuncs.com/${DOCKER_IMAGE_NAMESPACES}/${JOB_NAME}:${BUILD_NUMBER} ." } // 上传Docker image stage('docker-push') { sh "docker push tope365-registry-vpc.cn-beijing.cr.aliyuncs.com/${DOCKER_IMAGE_NAMESPACES}/${JOB_NAME}:${BUILD_NUMBER}" } // 更新K8S stage('k8s-update') { sh "kubectl --kubeconfig ${KUBECONFIG} set image deployment/${JOB_NAME} -n ${K8S_NAMESPACES} ${JOB_NAME}=tope365-registry-vpc.cn-beijing.cr.aliyuncs.com/${DOCKER_IMAGE_NAMESPACES}/${JOB_NAME}:${BUILD_NUMBER}" } } // 回滚代码 if (PROJECT == 'back') { stage('Initialization') { buildName "${ENV}--${PROJECT}--${BACK_TAG}" } if (ENV == 'prod') { stage('prod-back') { sh "kubectl --kubeconfig ${KUBECONFIG} set image deployment/${JOB_NAME} -n ${K8S_NAMESPACES} ${JOB_NAME}=tope365-registry-vpc.cn-beijing.cr.aliyuncs.com/${DOCKER_IMAGE_NAMESPACES}/${JOB_NAME}:${BACK_TAG}" } } if (ENV == 'qa') { stage('qa-back') { sh "kubectl --kubeconfig ${KUBECONFIG} set image deployment/${JOB_NAME} -n ${K8S_NAMESPACES} ${JOB_NAME}=tope365-registry-vpc.cn-beijing.cr.aliyuncs.com/${DOCKER_IMAGE_NAMESPACES}/${JOB_NAME}:${BACK_TAG}" } } } }
groovy
k8s 发布
- 从 prometheus 获取 k8s 信息
用于 jenkins 参数构建
获取 deployment 信息,即service_name
/* service_name = [] def json = '{"service_namespace":"taskcenter"}' text= ['bash', '-c',"curl -X POST -H \"Content-Type:application/json\" -d '${json}' http://47.74.244.216:9091/api/service_info/service/query"].execute().text text.eachLine { line, count ->if (count == 0) { service_name.push(line+':selected') }else{ service_name.push(line) }} return service_name */ import groovy.json.JsonSlurperClassic import jenkins.model.Jenkins def parseJSON(json) { return new groovy.json.JsonSlurperClassic().parseText(json) } response = ['bash', '-c', "curl -sS -G --data-urlencode 'query=kube_deployment_created{namespace=\"taskcenter\"}' 'http://pfgc-prometheus.gamepind.com/eks/api/v1/query'"].execute().text response = parseJSON(response) result = [] for (data in response.data.result.metric){ result += data.deployment } return result
从 harbor 获取镜像 tag信息
传 deploy(service_name) 信息获取镜像 tag
api 接口:harbor登录进页面可找到https://harbor.xxxx.com/#/artifact/listArtifacts
/* tag = [] def json = '{"service_namespace":"taskcenter","service_name":"'+service_name+'"}' println json text= ['bash', '-c',"curl -X POST -H \"Content-Type:application/json\" -d '$json' http://47.74.244.216:9091/api/service_info/tag/query"].execute().text text.eachLine { line, count ->if (count == 0) { tag .push(line+':selected') }else{ tag.push(line) }} return tag */ import groovy.json.JsonSlurperClassic import jenkins.* import jenkins.model.* import hudson.* import hudson.model.* // JSON实例化 def parseJSON(json) { return new groovy.json.JsonSlurperClassic().parseText(json) } // 获取Jenkins凭证 def Credentials(credentials) { jenkinsCredentials = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials( com.cloudbees.plugins.credentials.Credentials.class, Jenkins.instance, null, null ); for (creds in jenkinsCredentials) { if(creds.id == credentials) { result = ("${creds.username}:${creds.password}") print(result) return result } } } project = 'taskcenter' // 获取Harbor镜像 def Artifacts(projects, repos) { urls = "https://harbor.xxx.com/api/v2.0/projects" conn = "${urls}/${project}/repositories/${service_name}/artifacts?project_name=${project}&repository_name=${project}?&page_size=20&page=1" print(conn) http = new URL(conn).openConnection() users = Credentials("harbor.xxx.com") token = "Basic ${users.bytes.encodeBase64().toString()}"; http.setRequestProperty("Authorization", token) http.setRequestMethod('GET') result = [] code = http.getResponseCode() if(code.equals(200)) { // 格式化json response = parseJSON(http.getInputStream().getText()) // 镜像TAG列表 for (project in response){ for (image in project.tags){ result += image.name } } return result } } service_name = service_name.replace("-2","") service_name = service_name.replace("-1","") if (service_name =~ '^taskcenter-console-website') { service_name = 'taskcenter-website' } else if (service_name =~ '^taskcenter-console-server') { service_name = 'taskcenter-server' } else if (service_name =~ '^canal-*') { project = 'clevertap' } Artifacts(project, service_name)
获取 gitlab 分支信息
构建参数 git_branch
if(namespace == 'clevertap'){ if (service_name =~ 'canal*'){ def gettags = ("git ls-remote -h [email protected]:gpchina/bj_dev/canal.git").execute() return gettags.text.readLines().collect { it.split()[1].replaceAll('refs/heads/', '') }.unique() } if (service_name =~ 'clevertap-*'){ def gettags = ("git ls-remote -h [email protected]:gpchina/bj_dev/xxx-fence.git").execute() return gettags.text.readLines().collect { it.split()[1].replaceAll('refs/heads/', '') }.unique() } }
pipeline cd
pipeline { agent { node { label 'master' } } environment { project = "taskcenter" code_repo = "/data/jenkins/building/pfg-prod-k8s" // kubernetes 配置仓库 code_path = "${code_repo}/${project}/${service_name}" eks_deploy_file = "${service_name}-deployment.yaml" // 镜像仓库 harbor_url = "harbor.xxx.com/${project}" } stages { stage('clone code') { steps { script { currentBuild.displayName = "${project} ${service_name} ${tags}" } dir("${code_repo}") { deleteDir() checkout([ $class: "GitSCM", branches: [[name: "refs/heads/master"]], userRemoteConfigs: [[url: "[email protected]:gpchina/bj_dev/pfg-prod-k8s.git", credentialsId: "988f4dd4-c04e-4dd0-8222-a9ddfc8522a8"]] ]) } } } stage('docker image version') { steps { script { env.name = sh(script: "echo ${service_name} | sed -r 's/(-1|-2)//'", returnStdout:true).trim() // 镜像名称 env.image_name = sh( script: """ #! /bin/sh echo "${harbor_url}/${name}" """, returnStdout:true ).trim() // 镜像TAG env.image_tags = sh( script: """ #! /bin/sh grep '${image_name}' ${code_path}/${eks_deploy_file} | grep -vE '[ ]+#' |awk '{print \$NF}' """, returnStdout:true ).trim() // 差异服务 if (service_name == 'taskcenter-console-website'){ code_path = "${code_repo}/${project}/taskcenter-website" eks_deploy_file = "taskcenter-website-deployment.yaml" env.image_name = "${harbor_url}/taskcenter-website" env.image_tags = sh(script: "grep '${image_name}' ${code_path}/${eks_deploy_file} | grep -vE '[ ]+#' |awk '{print \$NF}'", returnStdout:true).trim() } else if (service_name == 'taskcenter-console-server'){ code_path = "${code_repo}/${project}/taskcenter-server" eks_deploy_file = "taskcenter-server-deployment.yaml" env.image_name = "${harbor_url}/taskcenter-server" env.image_tags = sh(script: "grep '${image_name}' ${code_path}/${eks_deploy_file} | grep -vE '[ ]+#' |awk '{print \$NF}'", returnStdout:true).trim() } else if (service_name =~ 'canal-*'){ env.image_name = "harbor.gamepind.com/clevertap/${service_name}" env.image_tags = sh(script: "grep '${image_name}' ${code_path}/${eks_deploy_file} | grep -vE '[ ]+#' |awk '{print \$NF}'", returnStdout:true).trim() } } } } stage('Deployment Argo') { steps { withCredentials([sshUserPrivateKey(credentialsId: '9ded0071-867a-4ba3-bb29-13d48cd2ba9a', keyFileVariable: 'SSH_KEY')]) { sh """#! /bin/sh ** set replicas echo "replicas: ${replicas}" if [ "${replicas}" != "" ]; then # sed -i "s#`grep 'replicas:' ${code_path}/${service_name}-deployment.yaml`#replicas: ${replicas}#" ${code_path}/${eks_deploy_file} sed -n "s#`grep 'replicas:' ${code_path}/${service_name}-deployment.yaml`#replicas: ${replicas}#p" ${code_path}/${eks_deploy_file} fi ** sed image echo "sed -i "s#${image_tags}#${image_name}:${tags}#" ${code_path}/${eks_deploy_file}" sed -i "s#${image_tags}#${image_name}:${tags}#" ${code_path}/${eks_deploy_file} ** git commit GIT_SSH_COMMAND="ssh -i ${SSH_KEY}" cd ${code_path} git add ${eks_deploy_file} git commit -m "update ${eks_deploy_file}" git push origin HEAD:master """ } } } } post { success { echo "执行成功" // dingtalk ( // robot: "755c351b-0330-4301-bcea-baabc6b309e6", // type: "MARKDOWN", // text: [ // "- 构建项目: ${service_name}", // "- 镜像版本: ${image_name}:${tags}", // "- 构建状态: <font color=green>成功</font>", // "- 持续时间: ${currentBuild.durationString}", // "- 操作人员: ${currentBuild.buildCauses.shortDescription}", // ], // atAll: false // ) } failure { echo "执行失败" // dingtalk ( // robot: "755c351b-0330-4301-bcea-baabc6b309e6", // type: "MARKDOWN", // text: [ // "- 构建项目: ${service_name}", // "- 镜像版本: ${image_name}:${tags}", // "- 构建状态: <font color=red>失败</font>", // "- 持续时间: ${currentBuild.durationString}", // "- 操作人员: ${currentBuild.buildCauses.shortDescription}", // ], // atAll: false // ) } } }
pipline ci
pipeline{ agent { node { label 'production' } } environment { GIT_URL_DIR = "" // namespace, service_name, git_address, git_branch 由构建参数提供 // 镜像地址 harbor_url = "harbor.xxx.com" // 代码位置 code_path="/gamepind/devops/k8s-ci-build/staging/${namespace}/${service_name}" // 制作镜像位置 dockerfile_document="/gamepind/devops/k8s-stg/${namespace}/${service_name}" // k8s 配置 build_path = "/gamepind/devops/pfg-dev-k8s" kubernetes = "${build_path}/${namespace}/${service_name}/kubernetes" } stages { stage('git-clone') { // 定义Build name && Pull仓库代码 steps { script { // 镜像标签 tags = sh(script: "date '+%Y%m%d_%H%M%S'", returnStdout: true).trim() // 镜像名称 image_name = "${harbor_url}/${namespace}/${service_name}:${tags}" // 构建名称 currentBuild.displayName = "${namespace} ${service_name} ${tags}" echo "名称空间:${namespace} 服务名:${service_name} 镜像 tag:${tags} 发布分支:${git_branch}" // code dir("${code_path}") { deleteDir() checkout([ $class: "GitSCM", branches: [[name: "refs/heads/${git_branch}"]], userRemoteConfigs: [[url: "${git_address}", credentialsId: "a7adfe2c-5e46-4162-818a-eb2133dedf2d"]] ]) } // k8s yaml dir("${build_path}") { //deleteDir() checkout([ $class: "GitSCM", branches: [[name: "refs/heads/jizheng"]], userRemoteConfigs: [[url: "[email protected]:gpchina/bj_dev/pfg-dev-k8s.git", credentialsId: "a7adfe2c-5e46-4162-818a-eb2133dedf2d"]] ]) } } } } // 编译代码 stage('node-build') { steps{ script{ echo "${code_path}" switch("$namespace") { case "poker": if ( service_name == "poker-cms-site" ) { sh """ rm -rf ${dockerfile_document}/${service_name} #sudo cp -a /gamepind/devops/k8s-ci-build/poker-cms-site /gamepind/devops/k8s-stg/poker/poker-cms-site/poker-cms-site cd /gamepind/devops/k8s-ci-build/poker-cms-site npm install --registry=https://registry.npm.taobao.org npm run build sudo cp -a dist ${dockerfile_document}/ """ } else { sh """ cd ${code_path} /usr/maven/bin/mvn clean package -P stg -Dmaven.test.skip=true sudo rm -rf /gamepind/devops/k8s-stg/poker/${service_name}/${service_name} sudo mv build/${service_name} /gamepind/devops/k8s-stg/poker/${service_name}/ """ } default: echo "************ wrong Job name ************" sh "exit 127" break } } } } // 编译 Docker image stage('docker-build') { steps { //withCredentials([usernamePassword(credentialsId: 'docker_image', passwordVariable: 'password', usernameVariable: 'username')]) { // sh "docker login -u ${username} -p ${password} ${harbor_url}" //} dir("${dockerfile_document}") { sh """ echo "编译 Docker image" docker build -t ${image_name} . """ } } } // 上传 Docker image stage('docker-push') { steps { sh """ echo "上传 Docker image" docker push ${image_name} """ } } // 删除本地镜像 stage('docker-image-del-local') { steps { sh """ echo "删除本地镜像" docker rmi ${image_name} """ } } // 更新 K8S stage('k8s-update') { steps { script { echo "更新 K8S" if (namespace == "staging-poker" || namespace == "staging-callbreak") { sh """ ssh 172.21.39.68 "python3 /home/pfg/scripts/updatp_deployment_image.py ${namespace} ${service_name} ${image_name}" """ } else { sh """ ssh 172.21.39.68 "kubectl -n ${namespace} set image deployment/${service_name} ${service_name}=${image_name}" """ } } } } // old flask // stage('k8s update deployment image') { // steps { // sh """curl -X POST -H "Content-Type:application/json" -d '{"namespace":"${namespace}","deployment":"${service_name}","new_tag":"${tag}"}' 'http://127.0.0.1:9092/api/update_deployment'""" // } } post { success { echo '构建成功' } failure { echo '构建失败' } } }
jenkins 安装
- 下载地址
www.jenkins.io/download
mirrors.jenkins.io/war-stable/ 2.222.4
- 安装java 1.8
- install
java -jar jenkins.war --httpPort=28080 &
浏览器访问:http://ip:28080
安装插件:
> Active Choices Plug-in > > Blue Ocean > > Blue Ocean Core JS > > Blue Ocean Executor Info > > Blue Ocean Pipeline Editor > > Build Pipline Plugin > > Credentials Binding Plugin > > Credentials Plugin > > Dashboard for Blue Ocean > > Declarative Pipline Migration Assistant > > Declarative Pipline Migration Assistant API > > Display URL API > > Display URL for Blue Ocean > > Git Parameter Plug-In > > Hidden Parameter plugin > > Kubernetes CLI Plugin > > Kubernetes Plugin > > List Git Branches Parameter PlugIn > > Parameterized Remote Trigger Plugin > > Parameterized Trigger Plugin > > Pipeline
Jenkinsfile 的使用
上面讲过流水线支持两种语法,即声明式和脚本式,这两种语法都支持构建持续交付流水线。并且都可以用来在 Web UI 或 Jenkinsfile 中定义流水线,不过通常将 Jenkinsfile 放置于代码仓库中(当然也可以放在单独的代码仓库中进行管理)。
创建一个 Jenkinsfile 并将其放置于代码仓库中,有以下好处:
- 方便对流水线上的代码进行复查/迭代
- 对管道进行审计跟踪
- 流水线真正的源代码能够被项目的多个成员查看和编辑
环境变量
1.静态变量
Jenkins有许多内置变量可以直接在Jenkinsfile中使用,可以通过 `sh "env"` 获取完整列表。目前比较常用的环境变量如下
- BUILD_ID:当前构建的ID,与Jenkins版本 1.597+中的BUILD_NUMBER完全相同
- BUILD_NUMBER:当前构建的 ID,和BUILD_ID一致
- BUILD_TAG:用来标识构建的版本号,格式为:jenkins-\({JOB_NAME}-\){BUILD_NUMBER}, 可以对产物进行命名,比如生产的jar包名字、镜像的TAG等;
- BUILD_URL:本次构建的完整 URL,比如:http://buildserver/jenkins/job/MyJobName/17/;
- JOB_NAME:本次构建的项目名称
- NODE_NAME:当前构建节点的名称;
- JENKINS_URL:Jenkins 完整的 URL,需要在SystemConfiguration设置;
- WORKSPACE:执行构建的工作目录。
示例如果一个流水线名称为print_env,第2次构建,各个变量的值。
BUILD_ID: 2 BUILD_NUMBER: 2 BUILD_TAG:jenkins-print_env-2 BUILD_URL:http://192.168.10.16:8080/job/print_env/2/ JOB_NAME:print_env NODE_NAME:built-in JENKINS_URL:http://192.168.10.16:8080/ WORKSPACE:/bitnami/jenkins/home/workspace/print_env
上述变量会保存在一个Map中,可以使用env.BUILD_ID或env.JENKINS_URL引用某个内置变量
pipeline { agent any stages { stage('print env') { parallel { stage('BUILD_ID') { steps { echo "$env.BUILD_ID" } } stage('BUILD_NUMBER') { steps { echo "$env.BUILD_NUMBER" } } stage('BUILD_TAG') { steps { echo "$env.BUILD_TAG" } } } } } }
2.动态变量
动态变量是根据某个指令的结果进行动态赋值,变量的值根据指令的执行结果而不同。如下所示
- returnStdout:将命令的执行结果赋值给变量,比如下述的命令返回的是clang,此时 CC 的值为“clang”。
- returnStatus:将命令的执行状态赋值给变量,比如下述命令的执行状态为 1,此时 EXIT_STATUS 的值为 1。
//Jenkinsfile (Declarative Pipeline) pipeline { agent any environment { // 使用 returnStdout CC = """${sh( returnStdout: true, script: 'echo -n "clang"' //如果使用shell命令的echo赋值变量最好加-n取消换行 )}""" // 使用 returnStatus EXIT_STATUS = """${sh( returnStatus: true, script: 'exit 1' )}""" } stages { stage('Example') { environment { DEBUG_FLAGS = '-g' } steps { sh 'printenv' } } } }
获取git commit短ID和时间戳
pipeline { agent any stages { stage('docker build & push') { steps { script { env.COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim() env.TIMESTRAP = sh(returnStdout: true, script: 'date +%Y%m%d%H%M%S').trim() env.DOCKER_TAG = "dev_${TIMESTRAP}_${COMMIT_ID}_${BUILD_NUMBER}" } } } } }
commit短ID变量
#+beigin_src shell stage('get_commit_msg') { steps { script { env.imageTag = sh (script: 'git rev-parse –short HEAD ${GIT_COMMIT}', returnStdout: true).trim() } } } #+end_src
凭证管理
Jenkins 的声明式流水线语法有一个 credentials()函数,它支持 secret text(加密文本)、username 和 password(用户名和密码)以及 secret file(加密文件)等。接下来看一下一些常用的凭证处理方法。
1.加密文本
本实例演示将两个 Secret 文本凭证分配给单独的环境变量来访问 Amazon Web 服务,需要 提前创建这两个文件的 credentials(实践的章节会有演示),Jenkinsfile 文件的内容如下
//Jenkinsfile (Declarative Pipeline) pipeline { agent any environment { AWS_ACCESS_KEY_ID = credentials('txt1') AWS_SECRET_ACCESS_KEY = credentials('txt2') } stages { stage('Example stage 1') { steps { echo "$AWS_ACCESS_KEY_ID" } } stage('Example stage 2') { steps { echo "$AWS_SECRET_ACCESS_KEY" } } } }
2.用户名密码
本示例用来演示 credentials 账号密码的使用,比如使用一个公用账户访问Bitbucket、GitLab、 Harbor 等。假设已经配置完成了用户名密码形式的 credentials,凭证ID为harbor-account
//Jenkinsfile (Declarative Pipeline) pipeline { agent any environment { BITBUCKET_COMMON_CREDS = credentials('harbor-account') } stages { stage('printenv') { steps { sh "env" } } }
上述的配置会自动生成3个环境变量
- BITBUCKET_COMMON_CREDS:包含一个以冒号分隔的用户名和密码,格式为 username:password
- BITBUCKET_COMMON_CREDS_USR:仅包含用户名的附加变量
- BITBUCKET_COMMON_CREDS_PSW:仅包含密码的附加变量。
3.加密文件
需要加密保存的文件,也可以使用 credential,比如链接到 Kubernetes 集群的 kubeconfig 文件等。
假如已经配置好了一个kubeconfig文件,此时可以在Pipeline中引用该文件
//Jenkinsfile (Declarative Pipeline) pipeline { agent { kubernetes { cloud 'kubernetes' slaveConnectTimeout 1200 workspaceVolume emptyDirWorkspaceVolume() yaml ''' kind: Pod metadata: name: jenkins-agent spec: containers: - args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\'] image: '192.168.10.15/kubernetes/jnlp:alpine' name: jnlp imagePullPolicy: IfNotPresent - command: - "cat" image: "192.168.10.15/kubernetes/kubectl:apline" imagePullPolicy: "IfNotPresent" name: "kubectl" tty: true restartPolicy: Never ''' } } environment { MY_KUBECONFIG = credentials('kubernetes-cluster') } stages { stage('kubectl') { steps { container(name: 'kubectl') { sh """ kubectl get pod -A --kubeconfig $MY_KUBECONFIG """ } } } } }
参数处理
使用多个代理
DevOps 平台建设
自动化构建流水线设计
- gitlab 代码仓库创建项目
- 配置 jenkins 集成 kubernetes 集群,后期 Jenkins 的 salve 将 kubernetes 中动态创建 slave
- jenkins 创建对应任务(job),集成该项目的 git 地址和 kubernetes 集群。
- 开发者将代码提交到 gitlab
- 如果配置了钩子,推送(push) 代码会自动触发 jenkins 构建,如果没有钩子,需要手动构建。
- jenkins 控制 Kubernetes 集群(使用的是 kubernetes 插件)创建 jenkins slave(pod 形式)
- jenkins slave 根据流水线(pipline) 定义步骤执行构建
- 通过 Dockerfile 生成镜像
- 将镜像推送(push) 到私有 harbor (或者其它镜像仓库)
- jenkins 再次控制 kubernetes 进行最新的镜像部署
- 流水线结束删除 jenkins slave
jenkins 安装
- 通过容器部署jenkins
需事先部署docker服务。
jenkins的镜像仓库:https://hub.docker.com/r/bitnami/jenkins
官方的镜像仓库以及停止更新了。
下载jenkins镜像
#注意选择debian的镜像,否则没有ssh命令 docker pull bitnami/jenkins:2.332.2-debian-10-r29
创建数据目录
mkdir /data/jenkins_data -p chmod -R 777 /data/jenkins_data docker run -d --name=jenkins --restart=always -e \ JENKINS_PASSWORD=admin123 -e JENKINS_USERNAME=admin -e \ JENKINS_HTTP_PORT_NUMBER=8080 -p 8080:8080 -p 50000:50000 -v \ /data/jenkins_data:/bitnami/jenkins bitnami/jenkins:2.303.1-debian-10-r29
说明:
- 8080 端口为web ui
- 50000 端口为与 master 节点通信端口
- 指定管理员账号密码 JENKINS_USERNAM、JENKINS_PASSWORD
启动jenkins
docker run -d --name=jenkins --restart=always --net host \ -e JENKINS_PASSWORD=admin123 \ -e JENKINS_USERNAME=admin \ -e JENKINS_HTTP_PORT_NUMBER=8080 \ -v /data/jenkins_data:/bitnami/jenkins \ 192.168.10.254:5000/bitnami/jenkins:2.332.2-debian-10-r29
验证启动
$ docker logs -f jenkins #日志看到Jenkins is fully up and running表示启动成功
之后通过 Jenkins 宿主机的 IP+8080 即可访问 Jenkins
插件安装
登录后点击 Manage Jenkins → Manage Plugins 安装需要使用的插件
在安装之前首先配置国内的插件源,点击 Advanced,将插件源更改为国内插件源 (https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json)
注意:如果已有 jenkins,在更新插件前一定要先备份插件,有可能会造成 jenkins 启不来。
需要安装的插件有:
Git Git Parameter Git Pipeline for Blue Ocean GitLab Credentials Credentials Binding Blue Ocean Blue Ocean Pipeline Editor Blue Ocean Core JS Pipeline SCM API for Blue Ocean Dashboard for Blue Ocean Build With Parameters Dynamic Extended Choice Parameter Plug-In Dynamic Parameter Plug-in Extended Choice Parameter List Git Branches Parameter Pipeline Pipeline: Declarative Kubernetes Kubernetes CLI Kubernetes Credentials Image Tag Parameter Active Choices
勾选后,点击 Download now and install after restart,jenkins安装完后后重启
gitlab 安装
略
gitlab 安装及使用
安装包下载地址:https://packages.gitlab.com/gitlab/gitlab-ce
rpm 包国内下载地址:https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/
ubuntu 国内下载地址:https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/
下载安装gitlab:
#这里事先从清华源下载最新版gitlab #内存最少4G $ free -h #安装 $ yum install gitlab-ce-14.9.4-ce.0.el7.x86_64.rpm -y
修改gitlab配置文件:
$ grep "^[a-Z]" /etc/gitlab/gitlab.rb external_url 'http://192.168.10.14' #访问地址 prometheus['enable'] = false #关闭监控不需要安装,可通过 gitlab exporter 与自建 prometheus 实现监控
初始化服务
#执行配置并启动服务,头一次加载服务比较慢,确保内存大于4G,等待启动完成 gitlab-ctl reconfigure # 默认密码在 /etc/gitlab/initial_root_password,文件有效期 24h。
创建一个组并在组下创建项目,然后可以通过 git 命令拉代码了。
在gitlab中添加密钥
# jenkins 生成密钥 ssh-keygen -t rsa -C "[email protected]" # 在gitlab中添加密钥
harbor 安装
略 下载地址:https://github.com/vmware/harbor/releases
安装文档: https://goharbor.io/docs/
由于Harbor是采用docker-compose一键部署的,所以Harbor服务器也需要安装Docker以及docker-compose
#上传harbor离线包,并且部署 $ tar xf harbor-offline-installer-v2.5.0.tgz -C /usr/local/
修改配置文件
$ cp /usr/local/harbor/harbor.yml.tmpl /usr/local/harbor/harbor.yml $ vim /usr/local/harbor/harbor.yml hostname: 192.168.10.15 #harbor服务器地址 harbor_admin_password: 123456 data_volume: /data #数据目录需要手动创建 #注意除了这三项外还需要注释https的全部注释
启动服务
$ cd /usr/local/harbor/ #预配置 $ ./prepare # 启动 $ ./install.sh
配置docker或者continerd
如果harbor使用的http需要修改容器服务配置,k8s中容器运行时也需要修改
docker配置
#修改docker配置 cat /etc/docker/daemon.json { "exec-opts": ["native.cgroupdriver=systemd"], "registry-mirrors": ["https://qai5ut9z.mirror.aliyuncs.com"], "insecure-registries": ["192.168.10.254:5000","192.168.10.15"] } systemctl daemon-reload systemctl restart docker #登录harbor docker login 192.168.10.15 #上传镜像 docker push 192.168.10.15/kubernetes/alpine:latest
continerd配置
#修改配置 $ vim /etc/containerd/config.toml [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.10.15".auth] #不是匿名仓库拉取镜像需要认证仓库认证信息 username = "admin" password = "123456" [plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.10.15"] #仓库设置 endpoint = ["http://192.168.10.15"] #重启 $ systemctl restart containerd.service #拉取镜像测试 $ crictl pull 192.168.10.15/kubernetes/alpine:latest ctr -n k8s.io image pull 192.168.10.15/kubernetes/alpine:latest --plain-http --user admin:123456
jenkins对接各种组件
- 修改jenkins必要设置
确保这俩个配置与Jenkins的访问地址一致。
Jenkins URL == Resource Root URL
同时如果配置了域名和 jenkins master 通信的 50000 端口也需要开放
- 配置jenkins的凭证
Harbor 的账号密码、GitLab 的私钥、Kubernetes 的证书均使用 Jenkins 的 Credentials 管理。
配置kubernetes配置文件
- 首先需要找到集群中的 KUBECONFIG,一般是 kubectl 节点的~/.kube/config 文件,或者是 KUBECONFIG 环境变量所指向的文件。
- 接下来只需要把证书文件放置于 Jenkins 的 Credentials 中即可。首先点击 Manage Jenkins, 之后点击 Manage Credentials
- 然后在点击 Jenkins,之后点击 Add Credentials 选择密钥文件
配置harbor账号密码
对于账号密码和 Key 类型的凭证,配置步骤是一致的,只是选择的凭证类型不一样。
接下来通过Jenkins凭证管理Harbor的账号密码。 在同样的位置点击 Add Credentials
选择类型为 Username with password
配置gitlab的key
点击 Add Credentials,类型选择为 SSH Username with private key
找到jenkins主机的~/.ssh/id_rsa之前生成的。
- 配置agent
通常情况下,Jenkins Slave 会通过 Jenkins Master 节点的 50000 端口与之通信,所以需要开启 Agent 的50000端口。
点击 Manage Jenkins,然后点击 Configure Global Security,在 Agents TCP port 中fixed 填写 50000。
实际使用时,没有必要把整个 Kubernetes 集群的节点都充当创建Jenkins Slave Pod 的节点, 可以选择任意的一个或多个节点作为创建 Slave Pod 的节点
kubectl label node 192.168.10.12 build=true
注意:
- 如果集群并非使用 Docker 作为 Runtime,但是由于构建镜像时,需要使用 Docker,所以该节点需要安装Docker.
- jenkins 配置 kubernetes 多集群
首先点击 Manage Jenkins –> Manage Nodes and Clouds,之后点击 Configure Clouds,祥情中只改凭据中找到 kubernetes 的。
最后点击 Save 即可,添加完 Kubernetes 后,在 Jenkinsfile 的 Agent 中,就可以选择该集群作为创建Slave的集群。
如果想要添加多个集群,重复上述的步骤即可。首先添加 Kubernetes 凭证,然后添加 Cloud 即可。
BlueOcean入门
推荐方式:
1.BlueOcean 创建 Pipline 2.在 gitlab上 创建一个独立的项目 3.BlueOcean 应用上面创建的 gitlab 4.会在 gitlab 上创建的独立项目中生成 Jenkinsfile 5.拷贝上面的 Jenkinsfile, 修改适配自己真实项目的 Jenkinsfile 应用到自己的真实项目中 6.job 调用 job的方式,去复用公共模块。比如说代码扫描等
官方文档:https://www.jenkins.io/projects/blueocean/
使用 Jenkins Pipeline 来自动化部署一个 Kubernetes 应用的方法,在实际的项目中,往往一个代码仓库都会有很多分支的,比如开发、测试、线上这些分支都是分开的,一般情况下开发或者测试的分支希望提交代码后就直接进行 CI/CD 操作,而线上的话最好增加一个人工干预的步骤,这就需要 Jenkins 对代码仓库有多分支的支持,当然这个特性是被 Jenkins 支持的。
图形化创建Jenkins案例
阿里云镜像仓库
https://cr.console.aliyun.com/cn-hangzhou/instance/dashboard
# login sudo docker login --username=jet**** registry.cn-hangzhou.aliyuncs.com
- 配置accessKey
- 在linux安装 CLI工具
https://www.alibabacloud.com/help/zh/doc-detail/139508.htm
tar xzvf aliyun-cli-linux-3.0.32-amd64.tgz sudo cp aliyun /usr/local/bin aliyun --help aliyun configure # 输入accessKey
# 获取镜像tag aliyun cr GetRepoTags --RepoNamespace 命名空间名称 --RepoName 镜像名称 | jq ".data.tags[].tag" -r
jenkins 其它配置
备份插件ThinBackup
https://plugins.jenkins.io/thinBackup/
备份目录: /data/jenkis_backup 备份时间:H 0 * * *
自动化构建应用
sprintboot: https://gitee.com/dukuan/spring-boot-project.git
pipeline { agent { kubernetes { cloud 'kubernetes' //这里需要指定相关jenkins中创建的kubernetes对接信息的名称 slaveConnectTimeout 1200 //超时配置 workspaceVolume emptyDirWorkspaceVolume(hostPath: "/opt/workspace", readOnly: false) //jenkins的工作目录,必须设置起到一个Pod中不同container的目录共享jenkins工作目录 yaml ''' //这里以下都是Pod定义信息 kind: Pod metadata: name: jenkins-agent namespace: jenkins spec: containers: - args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\'] image: '192.168.10.15/kubernetes/jnlp:alpine' name: jnlp //jnlp容器是必须的他负责连接jenkins,这里保持默认使用即可 imagePullPolicy: IfNotPresent //以下容器为具体的工作容器,所有流水线中的任何阶段的任务都在容器中执行,可以定义多个在流水线中指定任务使用那个容器进行执行 - command: //所有容器推荐使用cat命令保证容器在启动后保持运行不退出 - "cat" tty: true //保持tty,起到容器不退出 image: "192.168.10.254:5000/bash/alpine:latest" imagePullPolicy: "IfNotPresent" name: "echo" //container的名称 restartPolicy: Never nodeSelector: build: true ''' } } //具体流水线配置 stages { //这里为流水线定义 stage('echo') { //stage名称 steps { container(name: 'echo') { //这里定义这个步骤使用那个container进行执行,指定container的名称 sh "echo hello word" } } } }
java 应用
- jenkinsfile
pipeline { //顶层环境变量设置 environment { COMMIT_ID = "" HARBOR_ADDRESS = "192.168.10.15/bolo" //生成镜像存放镜像的仓库地址 REGISTRY_DIR = 'kubernetes' IMAGE_NAME = "sprintg-boot" NAMESPACE = "bolo" //服务部署在那个namespace中 GIT_ADDR = "[email protected]:kubernetes/bolo-spring-boot.git" //代码仓库地址 TAG = "" //镜像tag,会在下面生成,这里只是定义全局变量 } //全局配置 options { timestamps() //所有输出每行都会打印时间戳 buildDiscarder(logRotator(numToKeepStr: '5')) //保留5个历史构建版本 } //手动构建时选择分支参数 parameters { gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH') } //agent配置 agent { kubernetes { cloud 'kubernetes' slaveConnectTimeout 1200 workspaceVolume emptyDirWorkspaceVolume(hostPath: "/opt/workspace", readOnly: false) //这里使用临时目录共享jenkins的工作目录默认路径为/home/jenkins/agent,解决 nodejs 下载包问题 yaml ''' kind: Pod metadata: name: jenkins-agent namespace: jenkins spec: containers: - args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\'] image: '192.168.10.254:5000/kubernetes/jnlp:alpine' name: jnlp #这个容器必须有,保持默认即可 imagePullPolicy: IfNotPresent volumeMounts: - mountPath: "/etc/location" name: "localtime" readOnly: false - command: #所有容器推荐使用cat命令保证容器在启动后保持运行不退出 - "cat" env: - name: "LANGUAGE" value: "en_US:en" - name: "LC_ALL" value: "en_US.UTF-8" - name: "LANG" value: "en_US.UTF-8" image: "192.168.10.254:5000/kubernetes/maven:3.8.5-openjdk-8-slim" imagePullPolicy: "IfNotPresent" name: "build" volumeMounts: - mountPath: "/etc/location" name: "localtime" readOnly: false - mountPath: "/root/.m2" #持久化依赖包,重复构建不会进行重复下载 name: "cachedir" readOnly: false tty: true #保持tty,起到容器不退出 - command: - "cat" env: - name: "LANGUAGE" value: "en_US:en" - name: "LC_ALL" value: "en_US.UTF-8" - name: "LANG" value: "en_US.UTF-8" image: "192.168.10.254:5000/kubernetes/docker:alpine" imagePullPolicy: "IfNotPresent" name: "docker" #docker容器需要挂载docker.sock文件,需要调度到有docker的node节点 tty: true volumeMounts: - mountPath: "/etc/location" name: "localtime" readOnly: false - mountPath: "/var/run/docker.sock" name: "dockersock" readOnly: false - command: - "cat" env: - name: "LANGUAGE" value: "en_US:en" - name: "LC_ALL" value: "en_US.UTF-8" - name: "LANG" value: "en_US.UTF-8" image: "192.168.10.254:5000/kubernetes/kubectl:apline" imagePullPolicy: "IfNotPresent" name: "kubectl" #kubectl镜像 tty: true volumeMounts: - mountPath: "/etc/location" name: "localtime" readOnly: false volumes: - name: mvn-data persistentVolumeClaim: claimName: mvn - hostPath: path: "/var/run/docker.sock" name: "dockersock" restartPolicy: Never nodeSelector: #这里需要给有docker的node节点打标签调度Pod到这个节点 build: true securityContext: {} volumes: - hostPath: path: "/var/run/docker.sock" name: "dockersock" - hostPath: path: "/usr/share/zoneifo/Asia/Shanghai" name: "localtime" - hostPath: path: "/opt/m2" name: "cachedir" ''' } } //具体流水线配置 stages { //克隆代码 stage('Pulling Code') { //并行执行 //failFast true //并行执行的分支只要有一个失败立即结束流水线 parallel { //手动执行jenkins流水线 stage('Pulling Code by Jenkins') { when { expression { env.gitlabBranch == null } } steps { //git branch: "${BRANCH}", credentialsId: 'gitlab-key', url: "${GIT}" git(changelog: true, poll: true, url: "${GIT_ADDR}", branch: "${BRANCH}",credentialsId: 'gitlab-key') script { COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim() TAG = BUILD_TAG + '-' + COMMIT_ID println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}" } } } //gitlab触发构建 stage('git clone trigger') { when { expression { env.gitlabBranch != null } } steps { git branch: "${env.gitlabBranch}", credentialsId: 'gitlab-key', url: "${GIT}" git(changelog: true, poll: true, url: "${GIT_ADDR}", branch: env.gitlabBranch, credentialsId: 'gitlab-key') script { //TAG = sh(returnStdout: true, script: "echo -n ${env.gitlabBranch}-${env.BUILD_ID}") COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim() TAG = BUILD_TAG + '-' + COMMIT_ID println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}" } } } } } //打包java程序 stage('Building') { steps { container(name: 'build') { sh """ curl repo.maven.apache.org mvn clean install -DskipTests" ls target/* """ } } } //构建镜像并且推送镜像仓库 stage('Docker build for creating image') { environment { HARBOR_USER = credentials('harbor-account') //获取镜像仓库认证信息 } steps { container(name: 'docker') { sh """ echo ${HARBOR_USER_USR} ${HARBOR_USER_PSW} ${TAG} docker build -t ${HARBOR_ADDRESS}/${HARBOR_DIR}/${IMAGE_NAME}:${TAG} ." docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${HARBOR_ADDRESS}" docker push ${registries}/${NAME}:${TAG}" """ } } } //更新k8s相关应用 stage('Deploying to Kubernetes') { environment { MY_KUBECONFIG = credentials('kubernetes-cluster-stg') } steps { container(name: 'kubectl') { sh """ kubectl --kubeconfig $MY_KUBECONFIG set image deploy -n ${NAMESPACE} -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${HARBOR_DIR}/${IMAGE_NAME}:${TAG}" kubectl rollout status deployment -n ${NAMESPACE} ${IMAGE_NAME} --timeout=60s --kubeconfig $MY_KUBECONFIG" """ } } } } }
- dockerfile
- jenkins job
new item –> job 名为 git 项目名,选择 pipline –> Pipline 中选择 Pipline script from SCM,输入 git 项目地址及访问密钥 –> Save
Vue/H5 应用
把编译生成的 dist 目录构建成一个镜像
jenkinsfile 模板一样只是 build 中内容变化了。
- 编译镜像换成 nodejs,缓存目录不配置,因为 workspace 采用的是 hostPath,该目录被缓存到创建 Pod 节点的 /opt 目录
- 编译命令改成 npm 命令
Go 应用
jenkinsfile 模板一样只是 build 中内容变化了。
- 编译镜像换成 golang,缓存目录为 /go/pkg/,执行 go build 时下载的依赖包会缓存在该目录
- 编译命令改成 go env -w GOPROXY=https://goproxy.cn,direct; go build
dockerfile
FROM apline-glibc:alpine-3.9
COPY conf ./conf
COPY ./go-project ./
ENTRYPOINT ["./go-project"]
配置自动触发构建
之前的构建都是采用手动选择分支进行构建的,实际使用时,项目可能有很多,如果都是手动触发可能比较消耗人力。所以推荐可以按需配置自动触发,即提交代码后自动触发Jenkins进行构建任务。
配置jenkins
首先找到 Java 项目的 Job,点击 Configure
之后选择 Build Triggers,勾选 Build when a change…,记录 webhook URL
选择 Allow all branches,如果不想任何分支都可以触发该流水线,可以选择 Filter 进行条件匹配。之后点击 Generate 生成 Secret token, 最后点击 Save 即可。
配置gitlab
接下来配置 GitLab,首先点击 Menu→Admin,在 Settings –> Network 中勾选 Allow requests to the local network from web hooks and services, Allow requests to the local network from system hook
保存后,找到 Java 项目,点击 Settings→WebHooks,填写 jenkins job URL 和 token,确认无误后,点击 Add webhook。保存后没有问题可以进行测试。
jenkins 共享库
杂
单机启动监控
cat /data/script/monitoring-jenkins.py import requests import subprocess import logging import datetime logging.basicConfig(filename='/tmp/.jenkins_ops.log',level=logging.DEBUG) url = "http://10.204.68.159:8080/jenkins/login" cmd = "/data/app/pfg-jenkins-master-01/console restart" def getJenkinsStatus(): curr_time = datetime.datetime.now() resp = requests.get(url,timeout=5) resp.close() if resp.status_code == 502 or resp.status_code == 500: out = subprocess.Popen(cmd,stdout=subprocess.PIPE, shell=True) result=out.stdout.readlines() logging.debug("操作时间 " + str(curr_time)) logging.debug("操作结果如下 " + str(result)) else: logging.debug("运行正常,本次检测时间 " + str(curr_time)) if __name__ == '__main__': getJenkinsStatus() crontab -l */5 * * * * /usr/bin/python3.7 /data/script/monitoring-jenkins.py
定时备份
cat /etc/cron.d/jenkins_cron 40 23 * * * root /bin/bash /data/script/jenkins_back.sh cat /data/script/jenkins_back.sh #!/bin/bash sourceJobbackupDir="/data/jenkins_data/jobs" sourcePlugins="/data/jenkins_data/plugins" dectJobBackupDir="/data/jenkins_backup/jobs" destPluginsDir="/data/jenkins_backup/plugins" currentdate=$(date +%Y%m%d) cd $dectJobBackupDir /usr/bin/tar -zcvf jobs.$currentdate.tar.gz $sourceJobbackupDir find $dectJobBackupDir/ -type f -name "jobs.*.tar.gz" -mtime +15 -exec rm -f {} \; cd $destPluginsDir /usr/bin/tar -zcvf plugins.$currentdate.tar.gz $sourcePlugins find $destPluginsDir/ -type f -name "plugins.*.tar.gz" -mtime +15 -exec rm -f {} \;
Gitlab
harbor
具体使用参考,运维篇
Harbor镜像仓库地址:172.168.1.249 # 获取项目信息 curl -u "admin:Harbor12345" -X GET -H "Content-Type: application/json" "http://172.168.1.249/api/projects/2" # 获取所有项目信息 curl -u "admin:Harbor12345" -X GET -H "Content-Type: application/json" "http://172.168.1.249/api/projects?" # 搜索镜像 curl -u "admin:Harbor12345" -X GET -H "Content-Type: application/json" "http://172.168.1.249/api/search?q=asset" # 删除项目 curl -u "admin:Harbor12345" -X DELETE -H "Content-Type: application/json" "http://172.168.1.249/api/projects/3" # 创建项目 curl -u "admin:Harbor12345" -X POST -H "Content-Type: application/json" "http://172.168.1.249/api/projects" -d @createproject.json createproject.json为文件名,文件内容参考createproject.json # 0为私有 { "project_name": "项目名", "public": 0 } # 创建用户 curl -u "admin:Harbor12345" -X POST -H "Content-Type: application/json" "http://172.168.1.249/api/users" -d @user.json 文件内容参考user.json { "user_id": 5, "username": "test", "email": "[email protected]", "password": "Harbor12345", "realname": "test", "role_id": 0 } # 获取用户信息,除admin外 curl -u "admin:Harbor12345" -X GET -H "Content-Type: application/json" "http://172.168.1.249/api/users" # 查看当前用户信息 curl -u "admin:Harbor12345" -X GET -H "Content-Type: application/json" "http://172.168.1.249/api/users/current" # 删除用户,3是用户user_id curl -u "admin:Harbor12345" -X DELETE -H "Content-Type: application/json" "http://172.168.1.249/api/users/34" # 修改用户密码 curl -u "admin:Harbor12345" -X PUT -H "Content-Type: application/json" "http://172.168.1.249/api/users/4/password" -d @uppwd.json # 查看项目相关角色 curl -u "admin:Harbor12345" -X GET -H "Content-Type: application/json" "http://172.168.1.249/api/projects/2/members/" # 项目添加角色 curl -u "jaymarco:Harbor123456" -X POST -H "Content-Type: application/json" "http://172.168.1.249/api/projects/2/members/" -d @role.json # 查看镜像 curl -u "admin:Harbor12345" -X GET -H "Content-Type: application/json" "http://172.168.1.249/api/repositories?project_id=2&q=镜像名" # 删除镜像 curl -u "admin:Harbor12345" -X DELETE -H "Content-Type: application/json" "http://172.168.1.249/api/repositories/marktrace%2Fasset/tags/latest" # 获取镜像标签 curl -s -u "admin:Harbor12345" -X GET -H "Content-Type: application/json" "http://172.168.1.249/api/repositories/marktrace%2Fasset/tags/" |grep "digest" -C 2 |grep ""name""
具体使用参考,运维篇
下载地址:https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/
wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-13.0.3-ce.0.el7.x86_64.rpm yum install gitlab-ce-13.0.3-ce.0.el7.x86_64.rpm -y
vim /etc/gitlab/gitlab.rb external_url #修改 gitlab.test.com gitlab-ctl reconfigure # win 修改 win hosts 10.4.7.107 gitlab.test.com