这次我们来看一个 .NET 项目从开发到上线的完整实战。标题“NET10 API网站从发布到部署Ubuntu服务器七”已经点明了核心:这是一个基于 .NET 10 构建的 API 网站,并且最终要部署到 Ubuntu 服务器上。对于 .NET 开发者而言,从 Windows 的 Visual Studio 舒适区,将应用发布并部署到 Linux 服务器,是一个必须掌握的技能点。这个过程涉及项目发布、服务器环境配置、服务托管、反向代理设置以及持续集成/部署(CI/CD)的初步概念。
本文将带你走通这条路径。我们不会停留在概念,而是聚焦于可执行的操作:如何准备一个干净的 .NET 10 Web API 项目,如何将其发布为可移植的部署包,如何在 Ubuntu 服务器上配置运行时环境,以及如何通过 systemd 或 Docker 让 API 服务稳定、可靠地运行。同时,我们也会探讨如何配置 Nginx 反向代理、设置 HTTPS,并简要介绍如何与 CI/CD 工具(如 GitHub Actions)结合,实现自动化部署。无论你是刚接触 Linux 部署的 .NET 开发者,还是希望优化现有部署流程的运维人员,这篇文章都能提供一套可直接复用的操作指南。
1. 核心能力速览
在深入细节之前,我们先通过一个表格快速了解本次部署方案的核心要素和门槛,让你判断是否适合你的项目。
| 能力项 | 说明 |
|---|---|
| 技术栈 | .NET 10 (或 .NET 8 LTS), ASP.NET Core Web API |
| 服务器操作系统 | Ubuntu Server 22.04 LTS / 24.04 LTS (推荐) |
| 部署包形式 | 框架依赖 (FDD) 或 独立 (SCD) 发布包 |
| 服务托管方式 | systemd服务 (推荐) 或Docker容器 |
| Web 服务器/反向代理 | Nginx 或 Apache |
| 是否需要图形界面 | 否,全程命令行操作 |
| 核心前置技能 | 基础的 Linux 命令、.NET CLI 使用、SSH 连接 |
| 适合场景 | 个人项目、中小型企业级 API 后端服务、微服务部署 |
| 不适合场景 | 需要 Windows 特定功能(如 WPF、Windows 服务)的应用 |
2. 适用场景与使用边界
这个部署流程主要服务于基于 ASP.NET Core 开发的 Web API、MVC 或 Blazor Server 应用。它特别适合以下场景:
- 从 Windows 开发环境迁移到 Linux 生产环境:利用 Linux 服务器通常更高的性价比和稳定性。
- 构建微服务架构:轻量化的 .NET Core 应用非常适合在 Linux 容器或虚拟机中运行。
- 需要高并发和可扩展性的 API 服务:结合 Nginx 负载均衡和 Kestrel 服务器,可以构建高性能后端。
- 学习与实践 DevOps:通过自动化脚本或 CI/CD 管道,实现从代码提交到服务上线的自动化。
使用边界与注意事项:
- 应用兼容性:确保你的项目没有使用任何 .NET Core 不支持的 Windows 专有 API(如注册表访问、Windows 窗体)。大多数 ASP.NET Core 库是跨平台的。
- 文件路径:代码中所有文件路径操作应使用
Path.Combine()或确保使用正斜杠 (/),避免硬编码反斜杠 (\)。 - 权限管理:Linux 系统有严格的权限控制。部署时需要关注应用运行用户、文件目录权限以及端口绑定(如绑定 80/443 端口需要 root 权限,通常通过反向代理解决)。
- 数据与存储:数据库连接字符串需要调整为指向 Linux 服务器上的数据库实例(如 PostgreSQL, MySQL, SQL Server for Linux)。
3. 环境准备与前置条件
在开始部署之前,需要确保本地开发环境和目标服务器环境就绪。
3.1 本地开发环境 (Windows/macOS)
- .NET SDK: 安装 .NET 10 SDK(或你项目使用的版本)。可通过 .NET 官网 下载。
# 在终端检查版本 dotnet --version - 代码编辑器: Visual Studio 2022+, VS Code, 或 Rider。
- 项目准备: 一个可正常编译运行的 ASP.NET Core Web API 项目。确保
Program.cs和appsettings.json配置正确。
3.2 目标服务器环境 (Ubuntu)
你需要一台安装好 Ubuntu Server 的机器,可以是物理机、虚拟机(VMware/VirtualBox)、云服务器(阿里云、腾讯云等)。并通过 SSH 能够连接。
- 操作系统: Ubuntu 22.04 LTS 或 24.04 LTS。
- 网络: 确保服务器有公网 IP 或能在内网访问,防火墙已开放所需端口(如 SSH 的 22, HTTP 的 80, HTTPS 的 443,以及你的应用端口如 5000)。
- 权限: 拥有一个具有
sudo权限的用户账户。
4. 项目发布与打包
部署的第一步是将你的项目代码编译并打包成一个可以在目标服务器上运行的独立单元。
4.1 发布配置
在项目根目录,使用 .NET CLI 进行发布。有两种主要模式:
- 框架依赖发布 (FDD): 生成的包较小,但要求目标服务器已安装对应版本的 .NET 运行时。
# 在项目目录下执行 dotnet publish -c Release -o ./publish-output - 独立发布 (SCD): 生成的包包含所有依赖(包括 .NET 运行时),体积较大,但无需在服务器安装运行时。
dotnet publish -c Release -r linux-x64 --self-contained true -o ./publish-output # linux-arm64 适用于树莓派等 ARM 设备
对于服务器环境,推荐使用框架依赖发布 (FDD),并在服务器上统一安装 .NET 运行时,这样更便于管理和更新。
4.2 发布包内容检查
发布完成后,./publish-output目录应包含以下关键文件:
YourAppName.dll(主程序集)appsettings.json(配置文件)web.config或appsettings.Production.json(如有)wwwroot文件夹 (静态资源)- 各种
.dll依赖项
你可以将此publish-output文件夹整体压缩(如tar -czvf myapp.tar.gz publish-output/),准备上传到服务器。
5. 服务器环境配置
通过 SSH 连接到你的 Ubuntu 服务器,开始配置运行环境。
5.1 安装 .NET 运行时 (如果使用 FDD)
如果采用框架依赖发布,服务器需要安装对应的 .NET 运行时或 SDK。
# 更新包列表 sudo apt update && sudo apt upgrade -y # 安装 .NET 运行时 (以 .NET 8 LTS 为例,.NET 10 发布后请替换版本号) # 首先添加微软包仓库 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 # 安装 ASP.NET Core 运行时 (包含 .NET 运行时) sudo apt update sudo apt install -y aspnetcore-runtime-8.0 # 验证安装 dotnet --list-runtimes你应该能看到Microsoft.AspNetCore.App 8.0.x和Microsoft.NETCore.App 8.0.x。
5.2 准备应用目录
在服务器上创建一个专用目录来存放你的应用,并设置合适的权限。
# 创建一个目录,例如 /var/www/myapp sudo mkdir -p /var/www/myapp # 将本地打包的发布包上传到服务器,可以使用 SCP 或 SFTP 工具 # 例如从本地机器执行: scp -r ./publish-output/* user@your-server-ip:/var/www/myapp/ # 假设你已经将文件上传到了 /var/www/myapp,现在设置权限 # 创建一个专门运行应用的用户(可选但推荐) sudo useradd -r -s /bin/false myappuser # 将目录所有权赋予该用户 sudo chown -R myappuser:myappuser /var/www/myapp6. 配置 systemd 服务托管
使用systemd来管理你的 .NET 应用是最可靠的方式,它可以实现开机自启、自动重启、日志集中管理。
6.1 创建 service 文件
sudo nano /etc/systemd/system/myapp.service将以下内容粘贴进去,根据你的实际情况修改WorkingDirectory、ExecStart、User和环境变量。
[Unit] Description=My .NET 10 API Application After=network.target [Service] Type=exec # 运行应用的用户和组 User=myappuser Group=myappuser # 应用的工作目录 WorkingDirectory=/var/www/myapp # 启动命令 # 如果你的入口dll是 MyApp.Api.dll,则如下所示 ExecStart=/usr/bin/dotnet /var/www/myapp/MyApp.Api.dll # 环境变量,例如指定 ASPNETCORE_ENVIRONMENT Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false # 重启策略 Restart=always RestartSec=10 KillSignal=SIGINT # 标准输出和错误输出重定向到 systemd 日志 StandardOutput=journal StandardError=journal SyslogIdentifier=myapp-api # 安全相关限制(可选但推荐) # NoNewPrivileges=true # PrivateTmp=true [Install] WantedBy=multi-user.target6.2 启动并启用服务
# 重新加载 systemd 配置 sudo systemctl daemon-reload # 启动服务 sudo systemctl start myapp.service # 设置开机自启 sudo systemctl enable myapp.service # 检查服务状态 sudo systemctl status myapp.service如果状态显示active (running),并且日志没有报错,说明你的 API 服务已经在后台运行了。默认情况下,ASP.NET Core Kestrel 服务器会监听http://localhost:5000和https://localhost:5001(如果配置了 HTTPS)。
6.3 查看应用日志
# 查看最近的日志 sudo journalctl -u myapp.service -f # 查看特定时间段的日志 sudo journalctl -u myapp.service --since "2024-01-01" --until "2024-01-02"7. 配置 Nginx 反向代理
不建议直接将 Kestrel 暴露在公网。使用 Nginx 作为反向代理,可以提供静态文件服务、负载均衡、SSL 终止和缓冲请求等好处。
7.1 安装 Nginx
sudo apt install -y nginx7.2 配置站点
删除默认配置,为你的应用创建一个新的配置文件。
sudo rm /etc/nginx/sites-enabled/default sudo nano /etc/nginx/sites-available/myapp粘贴以下配置。假设你的应用运行在http://localhost:5000,并且你希望通过域名api.yourdomain.com访问。
server { listen 80; # 替换为你的域名或服务器IP server_name api.yourdomain.com your-server-ip; location / { 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_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; # 如果API响应较慢,可适当增加超时时间 proxy_read_timeout 300s; proxy_connect_timeout 75s; } # 可选:静态文件服务,如果前端是独立的 # location /wwwroot/ { # alias /var/www/myapp/wwwroot/; # expires 1y; # add_header Cache-Control "public, immutable"; # } }7.3 启用站点并测试配置
# 创建符号链接以启用站点 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 reload nginx现在,你应该可以通过服务器的公网 IP 或你配置的域名(HTTP)访问到你的 API 了。
8. 配置 HTTPS (SSL/TLS)
为了安全,必须启用 HTTPS。可以使用 Let‘s Encrypt 免费证书。
8.1 安装 Certbot
sudo apt install -y certbot python3-certbot-nginx8.2 获取并安装证书
确保你的域名api.yourdomain.com的 DNS 已解析到服务器 IP。
sudo certbot --nginx -d api.yourdomain.com按照交互提示操作。Certbot 会自动修改你的 Nginx 配置,将 HTTP 重定向到 HTTPS,并配置好证书路径。
8.3 自动续期
Let‘s Encrypt 证书有效期为 90 天,Certbot 会配置一个定时任务自动续期。你可以手动测试续期:
sudo certbot renew --dry-run9. 使用 Docker 部署(备选方案)
如果你更倾向于容器化部署,Docker 提供了更好的环境隔离和一致性。
9.1 在服务器上安装 Docker
# 使用官方脚本安装 curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER # 退出并重新登录使组权限生效 # 安装 Docker Compose (v2) sudo apt install -y docker-compose-plugin9.2 创建 Dockerfile
在你的项目根目录(不是发布目录)创建Dockerfile:
# 使用 .NET SDK 镜像来构建 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["MyApp.Api.csproj", "."] RUN dotnet restore "MyApp.Api.csproj" COPY . . RUN dotnet publish "MyApp.Api.csproj" -c Release -o /app/publish # 使用 ASP.NET 运行时镜像来运行 FROM mcr.microsoft.com/dotnet/aspnet:8.0 WORKDIR /app EXPOSE 80 EXPOSE 443 COPY --from=build /app/publish . # 指定入口点 ENTRYPOINT ["dotnet", "MyApp.Api.dll"]9.3 构建并运行容器
在服务器上,将项目代码(含 Dockerfile)上传到某个目录,例如/opt/myapp。
cd /opt/myapp # 构建镜像 sudo docker build -t myapp-api . # 运行容器 # -p 将容器内80端口映射到主机8080端口 # --name 指定容器名 # -d 后台运行 # -e 设置环境变量 sudo docker run -d -p 8080:80 \ --name myapp-api-container \ -e ASPNETCORE_ENVIRONMENT=Production \ -e DOTNET_PRINT_TELEMETRY_MESSAGE=false \ myapp-api此时,应用运行在容器内,并通过主机的 8080 端口对外服务。你仍需配置 Nginx 将流量从 80/443 端口反向代理到localhost:8080。
10. 接口 API 测试与验证
服务部署并配置好反向代理后,必须进行验证。
10.1 基础连通性测试
# 在服务器本地测试 Kestrel 是否工作 curl http://localhost:5000/weatherforecast # 或测试一个你已知的 API 端点 curl http://localhost:5000/api/health # 通过公网域名测试 Nginx 反向代理 curl http://api.yourdomain.com/api/health curl https://api.yourdomain.com/api/health10.2 使用 Postman 或浏览器测试
使用图形化工具(如 Postman、Swagger UI)进行更全面的 API 测试。
- 如果你的项目集成了 Swagger/OpenAPI,访问
https://api.yourdomain.com/swagger。 - 在 Postman 中创建新的请求,指向你的 API 端点,测试 GET、POST 等各类方法。
- 重点测试依赖数据库的接口,确保连接字符串配置正确。
11. 资源占用与性能观察
部署完成后,需要监控应用运行状态。
11.1 查看进程与资源
# 查看 systemd 服务的资源占用 (CPU, 内存) sudo systemctl status myapp.service # 更详细的资源查看 top -p $(pgrep -f 'MyApp.Api.dll') # 查看 Docker 容器资源占用 sudo docker stats myapp-api-container11.2 监控日志
持续关注应用日志,及时发现错误。
# 动态跟踪日志 sudo journalctl -u myapp.service -f # 或查看 Docker 容器日志 sudo docker logs -f myapp-api-container11.3 压力测试(可选)
使用工具如siege,ab(Apache Bench), 或wrk进行简单压力测试,观察应用在高并发下的表现。
# 安装 ab sudo apt install -y apache2-utils # 对某个 GET 接口进行测试,并发10,总请求1000 ab -n 1000 -c 10 https://api.yourdomain.com/api/someget12. 常见问题与排查方法
部署过程中难免会遇到问题,下表列出了一些常见情况及其解决方法。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
systemctl status显示failed | 应用启动失败,路径错误、依赖缺失、端口占用、权限不足。 | sudo journalctl -u myapp.service -xe查看详细错误日志。 | 根据日志修正:检查WorkingDirectory和ExecStart路径;确保.dll存在;检查appsettings.json配置;确保运行用户有目录读取权限。 |
| Nginx 502 Bad Gateway | Nginx 无法连接到后端 Kestrel 服务。 | 1. 检查 Kestrel 是否在运行:sudo systemctl status myapp.service。2. 检查 proxy_pass地址和端口是否正确。3. 检查防火墙是否阻止了本地回环地址的端口访问。 | 启动或重启 Kestrel 服务;确认proxy_pass指向http://localhost:5000;暂时关闭防火墙测试sudo ufw disable(生产环境请谨慎)。 |
| 应用启动成功,但接口返回 404 | 路由未匹配,或应用未监听正确地址。 | 1. 检查应用日志,看是否有启动信息。 2. 本地用 curl测试localhost:5000是否正常。3. 检查 Program.cs或Startup.cs中的路由配置。 | 确保在Program.cs中正确配置了UseRouting()和MapControllers();检查控制器和 Action 的路由属性。 |
| 数据库连接失败 | 连接字符串错误;数据库服务未启动;网络不通。 | 查看应用日志中的数据库连接异常信息。 | 检查appsettings.Production.json中的连接字符串;确保数据库服务(如 PostgreSQL)已安装并运行;检查防火墙规则。 |
| 权限被拒绝 (Permission denied) | 运行用户无权访问目录或文件。 | 查看journalctl日志。 | 使用ls -la /var/www/myapp检查目录权限,确保运行用户(如myappuser)有读取和执行权限。 |
| 端口已被占用 | 另一个进程占用了 5000 端口。 | sudo netstat -tulpn | grep :5000 | 停止占用端口的进程,或修改 Kestrel 的监听端口(在appsettings.json中配置Urls)。 |
| Docker 容器启动后立即退出 | Dockerfile 构建问题或应用在容器内启动失败。 | sudo docker logs myapp-api-container查看容器日志。 | 检查 Dockerfile 中ENTRYPOINT是否正确;检查应用在容器内的环境变量和配置文件;确保基础镜像版本与项目目标框架匹配。 |
13. 最佳实践与使用建议
- 使用配置管理:不要将生产环境密码、密钥硬编码在代码中。使用
appsettings.Production.json、环境变量或密钥管理服务(如 Azure Key Vault, HashiCorp Vault)。 - 分离静态资源:对于大量静态文件,考虑使用专门的 CDN 或对象存储(如 AWS S3, Azure Blob Storage),减轻应用服务器压力。
- 设置健康检查端点:在应用中添加一个
/health或/api/health端点,返回应用状态(包括数据库连接状态)。这便于监控和负载均衡器健康检查。 - 日志集中化:不要只依赖
journalctl。考虑使用 Serilog 等库将日志写入文件,并集成到 ELK Stack (Elasticsearch, Logstash, Kibana) 或 Seq 等日志聚合系统中。 - 进程管理:对于
systemd,合理配置Restart,RestartSec和资源限制(如MemoryLimit),防止应用内存泄漏导致系统崩溃。 - 备份与回滚:部署新版本前,备份当前版本的应用目录和数据库。准备好快速回滚的方案(如切换
systemd服务指向旧版本目录)。 - 安全加固:
- 为应用运行使用非 root 用户。
- 定期更新操作系统和 .NET 运行时安全补丁。
- 使用防火墙(
ufw)严格限制入站端口。 - 为 Nginx 和你的应用配置适当的安全头部(Security Headers)。
从 Visual Studio 的一个绿色运行按钮,到 Ubuntu 服务器上稳定对外服务的 API,这个过程涵盖了现代 .NET 应用部署的核心环节。最关键的一步是使用 systemd 托管服务,它提供了生产环境所需的可靠性和可管理性。而Nginx 反向代理则是将内部服务安全、高效暴露给外界的标准做法。
部署完成后,不要忘记建立监控和告警。简单的systemctl status和journalctl是起点,更复杂的监控可以集成 Prometheus 和 Grafana。下一步,你可以尝试将上述手动步骤脚本化,并集成到 GitHub Actions 或 GitLab CI 中,实现提交代码后自动测试、构建 Docker 镜像、推送到仓库并部署到服务器的完整 CI/CD 流水线。这将使你的发布过程更加高效和可靠。