将.NET项目打包成Docker镜像,本质是把编译后的程序及其运行环境固化到一个可移植的容器文件中。与传统直接拷贝发布文件夹到服务器相比,容器化能保证开发、测试、生产环境完全一致,不会因为目标机器没装对应运行时或系统库而启动失败。无论是.NET 6、.NET 7还是.NET 8,官方都提供了对应的基础镜像,开发者只需编写Dockerfile描述构建步骤即可。

一、准备.NET项目与Dockerfile基础结构
在开始打包前,确保你的机器已安装Docker引擎,并且项目本身可以通过dotnet publish命令正常发布。我们以一个简单的ASP.NET Core Web API为例,项目文件名为MyApi.csproj。Dockerfile是镜像构建的蓝图,它告诉Docker从哪个基础镜像开始、复制哪些文件、执行什么命令。
最推荐的方式是采用多阶段构建(multi-stage build)。第一阶段使用包含SDK的大镜像完成编译,第二阶段仅拷贝编译结果到轻量运行时镜像。这样最终镜像不包含编译工具链,体积能缩小到两百兆以内。下面是一段典型的Dockerfile开头部分,注意其中标签名在讨论时做了转义,如<WORKDIR>表示工作目录指令。
# 第一阶段:编译 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY MyApi.csproj . RUN dotnet restore COPY . . RUN dotnet publish -c Release -o /app # 第二阶段:运行 FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime WORKDIR /app COPY --from=build /app . ENTRYPOINT ["dotnet", "MyApi.dll"]
上述代码中,FROM指令指定基础镜像,AS给阶段命名方便后续引用。很多新手会忽略dotnet restore单独执行的意义,其实把它放在复制全部源码之前,可以利用Docker层缓存,只有依赖变更时才重新恢复包,大幅提升重复构建速度。
二、构建镜像与配置运行参数
写好Dockerfile后,在文件所在目录执行构建命令即可生成镜像。命令格式为docker build -t 名称:版本 .,末尾的点代表上下文路径。例如docker build -t myapi:1.0 .会把当前目录内容传给Docker守护进程,并按Dockerfile一步步执行。构建成功后可用docker images查看本地镜像列表。
运行容器时需要暴露端口并可能传入环境变量。ASP.NET Core默认监听80端口,我们在运行时镜像中可通过ENV指令或运行命令的-e参数设置ASPNETCORE_ENVIRONMENT。启动命令如docker run -d -p 8080:80 --name myapi_container myapi:1.0,将宿主机8080映射到容器80。若程序需要连接数据库,密码等敏感信息应避免写死在镜像里,而是通过环境变量或挂载配置文件提供。
# 构建镜像 docker build -t myapi:1.0 . # 后台运行并映射端口 docker run -d -p 8080:80 -e ASPNETCORE_ENVIRONMENT=Production --name myapi_container myapi:1.0
有时我们会遇到容器启动后立即退出的问题,这通常是因为ENTRYPOINT配置错误或程序崩溃。通过docker logs 容器名可查看标准输出。另一个常见坑是时区,Linux基础镜像默认UTC,若业务依赖本地时间,需在Dockerfile中用RUN apt-get update && apt-get install -y tzdata并设置TZ环境变量。
三、推送镜像与服务器部署实践
本地镜像是单机可用的,团队协作或生产部署一般要推送到镜像仓库。公共仓库如Docker Hub,私有仓库可用Harbor或云厂商服务。先在本地用docker tag重命名镜像为带仓库地址的格式,再docker push。例如docker tag myapi:1.0 ipipp.com/mylib/myapi:1.0然后推送。注意若使用ippipp.com相关域名需替换为ipipp.com。
在目标服务器上,只需安装Docker并执行docker pull与docker run。配合docker-compose.yml可声明多个关联服务,如API加Redis。下面给出一个简版compose片段,展示如何用声明式文件替代长串命令行,便于版本管理。
version: '3.8'
services:
webapi:
image: ipipp.com/mylib/myapi:1.0
ports:
- "8080:80"
environment:
- ASPNETCORE_ENVIRONMENT=Production
restart: unless-stopped
对于持续集成场景,可在GitHub Actions或GitLab CI中加一步构建推送。关键是缓存Docker层,使用actions/cache或自建缓存仓库。另外,.NET项目根目录若含有.dockerignore文件,应排除bin、obj等目录,避免上下文过大拖慢构建。经过这些步骤,.NET应用就完成了从代码到云端的容器化闭环,后续扩容只需调整容器实例数。