阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

基于Jenkins Pipeline的ASP.NET Core持续集成实践

174次阅读
没有评论

共计 11165 个字符,预计需要花费 28 分钟才能阅读完成。

最近在公司实践持续集成,使用到了 Jenkins 的 Pipeline 来提高团队基于 ASP.NET Core API 服务的集成与部署效率,因此这里总结一下。

一、关于持续集成与 Jenkins Pipeline

1.1 持续集成相关概念

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous integration,简称 CI)。

持续集成指的是,频繁地 (一天多次) 将代码集成到主干

它的好处主要有两个:

(1)快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。

(2)防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。

持续集成的目的,就是 让产品可以快速迭代,同时还能保持高质量

Martin Fowler 说:“持续集成并不能消除 Bug,而是让它们非常容易发现和改正。”

与持续集成相关的,还有持续交付和持续部署。

持续交付指的是:频繁地将软件的新版本,交付给质量团队或者用户,以供评审 。如果评审通过,代码就进入生产阶段。它强调的是, 不管怎么更新,软件是随时随地可以交付的

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

持续部署是持续交付的下一步,指的是 代码通过评审以后,自动部署到生产环境 。它强调的是 代码在任何时刻都是可部署的,可以进入生产阶段

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

1.2 Jenkins Pipeline

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

Jenkins 是一款流行的开源持续集成(CI)与持续部署(CD)工具,广泛用于项目开发,具有自动化构建、测试和部署等功能。有关 Jenkins 的安装,可以参考我的这一篇文章进行安装 https://www.linuxidc.com/Linux/2019-09/160627.htm。

相信很多童鞋都已经在使用 Jenkins 或者计划使用 Jenkins 来代替传统的人工发布流程了,因此我们创建了很多自由风格(Free Style)的构建任务用于多个 Job,而我们经常会听到说流水线任务,那么流水线是什么呢?

流水线 Pipeline 是一套运行于 Jenkins 上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的 复杂流程编排与可视化。下图是一个 Jenkins Pipeline 的实例效果:

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

Pipeline:Build => Test => Deploy

这里涉及到 Pipeline 中的几个重要概念,需要了解一下:

  • Stage: 阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作。注意,Stage 是一个逻辑分组的概念,可以跨多个 Node。如上图所示,Build,Test 和 Deploy 就是 Stage,代表了三个不同的阶段:编译、测试和部署。
  • Node: 节点,一个 Node 就是一个 Jenkins 节点,或者是 Master,或者是 Slave,是执行 Step 的具体运行期环境。
  • Step: 步骤,Step 是最基本的操作单元,小到创建一个目录,大到构建一个 Docker 镜像,由各类 Jenkins Plugin 提供。

二、准备 ASP.NET Core Docker 环境

2.1 安装 Docker 环境

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

可以参考我的这一篇《.NET Core 微服务之 ASP.NET Core on Docker》来安装和配置 Docker 环境,建议在 Linux 环境下配置。

2.2 安装 SFTP 服务

在 Linux 下,SSH 服务默认会安装,而在 Windows Server 下,需要单独安装,可以借助 FreeSSHD 这个免费工具来实现。由于我的物理机都是 Windows Server,物理机上的 VM 是 Linux(Docker 运行环境),所以需要给物理机配置 FreeSSHD,用来实现从 CI 服务器发布 Release 到物理服务器中的 VM。

至于如何安装配置 FreeSSHD,可以参考这一篇《freeSSHD 在 windows 环境下搭建 SFTP 服务器》。

三、配置 Jenkins Pipeline 流水线任务

3.1 总体目标

(1)持续集成:实现编译 + 单元测试的自动运行

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

这里我要实现的目标是:当有人 push 代码到 git server 中(这里我使用的 git server 是 Gogs,需要给 Gogs 设置一个 Webhook,如下图所示,需要注意的是设置的密钥文本要和在 Pipeline 中填写的一致,否则 Jenkins 无法正确接收 Web 钩子),git server 会触发一个 webhook 发送一个 post 的请求给 CI server,CI server 会触发 Pipeline 任务的构建,一路 pull 代码 + 编译 + 单元测试。

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

(2)持续发布:实现编译 + 发布到具体的测试环境

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

由于在开发阶段,我不需要每次 Push 都进行发布,因此我这里设置的是手动在 Jenkins 中触发发布任务来实现自动化发布。

3.2 全局设置

