尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
.NET 项目 Docker 化 + Nginx 反向代理,一篇讲透
大家好我是码农刚子。前几天帮朋友排查一个 .NET 项目的部署问题他说「我本地跑得好好的一上服务器就崩」。翻了一下他的 Dockerfile问题挺典型的——单阶段构建、镜像 800MB、端口没映射对、Nginx 配置抄了份三年前的老模板。今天把这一套从头到尾理一遍从 Dockerfile 到 Compose 再到 Nginx 反向代理每一步讲清楚「为什么这么写」而不是只给一段能跑的代码。1. 先搞懂你到底需要几层构建很多人写 .NET 的 Dockerfile上来就FROM mcr.microsoft.com/dotnet/sdk:8.0然后 build run 全在一个镜像里。结果镜像动不动七八百 MB部署拉取慢得要死。正确的做法是多阶段构建——用 SDK 镜像编译用 ASP.NET Core 运行时镜像跑。打个比方你盖房子的时候需要起重机SDK但住进去的时候不需要把起重机也留在家里。核心思路构建阶段用 SDK 镜像大而全运行阶段用运行时镜像小而精最终只保留运行时那一层。来看一份标准的 ASP.NET Core Dockerfile# 第一阶段构建publish FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src # 先拷贝 .csproj利用 Docker 缓存 COPY *.csproj . RUN dotnet restore # 再拷贝全部源码 COPY . . RUN dotnet publish -c Release -o /app/publish # 第二阶段运行runtime FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime WORKDIR /app COPY --frombuild /app/publish . EXPOSE 8080 ENTRYPOINT [dotnet, YourApp.dll]这里有两个细节很多人踩坑。第一个为什么先拷贝 .csproj 再拷贝全部代码因为 Docker 是分层缓存的。如果每次改一行代码就要重新 restore 一次 NuGet 包那构建速度会慢到怀疑人生。先拷贝 .csproj → restore → 再拷贝源码 → publish这样只要 .csproj 没变restore 那一层就能直接用缓存。第二个端口到底是 80 还是 8080这是个经典坑。.NET 8 开始官方 ASP.NET Core 镜像的默认端口从 80 改成了 8080。如果你还照着老教程写 EXPOSE 80容器跑起来之后你会发现怎么都访问不到——不是服务没启动是端口不对。2. Docker Compose把多服务串起来如果你的项目只有一个 Web 应用docker run 一下就够了。但现实场景里通常不止一个服务——Web 应用 数据库 Redis Nginx四五个容器是常态。这时候 Docker Compose 就是标配。来看一份典型的 docker-compose.ymlversion: 3.8 services: webapp: build: context: ./YourApp dockerfile: Dockerfile ports: - 5000:8080 environment: - ASPNETCORE_ENVIRONMENTProduction - ConnectionStrings__DefaultHostdb;Databaseappdb;Usernamepostgres;Passwordsecret depends_on: - db - redis restart: unless-stopped db: image: postgres:16-alpine environment: - POSTGRES_DBappdb - POSTGRES_PASSWORDsecret volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped redis: image: redis:7-alpine volumes: - redis_data:/data restart: unless-stopped volumes: postgres_data: redis_data:说几个容易忽略的点。depends_on 只保证启动顺序不保证服务就绪。很多人以为加了 depends_onWeb 就会等数据库准备好再启动。其实不是它只等容器启动不等服务就绪。PostgreSQL 容器起来了但数据库初始化还没完成这时候 Web 去连就会报错。解决办法有两种一是在程序里加重试逻辑推荐用 Polly二是用 healthcheck condition。# 在 db 服务里加健康检查 healthcheck: test: [CMD-SHELL, pg_isready -U postgres] interval: 5s timeout: 5s retries: 5 # 在 webapp 的 depends_on 里指定条件 depends_on: db: condition: service_healthy redis: condition: service_started数据一定要挂 volume。数据库的数据、Redis 的持久化文件、用户上传的文件这些都不能放在容器里。容器一删数据就没了到时候哭都来不及。3. Nginx 反向代理为什么需要它有人会问Kestrel 本身就是 Web 服务器直接暴露端口不行吗为什么还要在前面加一层 Nginx这个问题问得好。Kestrel 确实是个不错的服务器但它的设计目标是处理动态请求。静态文件、负载均衡、SSL 终止、限速、压缩这些事交给 Nginx 效率高得多。可以这么理解——Kestrel 是做菜的厨师Nginx 是前厅的服务员。厨师也能端菜但专业的事交给专业的人来做整体效率更高。来看一份完整的 Nginx 反向代理配置# /etc/nginx/conf.d/your-app.conf server { listen 80; server_name yourdomain.com www.yourdomain.com; # 静态文件直接由 Nginx 处理 location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2?)$ { root /var/www/your-app/wwwroot; expires 30d; access_log off; } # 动态请求转发给 Kestrel location / { proxy_pass http://webapp:8080; 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_buffering off; proxy_read_timeout 100s; } }这段配置看起来长但核心就两件事静态文件自己处理动态请求转发给后端。重点说几个 header 的作用很多人抄了配置但不知道为什么要加。X-Forwarded-For 和 X-Forwarded-Proto——告诉后端用户的真实 IP 和请求协议http 还是 https。没有这两个头你的 .NET 程序拿到的 IP 永远是 Nginx 容器的 IP而且会以为所有请求都是 http 的。Host——保留原始域名。不然重定向的时候会跳转到内部地址用户就懵了。Upgrade 和 Connection keep-alive——支持 WebSocket 和长连接。如果你的应用用了 SignalR这两行必须有。光配置 Nginx 还不够.NET 这边也要配合一下。在 Program.cs 里加上转发头中间件// Program.cs 里在 builder.Build() 之前加 builder.Services.ConfigureForwardedHeadersOptions(options { options.ForwardedHeaders ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); // 在 app.UseHttpsRedirection() 之前加 app.UseForwardedHeaders();KnownNetworks 和 KnownProxies 清空是因为 Docker 环境里代理 IP 不固定不清除的话转发头会被忽略——这个坑我踩过两次。4. 把 Nginx 也放进 Compose 里既然所有服务都容器化了Nginx 当然也不应该单独装在宿主机上。把 Nginx 加到 docker-compose.yml 里整个部署就完整了services: webapp: build: ./YourApp expose: - 8080 # 注意这里用 expose 而不是 ports # 因为只需要被 Nginx 访问不需要暴露到宿主机 restart: unless-stopped nginx: image: nginx:alpine ports: - 80:80 - 443:443 volumes: - ./nginx/conf.d:/etc/nginx/conf.d - ./nginx/ssl:/etc/nginx/ssl - ./nginx/logs:/var/log/nginx depends_on: - webapp restart: unless-stopped这里有个细节webapp 用 expose不用 ports。expose 只是声明容器内部用哪个端口不会映射到宿主机。所有外部请求都走 Nginxwebapp 不直接对外——安全上更干净。而且在同一个 Compose 网络里服务之间可以直接用服务名通信所以 Nginx 配置里proxy_pass http://webapp:8080直接就能通不需要查 IP。5. 几个踩过的坑最后说几个我自己踩过、也见别人踩过的坑帮大家省点时间。坑一HTTPS 重定向死循环Nginx 做了 SSL 终止和后端之间走的是 HTTP。但 .NET 里开了app.UseHttpsRedirection()它一看请求是 HTTP就重定向到 HTTPS。结果就是用户请求到 NginxHTTPS→ Nginx 转发给后端HTTP→ 后端说你应该用 HTTPS → 重定向 → 又到 Nginx → 无限循环。解决办法确保 UseForwardedHeaders 在 UseHttpsRedirection 之前调用这样 .NET 就能通过 X-Forwarded-Proto 头知道原始请求是 HTTPS 的就不会瞎重定向了。坑二时区不对Docker 容器默认时区是 UTC国内用户会发现时间差了 8 小时。日志时间不对、定时任务不准都是这个原因。在 docker-compose.yml 里给服务加上环境变量就行environment: - TZAsia/Shanghai坑三SignalR 连接不上如果用了 SignalR 或者 Blazor ServerNginx 配置里必须加上 WebSocket 支持的那几行 header而且proxy_buffering要关掉。还有个容易忽略的点proxy_read_timeout默认是 60 秒WebSocket 连接如果 60 秒没有数据传输就会被断开。建议调到 100 秒以上或者在应用层加心跳。坑四文件上传大小限制Nginx 默认请求体大小只有 1MB传个稍微大一点的文件就 413 报错。在 server 块里加一行client_max_body_size 50M;同时 .NET 那边也要调Program.cs里的MaxRequestBodySize两边都要放开才行。6. 收尾把这一套串起来之后部署就变成了一件很简单的事。代码提交了服务器上docker compose up -d --build一下完事。我一直觉得部署这件事的理想状态是「boring」——不需要什么花活稳定、可重复、出问题能快速回滚。一句话总结Dockerfile 做多阶段构建控体积Compose 做多服务编排管依赖Nginx 做反向代理扛流量——三件套各司其职部署就稳了。这篇里提到的所有配置文件都是我在生产环境用过的可以直接拿去改改用。如果有什么我没覆盖到的场景评论区聊。原文链接.NET 项目 Docker 化 Nginx 反向代理一篇讲透 - 码录集
RELATED

