.NET Web API 从发布到部署:Ubuntu + Nginx + Systemd 全链路实践
2026/9/12 20:45:29 网站建设 项目流程

1. 项目背景与核心概念

在当前的软件开发流程中,将本地开发的 .NET Web API 项目部署到 Linux 服务器,特别是 Ubuntu 系统上,已成为一项标准且高频的操作。无论是个人项目上线,还是企业级应用发布,掌握一套从代码发布到服务器部署的完整、可靠的流程,是每一位 .NET 开发者必须跨越的实践门槛。

本文将以一个典型的 .NET 6/7/8 Web API 项目(我们暂且称之为NET10 API)为例,手把手带你走通从 Visual Studio 发布、文件传输、服务器环境配置、服务进程守护到最终通过 Nginx 反向代理对外提供服务的全链路。这个过程不仅涉及基础的dotnet命令,更涵盖了生产环境中必须考虑的权限、日志、进程管理和高可用性配置。如果你之前部署时遇到过诸如“502 Bad Gateway”、“服务进程意外退出”、“依赖库缺失”等问题,那么本文将为你提供一套系统性的解决方案和避坑指南。

2. 环境准备与版本说明

在开始部署之前,请确保你拥有以下环境。版本差异可能导致命令或配置略有不同,本文会以主流稳定版本为例进行说明,并指出关键版本注意事项。

开发环境 (Windows/Mac):

  • 开发工具:Visual Studio 2022 或 Visual Studio Code。
  • .NET SDK:.NET 6.0, 7.0 或 8.0 LTS 版本。本文示例基于 .NET 6.0,但流程对更高版本完全兼容。
  • 项目类型:ASP.NET Core Web API 项目。
  • 发布配置:采用“框架依赖”或“独立”发布均可,本文将演示更通用的“框架依赖”模式。

服务器环境 (Ubuntu):

  • 操作系统:Ubuntu Server 20.04 LTS 或 22.04 LTS。本文以Ubuntu 22.04 LTS为例。
  • .NET 运行时:需要在服务器上安装与项目匹配的 .NET Runtime 或 SDK。如果项目是“框架依赖”发布,则必须安装对应版本的运行时。
  • Web 服务器:Nginx,用作反向代理服务器。
  • 进程管理:systemd,用于将我们的 API 应用托管为系统服务,实现开机自启、自动重启。
  • 远程连接工具:使用 SSH 客户端(如 PowerShell, Terminal, Xshell, MobaXterm)连接服务器。

版本兼容性核心提示:

  1. SDK 与 Runtime:确保服务器上安装的 .NET Runtime 版本大于等于你开发时使用的 SDK 版本。例如,用 .NET 6.0 SDK 开发,服务器至少安装 .NET 6.0 Runtime。
  2. Ubuntu 版本:不同 Ubuntu 版本对应的官方软件源可能不同,安装 .NET 的命令源需要根据系统版本选择。
  3. 项目端口:确保 API 项目监听的端口(如5000,5001)在服务器防火墙中是开放的。

3. 本地项目发布与打包

部署的第一步,是将开发好的代码编译、打包成一个可以在生产环境直接运行的程序集。

3.1 配置项目发布设置

在 Visual Studio 中,右键点击你的 Web API 项目,选择“发布”。

  1. 发布目标:选择“文件夹”。
  2. 配置:选择“Release”(发布)模式。务必不要使用 Debug 模式部署到生产环境
  3. 部署模式:选择“框架依赖”。这意味着生成的发布包较小,但要求目标服务器有对应的 .NET 运行时。如果选择“独立”,则包会包含运行时,体积大但环境兼容性好。
  4. 目标运行时:选择“linux-x64”。因为我们部署到 Ubuntu(Linux)系统。

点击“发布”按钮,Visual Studio 会在你指定的本地文件夹(如bin\Release\net6.0\publish\)下生成所有必需的文件。

3.2 检查发布包内容

发布完成后,进入publish文件夹,你应该看到类似以下结构的文件:

publish/ ├── NET10.API.dll # 项目的主程序集 ├── NET10.API.deps.json # 依赖关系文件 ├── NET10.API.runtimeconfig.json # 运行时配置 ├── appsettings.json # 应用配置文件 ├── appsettings.Production.json # 生产环境配置文件(如有) ├── web.config # (可能没有,对于Linux部署非必需) └── 其他依赖的 .dll 文件

