一、为什么我的DotNetCore应用总是部署失败?

最近在技术社区看到不少小伙伴吐槽:"明明本地跑得好好的DotNetCore应用,一到服务器就各种幺蛾子"。这种情况我太熟悉了,就像你精心准备的PPT,到了客户现场投影仪突然罢工一样让人抓狂。

让我们先看个典型例子。假设我们有个简单的WebAPI项目,使用.NET Core 3.1开发:

// Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

// Startup.cs
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

看起来人畜无害的代码对吧?但部署到IIS后可能会遇到"502 Bad Gateway"错误。这是因为默认情况下,.NET Core应用需要安装ASP.NET Core模块(ANCM)才能与IIS正常通信。

二、那些年我们踩过的部署坑

2.1 运行时版本不匹配

这是最常见的"新手杀手"。比如你在开发时用的是.NET Core 3.1,但服务器上只装了2.1运行时。解决方案很简单:

# 查看服务器已安装的运行时版本
dotnet --list-runtimes

# 安装指定版本的运行时(以3.1为例)
dotnet-install.ps1 -Runtime dotnet -Version 3.1.0

2.2 文件权限问题

Linux部署时经常遇到这个坑。比如你用Nginx做反向代理:

# 错误的权限设置可能导致应用无法启动
sudo chown -R www-data:www-data /var/www/myapp
sudo chmod -R 755 /var/www/myapp

# 正确的做法是只给执行权限
sudo chmod -R 755 /var/www/myapp
sudo chmod +x /var/www/myapp/MyApp

2.3 端口绑定冲突

默认情况下,Kestrel会监听5000和5001端口。如果这些端口被占用:

// 修改Program.cs指定端口
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
            webBuilder.UseUrls("http://*:8080"); // 指定端口
        });

或者在appsettings.json中配置:

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://*:8080"
      }
    }
  }
}

三、进阶部署技巧

3.1 使用Docker部署

Docker可以完美解决环境一致性问题。这里给出一个完整的Dockerfile示例:

# 使用官方.NET Core运行时镜像
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80

# 构建阶段
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore "MyApp.csproj"
COPY . .
RUN dotnet build "MyApp.csproj" -c Release -o /app/build

# 发布阶段
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish

# 最终镜像
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

构建和运行命令:

docker build -t myapp .
docker run -d -p 8080:80 --name myapp_instance myapp

3.2 使用Nginx反向代理

在Linux上部署时,Nginx是很好的选择。配置示例:

server {
    listen 80;
    server_name example.com;

    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_cache_bypass $http_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

四、部署后的监控与维护

部署成功只是开始,我们还需要确保应用稳定运行。.NET Core提供了丰富的诊断工具:

// 添加健康检查端点
services.AddHealthChecks()
    .AddCheck<ExampleHealthCheck>("example_health_check");

// 在Startup.Configure中添加
app.UseHealthChecks("/health");

然后可以通过curl测试:

curl http://localhost:5000/health

对于生产环境,建议配置日志:

Host.CreateDefaultBuilder(args)
    .ConfigureLogging(logging =>
    {
        logging.ClearProviders();
        logging.AddConsole();
        logging.AddDebug();
        logging.AddEventLog();
    })
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    });

五、总结与最佳实践

经过这些年的摸爬滚打,我总结了几个黄金法则:

  1. 始终确保开发、测试和生产环境的一致性
  2. 使用容器化技术简化部署流程
  3. 实施完善的日志和监控
  4. 自动化部署流程(CI/CD)
  5. 定期进行部署演练

记住,部署不是终点,而是新的起点。每次部署后都要密切监控应用状态,及时发现问题并优化。

最后分享一个实用的部署检查清单:

  1. [ ] 运行时版本匹配
  2. [ ] 依赖项完整
  3. [ ] 配置文件正确
  4. [ ] 服务端口可用
  5. [ ] 文件权限设置正确
  6. [ ] 日志系统正常工作
  7. [ ] 健康检查端点配置
  8. [ ] 备份机制就绪

希望这些经验能帮你少走弯路。部署路上坑虽多,但填平了就是通往成功的路!