当前位置: 首页 > 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.lryc.cn/news/611907.html

相关文章:

  • 【网络安全】入侵检测系统 Suricata 概述 | IDS
  • DHCP 服务器与DNS服务器
  • 如何将照片从POCO手机传输到Mac电脑
  • Linux基础命令的生产常用命令及其示例简单解释
  • Mac 洪泛攻击笔记总结补充
  • Vue2中实现数据复制到 Excel
  • OceanBase DBA实战营2期--自动分区分裂学习笔记
  • 虚幻GAS底层原理解剖四 (TAG)
  • 《爬虫实战指南:轻松获取店铺详情,开启数据挖掘之旅》
  • Adobe Analytics 数据分析平台|全渠道客户行为分析与体验优化
  • 时隔六年!OpenAI 首发 GPT-OSS 120B / 20B 开源模型:性能、安全与授权细节全解
  • 【WAIC 2025】AI安全的攻防前线:合合信息AI鉴伪检测技术
  • 算法训练营DAY55 第十一章:图论part05
  • 支持向量机(SVM)算法依赖的数学知识详解
  • 非机动车识别mAP↑28%!陌讯多模态融合算法在智慧交通的实战解析
  • Unity里的对象旋转数值跳转问题的原理与解决方案
  • Linux《进程间通信(上)》
  • Android 之 Kotlin中的符号
  • Linux---第二天---基础指令
  • 基于Python的超声波OFDM数字通信链路设计与实现
  • 2024年测绘程序设计比赛--空间探索性分析(数据为2025年第三次模拟数据)
  • 基于MCP提示构建工作流程自动化的实践指南
  • ipv6学习
  • ESP32:2.搭建UDP服务器
  • Wireshark协助捕获信号波形
  • 强化应急通信生命线:遨游三防平板、卫星电话破局极端灾害救援
  • OpenWebUI通过pipeline对接dify的workflow
  • 5G随身WiFi怎么选?实测延迟/网速/续航,中兴V50适合商务,格行MT700适合短租、户外党~避坑指南+适用场景全解析
  • 5G毫米波射频前端测试:OTA暗室与波束成形性能验证
  • 中宇联5G云宽带+4G路由器:解锁企业办公高效协同与门店体验升级