在ubuntu上使用jenkins部署.net8程序
目标
我有一台ubuntu服务器,上面运行着.net8程序,已经安装.net8,每次更新.net程序,需要做如下步骤:
- 先手动停止程序
- 复制文件到unbuntu服务器-
- 再启动服务
很繁琐,想着能不能搞个自动部署,于是就有了这篇文章。
本程序有两个部署环境,一个是测试环境,一个是生产环境,一般修改之后,部署到测试环境进行测试,测试没问题后再部署到生产环境
Jenkins安装官方文档
https://www.jenkins.io/doc/book/installing/linux/#debianubuntu
在Jenkins安装的时候,找过很多博客和各种其他网站,发现安装的有问题,后来就直接去官网,发现很方便就安装成功啦
安装JAVA
这个是为了安装Jenkin做准备的,最新的Jenkins需要java17或者java21,那直接整21吧
只需要一个命令即可安装(不需要各种下载压缩包然后解压):
sudo apt install openjdk-21-jdk
运行之后,输入java -version
验证:
安装Jenkins
下载Jenkins的war包
下载地址:https://mirrors.tuna.tsinghua.edu.cn/jenkins/war-stable/2.516.1/jenkins.war
下载之后,把war包拷贝到ubuntu服务器目录
启动Jenkins
cd到war包所在的目录,然后运行:
java -jar jenkins.war
或者后台运行:
nohup java -jar jenkins.war &
然后在浏览器输入http://localhost:8080就可以访问了,其中localhost
替换为服务器IP地址
初次进入需要进行一些设置,插件安装就选推荐的就可以了
配置Jenkins项目
由于本项目的局域网git服务器是使用用户名和密码访问的,所以提前配置好
配置访问git项目的用户名和密码
在System域这里点击全局
点击Create
后,会多一条记录
新建项目
这里选择Pipleline script from SCM
意思是Jenkinsfile从git项目那里获取,这里不用写
编写项目的Jenkinsfile
pipeline {agent anyparameters {choice(name: 'ENV', choices: ['test', 'prod'], description: '部署环境')}environment {// 项目配置PROJECT_NAME = 'Test2025'SOLUTION_FILE = 'Code/ServerTest20252025/Test20252025.sln'STARTUP_PROJECT_PATH = 'Code/Server/Test2025/Test2025.Startup'STARTUP_PROJECT_NAME = 'Test2025.Startup'// .NET版本DOTNET_VERSION = '8.0'// 构建配置BUILD_CONFIG = 'Release'}stages {stage('Initialize') {steps {echo "当前部署环境: ${params.ENV}"script {// 根据环境设置配置if (params.ENV == 'test') {env.PORT = '5001'env.PUBLISH_PATH = '/root/test2025/test2025_server/publish_test_env'} else {env.PORT = '5000'env.PUBLISH_PATH = '/root/test2025/test2025_server/publish'}echo "端口: ${env.PORT}"echo "发布路径: ${env.PUBLISH_PATH}"}}}stage('Checkout') {steps {echo '开始拉取代码...'script {// 使用Jenkins凭据进行完整的代码拉取(包括子模块)withCredentials([usernamePassword(credentialsId: 'test', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]) {// 删除现有工作空间sh 'rm -rf * .* 2>/dev/null || true'// 配置git凭据助手sh """git config --global credential.helper '!f() { echo "username=\${GIT_USERNAME}"; echo "password=\${GIT_PASSWORD}"; }; f'"""// 克隆主仓库(包含子模块)sh """git clone --recursive http://10.70.19.29:28000/production/upper-computer/Test2025.git ."""// 切换到指定分支(如果需要)sh """git checkout master"""}}echo '代码拉取完成'}}stage('Restore Dependencies') {steps {echo '开始还原NuGet包...'sh """dotnet restore "${SOLUTION_FILE}""""echo 'NuGet包还原完成'}}stage('Update Config') {steps {echo '更新配置文件...'script {if (params.ENV == 'test') {echo '测试环境:设置数据库为 test2025_test'sh """# 备份原配置文件cp "${STARTUP_PROJECT_PATH}/appsettings.json" "${STARTUP_PROJECT_PATH}/appsettings.json.backup"# 更新数据库连接字符串为测试环境sed -i 's/DATABASE=test2025;/DATABASE=test2025_test;/g' "${STARTUP_PROJECT_PATH}/appsettings.json"# 验证更新结果echo "当前数据库配置:"grep "DATABASE=" "${STARTUP_PROJECT_PATH}/appsettings.json"echo "配置文件已更新为测试环境""""} else {echo '生产环境:设置数据库为 test2025'sh """# 备份原配置文件cp "${STARTUP_PROJECT_PATH}/appsettings.json" "${STARTUP_PROJECT_PATH}/appsettings.json.backup"# 更新数据库连接字符串为生产环境sed -i 's/DATABASE=test2025_test;/DATABASE=test2025;/g' "${STARTUP_PROJECT_PATH}/appsettings.json"# 验证更新结果echo "当前数据库配置:"grep "DATABASE=" "${STARTUP_PROJECT_PATH}/appsettings.json"echo "配置文件已更新为生产环境""""}}}}stage('Build') {steps {echo '开始编译项目...'sh """dotnet build "${SOLUTION_FILE}" --configuration ${BUILD_CONFIG} --no-restore"""echo '项目编译完成'}}stage('Stop Service') {steps {echo '停止现有服务...'script {// 检查端口是否被占用def portCheck = sh(script: "netstat -tlnp 2>/dev/null | grep :${env.PORT} || ss -tlnp 2>/dev/null | grep :${env.PORT} || true", returnStdout: true).trim()if (portCheck != '') {echo "发现端口 ${env.PORT} 被占用,正在停止服务..."sh """# 使用fuser命令杀死占用端口的进程fuser -k "${env.PORT}/tcp" 2>/dev/null || true# 等待进程完全停止sleep 3# 再次检查端口是否释放if netstat -tlnp 2>/dev/null | grep :${env.PORT} || ss -tlnp 2>/dev/null | grep :${env.PORT}; thenecho "端口 ${env.PORT} 仍被占用,尝试强制杀死进程..."fuser -9 "${env.PORT}/tcp" 2>/dev/null || truesleep 2fi"""echo "端口 ${env.PORT} 的服务已停止"} else {echo "端口 ${env.PORT} 没有被占用,无需停止服务"}}}}stage('Publish') {steps {echo '开始发布项目...'sh """# 创建发布目录mkdir -p "${env.PUBLISH_PATH}"# 发布项目dotnet publish "${STARTUP_PROJECT_PATH}/${STARTUP_PROJECT_NAME}.csproj" --configuration ${BUILD_CONFIG} --output "${env.PUBLISH_PATH}" --no-build# 设置执行权限chmod +x "${env.PUBLISH_PATH}/${STARTUP_PROJECT_NAME}.dll""""echo '项目发布完成'}}stage('Start Service') {steps {echo '启动服务...'sh """cd "${env.PUBLISH_PATH}"# JENKINS_NODE_COOKIE=dontKillMe 让Jenkins不杀死进程JENKINS_NODE_COOKIE=dontKillMe nohup dotnet ${STARTUP_PROJECT_NAME}.dll --urls http://*:${env.PORT} > app.log 2>&1 &echo \$! > app.pidecho "服务已启动,PID: \$(cat app.pid)" sleep 10"""echo '服务启动完成'}}stage('Health Check') {steps {echo '检查服务健康状态...'script {def maxRetries = 10def retryCount = 0def isHealthy = falsewhile (retryCount < maxRetries && !isHealthy) {try {def response = sh(script: "curl -f http://localhost:${env.PORT}/api/Test 2>/dev/null", returnStdout: true).trim()if (response != '') {isHealthy = trueecho '服务健康检查通过'}} catch (Exception e) {echo "健康检查失败,重试 ${retryCount + 1}/${maxRetries}"}if (!isHealthy) {retryCount++sleep(5)}}if (!isHealthy) {error '服务健康检查失败,部署可能有问题'}}}}}post {always {echo '恢复配置文件...'sh """# 恢复原始配置文件if [ -f "${STARTUP_PROJECT_PATH}/appsettings.json.backup" ]; thencp "${STARTUP_PROJECT_PATH}/appsettings.json.backup" "${STARTUP_PROJECT_PATH}/appsettings.json"rm -f "${STARTUP_PROJECT_PATH}/appsettings.json.backup"echo "配置文件已恢复"fi"""echo '清理git凭据配置...'sh """git config --global --unset credential.helper || truerm -f ~/.git-credentials || true"""echo '清理工作空间...'// 注意:不要清理工作空间,因为服务进程依赖它// cleanWs()}success {echo '部署成功!'script {echo "部署时间: ${new Date().format('yyyy-MM-dd HH:mm:ss')}"echo "部署环境: ${params.ENV}"echo "部署路径: ${env.PUBLISH_PATH}"echo "服务端口: ${env.PORT}"}}failure {echo '部署失败!'script {echo "失败时间: ${new Date().format('yyyy-MM-dd HH:mm:ss')}"echo "部署环境: ${params.ENV}"// 尝试重启服务echo '尝试重启服务...'sh """if [ -f "${env.PUBLISH_PATH}/app.pid" ]; thenkill \$(cat "${env.PUBLISH_PATH}/app.pid") 2>/dev/null || truerm -f "${env.PUBLISH_PATH}/app.pid"ficd "${env.PUBLISH_PATH}"nohup dotnet ${STARTUP_PROJECT_NAME}.dll > app.log 2>&1 &echo \$! > app.pid"""}}}
}
说明:
-
本项目有两个部署环境:
prod
和test
环境 -
两个环境有如下差异:
- 发布后,生成文件到文件夹不一样,
test环境
是到publish_test_env
文件夹,prod环境
是到publish
文件夹 - 连接的数据库不一样,
test环境
连接的是test2025_test
数据库,prod环境
连接到test2025
数据库 - 监听的端口不一样,
test
环境监听5001
端口,prod
环境监听5000
端口
- 发布后,生成文件到文件夹不一样,
-
在Jenkins中使用界面Build时选择的参数:
声明:
parameters {choice(name: 'ENV', choices: ['test', 'prod'], description: '部署环境')}
使用:
params.ENV
注意:这里的name:ENV
的ENV
要和如下的对应:
提交Jenkinsfile到git仓库
编写之后的Jenkinsfile放这里:
git add Jenkinsfile
git commit -m "添加Jenkinsfile"
git push
构建
回到Jenkins的网页主界面:
这里随便选一个,然后点击Build,即可创建。