首先,肯定是 Jenkins 的插件安装了。

(1)Generic WebHook Trigger => 触发 WebHook 必备

(2)Gogs Plugin => 因为我使用的 Git Server 是 Gogs 搭建的

(3)MSBuild Plugin => 进行 sln、csproj 项目文件的编译

(4)MSTest & xUnit => 进行基于 MSTest 或基于 xUnit 的单元测试

(5)Nuget Plugin => 拉取 Nuget 包必备

(6)Pipeline => 实现 Pipeline 任务必备,建议将 Pipeline 相关插件都安装上

(7)Powershell Plugin => 如果你的 CI 服务器是基于 Windows 的,那么安装一下 Powershell 插件来执行命令吧

(8)Publish Over SSH => 远程发布 Release 必备

(9)WallDisplay => 电视投屏构建任务列表必备

其次,为了提示邮件,也要 Email 插件(Email Extension)的支持,并进行以下配置:

(1)第一处:Jenkins Location

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

(2)第二处:Email 扩展插件全局变量设置

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

这里主要是需要设置 Subject 和 Content,就可以在各个 Pipeline 中使用了。因此,这里贴出我的 Default Content 内容:

<!DOCTYPE html>  
<html>  
<head>  
<meta charset="UTF-8">  
<title>${ENV, var="JOB_NAME"}- 第 ${BUILD_NUMBER}次构建日志</title>  
</head>  
  
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"  
    offset="0">  
    <table width="95%" cellpadding="0" cellspacing="0"  style="font-size: 11pt; font-family: Microsoft YaHei, Tahoma, Arial, Helvetica">  
        <tr>  
            <td>各位同事,大家好,以下为 ${PROJECT_NAME} 构建任务信息</td>  
        </tr>  
        <tr>  
            <td><br />  
            <b style="font-weight:bold; color:#66cc00">构建信息</b>  
            <hr size="2" width="100%" align="center" /></td>  
        </tr>  
        <tr>  
            <td>  
                <ul>  
                    <li>任务名称:${PROJECT_NAME}</li>  
                    <li>构建编号:第 ${BUILD_NUMBER}次构建</li>  
                    <li>触发原因:${CAUSE}</li>  
                    <li>构建状态:<span style="font-weight:bold; color:#FF0000">${BUILD_STATUS}</span></li>  
                    <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>  
                    <li>构建  Url:<a href="${BUILD_URL}">${BUILD_URL}</a></li>  
                    <li>工作目录:<a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>  
                    <li>项目  Url:<a href="${PROJECT_URL}">${PROJECT_URL}</a></li>  
                </ul>  
            </td>  
        </tr>  
    </table>  
</body>  
</html>

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

为了能够发给更多的人,建议勾选以上两个选项。

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

这里是 Email 通知必填的 SMTP 服务器配置。

最后,是 SSH 服务器的声明,指定可以进行 SSH 发布的服务器有哪些,IP 又是多少:

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

3.3 新增 Pipeline 脚本

(1)持续集成 Pipeline

首先,填写 Webhook 的密钥文本:

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

其次,Build Triggers 的时机选择“Build when a change is pushed to Gogs”,即有人 push 代码到仓库就触发。当然,这里需要提前在 Gogs 设置 Webhook。

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

其次,编写 Pipeline 脚本,各个 Stage 写清楚职责:

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

具体的 Pipeline 脚本在下边:

 

