dotnet publish 打包前必须指定 --runtime linux-x64 和 --self-contained true,确保与 kubernetes 中 linux 容器运行时匹配,避免 exec format error;dockerfile 需多阶段构建且 sdk 与 runtime 镜像主版本一致(如 8.0);健康探针须配置正确 path(如 /healthz)并匹配应用绑定端口;configmap/secret 挂载需设置 defaultmode 和 runasuser 以避免权限问题。

用 dotnet publish 打包前必须确认目标运行时
直接在开发机上 dotnet publish 默认生成 Windows 平台的二进制,K8s Pod 里跑的是 Linux 容器(绝大多数情况),会启动失败。错误现象通常是容器反复 CrashLoopBackOff,kubectl logs 显示 “exec format error” 或 “no such file or directory”(其实是 ELF 头不匹配)。
正确做法是显式指定 --runtime 和 --self-contained:
dotnet publish -c Release -r linux-x64 --self-contained true -o ./publish
-
-r linux-x64:目标运行时,和基础镜像保持一致(如mcr.microsoft.com/dotnet/runtime:8.0是 linux-x64) -
--self-contained true:避免容器内缺失 .NET 运行时;若用runtime-deps镜像可设为 false,但需确保镜像含对应版本的 native 依赖(如 libicu) - 输出目录
./publish后续 COPY 到 Docker 镜像中
Dockerfile 必须用多阶段构建且基础镜像要匹配 SDK/Runtime 版本
常见错误是用 mcr.microsoft.com/dotnet/sdk:8.0 构建,却用 mcr.microsoft.com/dotnet/aspnet:7.0 运行——版本不匹配会导致 System.MissingMethodException 或启动时静默退出。
推荐写法(以 ASP.NET Core 为例):
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY . . RUN dotnet publish -c Release -r linux-x64 --self-contained true -o /app/publish FROM mcr.microsoft.com/dotnet/aspnet:8.0 WORKDIR /app COPY --from=build /app/publish . ENTRYPOINT ["./YourApp"]
- SDK 和 runtime 镜像主版本号(8.0)必须严格一致
- 不要用
latest标签,它可能指向不同大版本,CI 环境易出错 - 若应用不带 Web 依赖,用
runtime:8.0替代aspnet:8.0,镜像体积更小
K8s Deployment 中 livenessProbe 和 readinessProbe 要适配 .NET 健康检查端点
.NET 6+ 默认启用 /healthz(或自定义路径),但 K8s probe 若直接用 httpGet 且未配置 path,默认请求 /,返回 404 导致探针失败、Pod 反复重启。
Deployment 片段示例:
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
- 确保
Program.cs中已注册健康检查中间件:app.UseHealthChecks("/healthz") -
port必须和容器EXPOSE及app.Run("http://*:8080")绑定端口一致 -
initialDelaySeconds要大于应用冷启动时间(尤其含 EF Core 迁移或外部依赖初始化时)
Secret 和 ConfigMap 挂载后权限问题常导致 .NET 应用读取失败
Linux 容器中,挂载的 ConfigMap 或 Secret 文件默认权限是 644,但 .NET 的 IConfiguration 从文件加载 JSON 时若路径含特殊字符或父目录不可读,会抛 UnauthorizedAccessException —— 实际不是权限不足,而是挂载点父目录(如 /app/config)被设为 700 且属主非容器用户。
解决方法:
- 挂载时显式设置
defaultMode: 0644和runAsUser: 1001(与镜像中非 root 用户 UID 一致) - 避免挂载到深层嵌套路径,优先用
/app/config/appsettings.json这类扁平结构 - 在
Startup.cs或Program.cs中加日志确认配置源是否加载成功:Console.WriteLine($"Config loaded from: {builder.Configuration.AsEnumerable()}");
最易被忽略的是:.NET 应用默认以非 root 用户(如 UID 1001)运行,而 ConfigMap 挂载点的父目录若由 root 创建且无执行权限(755 以上才允许进入),就会卡在目录访问环节,错误日志里却只显示“文件不存在”。










