.Net在Windows上使用Jenkins做CI/CD的那些事
背景
最近入职了一家新公司,公司各个方面都让我非常的满意,我也怀着紧张与兴奋的心情入职后,在第一天接到了领导给我的第一个任务——把整个项目的依赖引用重新整理并实施项目的CI/CD。
本篇的重点主要分享这次在Windows平台使用Jenkins做.Net Framework的自动化,真是所谓一波N折。
有关注过我的博客的朋友们,应该知道我之前都是在Linux上实施.Net Core,好不容易把Linux技能点起来,现在玩回Windows+.Net Framework感觉有一种回到钻木取火的年代。
在实施过程中让我感悟最多的是,dotnet core命令的便捷性,Linux运维的便捷性,下文我有很多构建脚本都是shell脚本来的,当时想着学习一次就不需要学习bat脚本了,最终还是躲不过。
在远程传送并远程执行命令我折腾得最久,开始想着用FTP与Telnet,谁知道Telnet命令没有附带账号密码参数,脚本不好写,后改成PsExec.exe,使用过程中很多奇怪的问题,后来不得不去折腾OpenSSH,说实话还是SSH好用,直接代替了FTP与PsExec。中间还遇到Jenkins使用本机账号执行的权限问题。所有“坑”的我在下面步骤尽可能给大家规避了。
引用整理
项目(包含所以依赖的项目)依赖引用丢失,主要体现以下几点:
- 官方依赖包没有使用NuGet
- 内部项目引用通过本地引用,项目没有拉取则编译失败
- NuGet包多个版本的引用
- 同属一个解决方案,但是项目分散到各个仓库
依赖没有规范化对软件工程主要带来以下几点影响:
- 缺少代码或者代码路径不一致则无法正常编译通过
- 对于新人同事不友好
- 实现CI/CD会有很大的阻力
解决方案与顺序:
- 整理组件库到统一解决方案,日后有需要拆分再按需拆分,优先保证需要依赖的在一个方案。
- 实现组件库的自动发布到私有Nuget
- 整理API项目的依赖,抛弃以前本地依赖的方式,统一使用Nuget
- 实现API项目的自动发布到IIS
工具准备
工具名称 |
下载地址 |
描述 |
---|---|---|
jdk-8u261-windows-i586.exe |
https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html |
Jenkins依赖 |
nuget.exe CLI |
https://dist.nuget.org/win-x86-commandline/latest/nuget.exe |
发布、还原.Net依赖包 |
MsBuild |
https://visualstudio.microsoft.com/zh-hans/vs/community/ |
msbuild在vs安装程序里 |
Jenkins |
https://www.jenkins.io/zh/download/ |
|
git |
https://git-scm.com/download/win |
|
OpenSSH服务端 |
https://github.com/PowerShell/Win32-OpenSSH/releases |
远程传输与远程执行命令 |
物理部署图
文章会分享跨网络环境的场景,我们使用了Gitee的企业仓储,Jenkins与Nuget.Server会放在阿里云服务器,机房会开放一台服务器与阿里云服务器进行SSH的通信。
以上是基本的信息概要,下面会进入具体的实施步骤。
NuGet.Server的部署
新建一个ASP.Net Web应用程序(.NetFramework),并选择空模板,搜索NuGet.Server选择3.4.1版本安装后,打开Web.config文件设置apiKey,然后就可以部署到47.115.111.58的IIS了。
官方部署文档可以查看 https://docs.microsoft.com/zh-cn/nuget/hosting-packages/nuget-server
安装MsBuild
在服务器47.115.111.58打开通过上面表格的链接下载VS的安装包,点开单个组件选项卡,勾选Msbuild,等待一会即可完成。
配置nuget.exe CLI
在服务器47.115.111.58根据上面表格nuget.exe CLI,下载后放到一个合适的文件目录,并在【环境变量配置】配置好路径,打开cmd并输入指令nuget help出现以下画面证明成功。
添加NuGet还原源配置项
在部署Jenkins服务器47.115.111.58,打开C:Windowssystem32configsystemprofileAppDataRoamingNuGetNuGet.Config,没有的化可以自己建一个,输入以下脚本。
如果没有配置好,接下来的应用项目可能会编译时还原包失败导致无法编译通过。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="私有NuGet服务" value="http://47.115.11.58:8008/nuget" />
<add key="Microsoft Visual Studio Offline Packages" value="C:Program Files (x86)Microsoft SDKsNuGetPackages" />
</packageSources>
</configuration>
安装OpenSSH服务端
根据上面表格的地址下载解压后,在服务器47.115.151.108以管理员启动CMD
cd C:toolsOpenSSH-Win64
#安装服务端
powershell.exe -ExecutionPolicy Bypass -File install-sshd.ps1
#启动服务
net start ssh-agent
sc config ssh-agent start= auto
net start sshd
sc config sshd start= auto
#防火墙打开22端口
#大于等于 Windows Server 2012
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
#小于 Windows Server 2012
netsh advfirewall firewall add rule name=sshd dir=in action=allow protocol=TCP localport=22
这样就可以通过administrator账号或者管理员账号在客户端用ssh登录了。
以上是各个服务器基本依赖安装。
安装Jenkins
在服务器47.115.111.58,先把jdk与git安装好后,点开Jenkins安装,选择默认路径下一步,遇到设置服务登录账户的时候,必须选择以administrator安装,不然后续会有很多执行的权限问题。
如果点击Test Credentials无法通过,可以在运行输入mmc打开策略编辑器,并在计算机配置-[windows设置]-[安全设置]-[本地策略]-[用户权限分配]-[作为服务登录],填写administrator或者您的管理员账号。
接着点击下一步等待完成后,会弹出一个localhost:8080的页面,根据他的指示初始化界面就可以了。输入完管理员账号后这个时候可以去修改workspace的地址,当时我没有修改地址的时候,因为路径有特殊字符导致无论如何都无法用msbuild编译通过。
打开C:WindowsSystem32configsystemprofileAppDataLocalJenkins.jenkinsconfig.xml,修改workspaceDir属性的值为C:/jenkins_workspace/${ITEM_FULL_NAME},尽可能给一个简单纯英文的路径。
配置Jenkins
在主界面点击【Manage Jenkins】-【Configure System】界面,定位到Shell,输入您刚安装的git路径的sh.exe,C:Program FilesGitbinsh.exe
同样界面定位到SSH Servers点击Add Server把刚刚安装OpenSSL的服务器47.115.151.108填写进去,账号密码是47.115.151.108服务器管理员的账号密码。
在主界面点击【Manage Jenkins】-【Global Tool Configuration】界面,定位到Git,输入您刚安装的git路径的git.exe,C:Program FilesGitbingit.exe
到这里Jenkins的配置基本上完成了。
自动发布组件包到NuGet
新建Freestyle project,并在【源码管理】、【构建】填入您的构建信息,构建脚本我是保存在服务器上,因为保存在服务器上更加可靠。
构建脚本shell示例
#!/bin/bash
#脚本开始执行
echo '脚本开始执行'
base_path=C:/jenkins_workspace/Librariy
nuget_url=http://47.115.111.58:8008/nuget
nuget_api_key=B82D6DDB-C6F958E8C945
nuget restore $base_path
for project_name in $base_path/*
do
project_path=$project_name
package_path=$project_path/packages
rm -rf $package_path
nuget pack $project_path -Build -IncludeReferencedProjects -Properties Configuration=Release -OutputDirectory $package_path &&
nuget push -ApiKey $nuget_api_key -Source $nuget_url $package_path/*.nupkg
if [ $? -eq 0 ]; then
echo '发布成功:'$project_name''
else
echo '发布失败:'$project_name''
fi
done
echo '脚本执行结束'
以上发布组件包到私有NuGet的步骤就实施完毕了。
自动发布Web应用到IIS
新建Freestyle project,并在【源码管理】、【构建】填入您的构建信息。[Publish Over SSH]插件跟OpenSSL能把编译好的文件发送到相应的服务器,并执行对应的集群分发脚本。
示例构建shell脚本
#!/bin/bash
echo '脚本开始执行'
base_path=C:/jenkins_workspace/API
project_path=$base_path/API.csproj
publish_path=$base_path/publish
rm -rf $publish_path
nuget restore $base_path &&
MSBuild.exe $project_path -t:"rebuild;publish;ResolveReferences;_CopyWebApplication" -p:"Configuration=Release;OutputPath=$publish_path"
if [ $? -eq 0 ]; then
echo '发布成功:'$project_name''
else
echo '发布失败:'$project_name''
fi
echo '脚本执行结束'
示例分发bat脚本
记得在局域网把相应服务器的共享文件夹启动
net use \192.168.1.2app "password" /user:"administrator"
xcopy/a/s/h/y C:appftpSFNovelAPI \192.168.1.2appAPI
结束
以上记录并分享了我在Windows实施自动化的过程与步骤,如果大家有更好的建议与提议,可以在下方评论反馈给我。
- (cljs/run-at (JSVM. :browser) "搭建刚好可用的开发环境!")
- (cljs/run-at (->JSVM :browser) "语言基础")
- 微博爬虫
- 电话域名受欢迎,微语言融资3000万
- 前端魔法堂——异常不仅仅是try/catch
- (cljs/run-at (JSVM. :all) "一起实现柯里化")
- (cljs/run-at (JSVM. :browser) "简单类型可不简单啊~")
- 前端魔法堂:解秘FOUC
- JS魔法堂:深究JS异步编程模型
- 前端魔法堂:屏蔽Backspace导致页面回退
- “表情包”火爆全球,域名emojis.com小六位易主
- 前端魔法堂:onsubmit和submit事件处理函数怎么不生效呢?
- (cljs/run-at (JSVM. :all) "Metadata就这样哦")
- (cljs/run-at (JSVM. :all) "细说函数")
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 一行代码实现display"过渡动画"原理
- 3D星空图
- shading-jdbc 4.1.1 + tk.mybatis + pagehelper 1.3.x +spring boot 2.x 使用注意事项
- 用辗转相除法求两个正整数的最大公约数
- Js根据数组相同的值生成二维数组
- 顺序消息管道《Message Pipe》v1.0.2版本发布
- Ts安装及自动编译ts文件
- python 自动化测试(1):获取验证码图片,实现自动登录
- RocketMQ学习第一步之源码构建
- python 库学习之:openpyxl
- python 学习之:读取xml配置文件
- 我的C语言入门笔记~!
- 宜信OCR技术探索之版面分析业务实践|技术沙龙直播速记
- python 学习之:将字符串转换成变量,调用该变量实例对象的方法
- iOS 登录接口封装实践