关键文件说明:

  • NET10.API.dll: 这是应用程序的入口点。
  • *.deps.json*.runtimeconfig.json: .NET Core/5+ 应用运行所必需的清单文件,切勿删除。
  • appsettings.json: 配置文件。重要:生产环境的数据库连接字符串、密钥等敏感信息,切勿直接写在此文件中。应使用环境变量、密钥管理服务或appsettings.Production.json来覆盖(并确保该文件已被.gitignore忽略)。

3.3 打包并传输到服务器

为了方便传输,我们将整个publish文件夹压缩。在 Windows 上,可以将其压缩为NET10_API_Publish.zip

接下来,需要将压缩包上传到 Ubuntu 服务器。我们使用scp命令(Secure Copy),这是通过 SSH 进行安全文件传输的标准工具。

打开你的本地终端(PowerShell 或 CMD),执行以下命令:

scp -r /本地路径/NET10_API_Publish.zip username@your_server_ip:/home/username/
  • /本地路径/NET10_API_Publish.zip: 替换为你本地 zip 文件的实际路径。
  • username: 替换为你的 Ubuntu 服务器用户名(如ubuntu,root或自定义用户)。
  • your_server_ip: 替换为你的服务器公网 IP 地址。
  • /home/username/: 替换为你希望存放文件的目标目录,通常放在用户家目录下。

输入对应用户的密码后,文件即开始传输。

4. 服务器环境配置与项目部署

通过 SSH 连接到你的 Ubuntu 服务器:

ssh username@your_server_ip

4.1 安装 .NET 运行时

如果你的项目是“框架依赖”模式,服务器必须安装对应的 .NET 运行时。

  1. 添加 Microsoft 包存储库和安装依赖:

    # 更新包列表 sudo apt-get update # 安装 HTTPS 传输和证书管理工具 sudo apt-get install -y apt-transport-https ca-certificates # 导入 Microsoft 存储库的 GPG 密钥 wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb rm packages-microsoft-prod.deb
  2. 安装 .NET 运行时 (以 .NET 6 为例):

    sudo apt-get update sudo apt-get install -y dotnet-runtime-6.0
    • 如果需要安装 .NET 7 运行时,将6.0替换为7.0
    • 如果需要安装 SDK(以便在服务器上进行dotnet命令操作),则安装dotnet-sdk-6.0
  3. 验证安装:

    dotnet --list-runtimes

    如果安装成功,你会看到类似Microsoft.NETCore.App 6.0.25 [/usr/share/dotnet/shared/Microsoft.NETCore.App]的输出。

4.2 部署应用程序文件

  1. 解压文件并移动到部署目录:

    # 解压到当前目录 unzip NET10_API_Publish.zip -d NET10_API # 创建一个专用的应用程序目录,通常放在 /var 下 sudo mkdir -p /var/www/NET10_API # 将解压的文件复制到应用目录,并设置所有权 sudo cp -r NET10_API/* /var/www/NET10_API/ # 设置目录所有权给一个非root用户(更安全),这里假设你的用户名是‘ubuntu’ sudo chown -R ubuntu:ubuntu /var/www/NET10_API # 给予执行权限 sudo chmod +x /var/www/NET10_API/NET10.API.dll
  2. 测试应用能否直接运行:

    cd /var/www/NET10_API dotnet NET10.API.dll --urls "http://*:5000"
    • --urls “http://*:5000”参数指定应用监听 5000 端口的所有网络接口。
    • 如果看到类似Now listening on: http://[::]:5000的输出,说明应用启动成功。
    • Ctrl+C停止测试。这只是临时运行,我们需要一个更稳定的方式。

5. 配置 Systemd 服务守护进程

