如果你想实现以下内容,请看此文:
1、代码一提交,就自动发布测试环境 ---jenkins触发器实现
2、可以指定版本号、分支名,拉取代码 ---git命令实现
3、部署生产之前,发一份邮件给老板,请他审核同意发布 --jenkins的邮件功能实现
4、不依赖harbor,加快镜像的io传输速度 ---scp和ssh命令实现
前提
1、部署好你的jenkins
https://www.jenkins.io/zh/doc/book/installing/
2、容器内安装好maven
3、下载好下图的红框的插件
4、配置好ssh、email插件,配置好git凭证
下载好以下插件
设置构建时输入的参数
发布模式参数
分支参数
代码提交的版本号
部署的测试机器列表参数
部署的生产机器列表参数
审核人邮件参数
其实建议审核人邮件写死,但为了快速测试,所有使用手填
构建页面展示
配置好上面这些构建参数后,你的界面应该如下所示:
流水线jenkinsfile文件
我自己写的jenkinsfile,仅供参考
def skipProd = true //是否跳过生产部署
def timeout_mins = 5 //超时时间
def input_message //提示语
def randomToken //发布秘钥
//系统个人信息
def Applier_id
def Applier_name
def Applier_mail
// 项目信息
def project_name = "test"
def version = "0.1.0"
// 系统凭证
def git_auth = "git_auth"
pipeline{
agent any
options { //调用颜色插件
ansiColor('xterm')
skipDefaultCheckout(true)
}
environment {
Applier_name = ""
Applier_mail = ""
Operator_mail = ""
input_message = ""
}
stages{
stage('构建初始化') {
parallel {
stage("拉取代码"){
steps{
withCredentials([usernamePassword(credentialsId: "${git_auth}", usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
echo "\033[32m ******开始拉取代码${branch}分支代码,revision为${commit}的代码(revision为空时拉取该分支最新代码)****** \033[0m"
sh "rm -rf zzw; mkdir zzw;cd zzw;rm -rf test;git clone https://${USERNAME}:${PASSWORD}@gitee.com/mylittlebrother/test.git;cd test;git pull origin ${branch};git reset --hard ${commit} "
}
}
}
stage("是否发送token邮件"){
steps{
wrap([$class: 'BuildUser']) {
script {
//获取当前登录用户账户、姓名、邮箱
Applier_id = "${BUILD_USER_ID}"
Applier_name = "${env.BUILD_USER}"
Applier_mail = "${env.BUILD_USER_EMAIL}"
}
}
script{
// 判断是否需要审批
if ("$Mode" == "test" || "$Mode" == "" ){
echo "\033[32m ******发布测试操作无需审批,待自动执行测试****** \033[0m"
skipProd = true
}else{
skipProd = false
echo "\033[32m ******发布生产操作需审批,待自动执行测试环境后,同时还将执行生产审批流程****** \033[0m"
randomToken = sh (script: "echo \$RANDOM" , returnStdout: true).trim()
input_message = " $Applier_name 申请发布生产"
emailext(
subject:"【请审批】${env.JOB_NAME}(#${env.BUILD_NUMBER})生产部署任务",
body:"""$input_message,随机验证码是:${randomToken}!!! <br> <a href="${BUILD_URL}input">请点击该链接登录后审批填入token发布</a><br><h3>或者将token值${randomToken}告诉${Applier_name},让其输入token</h3>""",
to:"${emails}"
)
echo "\033[32m ******申请邮件已经发送,待$adminUser 审批****** \033[0m"
}
}
}
}
}
}
stage('编译测试镜像') {
steps{
sh "source /etc/profile;java -version;mvn -v;cd zzw/test; mvn clean package -P test dockerfile:build -Dmaven.test.skip=true"
}
}
stage('上传测试镜像') {
steps{
script{
def imageName = "${project_name}-test:${version}"
sh "docker tag ${project_name}:latest ${imageName}"
try { //删除镜像
sh "docker images|grep none|awk '{print \$3 }'|xargs docker rmi"
}catch(err) {
echo "\033[31m 警告!!!该镜像可能不存在 \033[0m"
}
sh "pwd; docker save -o ${project_name}-test.tar ${imageName};ls" //生成测试镜像
echo "\033[32m ******测试机器输入是 ${testMachines}****** \033[0m"
def host = "$testMachines".split('@')
echo "\033[32m ******测试机器ip是 ${host}****** \033[0m"
for (machine in host){
if(machine!=null && machine !=''){
def ip = machine.split(':')[0]
sh " scp -r ${project_name}-test.tar root@${ip}:/opt/"
echo "\033[32m ******${ip}拷贝镜像文件${project_name}-test.tar完成****** \033[0m"
}
}
}
}
}
stage('发布测试') {
steps{
script{
def imageName = "${project_name}-test:${version}" //测试镜像
def delete_running_container = "docker rm -f \$(docker ps | grep ${project_name}-test | awk '{print \$1}')" //删除指定镜像的容器
def delete_iamge = "docker rmi -f \$(docker images | grep ${project_name}-test | awk '{print \$3}')" //删除指定镜像
def load_image = "cd /opt; docker load -i ${project_name}-test.tar " //加载指定image
def run_test = "docker run -di -p 1993:1993 ${imageName} "
try { //停止镜像
sshPublisher(publishers: [sshPublisherDesc(configName: 'node01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${delete_running_container}", execTimeout: 60000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}catch(err) {
echo "\033[31m 警告!!!该容器可能不存在 \033[0m"
}
try { //停止镜像
sshPublisher(publishers: [sshPublisherDesc(configName: 'node01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${delete_iamge}", execTimeout: 60000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}catch(err) {
echo "\033[31m 警告!!!该镜像可能不存在 \033[0m"
}
try { // 加载镜像
sshPublisher(publishers: [sshPublisherDesc(configName: 'node01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${load_image}", execTimeout: 60000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}catch(err) {
}
echo "******开始发布测试环境*****"
sshPublisher(publishers: [sshPublisherDesc(configName: 'node01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${run_test}", execTimeout: 60000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
stage("请输入生产token"){
when { expression {!skipProd}}
steps{
script{
def isAbort = false //取消按钮
timeout(timeout_mins){ //等待审批人审批,并通过timeout设置任务过期时间,防止任务永远挂起
try {
def userInput = input(
id: 'inputap', message: "$input_message", ok:"确认token", submitter:"$adminUser", parameters: [
[$class: 'StringParameterDefinition',defaultValue: "", name: 'token',description: '请输入发布的秘钥' ]
])
if (userInput == randomToken) {
skipProd = false
} else {
skipProd = true
echo "\033[31m 秘钥错误 \033[0m"
}
echo "\033[31m 当前输入秘钥为: ${userInput} \033[0m"
}catch(err) { // input false
echo "******主动拒绝*****"
skipProd = true
}
}
}
}
}
stage('编译生产镜像') {
when { expression {!skipProd} }
steps{
sh "source /etc/profile;java -version;mvn -v;cd zzw/test; mvn clean package -P prod dockerfile:build -Dmaven.test.skip=true"
}
}
stage("上传生产镜像"){
when { expression {!skipProd} }
steps{
script{
def imageName = "${project_name}-prod:${version}"
sh "docker tag ${project_name}:latest ${imageName}"
try { //删除镜像
sh "docker images|grep none|awk '{print \$3 }'|xargs docker rmi"
}catch(err) {
echo "\033[31m 警告!!!该镜像可能不存在 \033[0m"
}
sh "pwd; docker save -o ${project_name}-prod.tar ${imageName};ls" //生成生产镜像
def host = "$prodMachines".split('@')
echo "\033[32m ******测试机器ip是 ${host}****** \033[0m"
for (machine in host){
if(machine!=null && machine !=''){
def ip = machine.split(':')[0]
sh " scp -r ${project_name}-prod.tar root@${ip}:/opt/"
echo "\033[32m ******${ip}拷贝镜像文件${project_name}-prod.tar完成****** \033[0m"
}
}
}
}
}
stage("发布生产"){
when { expression {!skipProd} }
steps{
script{
def imageName = "${project_name}-prod:${version}" //测试镜像
def delete_running_container = "docker rm -f \$(docker ps | grep ${project_name}-prod | awk '{print \$1}')" //删除指定镜像的容器
def delete_iamge = "docker rmi -f \$(docker images | grep ${project_name}-prod | awk '{print \$3}')" //删除指定镜像
def load_image = "cd /opt; docker load -i ${project_name}-prod.tar " //加载指定image
def run_prod = "docker run -di -p 1999:1999 ${imageName} "
try { //停止镜像
sshPublisher(publishers: [sshPublisherDesc(configName: 'node01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${delete_running_container}", execTimeout: 60000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}catch(err) {
echo "\033[31m 警告!!!该容器可能不存在 \033[0m"
}
try { //停止镜像
sshPublisher(publishers: [sshPublisherDesc(configName: 'node01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${delete_iamge}", execTimeout: 60000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}catch(err) {
echo "\033[31m 警告!!!该镜像可能不存在 \033[0m"
}
try { // 加载镜像
sshPublisher(publishers: [sshPublisherDesc(configName: 'node01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${load_image}", execTimeout: 60000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}catch(err) {
}
echo "******开始发布生产环境*****"
sshPublisher(publishers: [sshPublisherDesc(configName: 'node01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${run_prod}", execTimeout: 60000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
}
}
执行情况