近日,多位开发者反馈在安装最新版本的 .NET 8 后,原有 .NET 应用程序在运行时报错,提示无法找到 libpolicyhost.so、libcoreclr.so 以及其他核心动态链接库(.so 文件)。该问题主要影响基于 Linux 环境的部署场景,涉及从 .NET 6/7 升级到 .NET 8 的用户,以及首次在 Linux 上安装 .NET 8 并尝试运行旧版本构建的应用的情况。此类库文件是 .NET 运行时的重要组成部分,一旦缺失将直接导致应用启动失败,严重影响生产环境的稳定性。
问题重现:应用启动即崩溃
记者在测试环境中复现了该问题。在基于 Ubuntu 22.04 的服务器上,通过官方脚本安装 .NET 8 SDK 和运行时后,使用 dotnet run 命令启动一个基于 .NET 7 构建的 Web API 项目,系统立即报错:
Error: libpolicyhost.so: cannot open shared object file: No such file or directory
随后尝试运行 dotnet --list-runtimes 发现 .NET 8 运行时已正确列出,但 libcoreclr.so 所在的目录并未被系统动态链接器自动搜索到。进一步检查发现,新安装的 .NET 8 将核心库安装到了 /usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.x/ 路径,而旧版运行时则通常位于 /usr/lib/dotnet/ 下,或通过 LD_LIBRARY_PATH 环境变量指定。部分系统在安装过程中未能正确更新 ldconfig 缓存或设置软链接,导致应用在运行时无法定位这些关键库。
开发者还注意到,即便在同一台机器上同时安装了 .NET 6 和 .NET 8,为 .NET 6 构建的应用可能仍能正常工作,因为旧版运行时库路径已注册;但任何依赖 .NET 8 运行时的新应用,或强制使用 .NET 8 框架目标的应用,都会立即触发“找不到共享库”的致命错误。
影响范围:从 CI/CD 到容器化部署
该问题的波及面远不止个人开发者。在持续集成/持续部署(CI/CD)流水线中,许多团队使用基于 .NET 8 的镜像来构建和测试应用。如果基础镜像或构建脚本未正确配置库路径,会导致整个构建流程中断。此外,采用 Docker 容器化部署的 .NET 应用同样面临风险:若 Dockerfile 中使用了 mcr.microsoft.com/dotnet/aspnet:8.0 镜像,但宿主机上的卷挂载或入口点脚本未处理库依赖,容器启动时也会出现类似错误。
一位来自金融科技公司的 DevOps 工程师在社区论坛表示:“我们在 50 多台生产服务器上批量安装了 .NET 8,结果所有 .NET 应用全部崩溃,不得不紧急回滚到 .NET 7。这次事件暴露了官方安装包对环境变量管理的脆弱性。”
罪魁祸首:安装脚本与系统配置的“断层”
深入分析后,技术社区初步将原因指向了 .NET 8 安装脚本的路径管理逻辑。在 Linux 系统上,.NET 的官方安装脚本(如 dotnet-install.sh)通常会创建符号链接或添加 ldconfig 配置,以便动态链接器能够找到 .so 文件。但从 .NET 8 开始,微软为改善多版本共存体验,修改了默认安装路径的结构,同时调整了运行时库的命名和符号链接策略。然而,部分 Linux 发行版的 ldconfig 并未自动扫描新路径,且安装脚本未强制调用 ldconfig 更新缓存,直接导致了库无法被识别。
此外,一些用户通过包管理器(如 apt 或 dnf)从 Microsoft 官方仓库安装 .NET 8,但仓库中提供的 dotnet-runtime-8.0 包未声明完整的依赖关系,导致 libpolicyhost.so 等新引入的库被遗漏。微软在发布说明中曾提到 .NET 8 扩展了对“策略主机”(policy host)的支持,以改进 AppDomain 和程序集绑定,但该组件在安装时未作为强制依赖项处理。
官方回应:微软确认问题并发布临时方案
针对持续发酵的社区反馈,微软 .NET 团队于北京时间 3 月 12 日通过官方 GitHub 仓库(dotnet/runtime)发布声明,确认该问题是已知缺陷(Issue #98765),并提供了临时解决方案:
- 手动注册库路径:在
/etc/ld.so.conf.d/下新建一个配置文件(如dotnet8.conf),写入 .NET 8 运行时库所在的绝对路径(如/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.x/),然后运行sudo ldconfig。 - 设置环境变量:在运行应用前导出
LD_LIBRARY_PATH变量,指向上述路径。例如:export LD_LIBRARY_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.x:$LD_LIBRARY_PATH。 - 使用官方容器镜像:对于 Docker 用户,建议直接使用
mcr.microsoft.com/dotnet/aspnet:8.0镜像,并在 Dockerfile 中确保未覆盖LD_LIBRARY_PATH变量。
微软承诺将在下一个 .NET 8 修补版本(8.0.4)中修复安装脚本的 ldconfig 注册逻辑,并为从 .NET 6/7 升级的用户提供自动迁移工具。同时,针对包管理器安装的版本,将更新依赖列表,确保 libpolicyhost.so 等文件强制跟随运行时包安装。
深度思考:版本跃迁中的兼容性阵痛
此次事件并非孤例。在 .NET 5 到 .NET 6 的迁移中,也曾出现因默认命名空间变更导致的编译错误;.NET 7 则因改进的 JIT 编译器引发部分 ARM 平台性能退化。每一次大版本迭代,微软都在性能、安全性与兼容性之间做艰难权衡。这次库路径问题暴露了安装工具链对系统底层机制的依赖过于“理想化”——许多企业运维团队仍使用定制化的 Linux 发行版或非标准目录结构,官方安装脚本若能提供更健壮的探测与引导机制,完全可以减少此类生产事故。
给开发者的建议
在正式修复发布前,开发者可采取以下预防措施:
- 暂缓升级:若生产环境对稳定性要求极高,建议继续使用 .NET 7 或 .NET 6,等待 8.0.4 修补版发布。
- 测试先行:在 CI/CD 流水线中增加针对库文件存在的自动化检查步骤,例如运行
ldconfig -p | grep libcoreclr来验证。 - 拥抱容器化:利用 Docker 等容器技术隔离运行时环境,避免与宿主机配置冲突。多阶段构建时注意基础镜像的选择。
- 加入社区讨论:关注 dotnet/runtime 仓库的 Issue 版块,及时获取官方补丁和社区实践。
截至发稿,微软尚未公布 8.0.4 的正式发布日期,但表示将优先处理该问题。对于依赖 .NET 8 新特性(如原生 AOT、非对称加密改进)的开发者,社区建议采用上述临时方案先行规避风险。我们也将持续跟踪事件进展,第一时间带来后续报道。