ASP.NET Core 部署
大约 9 分钟约 2843 字
ASP.NET Core 部署
简介
ASP.NET Core 应用支持多种部署方式:IIS(Windows)、Docker(跨平台)、直接运行(Linux)、Kubernetes(云原生)。选择合适的部署方式取决于团队技术栈、基础设施和运维能力。掌握多种部署方式是全栈开发者的必备技能。
特点
部署方式对比
| 方式 | 适用场景 | 难度 | 可扩展性 |
|---|---|---|---|
| IIS | Windows 企业内网 | 低 | 中 |
| Docker | Linux 服务器 | 中 | 高 |
| 直接运行 | 简单 Linux 部署 | 低 | 低 |
| Kubernetes | 云原生、微服务 | 高 | 极高 |
IIS 部署
安装 Hosting Bundle
下载对应版本的 ASP.NET Core Hosting Bundle:https://dotnet.microsoft.com/zh-cn/download/dotnet

安装完成后,IIS 模块中会出现 AspNetCoreModuleV2。
发布项目
在 Visual Studio 中右键项目 → 发布:
- 目标:文件夹
- 配置:Release
- 目标框架:net8.0
- 部署模式:框架依赖
- 生成后检查
web.config文件是否正确

配置 IIS
- 创建应用程序池 → .NET CLR 版本选择 无托管代码
- 创建网站 → 指向发布文件夹
- 绑定端口和域名
- 浏览验证

