Jenkins是目前大多数中小公司使用的CI、CD工具,其中Jenkins的任务又分普通任务和流水线任务,普通任务的构建和部署在我之前的一篇文章中写过使用教程# 基于 Docker 安装 Jenkins,并配置使用 Jenkins 打包 Node 前后端服务部署到远程服务器,但其中流水线任务可实现我们更复杂的需求也更自由,不过上手难度也稍微高点。
推荐使用 Docker 来安装Jenkins,更方便后期的迁移部署等,具体安装步骤可参考
# 基于 Docker 安装 Jenkins,并配置使用 Jenkins 打包 Node 前后端服务部署到远程服务器
这里我将演示使用流水线来部署一个前端项目,其他项目也同样是这几个步骤
首先创建一个流水线任务
在流水线配置这有两种方式,第一种是直接把流水线脚本写在配置文本框这,第二种是把脚本写在项目根目录下,用Jenkinsfile
文件来写入,图下面可以看到有个流水线语法
的按钮,是可以把具体操作用可视化的方式生成脚本。
我们在文本框这写入以下脚本内容:
pipeline {
agent any
stages {
stage('Build') {
steps {
nodejs('node16') {
sh '''
if hash pnpm 2>/dev/null;
then
echo "pnpm"
else
npm i pnpm -g --registry https://registry.npmmirror.com/
fi
pnpm i
pnpm run build
'''
}
echo '构建完成'
}
}
stage('Zip') {
steps {
sh '''
tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
rm -rf ./dist/*
mv ${JOB_BASE_NAME}.tgz ./dist
'''
echo '打包完成'
}
}
stage('Deploy') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
}
Stages
: 这个字段下分了几个单独的stage
,会从上至下依次执行stage
,如果是刚才说的第一种方式,应该还会比上面多个拉取代码的阶段。具体拉取代码的语法可以用上面的流水线语法
页面可视化生成。
stage('Build')
: 代码构建阶段,这里因为是前端项目,用到了node
来构建,需要安装NodeJS
插件,然后去全局工具配置里安装一下具体的node
版本及设置下别名。
stage('Zip')
: 压缩阶段,因为我们前端代码部署只需要部署dist目录
,把这个目录tgz压缩
一下发到目标服务器。
stage('Deploy')
: 部署阶段,需要安装一个Publish Over SSH
插件,然后通过上面的流水线语法
去可视化配置部署到服务器的配置,最后把生成的脚本粘贴到这就行。
到这我们构建及部署代码到服务器的基本配置就完成了,大部分项目其实发版流程就是这几步,下面还有几种流水线进阶用法。
有时候我们会遇到多环境部署的情况,如开发坏境,生产环境等,大概就是我们通过在流水线添加一个部署坏境的参数来控制,在每次构建前选择一下部署的坏境,具体脚本如下:
pipeline {
agent any
parameters {
choice(
description: '你需要哪个机器进行部署?',
name: 'deploy_hostname',
choices: ['tencent', 'dev01', 'tencent、dev01']
)
}
stages {
stage('Build') {
steps {
nodejs('node16') {
sh '''
if hash pnpm 2>/dev/null;
then
echo "pnpm"
else
npm i pnpm -g --registry https://registry.npmmirror.com/
fi
pnpm i
pnpm run build
'''
}
echo '构建完成'
}
}
stage('Zip') {
steps {
sh '''
tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
rm -rf ./dist/*
mv ${JOB_BASE_NAME}.tgz ./dist
'''
}
}
stage('Deploy to tencent'){
when {
expression { deploy_hostname == 'tencent' }
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('Deploy to dev01'){
when {
expression { deploy_hostname == 'dev01' }
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('Deploy to tencent、dev01'){
when {
expression { deploy_hostname == 'tencent、dev01' }
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
}
可以看到除了我们之前配置的agent
和stages
还多了一个parameters
的参数配置,添加了一个deploy_hostname
的选择参数,有三个值'tencent', 'dev01', 'tencent、dev01'
在具体的stage
里面也多了when
的配置,就是根据我们选择的部署环境参数来执行相应坏境的部署流程,当when
里面的条件不满足时,流水线会跳过里面的steps
还有种情况是项目多分支的情况下,每个分支可能对应的部署坏境,或者执行条件不一样,就会用到Jenkins的多分支流水线
在新建Jenkins任务时选择多分支流水线
在分支源里配置对应的git项目地址
和认证凭据
,保存后他会自动扫描项目里面的分支,我们需要在每个分支下创建一个Jenkinsfile
文件,把我们的脚本写在这个文件里
具体的构建部署脚本可参考之前的普通流水线,如果需要WebHook自动触发的可参考下面脚本
pipeline {
agent any
triggers {
GenericTrigger (
causeString: 'Triggered',
genericVariables: [[key: 'ref', value: '$.ref']],
printContributedVariables: true,
printPostContent: true,
token: 'test01'
)
}
stages {
stage('Build') {
steps {
nodejs('node16') {
sh '''
if hash pnpm 2>/dev/null;
then
echo "pnpm"
else
npm i pnpm -g --registry https://registry.npmmirror.com/
fi
pnpm i
pnpm run build
'''
}
echo '构建完成'
}
}
stage('Zip') {
steps {
sh '''
tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
rm -rf ./dist/*
mv ${JOB_BASE_NAME}.tgz ./dist
'''
}
}
stage('Deploy to tencent'){
when {
branch 'master'
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('Deploy to dev01'){
when {
branch 'dev'
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
}
可以看到上面添加了一个triggers
配置,这个需要安装Generic Webhook Trigger
插件,然后按上面那样配置,其中的token配置可以随意改动,就是我们最终触发hooks的url最后面的参数。
配置完成后,就可以通知http://JENKINS_URL/generic-webhook-trigger/invoke?token=test01
来触发我们的hook,一般我们需要在gitlab的Webhooks进行配置触发hooks
到此Jenkins多种流水线的配置介绍就完成了,具体细节有不了解的小伙伴可在下面评论区留言。关于流水线语法每个配置的详解可参考# Jenkinsfile声明式语法详解