Jenkins-Pipeline java自动化打包部署到 k8s
·
一、 发布流程分析
- 拉取代码
- NPM编译打包
- 代码质量检查
- 容器镜像封装
- 推送镜像到镜像仓库
- 发布到k8s集群平台
二、 Jenkins 配置如下:

三、 Pipeline1 脚本如下
pipeline {
agent {
label 'master'
}
// 构建参数
parameters {
gitParameter(branchFilter: 'origin.*/(master|feature.*|hotfix.*|release.*)', name: 'BRANCH_TAG', defaultValue: 'master',selectedValue: 'DEFAULT', type: 'PT_BRANCH_TAG')
choice(name: '请选择操作', choices: [ "拉取代码-编译",'-----' ,"拉取代码-编译-单元测试", '-----',"重启服务",'-----' ,"停止服务",'-----',"扩容服务",'-----',"回滚服务"])
// string(name: 'version_id', defaultValue: '1', description: '构建生产历史版本号,1表示回滚到上一个版本,2表示回滚上两个版本')
string(name: 'pod_num', defaultValue: '2', description: '默认pod数据数量2,输入扩容副本数量')
}
environment {
// 参数选项
ACTION = "${params.请选择操作}"
// 项目名称
PROJECT_NAME= "cnh"
// 指定代码所在GitLab仓库地址
// GITLAB_URL = "ssh://git@gitlab.iotcloud.wl:2222/xxx.git"
GITLAB_URL = "https://xxx.git"
// 指定GitLab仓库的分支
// GITLAB_BRANCH = "master"
// GitLab拉取代码的用户认证文件ID
// GITLAB_SERCET = "11d180dd-1afc-4e40-952a-d0d9db29ffde"
GITLAB_SERCET = "f07ce25c-490b-475f-adce-1dd19d13149f"
// docker镜像仓库地址
// DOCKER_REGISTRY_URL = "172.16.0.100:5000"
// DOCKER_LOCAL_REGISTRY_URL = "172.16.0.100:8090"
// DOCKER_REGISTRY_LOGIN_USER = "docker"
// DOCKER_REGISTRY_LOGIN_PASSWORD= "CNH@Docker2025"
DOCKER_REGISTRY_URL = "registry.iotcloud.wl"
DOCKER_LOCAL_REGISTRY_URL = "registry.iotcloud.wl"
DOCKER_REGISTRY_LOGIN_USER = "docker"
DOCKER_REGISTRY_LOGIN_PASSWORD= "CNH@Docker2025"
DOCKER_JDK_BASE_IMAGE = "${DOCKER_LOCAL_REGISTRY_URL}/cnmirrors/openjdk:8u201-alpine-tools"
// // 指定Dockerfile文件所在路径
DOCKER_BUILD_FILE = "cnh-iconnect-system/${JOB_BASE_NAME}/Dockerfile"
// // 指定构建Docker容器时所在路径
DOCKER_BUILD_PATH = "cnh-iconnect-system/${JOB_BASE_NAME}"
// K8S命令的路径
K8S_COMMAND_PATH = "${JENKINS_HOME}/tools/kubectl"
// NPM_COMMAND = "${JENKINS_HOME}/node-v14.15.0-linux-x64/bin/npm"
// MVN_COMMAND = "/var/jenkins_home/maven"
// 配置K8S执行命令的环境
K8S_CONFIG_SERCET = "k8s-jenkins-prod"
// 配置K8S服务开放容器的端口
K8S_PORT = "8080"
// K8s 名称空间
K8S_NAMESPACE_NAME = "cnh"
// 创建服务的复本数量
K8S_POD_NUM = 2
// RELEASE 发布标签
// K8S_RELEASE = 'dev'
// PROFILES_ACTIVE="sit"
JVM_OPTIONS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Xms512m -Xmn512M -XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=45 -XX:ParallelGCThreads=2 -XX:ConcGCThreads=1 -Djava.security.egd=file:/dev/./urandom -Dapp=${JOB_BASE_NAME} -Dspring.profiles.active=prod"
// 显示选择镜像版本号的数量
DOCKER_NUM = 10
// SonarQube Scanner
scannerHome = "${JENKINS_HOME}/tools/sonar-scanner-4.3.0.2102"
}
// pipeline选项
options {
// 保存最近历史构建记录数量
buildDiscarder(logRotator(numToKeepStr: '10'))
// 禁止pipeline同时执行
disableConcurrentBuilds()
// 失败时重试总次数
// retry(2)
// pipeline执行时间过长,超过timeout时间就停止掉pipeline
timeout(time: 30, unit: 'MINUTES')
}
tools {
maven "mvn";
jdk "jdk8"
}
stages {
stage("清理工作空间") {
steps {
deleteDir() // 清理工作空间
}
}
stage('GitLab代码拉取') {
when {
anyOf {
environment name: 'ACTION', value: '拉取代码-编译-单元测试';
environment name: 'ACTION', value: '拉取代码-编译'
}
}
steps {
script {
// git branch: env.GITLAB_BRANCH, credentialsId: env.GITLAB_SERCET, url: env.GITLAB_URL
checkout([$class: 'GitSCM',
branches: [[name: "${params.BRANCH_TAG}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [], submoduleCfg: [],
userRemoteConfigs: [[credentialsId: env.GITLAB_SERCET, url: env.GITLAB_URL]]])
// 获取最新的标签
// def VERSION = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()"
// def VERSION = sh(script: 'git tag | sort -V | tail -n 1', returnStdout: true).trim()
// 获取当前分支的最近一次提交的哈希值
def commitHash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
// // 获取当前分支的名称
// // def branchName = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()
def branchName = "${params.BRANCH_TAG}"
echo "${params.BRANCH_TAG}"
// 将这些值设置为环境变量,以便在后续步骤中使用
env.COMMIT_HASH = commitHash
env.BRANCH_NAME = branchName
env.VERSION = "${COMMIT_HASH}"
// 根据分支名称设置不同的标签版本
def TAG_VERSION
if (branchName == 'master') {
TAG_VERSION = "stable-${VERSION}"
} else if (branchName ==~ /feature.+/) {
TAG_VERSION = "feature-${VERSION}"
} else if (branchName ==~ /hotfix.+/) {
TAG_VERSION = "hotfix-${VERSION}"
} else if (branchName ==~ /release.+/) {
TAG_VERSION = "release-${VERSION}"
} else {
TAG_VERSION = "custom-${VERSION}"
}
env.TAG_VERSION = TAG_VERSION
// 构建完整的版本号
def BUILD_NUMBER = env.BUILD_NUMBER
def BUILD_TIMESTAMP = sh(script: 'date "+%Y%m%d%H%M"', returnStdout: true).trim()
def FULL_VERSION = "${TAG_VERSION}-${BUILD_NUMBER}-${BUILD_TIMESTAMP}"
// 设置 Docker 镜像名称
DOCKER_IMAGE_NAME = "${DOCKER_REGISTRY_URL}/${PROJECT_NAME}/${JOB_BASE_NAME}:${FULL_VERSION}"
DOCKER_DEPLOY_IMAGE_NAME = "${DOCKER_LOCAL_REGISTRY_URL}/${PROJECT_NAME}/${JOB_BASE_NAME}:${FULL_VERSION}"
env.DOCKER_IMAGE_NAME = DOCKER_IMAGE_NAME
env.DOCKER_DEPLOY_IMAGE_NAME = DOCKER_DEPLOY_IMAGE_NAME
// 打印信息
// echo "Latest tag: ${env.TAG_VERSION}"
// echo "Commit hash: ${env.COMMIT_HASH}"
// echo "Branch name: ${env.BRANCH_NAME}"
echo "DEPLOY IMAGES NAME : ${DOCKER_DEPLOY_IMAGE_NAME}"
}
}
}
stage ('并行处理依赖项目') {
when {
environment name: 'ACTION', value: '拉取代码-编译'
}
parallel {
stage("cdh-common依赖处理") {
steps {
sh '''
cd cnh-common
mvn clean compile
mvn install -DskipTests=true
'''
}
}
}
}
stage('拉取代码-编译') {
when {
environment name: 'ACTION', value: '拉取代码-编译'
}
tools {
maven "mvn";
jdk "jdk8"
}
steps {
sh '''
cd cnh-iconnect-system/${JOB_BASE_NAME}
mvn clean compile
mvn -Pprod install -DskipTests=true
'''
}
}
stage('拉取代码-编译-单元测试') {
when {
environment name: 'ACTION', value: '拉取代码-编译-单元测试'
}
tools {
maven "mvn";
jdk "jdk8"
}
steps {
sh '''
cd cnh-iconnect-system/${JOB_BASE_NAME}
mvn clean compile
mvn -Pprod install -DskipTests=true
mvn deploy
'''
}
}
// stage('代码检查') {
// when {
// anyOf {
// environment name: 'ACTION', value: '拉取代码-编译-单元测试';
// environment name: 'ACTION', value: '拉取代码-编译'
// }
// }
// tools {
// maven "mvn";
// jdk "jdk8"
// }
// steps {
// echo "启动sonar代码分析......"
// withSonarQubeEnv('sonar7.9') {
// //注意这里withSonarQubeEnv()中的参数要与之前SonarQube servers中Name的配置相同
// sh """
// cd cnh-iconnect-system/${JOB_BASE_NAME}
// ${scannerHome}/bin/sonar-scanner \
// -Dsonar.projectKey=${JOB_BASE_NAME} \
// -Dsonar.projectName=${JOB_BASE_NAME} \
// -Dsonar.projectBaseDir=./ \
// -Dproject.settings=../../sonar-project.properties
// """
// // sh """
// // cd ${JOB_BASE_NAME}
// // mvn clean verify sonar:sonar
// // """
// // sh """
// // cd ${JOB_BASE_NAME}
// // mvn clean verify sonar:sonar \
// // -Dsonar.core.codeCoveragePlugin=jacoco \
// // -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco-merged-test-coverage-report/jacoco.xml
// // """
// //指定了maven插件,如果不指定使用最新的
// // sh 'cd ${JOB_BASE_NAME}'
// // sh "mvn clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:3.6.0.1398:sonar "
// // sh """
// // cd cnh-api-gateway/${JOB_BASE_NAME}
// // ${scannerHome}/bin/sonar-scanner \
// // -Dsonar.projectKey=${JOB_BASE_NAME} \
// // -Dsonar.projectName=${JOB_BASE_NAME} \
// // -Dsonar.sources=. \
// // -Dsonar.sources=src \
// // -Dsonar.exclusions=**/bean/**,**/vo/**,**/dto/**,**/po/**,**/test/**,**/**/*Test \
// // -Dsonar.java.binaries=./target/classes \
// // -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml \
// // -Dsonar.issue.ignore.multicriteria.e1.ruleKey=java:S482 \
// // -Dsonar.issue.ignore.multicriteria.e1.resourceKey=**/** \
// // -Dsonar.issue.ignore.multicriteria.e2.ruleKey=java:S1214 \
// // -Dsonar.issue.ignore.multicriteria.e2.resourceKey=**/** \
// // -Dsonar.issue.ignore.multicriteria.e3.ruleKey=java:S125 \
// // -Dsonar.issue.ignore.multicriteria.e3.resourceKey=**/** \
// // -Dsonar.issue.ignore.multicriteria.e4.ruleKey=java:S1488 \
// // -Dsonar.issue.ignore.multicriteria.e4.resourceKey=**/**
// // """
// }
// }
// }
stage('Docker镜像构建') {
when {
anyOf {
environment name: 'ACTION', value: '拉取代码-编译-单元测试';
environment name: 'ACTION', value: '拉取代码-编译'
}
}
steps {
sh """
echo "FROM ${DOCKER_JDK_BASE_IMAGE}" >${DOCKER_BUILD_FILE}
echo "COPY target/*.jar /app.jar" >>${DOCKER_BUILD_FILE}
echo "ENV SW_AGENT_NAME=${JOB_BASE_NAME}" >>${DOCKER_BUILD_FILE}
echo "ENV APP_NAME=${JOB_BASE_NAME}" >>${DOCKER_BUILD_FILE}
"""
sh """
docker buildx build -t ${DOCKER_IMAGE_NAME} --file=${DOCKER_BUILD_FILE} ${DOCKER_BUILD_PATH}
"""
}
}
stage('推送Docker镜像到镜像仓库') {
when {
anyOf {
environment name: 'ACTION', value: '拉取代码-编译-单元测试';
environment name: 'ACTION', value: '拉取代码-编译'
}
}
steps {
sh """
echo "${DOCKER_REGISTRY_LOGIN_PASSWORD}" | docker login --username ${DOCKER_REGISTRY_LOGIN_USER} --password-stdin ${DOCKER_REGISTRY_URL}
docker push ${DOCKER_IMAGE_NAME}
docker rmi ${DOCKER_IMAGE_NAME}
"""
}
}
stage('重启K8S微服务') {
when {
environment name: 'ACTION', value: '重启服务'
}
steps {
script {
withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
env.RETURN_VALUME = "$RETURN_VALUME"
if (env.RETURN_VALUME != ''){
sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} rollout restart deployments ${JOB_BASE_NAME}'
} else {
sh 'echo "服务:${JOB_BASE_NAME} 不存在"'
sh 'exit 1'
}
}
}
}
}
stage('停止K8S微服务') {
when {
environment name: 'ACTION', value: '停止服务'
}
steps {
script {
withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
env.RETURN_VALUME = "$RETURN_VALUME"
if (env.RETURN_VALUME != ''){
sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} scale --replicas=0 deployment ${JOB_BASE_NAME}'
} else {
sh 'echo "服务:${JOB_BASE_NAME} 不存在"'
sh 'exit 1'
}
}
}
}
}
stage('扩容K8S微服务') {
when {
environment name: 'ACTION', value: '扩容服务'
}
steps {
script {
withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
env.RETURN_VALUME = "$RETURN_VALUME"
if (env.RETURN_VALUME != ''){
sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} scale --replicas=${pod_num} deployment ${JOB_BASE_NAME}'
} else {
sh 'echo "服务:${JOB_BASE_NAME} 不存在"'
sh 'exit 1'
}
}
}
}
}
stage("回滚微服务") {
when {
environment name: 'ACTION', value: '回滚服务'
}
steps {
script {
dir("${JENKINS_HOME}/shell"){
env.DOCKER_VERSIONS = sh(returnStdout: true, script: 'python3 pull_images_tag.py ${DOCKER_LOCAL_REGISTRY_URL} ${JOB_BASE_NAME} ${PROJECT_NAME} ${DOCKER_NUM}')
println env.DOCKER_VERSIONS
}
env.RELEASE_SCOPE = input message: '选择要发布的版本号', ok: '继续!',
parameters: [choice(name: 'RELEASE_SCOPE', choices: env.DOCKER_VERSIONS, description: '在测试环境经过发布过的版本号')]
// 定义镜像
env.ROLLBACK_IMAGE_NAME = "${DOCKER_REGISTRY_URL}/${PROJECT_NAME}/${JOB_BASE_NAME}:${RELEASE_SCOPE}"
println "${JOB_BASE_NAME} 回滚版本号为: ${env.RELEASE_SCOPE}"
println "${JOB_BASE_NAME} 镜像地址: ${env.ROLLBACK_IMAGE_NAME}"
withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
env.RETURN_VALUME = "$RETURN_VALUME"
if (env.RETURN_VALUME != ''){
sh """
${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} set image deployment ${JOB_BASE_NAME} ${JOB_BASE_NAME}=${ROLLBACK_IMAGE_NAME}
"""
} else {
sh 'echo "服务:${JOB_BASE_NAME} 不存在"'
sh 'exit 1'
}
}
}
}
}
stage('微服务部署到K8S') {
when {
anyOf {
environment name: 'ACTION', value: '拉取代码-编译-单元测试';
environment name: 'ACTION', value: '拉取代码-编译'
}
}
steps {
script {
withKubeConfig([credentialsId: env.K8S_CONFIG_SERCET, serverUrl: '']) {
def RETURN_VALUME = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get deployments -l app=${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
env.RETURN_VALUME = "$RETURN_VALUME"
if (env.RETURN_VALUME == ''){
sh '${JENKINS_HOME}/yaml/k8s_build ${JENKINS_HOME}/yaml/deployment_template-nacos-check.yaml ${JOB_BASE_NAME} --namespace ${K8S_NAMESPACE_NAME} --replicas=${K8S_POD_NUM} --env JVM_OPTIONS="${JVM_OPTIONS}" --image-name ${PROJECT_NAME}/${JOB_BASE_NAME}:${BUILD_TIMESTAMP} --docker-registry ${DOCKER_LOCAL_REGISTRY_URL} --limit_memory 3Gi'
sh '${K8S_COMMAND_PATH} apply -f ${JOB_BASE_NAME}-dep.yaml'
// HPA
//sh '${JENKINS_HOME}/yaml/k8s_build ${JENKINS_HOME}/yaml/template_autoscale.yaml ${JOB_BASE_NAME} --type hpa --memory_scale 80 --min_replica 1 --max_replica 3'
//sh '${K8S_COMMAND_PATH} apply -f ${JOB_BASE_NAME}-hpa.yaml'
// sh '''
// ${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} run ${JOB_BASE_NAME}-${VERSION_NUM} --image=${DOCKER_IMAGE_NAME} --replicas=${K8S_POD_NUM} --labels="app=${JOB_BASE_NAME},version=${VERSION_NUM}" --overrides='{"spec": {"minReadySeconds": 120, "strategy": {"type": "RollingUpdate", "rollingUpdate": {"maxSurge": 1, "maxUnavailable": 0}}}}'
// '''
} else {
sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} set image deployment ${JOB_BASE_NAME} ${JOB_BASE_NAME}=${DOCKER_DEPLOY_IMAGE_NAME}'
sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} scale --replicas=${K8S_POD_NUM} deployment ${JOB_BASE_NAME}'
}
def SVC_STATUS = sh(script: '${K8S_COMMAND_PATH} --namespace ${K8S_NAMESPACE_NAME} get service ${JOB_BASE_NAME} --ignore-not-found=true --no-headers', returnStdout: true).trim()
env.SVC_STATUS = "$SVC_STATUS"
if (env.SVC_STATUS == ''){
sh '${JENKINS_HOME}/yaml/k8s_build ${JENKINS_HOME}/yaml/service_template.yaml ${JOB_BASE_NAME} --namespace ${K8S_NAMESPACE_NAME} --type=service --port=${K8S_PORT} '
sh '${K8S_COMMAND_PATH} apply -f ${JOB_BASE_NAME}-svc.yaml'
// sh '${K8S_COMMAND_PATH} --namespace=${K8S_NAMESPACE_NAME} create service clusterip ${JOB_BASE_NAME} --tcp=${K8S_PORT1}:${K8S_PORT1} --tcp=${K8S_PORT2}:${K8S_PORT2}'
}
}
}
}
}
}
}
四、 验证功能


更多推荐




所有评论(0)