pipeline{
    agent any
    stages {stage('XDP Core Services Checkout') {
            steps{checkout([$class: 'GitSCM', branches: [[name: '*/dev-xds']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/EDC.ITC.XDP.Core/EDC.XDP.Core.git']]])
             echo 'Core Services Checkout Done' 
            }
        }
        stage('XDP Core Services Build') {
            steps{bat  '''cd "D:\\Jenkins\\workspace\\XDS.Dev.CI.Pipeline\\src\\services\\EDC.XDP.Core\\"
              dotnet build EDC.XDP.Core-All.sln'''echo'Core Services Build Done' 
            }
        }
        stage('Core Delivery Service Unit Test') {
            steps{bat  '''cd "D:\\Jenkins\\workspace\\XDS.Dev.CI.Pipeline\\src\\services\\EDC.XDP.Core\\Services\\EDC.XDP.Core.Delivery.UnitTest"
                dotnet test -v n --no-build EDC.XDP.Core.Delivery.UnitTest.csproj'''echo'Core Delivery Service Unit Test Done'  
            }
        }
        stage('XDS Delivery Service Checkout') {
            steps{checkout([$class: 'GitSCM', branches: [[name: '*/dev-service']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/EDC.ITC.XDP.XDS/EDC.XDP.XDS.git']]])
             echo 'Core Delivery Service Checkout Done' 
            }
        }
        stage('XDS Delivery Service Build') {
            steps{bat  '''cd "D:\\Jenkins\\workspace\\XDS.Dev.CI.Pipeline\\src\\services\\EDC.XDP.XDS"
               dotnet build EDC.XDP.XDS.sln'''echo'XDS Service Build Done' 
            }
        }
        stage('XDS Delivery Service Unit Test') {
            steps{bat  '''cd "D:\\Jenkins\\workspace\\XDS.Dev.CI.Pipeline\\src\\services\\EDC.XDP.XDS\\EDC.XDP.XDS.Delivery.UnitTest"
                dotnet test -v n --no-build EDC.XDP.XDS.Delivery.UnitTest.csproj'''echo'XDS Service Unit Test Done'  
            }
        } 
    }
    post{
        failure {
            emailext (subject: '${DEFAULT_SUBJECT}',
                body: '${DEFAULT_CONTENT}',
                to: "edisonchou@qq.com,xxxxx@qq.com")
        }
    }
}

(2)持续发布 Pipeline

持续发布 Pipeline 与持续集成 Pipeline 类似,只是在脚本处有所不同:

pipeline{
    agent any
    stages {stage('Core Delivery Service Checkout') {
            steps{checkout([$class: 'GitSCM', branches: [[name: '*/dev-xds']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/EDC.ITC.XDP.Core/EDC.XDP.Core.git']]])
             echo 'Core Delivery Service Dev Branch Checkout Done' 
            }
        }
        stage('Core Delivery Service Build & Publish') {
            steps{bat  '''cd "D:\\Jenkins\\workspace\\XDS.API.Dev.CD.Pipeline\\src\\services\\EDC.XDP.Core"
               dotnet build EDC.XDP.Core-DataServices.sln
               dotnet publish "%WORKSPACE%\\src\\services\\EDC.XDP.Core\\Services\\EDC.XDP.Core.Delivery.API\\EDC.XDP.Core.Delivery.API.csproj" -o "%WORKSPACE%\\EDC.XDP.Core.Delivery.API/publish" --framework netcoreapp2.1
               '''echo'Core Delivery Service Build & Publish Done'
            }
        }
        stage('Core Delivery Service Deploy To 190 Server') {
            steps{sshPublisher(publishers: [sshPublisherDesc(configName: 'XDP-DEV-Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand:'''docker stop xdp_core_deliveryservice; docker rm xdp_core_deliveryservice; docker run --ulimit core=0 --restart=always -v /etc/localtime:/etc/localtime -d -e ASPNETCORE_ENVIRONMENT=dev --privileged=true --name=xdp_core_deliveryservice -p 8010:80 -v /XiLife/publish/EDC.XDP.Core.Delivery.API/:/app -w /app xdp_service_runtime:latest  dotnet EDC.XDP.Core.Delivery.API.dll''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator:'[,]+', remoteDirectory:'EDC.XDP.Core.Delivery.API/', remoteDirectorySDF: false, removePrefix:'EDC.XDP.Core.Delivery.API/publish/', sourceFiles:'EDC.XDP.Core.Delivery.API/publish/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo 'Delivery Service Deploy To 190 Done'    
            }
        }
        stage('Core Delivery Service Deploy To 175 Server') {
            steps{sshPublisher(publishers: [sshPublisherDesc(configName: 'XDP-DEV-MT-Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand:'''docker stop xdp_core_deliveryservice; docker rm xdp_core_deliveryservice; docker run --ulimit core=0 --restart=always -v /etc/localtime:/etc/localtime -d -e ASPNETCORE_ENVIRONMENT=devmt --privileged=true --name=xdp_core_deliveryservice -p 8010:80 -v /XiLife/publish/EDC.XDP.Core.Delivery.API/:/app -w /app xdp_service_runtime:latest  dotnet EDC.XDP.Core.Delivery.API.dll''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator:'[,]+', remoteDirectory:'EDC.XDP.Core.Delivery.API/', remoteDirectorySDF: false, removePrefix:'EDC.XDP.Core.Delivery.API/publish/', sourceFiles:'EDC.XDP.Core.Delivery.API/publish/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo 'Delivery Service Deploy To 175 Done'    
            }
        }
        stage('XDS Delivery Service Checkout') {
            steps{checkout([$class: 'GitSCM', branches: [[name: '*/dev-service']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/EDC.ITC.XDP.XDS/EDC.XDP.XDS.git']]])
             echo 'XDS Delivery Service Checkout Done' 
            }
        }
        stage('XDS Delivery Service Build & Publish') {
            steps{bat  '''cd "D:\\Jenkins\\workspace\\XDS.API.Dev.CD.Pipeline\\src\\services\\EDC.XDP.XDS"
               dotnet build EDC.XDP.XDS.sln
               dotnet publish "%WORKSPACE%\\src\\services\\EDC.XDP.XDS\\EDC.XDP.XDS.Delivery.API\\EDC.XDP.XDS.Delivery.API.csproj" -o "%WORKSPACE%\\EDC.XDP.XDS.Delivery.API/publish" --framework netcoreapp2.1
               '''echo'XDS Delivery Service Build & Publish Done' 
            }
        }
        stage('XDS Delivery Service Deploy To 190 Server') {
            steps{sshPublisher(publishers: [sshPublisherDesc(configName: 'XDP-DEV-Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand:'''docker stop xdp_xds_delivery_service;docker rm xdp_xds_delivery_service; docker run --ulimit core=0 --restart=always -v /etc/localtime:/etc/localtime -d -e ASPNETCORE_ENVIRONMENT=dev --privileged=true --name=xdp_xds_delivery_service -p 9020:80 -v /XiLife/publish/EDC.XDP.XDS.Delivery.API/:/app -w /app xdp_service_runtime:latest  dotnet EDC.XDP.XDS.Delivery.API.dll''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator:'[,]+', remoteDirectory:'EDC.XDP.XDS.Delivery.API/', remoteDirectorySDF: false, removePrefix:'EDC.XDP.XDS.Delivery.API/publish/', sourceFiles:'EDC.XDP.XDS.Delivery.API/publish/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo 'XDS Delivery Service Deploy to 190 Done'    
            }
        }
        stage('XDS Delivery Service Deploy To 175 Server') {
            steps{sshPublisher(publishers: [sshPublisherDesc(configName: 'XDP-DEV-MT-Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand:'''docker stop xdp_xds_delivery_service;docker rm xdp_xds_delivery_service; docker run --ulimit core=0 --restart=always -v /etc/localtime:/etc/localtime -d -e ASPNETCORE_ENVIRONMENT=devmt --privileged=true --name=xdp_xds_delivery_service -p 9020:80 -v /XiLife/publish/EDC.XDP.XDS.Delivery.API/:/app -w /app xdp_service_runtime:latest  dotnet EDC.XDP.XDS.Delivery.API.dll''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator:'[,]+', remoteDirectory:'EDC.XDP.XDS.Delivery.API/', remoteDirectorySDF: false, removePrefix:'EDC.XDP.XDS.Delivery.API/publish/', sourceFiles:'EDC.XDP.XDS.Delivery.API/publish/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            echo 'XDS Delivery Service Deploy to 175 Done'    
            }
        }
    }
}

这里由于我的测试环境分为两个,一个是开发人员联调环境 190,另一个是集成测试环境 175,统一在一个 Pipeline 任务中进行发布。

对于 Master 分支,我们还可以将 Web 系统的发布也集成到同一个 Pipeline 任务中,实现一个一条龙的发布流水线任务,由于各个 Web 系统的实现技术不一样,这里就不再贴脚本了。

四、效果演示

(1)持续集成示例

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

(2)持续发布示例

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

(3)构建失败告警

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

(4)构建大屏显示

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

再来一张投屏到工作区域电视屏幕中的效果,大家抬头就可以看到构建结果,是绿了还是红了?当然,我们都喜欢“绿”的,呼呼。

基于 Jenkins Pipeline 的 ASP.NET Core 持续集成实践

五、小结

借助持续集成和持续发布,我们开发人员可以节省很多质量保证和发布部署的时间,从而减少很多因为人为 QA 和 Deploy 造成的失误影响,从另一个层面上,它也可以使我们避免 996(好吧,虽然关联有点牵强)。后续,我还会探索 K8S,到时候希望能够分享一个 ASP.NET Core on K8S 的系列文章,敬请期待。

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-21发表,共计11165字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中