从‘它怎么又挂了?’到‘服务稳如狗’:我是如何用Docker给老旧.NET应用续命的

张开发
2026/4/17 21:14:22 15 分钟阅读

分享文章

从‘它怎么又挂了?’到‘服务稳如狗’:我是如何用Docker给老旧.NET应用续命的
从“它怎么又挂了”到“服务稳如狗”我是如何用Docker给老旧.NET应用续命的三年前接手这个项目时每次半夜被报警短信吵醒看到“服务不可用”的提示我的第一反应总是“它怎么又挂了”——这是一套运行在Windows Server 2008上的古董级内部系统依赖着特定版本的IIS和.NET Framework每次部署都像在拆炸弹。直到去年我们用Docker完成了容器化改造现在同事们开玩笑说这服务“稳如狗”。今天就来聊聊这段“拯救大兵瑞恩”式的技术历险。1. 为什么容器化是老旧.NET应用的救命稻草那个周五下午当我第N次蹲在机房给这台老爷机做“心肺复苏”时突然意识到环境依赖才是真正的凶手。这套系统需要IIS 7.5不能多也不能少.NET Framework 4.5.2特定版本的C运行时库一个早已停止维护的第三方COM组件更可怕的是我们的部署文档里写着“安装完系统后请手动调整以下15项IIS设置”。这种“祖传配置”就像中世纪炼金术配方每次新服务器部署都是场豪赌。容器化带来的三大救赎环境固化把IIS、.NET、COM组件全部打包成不可变镜像部署标准化从“手工艺术品”变成可重复的工业化流程版本控制每个镜像版本都是可追溯的快照提示Windows容器虽然体积较大但对遗留系统兼容性远胜Linux容器2. 改造实战从物理机到容器的惊险跳跃2.1 选择合适的基础镜像在Docker Hub上翻找微软官方镜像时发现版本选择比想象中复杂镜像标签包含组件适用场景mcr.microsoft.com/dotnet/framework/aspnet:4.8IIS ASP.NET大多数Web应用mcr.microsoft.com/dotnet/framework/runtime:4.8仅运行时后台服务mcr.microsoft.com/windows/servercore:ltsc2019纯OS需要完全自定义我们最终选择了aspnet:4.8-windowsservercore-ltsc2019这个标签因为需要IIS支持保持与旧系统相同的Windows Server 2019内核长期支持版本(LTSC)更稳定# 基础镜像选择是成功的第一步 FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc20192.2 处理那些“祖传配置”迁移Web.config时遇到了经典问题原本直接写在配置文件里的服务器IP和路径。我们的解决方案是用环境变量替代硬编码配置通过Dockerfile的ARG和ENV实现多环境适配保留原配置作为默认值确保向后兼容!-- 改造前的死亡配置 -- add keyReportPath value\\192.168.1.100\reports\ / !-- 改造后的灵活配置 -- add keyReportPath value#{REPORT_PATH}# /然后在Dockerfile中使用sed进行替换RUN powershell -Command \ (Get-Content C:\inetpub\wwwroot\Web.config).Replace(#{REPORT_PATH}#, $env:REPORT_PATH) | \ Set-Content C:\inetpub\wwwroot\Web.config2.3 搞定顽固的文件权限那个第三方COM组件有个坏习惯必须往C:\Program Files\Vendor里写日志。在容器世界里这相当于在别人家墙上涂鸦。我们的破解方案在镜像中创建相同路径的目录给IIS应用池身份赋予完全控制权用卷挂载持久化这些“非法建筑”# 在Dockerfile中添加权限设置 RUN mkdir C:\Program Files\Vendor \ icacls C:\Program Files\Vendor /grant IIS AppPool\DefaultAppPool:(OI)(CI)F3. 部署流水线从“手工耿”到工业化生产3.1 构建自动化流水线原来的部署流程手动复制文件到服务器运行5个不同的PowerShell脚本顺序还不能错在IIS管理器里点点点祈祷不要报错现在的CI/CD流程graph LR A[代码提交] -- B[构建镜像] B -- C[运行测试] C -- D[推送镜像到仓库] D -- E[蓝绿部署]实际实现时我们用Jenkins pipeline脚本pipeline { agent any stages { stage(Build) { steps { bat docker build -t legacy-app:%BUILD_NUMBER% . } } stage(Test) { steps { bat docker run --rm legacy-app:%BUILD_NUMBER% powershell .\RunTests.ps1 } } stage(Deploy) { when { branch main } steps { bat kubectl rollout restart deployment/legacy-app } } } }3.2 回滚时光机器的魔法以前回滚意味着找出上周的备份磁带花2小时恢复丢失所有新数据现在只需要# 回滚到上一个版本 kubectl rollout undo deployment/legacy-app # 或者指定历史版本 kubectl rollout history deployment/legacy-app kubectl rollout undo deployment/legacy-app --to-revision34. 那些年我们踩过的坑4.1 内存泄漏容器不是虚拟机最初我们以为容器化能自动解决内存泄漏问题——太天真了。某次半夜服务再次崩溃后我们学会了在Dockerfile中设置内存限制使用Windows性能计数器监控配置自动重启策略# 限制内存为4GB CMD [powershell, New-WebAppPool -Name DefaultAppPool; \ Set-ItemProperty IIS:\AppPools\DefaultAppPool -Name processModel -Value {memoryLimit4096}; \ C:\\ServiceMonitor.exe w3svc]4.2 日志收集别让容器成为黑匣子最初几天我们完全不知道容器内部发生了什么直到建立了ELK日志系统用Filebeat收集IIS日志将应用日志重定向到控制台使用Docker的日志驱动转发到中央仓库# 让应用日志输出到stdout RUN Set-WebConfigurationProperty -PSPath IIS:\ -Filter system.applicationHost/log \ -Name centralLogFileMode -Value CentralW3C5. 效果对比从看护到放养改造前后的关键指标对比指标改造前改造后部署时间4小时15分钟回滚时间2小时30秒崩溃频率每周2-3次半年1次新成员上手时间1个月2天最让我欣慰的是现在可以安心休假了——去年春节七天长假系统零报警。那些曾经让我夜不能寐的“定时炸弹”如今在容器里乖巧得像被驯服的野兽。

更多文章