web.config 说明
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*"
modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet"
arguments=".\MyApp.dll"
stdoutLogEnabled="false"
stdoutLogFile=".\logs\stdout"
hostingModel="inprocess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>Docker 部署
Dockerfile
# 构建阶段
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish
# 运行阶段
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
# 非 root 用户运行
RUN addgroup --system appgroup && adduser --system appuser --ingroup appgroup
USER appuser
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
ENTRYPOINT ["dotnet", "MyApp.dll"]构建和运行
# 构建镜像
docker build -t myapp:latest .
# 运行容器
docker run --name myapp \
--restart=always \
-d -p 8080:8080 \
-e ASPNETCORE_ENVIRONMENT=Production \
-v /var/log/myapp:/app/logs \
myapp:latest
# 防火墙放行
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --reload
Docker Compose 部署
# docker-compose.yml
version: "3.8"
services:
web:
image: myapp:latest
container_name: myapp
restart: always
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__Default=Server=db;Database=MyApp;User=sa;Password=YourPassword
depends_on:
- db
db:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: myapp-db
restart: always
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourPassword123!
volumes:
- sqldata:/var/opt/mssql
volumes:
sqldata:docker compose up -dLinux 直接部署
安装运行时
# CentOS 7
sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm
sudo yum install -y aspnetcore-runtime-8.0
# 验证安装
dotnet --info上传发布文件
mkdir -p /var/www/myapp
# 使用 scp 或 ftp 上传发布文件到该目录配置 systemd 服务
# /etc/systemd/system/myapp.service
[Unit]
Description=My ASP.NET Core App
After=network.target
[Service]
Type=notify
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/dotnet /var/www/myapp/MyApp.dll --urls http://0.0.0.0:8080
Restart=always
RestartSec=10
SyslogIdentifier=myapp
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target# 启用并启动服务
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
sudo systemctl status myapp配合 Supervisor(备选)
进程守护教程参考 Supervisor 进程管理
[program:myapp]
command=dotnet MyApp.dll --urls http://0.0.0.0:8080
directory=/var/www/myapp
autorestart=true
startsecs=3
startretries=3
stdout_logfile=/var/log/myapp/out.log
stderr_logfile=/var/log/myapp/err.log
stdout_logfile_maxbytes=2MB
stderr_logfile_maxbytes=2MB
user=root
priority=999
numprocs=1
process_name=%(program_name)s_%(process_num)02dNginx 反向代理
生产环境务必在 Linux 上配置 Nginx 反向代理,提升安全性和性能。
server {
listen 80;
server_name api.example.com;
# 强制 HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/api.crt;
ssl_certificate_key /etc/nginx/ssl/api.key;
location / {
proxy_pass http://127.0.0.1:8080;
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;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时配置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 300s;
# 请求体大小限制
client_max_body_size 50M;
}
}环境配置管理
appsettings 按环境区分
appsettings.json ← 默认配置
appsettings.Development.json ← 开发环境
appsettings.Staging.json ← 预发布环境
appsettings.Production.json ← 生产环境环境变量覆盖
# Docker 环境变量(优先级最高)
docker run -e ConnectionStrings__Default="Server=prod-db;..." myapp
# Linux 环境变量
export ASPNETCORE_ENVIRONMENT=Production
export ConnectionStrings__Default="Server=prod-db;..."Docker 部署进阶
多阶段构建优化
# 多阶段构建 — 减小镜像体积
# Stage 1: 构建
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# 先复制 csproj,利用 Docker 缓存层
COPY ["MyApp/MyApp.csproj", "MyApp/"]
RUN dotnet restore "MyApp/MyApp.csproj"
# 再复制源码并发布
COPY . .
WORKDIR "/src/MyApp"
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish \
--no-restore \
/p:PublishTrimmed=true \
/p:PublishReadyToRun=true
# Stage 2: 运行时(Alpine 更小)
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
WORKDIR /app
# 非 root 用户运行
RUN addgroup -S app && adduser -S app -G app
USER app
COPY --from=build /app/publish .
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:5000/health || exit 1
ENV ASPNETCORE_URLS=http://+:5000
EXPOSE 5000
ENTRYPOINT ["dotnet", "MyApp.dll"]Docker Compose 完整配置
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "5000:5000"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__Default=Server=db;Database=MyApp;User=sa;Password=Your@Password123;
- Redis__Configuration=redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: '1.0'
reservations:
memory: 256M
cpus: '0.5'
networks:
- app-network
db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=Your@Password123
ports:
- "1433:1433"
volumes:
- db-data:/var/opt/mssql
healthcheck:
test: /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "Your@Password123" -Q "SELECT 1"
interval: 10s
timeout: 3s
retries: 10
networks:
- app-network
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- app-network
volumes:
db-data:
redis-data:
networks:
app-network:
driver: bridgeNginx 反向代理配置
完整 Nginx 配置
# /etc/nginx/nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式(包含上游响应时间)
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'upstream_addr=$upstream_addr '
'upstream_response_time=$upstream_response_time '
'request_time=$request_time';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
client_max_body_size 50m;
# Gzip 压缩
gzip on;
gzip_types text/plain application/json application/javascript text/css;
gzip_min_length 1000;
# 上游服务
upstream kestrel {
server app:5000;
keepalive 32;
}
# HTTPS 重定向
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 安全头
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
location / {
proxy_pass http://kestrel;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 静态文件缓存
location /static/ {
proxy_pass http://kestrel;
proxy_cache_valid 200 30d;
expires 30d;
add_header Cache-Control "public, immutable";
}
# 健康检查(不走日志)
location /health {
proxy_pass http://kestrel;
access_log off;
}
}
}Systemd 服务管理
Linux Systemd 配置
# /etc/systemd/system/myapp.service
[Unit]
Description=My ASP.NET Core Application
After=network.target
[Service]
Type=notify
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/dotnet /var/www/myapp/MyApp.dll
Restart=always
RestartSec=5
SyslogIdentifier=myapp
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
# 环境变量文件
EnvironmentFile=/etc/myapp/env
# 安全加固
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/var/www/myapp/logs
# 资源限制
LimitNOFILE=65536
MemoryMax=512M
[Install]
WantedBy=multi-user.target# 服务管理命令
sudo systemctl daemon-reload
sudo systemctl enable myapp # 开机自启
sudo systemctl start myapp # 启动
sudo systemctl status myapp # 状态
sudo systemctl restart myapp # 重启
sudo journalctl -u myapp -f # 查看日志滚动更新与零停机部署
蓝绿部署
# 蓝绿部署策略
# 1. 当前运行蓝色版本(v1)
# 2. 部署绿色版本(v2)但不切换流量
# 3. 验证绿色版本健康
# 4. 切换 Nginx upstream 到绿色版本
# 5. 保留蓝色版本以便回滚
# Nginx 切换
# upstream kestrel {
# server app-green:5000; # 切换到绿色
# # server app-blue:5000; # 注释掉蓝色
# }
# sudo nginx -s reload # 平滑重载,不影响现有连接优雅关闭
// Program.cs — 优雅关闭配置
builder.WebHost.ConfigureKestrel(options =>
{
options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30);
options.ShutdownTimeout = TimeSpan.FromSeconds(30); // 关闭超时
});
// 自定义关闭逻辑
var app = builder.Build();
var lifetime = app.Lifetime;
lifetime.ApplicationStopping.Register(() =>
{
Console.WriteLine("应用正在关闭...");
// 释放资源、关闭连接
});
lifetime.ApplicationStopped.Register(() =>
{
Console.WriteLine("应用已关闭");
// 记录关闭指标
});优点
缺点
缺点
总结
ASP.NET Core 部署核心:Windows 用 IIS + Hosting Bundle,Linux 用 Docker + Nginx 反向代理。生产环境务必开启 HTTPS、配置反向代理、使用 systemd 或 Supervisor 守护进程。环境配置通过 appsettings.{Env}.json 和环境变量分层管理。Docker 部署是最推荐的现代化方案。
关键知识点
- 先分清这个主题位于请求链路、后台任务链路还是基础设施链路。
- 服务端主题通常不只关心功能正确,还关心稳定性、性能和可观测性。
- 任何框架能力都要结合配置、生命周期、异常传播和外部依赖一起看。
项目落地视角
- 画清请求进入、业务执行、外部调用、日志记录和错误返回的完整路径。
- 为关键链路补齐超时、重试、熔断、追踪和结构化日志。
- 把配置与敏感信息分离,并明确不同环境的差异来源。
常见误区
- 只会堆中间件或组件,不知道它们在链路中的执行顺序。
- 忽略生命周期和线程池、连接池等运行时资源约束。
- 没有监控和测试就对性能或可靠性下结论。
进阶路线
- 继续向运行时行为、可观测性、发布治理和微服务协同深入。
- 把主题和数据库、缓存、消息队列、认证授权联动起来理解。
- 沉淀团队级模板,包括统一异常处理、配置约定和基础设施封装。
适用场景
- 当你准备把《ASP.NET Core 部署》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合 API 服务、后台任务、实时通信、认证授权和微服务协作场景。
- 当需求开始涉及稳定性、性能、可观测性和发布流程时,这类主题会成为基础设施能力。
落地建议
- 先定义请求链路与失败路径,再决定中间件、过滤器、服务边界和依赖方式。
- 为关键链路补日志、指标、追踪、超时与重试策略。
- 环境配置与敏感信息分离,避免把生产参数写死在代码或镜像里。
排错清单
- 先确认问题发生在路由、模型绑定、中间件、业务层还是基础设施层。
- 检查 DI 生命周期、配置来源、序列化规则和认证上下文。
- 查看线程池、连接池、缓存命中率和外部依赖超时。
复盘问题
- 如果把《ASP.NET Core 部署》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《ASP.NET Core 部署》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《ASP.NET Core 部署》最大的收益和代价分别是什么?