使用systemd来管理我们的 API 应用,可以确保应用在服务器启动时自动运行,在崩溃时自动重启,并方便地查看日志和管理服务状态。

  1. 创建 systemd 服务文件:

    sudo nano /etc/systemd/system/net10-api.service
  2. 编辑服务文件内容:

    [Unit] Description=NET10 API Service After=network.target [Service] # 指定运行服务的用户和组,建议使用非root用户 User=ubuntu Group=ubuntu # 工作目录,即我们的应用目录 WorkingDirectory=/var/www/NET10_API # 启动命令。%i 代表服务实例名 ExecStart=/usr/bin/dotnet /var/www/NET10_API/NET10.API.dll # 重启策略:在发生故障时总是重启 Restart=always # 如果服务在10秒内没有正常关闭,强制杀死 KillSignal=SIGINT TimeoutStopSec=10 # 设置环境变量,如 ASPNETCORE_ENVIRONMENT Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false [Install] WantedBy=multi-user.target

    关键配置解释:

    • User/Group: 使用非 root 用户运行服务是重要的安全实践。
    • WorkingDirectory: 必须设置,否则应用可能找不到appsettings.json等文件。
    • ExecStart: 直接使用dotnet命令启动我们的 DLL。
    • Restart=always: 确保服务异常退出后能自动恢复。
    • Environment: 设置环境变量为Production,这会促使应用读取appsettings.Production.json配置文件(如果存在)。
  3. 启用并启动服务:

    # 重新加载 systemd 配置,使其识别新服务 sudo systemctl daemon-reload # 设置服务开机自启 sudo systemctl enable net10-api.service # 立即启动服务 sudo systemctl start net10-api.service # 查看服务状态,确认是否运行成功 sudo systemctl status net10-api.service

    如果状态显示为active (running),并且下面没有红色的错误日志,说明服务已成功启动。

  4. 查看应用日志:

    # 查看服务的所有日志 sudo journalctl -u net10-api.service # 实时跟踪最新日志(类似 tail -f) sudo journalctl -u net10-api.service -f

    通过日志,你可以排查应用启动过程中的任何问题,例如数据库连接失败、配置错误等。

6. 配置 Nginx 反向代理