相关推荐

掌握Loop Engineering:让AI替你写Prompt,提升编程效率

掌握Loop Engineering:让AI替你写Prompt,提升编程效率

Loop Engineering是Google Chrome开发者关系负责人Addy Osmani提出的概念,旨在通过设计系统自动为AI Agent生成提示,而非手动编写。该概念包含五个核心构件:自动化心跳、工作树、技能、插件与连接器、子Agent,以及记忆。这些构件旨…

📅 2026/10/6 20:51:15
大白话吃透Transformer:词嵌入与位置编码(小白零基础入门)

大白话吃透Transformer:词嵌入与位置编码(小白零基础入门)

现在大火的 ChatGPT、AI 代码助手、各类大模型应用,底层核心全部依托 Transformer 架构。 2017年那篇 《Attention Is All You Need》(注意力即是一切)的论文,彻底颠覆了传统序列模型,淘汰了训练缓慢、长文本乏力的 RN…

📅 2026/10/6 20:51:15
老游戏超分怎么升到最新:用 DLSS Swapper 免费替换 DLSS/FSR/XeSS DLL 完整指南(一键回滚)

老游戏超分怎么升到最新:用 DLSS Swapper 免费替换 DLSS/FSR/XeSS DLL 完整指南(一键回滚)

老游戏超分怎么升到最新:用 DLSS Swapper 免费替换 DLSS/FSR/XeSS DLL 完整指南(一键回滚) 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper DLSS Swapper 是一款免费开源的 Windows 工…

