为什么要写企业级持续集成(jenkins + pipeline + k8s)?
目前网上自动化持续集成的资料很多,但基本上都是局限于jenkins自由风格的job,结合shell脚本来实现持续集成,这种方式的缺点也很明显:
1、构建出问题,排查困难
2、构建节点挂了,就不能完成构建任务
而当前主流技术是 “ k8s + 微服务 ” 等,我们完全可以利用k8s的优势来完成持续构建任务,每次构建时可以调度到任意节点,或者是具有指定标签的节点,这就实现了高可用,另外,结合pipeline,可以轻松简单实现持续集成,并且哪个阶段出问题了一目了然,排查起来也容易。
自动化测试框架
0基础到实现:java + testng + httpclient + allure
参考:https://www.cnblogs.com/uncleyong/p/15867903.html
企业级持续集成技术栈
git + gitlab + jenkins + pipeline + maven + harbor + docker + k8s
可以整合python、java等各种自动化测试框架
流程:拉取代码--》mvn打包--》构建镜像--》新镜像发布到k8s--》拉取自动化测试代码--》自动化测试--》allure报告
环境:jenkins使用k8s作为构建环境,在某个节点执行测试
环境规划
192.168.117.160:harbor、jenkins、maven
192.168.117.161:k8s-master
192.168.117.162:k8s-node01
192.168.117.163:k8s-node02
192.168.117.180:gitlab
每台虚拟机都安装了git
环境搭建(含必备基础)
git安装及使用:https://www.cnblogs.com/uncleyong/p/10854115.html
gitlab安装及使用:https://www.cnblogs.com/uncleyong/p/16557785.html
maven安装及使用:https://www.cnblogs.com/uncleyong/p/10743181.html
docker安装及使用:https://www.cnblogs.com/uncleyong/p/8894133.html
harbor安装以及使用:https://www.cnblogs.com/uncleyong/p/15469575.html
k8s安装:https://www.cnblogs.com/uncleyong/p/15499732.html(k8s操作:https://www.cnblogs.com/uncleyong/p/15499743.html)
jenkins搭建及devops自动化平台相关配置:https://www.cnblogs.com/uncleyong/p/16555667.html
allure报告:allure-commandline下载、安装、配置(linux或者docker),https://www.cnblogs.com/uncleyong/p/16726826.html
jenkins把自动化测试结果发送到钉钉群:https://www.cnblogs.com/uncleyong/p/16724590.html
pipeline设计
pipeline常用功能:https://www.cnblogs.com/uncleyong/p/16705620.html
使用Blue Ocean设计pipeline脚本:https://www.cnblogs.com/uncleyong/p/16727971.html
说明:由于资源不足,暂未加入sonarqube等
pipeline具体实现
结合上面在Blue Ocean中设计的pipeline骨架,我们来完善并实现整个过程
parameters
可以选择要构建的分支
parameters {
gitParameter branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: '构建的分支', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition'
}
环境变量
environment {
HARBOR_ADDRESS = "192.168.117.160"
IMAGE_NAME = "gift"
NAMESPACE = "gift"
}
agent
k8s作为构建环境
agent {
kubernetes {
cloud 'qzcsbj_kubernetes'
yaml '''apiVersion: v1
kind: Pod
spec:
containers:
- image: 'registry.cn-chengdu.aliyuncs.com/qzcsbj6/jnlp:alpine'
name: jnlp
imagePullPolicy: IfNotPresent
args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
volumeMounts:
- mountPath: "/etc/localtime"
name: "localtime"
readOnly: false
- image: "registry.cn-chengdu.aliyuncs.com/qzcsbj6/kubectl"
imagePullPolicy: "IfNotPresent"
name: "kubectl"
tty: true
command:
- "cat"
env:
- name: "LANGUAGE"
value: "en_US:en"
- name: "LANG"
value: "en_US.UTF-8"
volumeMounts:
- mountPath: "/etc/localtime"
name: "localtime"
readOnly: false
- image: "registry.cn-chengdu.aliyuncs.com/qzcsbj6/docker"
imagePullPolicy: "IfNotPresent"
name: "docker"
tty: true
command:
- "cat"
env:
- name: "LANGUAGE"
value: "en_US:en"
- name: "LANG"
value: "en_US.UTF-8"
volumeMounts:
- mountPath: "/etc/localtime"
name: "localtime"
readOnly: false
- mountPath: "/var/run/docker.sock"
name: "dockersock"
readOnly: false
- image: "registry.cn-chengdu.aliyuncs.com/qzcsbj6/maven"
imagePullPolicy: "IfNotPresent"
name: "maven"
tty: true
command:
- "cat"
env:
- name: "LANGUAGE"
value: "en_US:en"
- name: "LANG"
value: "en_US.UTF-8"
volumeMounts:
- mountPath: "/etc/localtime"
name: "localtime"
- mountPath: "/root/.m2/"
name: "m2dir"
readOnly: false
- image: "registry.cn-chengdu.aliyuncs.com/allure-commandline"
imagePullPolicy: "IfNotPresent"
name: "allure"
tty: true
command:
- "cat"
env:
- name: "LANGUAGE"
value: "en_US:en"
- name: "LANG"
value: "en_US.UTF-8"
volumeMounts:
- mountPath: "/etc/localtime"
name: "localtime"
readOnly: false
restartPolicy: "Never"
volumes:
- name: "dockersock"
hostPath:
path: "/var/run/docker.sock"
- name: "localtime"
hostPath:
path: "/usr/share/zoneinfo/Asia/Shanghai"
- name: "m2dir"
hostPath:
path: "/opt/m2"
'''
}
}
pull project code
stage('pull project code') {
parallel {
stage('ui build') {
when {
expression {
env.gitlabBranch == null
}
}
steps {
sh """
echo '=================开始拉取项目代码'
"""
git(url: '[email protected]:qzcsbj/gift.git', branch: "${BRANCH}", credentialsId: 'qzcsbj_gitlab')
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}"
}
}
post {
failure {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 拉取项目代码失败",
text: ["#### '${JOB_NAME}'项目代码拉取失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
)
}
}
}
stage('trigger build') {
steps {
sh """
echo "待更新。。。"
"""
}
}
}
}
mvn package
stage('mvn package') {
steps {
container(name: 'maven') {
sh """
echo '=================mvn开始打包'
mvn clean package -Dmaven.test.skip=true
echo '=================mvn打包完成'
"""
}
}
post {
failure {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: mvn package失败",
text: ["#### '${JOB_NAME}'项目mvn package失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
)
}
}
}
build and push image
stage('build and push image') {
environment {
HARBOR_USER = credentials('qzcsbj_harbor')
}
steps {
container(name: 'docker') {
sh """
echo '=================开始构建镜像'
docker build -t ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} .
docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${HARBOR_ADDRESS}
docker push ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG}
echo '=================镜像推送完成'
"""
}
}
post {
failure {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 构建或者推送镜像失败",
text: ["#### '${JOB_NAME}'项目构建或者推送镜像失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
)
}
}
}
deploy to k8s
stage('deploy to k8s') {
environment {
MY_KUBECONFIG = credentials('qzcsbj_k8s')
}
steps {
container(name: 'kubectl'){
sh """
echo '=================开始部署到k8s'
/usr/local/bin/kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE
echo '=================部署到k8s完成'
"""
}
}
post {
success {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 自动化部署到k8s完成",
text: ["#### '${JOB_NAME}'项目自动化部署到k8s完成\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
)
}
failure {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 自动化部署到k8s失败",
text: ["#### '${JOB_NAME}'项目自动化部署到k8s失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
)
}
}
}
pull autotest code
stage("pull autotest code"){
steps{
sh """
echo '=================开始拉取自动化测试代码'
"""
git(
credentialsId: 'qzcsbj_gitlab',
url: '[email protected]:root/apiautotest.git'
)
}
post {
failure {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 拉取自动化测试代码失败",
text: ["#### 拉取自动化测试代码失败\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
)
}
}
}
run autotest
stage("run autotest"){
steps{
sh """
mvn clean test -Dsurefire.suiteXmlFiles=testngXML/testng.xml
"""
}
post {
success {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 自动化测试完成",
text: ["#### '${JOB_NAME}'项目自动化测试完成\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL}console)"]
)
}
failure {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 自动化测试运行出错",
text: ["#### '${JOB_NAME}'项目自动化测试运行出错\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
)
}
}
}
allure report
stage("allure report"){
steps{
sh """
echo '=================开始生成测试报告'
"""
allure(
includeProperties: false,
jdk: '',
results: [[path: 'target/allure-results']]
)
}
post {
success {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 自动化测试报告已生成,全部通过",
text: ["#### '${JOB_NAME}'项目自动化测试报告已生成,全部通过\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次测试报告](${BUILD_URL}allure)"]
)
}
unstable {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 自动化测试报告已生成,有未通过的",
text: ["#### '${JOB_NAME}'项目自动化测试报告已生成,有未通过的\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次测试报告](${BUILD_URL}allure)"]
)
}
failure {
dingtalk (
robot:'dd01',
type:'MARKDOWN',
atAll: true,
title: "notice: 自动化测试报告生成出错",
text: ["#### '${JOB_NAME}'项目自动化测试报告生成出错\n - 构建:第'${BUILD_NUMBER}'次\n - 状态:'${currentBuild.result}'\n - [查看本次构建详情](${BUILD_URL})"]
)
}
}
}
构建及效果展示
pipeline脚本可以放在流水线项目的脚本框中
也可以放gitlab上,选择SCM
pipeline脚本放到Jenkinsfile中,Jenkinsfile在gift项目master分支下
第一次构建,点击“Build Now”,会失败,因为我们使用了参数化构建,这次构建获取不到参数值
刷新页面,就可以看到参数化构建了,就可以选择要构建的分支
部署到k8s的通知
消息
运行自动化测试的通知
消息
生成allure测试报告的通知
消息
测试报告的通知可以优化,直接链接到allure测试报告
测试报告
【建议收藏备用】热门技术、项目实战、简历、笔试题、面试题、职业规划(助你少走弯路,轻松跳槽加薪)
点击学习:https://www.cnblogs.com/uncleyong/p/15777706.html
包含以下内容:
测试实战:
性能:jmeter + k8s + 微服务 + skywalking + efk,测试都在学的热门技术
自动化:python版、java版
测开:待更新
简历:写出一份高质量简历
笔试题:linux、shell、mysql、java、python、测开、性能、自动化、docker、k8s等
职业规划:让你少走弯路,尽早跳槽加薪,方向不对,努力白费
摸鱼:上班正确的摸鱼方式
原文会持续更新,原文地址: https://www.cnblogs.com/uncleyong/p/16721826.html