当前位置: 首页 > news >正文

在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"""}}}
} 

说明:

  • 本项目有两个部署环境:prodtest环境

  • 两个环境有如下差异:

    • 发布后,生成文件到文件夹不一样,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:ENVENV要和如下的对应:
在这里插入图片描述

提交Jenkinsfile到git仓库

编写之后的Jenkinsfile放这里:
在这里插入图片描述

git add Jenkinsfile
git commit -m "添加Jenkinsfile"
git push

构建

回到Jenkins的网页主界面:
在这里插入图片描述
在这里插入图片描述

这里随便选一个,然后点击Build,即可创建。

http://www.dtcms.com/a/317861.html

相关文章:

  • 【概念学习】早期神经网络
  • Redis 缓存三大核心问题:穿透、击穿与雪崩的深度解析
  • [AI 生成] hive 面试题
  • Document Object Model
  • 机器学习-LinearRegression
  • harbor仓库搭建(配置https)
  • MCU程序的编译与链接及格式转换
  • 防御保护防火墙简单实验报告
  • Git 乱码文件处理全流程指南:从识别到彻底清除
  • MySQL的约束条件:
  • 【Linux】调试器gdb/cgdb的使用
  • 生成式 AI 重塑自动驾驶仿真:4D 场景生成技术的突破与实践
  • vector使用模拟实现
  • 牛客AI简历筛选:破解秋招效率难题
  • 向量数据库基础入门:RAG 与向量检索基础认知构建
  • 《C语言程序设计》笔记p9
  • LLM——浅谈 LangGraph 中断式工作流:构建一个可交互的问答流程
  • 4、docker数据卷管理命令 | docker volume
  • 【关于Java的对象】
  • vue3 el-dialog自定义实现拖拽、限制视口范围增加了拖拽位置持久化的功能
  • 实战教程:从“对象文件为空“到仓库重生——修复 Git 仓库损坏全记录
  • 大数据存储域——Hive数据仓库工具
  • STM32 APP跳转后无法进入中断
  • QT----不同线程中信号发送了槽函数没反应QObject::connect: Cannot queue arguments of typeXXX
  • C++编程语言:标准库:工具类(Bjarne Stroustrup)
  • Python赋能气象与气候数据分析的生态构建与实战路径
  • Linux中systemd与systemctl管理指南
  • LlaMA_Factory实战微调VL大模型
  • 【数据分享】西藏土壤类型数据库
  • AlphaEarth模型架构梳理及借鉴哪些深度学习领域方面的思想