📅 2026/10/6 20:46:15
MORE NEWS

更多资讯

📰

赛事直播高并发架构实战:从容量规划到故障排查的完整指南

87天,听起来还有段时间。但对做赛事直播的技术团队来说,这个数字已经很扎眼了。世界杯的赛程是固定的,服务器可不会因为你的代码还没改完就把开球时间往后推。104场比赛,意味着从小组赛到决赛,每天都要处理高规格的直播…

📰

Superpowers开发者工具链:本地化AI编程范式实战指南

1. “Superpowers”不是超能力,是开发者工具链的代际跃迁最近在好几个技术群和开源社区里,频繁看到“superpowers”这个词被反复提起——不是漫威电影里的变种人设定,也不是什么玄学概念,而是真实存在于你编辑器侧边栏、终端命令行…

📰

CircuitJS在线电路模拟器实战:快速调节PWM占空比并验证波形

如果你在搜索引擎里输入“circuitjs 占空比”,大概率是手上正拿着一个LED调光或电机调速的需求,想在动手焊接之前先看看PWM波形到底长什么样。我遇到过很多类似的场景:只想验证一个分压电阻、看一眼RC充放电曲线、确认555电路输出的占空比公式…

📰

OpenShell 使用指南:经典开始菜单、资源管理器增强与企业部署

不知道还有多少朋友和我一样,习惯了 Windows 7 那一套开始菜单之后,用 Win10、Win11 总觉得别扭。老菜单左边是常用程序列表,右边是文档、控制面板、关机按钮,整个界面就是一份“常用操作清单”,鼠标滑过去点一下就行了…

📰

Java程序员转大模型开发:向量数据库是必须跨过的第一道坎

最近不少Java后端的同行问我:转大模型开发是不是得先把Python啃透,再补完整套深度学习理论?我的回答一直很直接:先别急着买书。Java程序员转向大模型应用开发,第一个真正需要从零开始补的底层模块,是向量数…

📰

ReentrantLock源码解析:从AQS到CLH队列的并发锁实现

1. 从一次“抢座位”说起:ReentrantLock到底在锁什么我在写并发代码时,遇到过一个很有意思的场景:办公室里只有一个会议室,大家都在抢。有人进去开会了,后面来的人就得在门外排队等候,会议结束出来一个&…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