
这次我们来看一个 .NET 项目从开发到上线的完整流程。项目标题是“NET10 API网站从发布到部署Ubuntu服务器三”这清晰地指向了三个核心环节使用 .NET 10 开发一个 API 网站、将其发布为可部署的包以及最终部署到 Ubuntu 服务器上。对于 .NET 开发者而言尤其是习惯了 Windows 环境的开发者将应用部署到 Linux 服务器是一个必须掌握的技能点。这篇文章的重点不是探讨复杂的架构设计而是提供一套清晰、可落地的操作指南让你能快速完成从代码到服务的跨越。本文将围绕一个假设的 .NET 10 Web API 项目展开带你走通全流程。我们会重点关注几个实用问题.NET 10 在 Linux 下的运行时要求是什么如何将项目发布为适合 Linux 部署的包在 Ubuntu 服务器上需要安装哪些依赖如何配置 Nginx 反向代理和守护进程以及如何验证 API 服务是否正常运行。整个过程不涉及复杂的容器化Docker而是采用更直接、更易于理解的传统部署方式适合初次进行 Linux 部署的开发者。如果你关心如何将自己的 .NET Core/ .NET 5 API 项目部署到生产环境的 Ubuntu 服务器并希望了解服务管理、日志查看和简单运维那么这篇文章可以直接跟着操作。1. 核心能力速览在开始具体步骤前我们先通过下表快速了解本次部署任务的核心要素和所需条件。能力项说明与要求项目类型.NET 10 Web API 应用程序部署目标Ubuntu Server 22.04 LTS 或 24.04 LTS运行时.NET 10 Runtime 或 SDKLinux x64Web服务器Kestrel (内置) Nginx (反向代理)进程管理systemd (服务守护)部署方式框架依赖发布 手动部署关键验证本地运行、服务器运行、反向代理访问、API接口调用适合场景个人项目、中小型生产环境、需要掌控部署细节的团队2. 适用场景与使用边界这个部署流程主要服务于特定的开发者和项目需求。适合谁.NET 后端开发者希望将个人项目或公司项目部署到性价比更高的 Linux 服务器。全栈开发者使用 .NET 编写 API前端独立部署需要稳定的后端服务。运维初学者想了解 .NET 应用在 Linux 下的基本运维管理。学生或学习者在云服务器上搭建自己的项目作品集。能解决什么问题环境一致性通过明确的步骤确保开发、构建、部署环境一致减少“在我机器上能跑”的问题。服务化部署将应用配置为系统服务实现开机自启、自动重启、集中日志管理。资源优化利用 Nginx 处理静态文件、负载均衡如需和 SSL 终结提升 Kestrel 处理动态请求的效率。流程标准化形成一套可重复的发布-部署脚本提高后续迭代部署的效率。不适合什么场景超大规模微服务集群本文的 systemd Nginx 方式适合单体或少数几个服务。大规模集群建议采用 Docker Kubernetes。需要极致弹性伸缩云原生 PaaS 服务如 Azure App Service Linux或容器编排平台能提供更好的自动伸缩能力。完全无服务器架构如果业务逻辑适合直接使用 Azure Functions 或 AWS Lambda 等 Serverless 服务可能更简单。安全与合规边界防火墙务必在服务器防火墙中仅开放必要的端口如 80, 443, 22禁止随意暴露 Kestrel 的监听端口如 5000到公网。权限最小化运行 .NET 应用的系统服务账户应具有最小必要权限避免使用 root 用户直接运行应用。依赖安全定期更新 .NET 运行时、系统包以及项目依赖的 NuGet 包以修复安全漏洞。敏感信息连接字符串、API密钥等敏感配置绝不能硬编码在代码或配置文件中。应使用环境变量、密钥管理服务或安全的配置提供程序。3. 环境准备与前置条件部署工作始于本地开发环境终于远程服务器。请确保以下环境就绪。本地开发环境操作系统Windows 10/11, macOS 或 Linux。本文以 Windows 为例但步骤通用。.NET SDK确保已安装 .NET 10 SDK。在终端运行dotnet --version检查应输出10.x.x。代码编辑器Visual Studio 2022、Visual Studio Code 或 Rider。待部署项目一个可正常运行的 .NET 10 Web API 项目。你可以使用模板创建dotnet new webapi -n MyApiService。目标服务器环境操作系统Ubuntu Server 22.04 LTS 或 24.04 LTS。这是长期支持版本社区支持完善。服务器访问拥有一个云服务器如阿里云、腾讯云、AWS EC2或物理服务器并通过 SSH 可以访问。网络配置服务器已配置公网 IP安全组/防火墙规则允许 SSH22端口、HTTP80端口、HTTPS443端口入站流量。基础工具服务器需能连接互联网以下载安装包。4. 安装部署与启动方式部署的核心是将本地构建的产物传输到服务器并在服务器上配置运行环境和服务。我们分三步走发布、传输、配置。4.1 第一步在本地发布项目我们不采用“独立”发布而是使用“框架依赖”发布这样发布包更小但要求服务器安装对应的运行时。打开终端导航到你的 API 项目根目录包含.csproj文件的目录。执行发布命令。以下命令将项目发布到./publish-linux目录并指定运行时为 Linux x64。dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish-linux-c Release使用 Release 配置进行构建性能更优。-r linux-x64指定目标运行时为 Linux 64位。--self-contained false这是关键表示生成“框架依赖”的部署包。发布包内不包含 .NET 运行时体积更小。-o ./publish-linux指定输出目录。验证发布包。完成后publish-linux目录下应包含你的应用 DLL如MyApiService.dll、appsettings.json、web.config以及所有依赖的 DLL。注意web.config是给 IIS 用的在 Linux 下由 Kestrel 直接运行此文件可忽略或删除。4.2 第二步将发布包传输到 Ubuntu 服务器使用scp命令或 SFTP 工具如 WinSCP、FileZilla将整个publish-linux目录上传到服务器。这里以scp为例。假设你的服务器 IP 是123.123.123.123你打算将应用放在/var目录下# 在本地终端执行 scp -r ./publish-linux username123.123.123.123:/var/myapp输入服务器密码后文件开始传输。完成后通过 SSH 登录服务器检查文件是否存在。ssh username123.123.123.123 ls -la /var/myapp4.3 第三步在 Ubuntu 服务器上安装 .NET 运行时由于我们发布的是“框架依赖”包服务器必须安装对应的 .NET 运行时。更新包列表并安装依赖sudo apt update sudo apt install -y wget添加 Microsoft 包仓库# 下载 Microsoft 包签名密钥 wget https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb # 安装密钥包 sudo dpkg -i packages-microsoft-prod.deb # 删除下载的deb文件 rm packages-microsoft-prod.deb安装 .NET 10 运行时sudo apt update sudo apt install -y dotnet-runtime-10.0验证安装dotnet --list-runtimes你应该能看到类似Microsoft.NETCore.App 10.0.x [/usr/share/dotnet/shared/Microsoft.NETCore.App]的输出。4.4 第四步配置 systemd 服务守护进程使用 systemd 来管理应用可以实现开机自启、崩溃重启、日志统一收集。创建服务文件sudo nano /etc/systemd/system/myapp.service编辑服务配置将以下内容粘贴进去根据你的实际情况修改WorkingDirectory、ExecStart、User和Group。[Unit] DescriptionMy .NET 10 API Application Afternetwork.target [Service] Typeexec # 应用的工作目录即你上传的publish目录 WorkingDirectory/var/myapp # 启动命令指向你的主 DLL 文件 ExecStart/usr/bin/dotnet /var/myapp/MyApiService.dll # 如果应用需要绑定特定端口或URL可以在ExecStart后添加参数例如 # ExecStart/usr/bin/dotnet /var/myapp/MyApiService.dll --urls http://localhost:5000 Restartalways RestartSec10 KillSignalSIGINT SyslogIdentifiermyapp # 建议使用一个非root用户来运行更安全 Userwww-data Groupwww-data # 环境变量可以在这里设置例如数据库连接字符串 # EnvironmentASPNETCORE_ENVIRONMENTProduction # EnvironmentDOTNET_PRINT_TELEMETRY_MESSAGEfalse [Install] WantedBymulti-user.target关键参数解释WorkingDirectory必须设置为你的应用目录这样应用才能正确读取该目录下的appsettings.json等文件。ExecStart启动命令。/usr/bin/dotnet是 .NET 运行时的路径后面跟着你的主程序集路径。User/Group使用www-data用户运行是常见做法该用户通常也被 Nginx 使用。你也可以创建一个专用用户。Environment可以在这里设置生产环境变量覆盖appsettings.json中的配置。保存并退出在 nano 编辑器中按CtrlX然后按Y再按Enter。重新加载 systemd 配置sudo systemctl daemon-reload启动服务并设置开机自启sudo systemctl start myapp sudo systemctl enable myapp检查服务状态sudo systemctl status myapp如果状态显示为active (running)并且下面没有红色的错误日志说明你的 .NET 应用已经在后台运行了。默认情况下Kestrel 会监听http://localhost:5000。4.5 第五步配置 Nginx 反向代理我们不直接对外暴露 Kestrel 的 5000 端口而是用 Nginx 作为反向代理处理外部请求并转发给 Kestrel。这能提供更好的性能、安全性和静态文件处理能力。安装 Nginxsudo apt install -y nginx创建 Nginx 站点配置文件sudo nano /etc/nginx/sites-available/myapp编辑配置文件以下是一个基本的反向代理配置。server { listen 80; # 将 your_domain_or_ip 替换为你的服务器域名或IP地址 server_name your_domain_or_ip; location / { # 将请求代理到运行在 localhost:5000 的 Kestrel proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 可选处理静态文件性能更好 # location /wwwroot/ { # root /var/myapp; # expires 1y; # add_header Cache-Control public, immutable; # } }启用站点并测试配置# 创建符号链接到 sites-enabled sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ # 测试 Nginx 配置语法是否正确 sudo nginx -t如果输出syntax is ok和test is successful则说明配置正确。重启 Nginx 以应用更改sudo systemctl restart nginx5. 功能测试与效果验证部署完成后必须进行多层次的测试来确保服务完全正常。5.1 测试一验证 Kestrel 服务本身首先确认 systemd 服务运行正常并且 Kestrel 在本地正确监听。检查服务状态与日志sudo systemctl status myapp sudo journalctl -u myapp -f --no-pager观察日志看是否有应用启动成功的消息例如Now listening on: http://localhost:5000。在服务器内部测试 API# 使用 curl 测试本地 5000 端口 curl http://localhost:5000/weatherforecast如果 API 设计有默认端点如 WeatherForecast此命令应返回 JSON 数据。如果返回Connection refused说明 Kestrel 服务未成功启动需要检查上述日志。5.2 测试二验证 Nginx 反向代理确认 Nginx 能够将外部请求正确转发给 Kestrel。测试 Nginx 配置之前已做sudo nginx -t从服务器内部测试 Nginx 转发的 80 端口curl http://localhost/weatherforecast这次请求的是 80 端口由 Nginx 处理。如果返回与上一步相同的 JSON 数据说明反向代理配置成功。5.3 测试三从外部网络访问这是最终验证确保公网用户可以访问你的服务。在本地浏览器或使用 Postman/curl访问你的服务器 IP 或域名。http://your_server_ip/weatherforecast预期结果成功收到 API 返回的 JSON 数据。判断成功HTTP 状态码为 200且返回内容符合预期。常见失败原因服务器防火墙/安全组未开放 80 端口检查云服务商控制台的安全组规则。Nginx 配置错误检查/var/log/nginx/error.log日志。域名解析问题如果使用域名确保 DNS 已正确解析到服务器 IP。6. 接口 API 与批量任务对于 API 服务部署后的管理和测试至关重要。6.1 API 接口管理与测试Swagger/OpenAPI 支持如果你的项目启用了 Swagger在生产环境可能需要禁用它。可以通过环境变量或修改Program.cs中的条件编译来关闭。在myapp.service文件中添加EnvironmentASPNETCORE_ENVIRONMENTProduction在代码中if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); }使用 Postman 或 curl 进行自动化测试创建测试集合对关键 API 端点进行健康检查、功能测试和压力测试。6.2 处理后台任务或批量作业如果你的 API 涉及长时间运行的后台任务或定时批量处理如发送邮件、处理队列有几种处理方式IHostedService在 .NET 中实现IHostedService接口创建后台服务。它会在应用启动时开始运行并随应用停止。注意如果任务异常导致进程崩溃整个 Web 应用都会重启。Worker Service 项目对于独立的后台任务可以创建一个 Worker Service 项目并同样使用 systemd 部署为一个独立的服务。这样与 Web API 解耦互不影响。使用消息队列将耗时任务放入消息队列如 RabbitMQ、Azure Service Bus由单独的工作者服务消费处理。这是更健壮、可扩展的方式。对于简单的批量脚本可以编写一个 .NET 控制台应用发布后通过cron定时任务来调用。# 编辑 cron 任务 crontab -e # 添加一行例如每天凌晨2点运行 0 2 * * * /usr/bin/dotnet /path/to/your/batchjob/BatchJob.dll /var/log/myapp/batch.log 217. 资源占用与性能观察部署后需要监控应用的健康状况和资源使用情况。查看进程资源占用# 查看 myapp 服务的进程ID和资源使用 sudo systemctl status myapp | grep PID top -p PID # 或者使用 htop (需安装) htop重点关注%CPU、%MEM、VIRT虚拟内存和RES常驻内存。监控 .NET 应用性能日志应用日志通过 systemd 的 journalctl 管理。使用sudo journalctl -u myapp -f实时跟踪。内置指标.NET 8/10 提供了丰富的内置指标可以通过dotnet-counters工具监控。# 安装诊断工具 dotnet tool install --global dotnet-counters # 监控你的应用进程 dotnet-counters monitor -n MyApiService --counters System.Runtime,Microsoft.AspNetCore.HostingNginx 访问日志与错误日志# 实时查看访问日志 sudo tail -f /var/log/nginx/access.log # 实时查看错误日志 sudo tail -f /var/log/nginx/error.log访问日志可以帮助你分析请求量、响应状态、客户端IP等。错误日志是排查 502 Bad Gateway 等问题的重要依据。性能调优提示Kestrel 并发连接可以在appsettings.Production.json或代码中配置Kestrel.Limits。Nginx 缓冲区对于 API 服务可以适当调整proxy_buffer_size和proxy_buffers配置。数据库连接池确保数据库连接字符串中设置了合理的Poolingtrue;Max Pool Size...。8. 常见问题与排查方法部署过程中难免会遇到问题下表列出了一些典型问题及排查思路。问题现象可能原因排查方式解决方案sudo systemctl status myapp显示failed1. 应用启动参数错误2. 依赖缺失3. 端口被占用4. 文件权限不足sudo journalctl -u myapp -n 50 --no-pager查看详细错误日志。根据日志修正检查 DLL 路径、运行时版本、修改端口、更改文件所有者 (sudo chown -R www-data:www-data /var/myapp)访问http://服务器IP返回502 Bad Gateway1. Kestrel 服务未运行2. Nginx 配置中proxy_pass地址错误3. Kestrel 监听地址不是localhost:50001.systemctl status myapp2.curl http://localhost:50003. 检查应用日志确认监听地址1. 启动服务2. 修正 Nginx 配置3. 在服务文件ExecStart后明确指定--urls http://localhost:5000访问 API 超时或无响应1. 防火墙/安全组阻止2. 应用内部异常卡死3. 服务器负载过高1.sudo ufw status(如果用了UFW)2. 查看应用日志和服务器资源 (top)3. 从服务器内部curl测试1. 开放端口2. 重启服务检查代码逻辑3. 优化代码或升级服务器配置应用启动成功但无法连接数据库1. 连接字符串错误2. 数据库服务器网络不通3. 数据库用户权限不足1. 检查appsettings.json或环境变量2. 从服务器telnet db_host db_port3. 查看数据库日志1. 修正连接字符串2. 配置数据库服务器的防火墙规则3. 授予足够权限更新应用后服务未使用新版本1. 文件传输不完整2. 未重启 systemd 服务1. 检查服务器上文件修改时间2.sudo systemctl restart myapp1. 重新传输文件2. 执行重启命令并systemctl status确认日志文件增长过快1. 日志级别设置过低如 Information2. 存在循环日志记录错误1. 检查appsettings.Production.json中的LogLevel2. 分析日志内容1. 在生产环境将日志级别设为Warning或Error2. 修复导致循环报错的代码9. 最佳实践与使用建议遵循以下建议可以让你的 .NET API 在 Ubuntu 服务器上运行得更稳定、更安全。使用非 root 用户运行永远不要用 root 用户直接运行应用。使用www-data或创建一个专用用户如myappuser并确保该用户对应用目录有读和执行权限对日志目录有写权限。sudo useradd -r -s /bin/false myappuser sudo chown -R myappuser:myappuser /var/myapp # 然后在 myapp.service 中修改 User 和 Group分离配置与环境将数据库连接字符串、API 密钥等敏感信息从appsettings.json移到环境变量、Azure Key Vault 或 HashiCorp Vault 中。在服务文件中使用Environment行来设置。设置合理的日志策略生产环境避免使用Console日志提供程序因为它会丢失。使用journaldsystemd 集成或文件日志并设置日志轮转防止磁盘被撑满。对于文件日志可以在appsettings.Production.json中配置Serilog或NLog等第三方库。对于 systemd日志自动由 journald 管理可使用journalctl查询和导出。实现健康检查端点在 API 中添加一个健康检查端点如/health返回应用和依赖如数据库的健康状态。这便于监控系统如 Prometheus或负载均衡器探测。准备部署脚本将上述步骤发布、传输、重启服务编写成 Shell 脚本如deploy.sh实现一键部署减少人为失误。#!/bin/bash echo “发布项目...” dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish-linux echo “传输文件到服务器...” scp -r ./publish-linux userserver:/var/myapp_new echo “在服务器上切换版本...” ssh userserver “sudo systemctl stop myapp rm -rf /var/myapp_old mv /var/myapp /var/myapp_old mv /var/myapp_new /var/myapp sudo systemctl start myapp” echo “部署完成”备份与回滚在更新应用前备份当前版本的文件和数据库。在服务文件中使用ExecStop指令可以自定义优雅停止的逻辑。保留旧版本目录以便快速回滚。10. 总结与下一步通过以上步骤我们完成了一个 .NET 10 API 项目从本地发布到 Ubuntu 服务器部署的全过程。核心在于理解几个关键组件的协作关系你的 .NET 应用作为业务逻辑载体由 systemd 负责守护进程的生命周期管理而 Nginx 则作为面向公网的反向代理和第一道防线。这个方案最值得尝试的点在于其简单直接和可控性强。你不需要学习 Docker 和 Kubernetes 的复杂概念就能获得一个稳定、可开机自启、便于监控的生产级服务。对于个人项目、初创公司或内部工具来说这通常足够了。最先应该验证的功能就是你的核心 API 接口能否通过公网正常访问以及 systemd 服务在服务器重启后能否自动恢复。最容易踩的坑集中在文件权限、服务配置文件路径错误以及防火墙端口未开放这几个方面按照第 8 节的排查方法基本都能解决。后续你可以在此基础上继续深化配置 HTTPS使用 Let‘s Encrypt 的 Certbot 为你的域名免费申请 SSL 证书并在 Nginx 中配置让 API 通过 HTTPS 安全访问。搭建 CI/CD使用 GitHub Actions、GitLab CI 或 Jenkins将代码推送后的构建、测试、部署流程自动化。容器化部署学习 Docker将应用及其环境打包成镜像实现更彻底的环境一致性和更便捷的横向扩展。完善监控集成更专业的监控工具如 Prometheus Grafana监控应用性能指标、服务器资源和业务日志。建议将本文中的关键命令和配置文件保存下来作为你自己的部署手册。在实际操作中结合具体项目的细微调整你就能越来越熟练地将任何 .NET 应用部署到 Linux 环境。