目前我们的服务运行在5000端口,只能通过http://服务器IP:5000访问。为了使用标准的 HTTP/HTTPS 端口(80/443),并提供静态文件服务、负载均衡等能力,我们需要配置 Nginx 作为反向代理。

  1. 安装 Nginx:

    sudo apt-get update sudo apt-get install -y nginx
  2. 配置 Nginx 站点:删除默认配置,为我们的 API 创建新的配置文件。

    sudo rm /etc/nginx/sites-enabled/default sudo nano /etc/nginx/sites-available/net10-api
  3. 编辑 Nginx 配置:

    server { listen 80; # 将 your_domain.com 替换为你的域名或服务器IP server_name your_domain.com; location / { # 将请求代理到运行在 localhost:5000 上的 .NET 应用 proxy_pass http://localhost:5000; # 传递原始请求头信息,这对于获取真实客户端IP、协议等信息很重要 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; # 设置代理超时时间 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # 可选:配置静态文件服务,如果你的API有前端页面 # location /wwwroot/ { # root /var/www/NET10_API; # expires 1h; # } }
  4. 启用配置并测试:

    # 创建符号链接以启用站点 sudo ln -s /etc/nginx/sites-available/net10-api /etc/nginx/sites-enabled/ # 测试 Nginx 配置语法是否正确 sudo nginx -t

    如果输出syntax is oktest is successful,则说明配置正确。

  5. 重启 Nginx 使配置生效:

    sudo systemctl restart nginx

现在,你可以通过浏览器或curl访问http://your_domain.comhttp://your_server_ip,Nginx 会将请求转发给运行在5000端口的 .NET API 应用。

7. 防火墙与安全配置

为了服务器安全,我们需要配置防火墙,只开放必要的端口。

  1. 启用并配置 UFW (Uncomplicated Firewall):

    # 允许 SSH 连接(默认22端口),确保你不会把自己锁在外面 sudo ufw allow OpenSSH # 允许 HTTP (80) 和 HTTPS (443) 端口 sudo ufw allow 80/tcp sudo ufw allow 443/tcp # 启用防火墙 sudo ufw enable # 查看防火墙状态 sudo ufw status verbose

    输出应显示80/tcp443/tcpALLOW状态。

  2. (重要)保护你的 API:

    • 使用 HTTPS:申请 SSL 证书(如 Let‘s Encrypt 免费证书),并在 Nginx 中配置 HTTPS,强制将所有 HTTP 请求重定向到 HTTPS。
    • API 密钥/令牌:为你的 API 设计认证和授权机制,不要将敏感接口直接暴露在公网。
    • 环境变量管理:数据库连接字符串、JWT Secret 等敏感信息,务必通过服务器环境变量或专业的密钥管理工具注入,而不是写在appsettings.json文件中。
    • 定期更新:定期运行sudo apt update && sudo apt upgrade更新系统和软件包,修复安全漏洞。

8. 常见问题与排查思路

部署过程中难免会遇到问题,下面是一个快速排查清单:

问题现象可能原因排查步骤与解决方案
访问http://服务器IP返回 502 Bad Gateway1. .NET 应用服务未运行。
2. Nginx 配置中proxy_pass地址或端口错误。
3. 应用监听的地址不是localhost0.0.0.0
1.sudo systemctl status net10-api.service检查服务状态,查看日志sudo journalctl -u net10-api.service
2. 检查/etc/nginx/sites-available/net10-apiproxy_pass是否指向http://localhost:5000
3. 确认应用启动时监听了*:5000(在Program.csappsettings.json中配置Urls)。
服务状态为failedinactive1. 应用本身有运行时错误(如缺少依赖、配置错误)。
2.systemd服务文件配置错误(如路径、用户权限)。
3. 端口已被占用。
1. 仔细查看服务日志sudo journalctl -u net10-api.service -n 50 --no-pager
2. 检查服务文件中WorkingDirectoryExecStart的路径是否正确,以及User是否有该目录的读取和执行权限。
3. 使用 `sudo netstat -tlnp
dotnet命令未找到.NET 运行时/ SDK 未安装或未正确安装。运行dotnet --info确认。重新执行安装步骤,并确保添加了正确的微软源。
Nginx 配置测试失败 (nginx -t报错)Nginx 配置文件语法错误。根据错误提示,检查配置文件,常见错误有缺少分号;、括号不匹配、路径错误等。
应用运行但无法连接数据库1. 生产环境数据库连接字符串错误。
2. 服务器防火墙未开放数据库端口(如 MySQL 3306)。
3. 数据库用户权限不足或不允许远程连接。
1. 检查appsettings.Production.json或环境变量中的连接字符串。
2. 如果是云服务器,还需检查云服务商的安全组规则。
3. 登录数据库,检查用户授权GRANT语句。

9. 最佳实践与工程建议

  1. 使用进程管理工具 (systemd):永远不要仅通过nohup&在后台运行生产服务。systemd提供了完善的监控、日志收集和生命周期管理。
  2. 非 Root 用户运行:始终使用一个专用的、权限受限的系统用户来运行你的应用程序,这能极大限制漏洞被利用后的影响范围。
  3. 配置与代码分离:将数据库连接字符串、API 密钥、第三方服务凭证等所有敏感信息从代码库中移除。使用appsettings.Production.json(不被 Git 跟踪)或环境变量来管理。
  4. 结构化日志:Program.cs中使用 Serilog 或 NLog 等日志框架,将日志输出到文件,并配置日志轮转,避免日志文件无限增大。同时,在systemd服务文件中可以设置StandardOutput=journalStandardError=journal来将日志集成到系统日志。
  5. 健康检查端点:在 API 项目中实现一个简单的健康检查端点(如/health),返回应用状态。这便于监控系统(如 Prometheus)或负载均衡器判断服务是否健康。
  6. 使用 CI/CD 流水线:对于频繁更新的项目,考虑使用 GitHub Actions, GitLab CI/CD 或 Jenkins 等工具自动化构建、测试和部署过程。流水线可以自动完成本文中大部分手动步骤。
  7. 备份与回滚:在更新应用前,备份当前的发布目录和数据库。准备好快速回滚的方案,例如通过切换systemd服务文件指向旧版本目录。

从 Visual Studio 的一个发布按钮,到用户通过浏览器稳定访问你的 API,中间跨越了环境配置、文件传输、服务托管、网络代理和安全加固等多个环节。本文详细拆解了每一步的操作和原理,旨在为你构建一条可重复、可维护的部署路径。掌握这套流程后,你不仅可以部署自己的 .NET API,其核心思想(进程守护、反向代理、配置分离)同样适用于其他语言(如 Python Flask, Node.js)的应用部署。接下来,你可以进一步探索 Docker 容器化部署,它将应用及其所有依赖打包成一个镜像,能提供更好的环境一致性和部署效率,是现代化部署的进阶方向。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询