使用Docker Compose运行.NET Docker容器时本地与容器编译冲突求助
我已经查阅了相关文档,但在通过Docker Compose启动容器时遇到了问题。因为项目中有多个对应目录下csproj文件的应用,所以采用Docker Compose来管理容器;但文档里提到的Dockerfile属性增加了复杂度。
我的docker-compose.yml配置如下:
version: '3' services: app1: image: mcr.microsoft.com/dotnet/core/sdk:2.2 container_name: app1 restart: on-failure working_dir: /service command: bash -c "dotnet build && dotnet bin/Debug/netcoreapp2.2/App1.dll" ports: - 5001:5001 - 5000:5000 volumes: - "./App1:/service" app2: image: mcr.microsoft.com/dotnet/core/sdk:2.2 container_name: app2 restart: on-failure working_dir: /service command: bash -c "dotnet build && dotnet bin/Debug/netcoreapp2.2/App2.dll" ports: - 5001:5001 - 5000:5000
项目结构:
Microservice.sln App1/App1.csproj App2/App2.csproj
运行容器后,IDE出现大量语法错误提示,本地构建直接失败。请问有没有办法同时支持应用的本地编译与Docker内编译?
你碰到的是Docker构建产物和本地开发环境冲突的典型问题——当把本地项目目录挂载到容器里执行dotnet build时,容器生成的bin、obj目录会同步回本地。这些文件要么带着容器内的root权限(本地IDE读写不了),要么和本地构建的缓存、依赖版本不兼容,直接导致IDE报错、本地编译失败。下面给你几个实用的解决思路:
1. 快速修复:隔离Docker的中间产物
不需要改太多配置,只要让Docker在容器内生成的bin/obj不污染本地目录就行:
第一步:添加.dockerignore
在App1/和App2/目录下各建一个.dockerignore文件,内容很简单:
bin/ obj/
这能让Docker构建时忽略本地的这两个目录,同时也避免容器把自己生成的这两个目录同步回本地。
第二步:修改Docker Compose的卷配置
给每个服务的卷加上匿名卷,专门隔离容器内的bin和obj:
version: '3' services: app1: image: mcr.microsoft.com/dotnet/core/sdk:2.2 container_name: app1 restart: on-failure working_dir: /service command: bash -c "dotnet build && dotnet bin/Debug/netcoreapp2.2/App1.dll" ports: - "5002:5001" # 这里要改!原来两个服务都用5001端口会冲突 - "5000:5000" volumes: - "./App1:/service" - "/service/bin" # 匿名卷,容器内的bin目录不会同步到本地 - "/service/obj" # 同理隔离obj目录 app2: image: mcr.microsoft.com/dotnet/core/sdk:2.2 container_name: app2 restart: on-failure working_dir: /service command: bash -c "dotnet build && dotnet bin/Debug/netcoreapp2.2/App2.dll" ports: - "5003:5001" # 换成另一个宿主机端口 - "5001:5000" volumes: - "./App2:/service" - "/service/bin" - "/service/obj"
特别提醒:你原来的配置里两个服务都映射了
5001:5001,这会导致端口冲突,必须改成不同的宿主机端口,比如app1用5002,app2用5003。
这样改完后,Docker构建的中间文件会存在容器的匿名卷里,不会影响本地项目,本地IDE和Docker构建就能互不干扰了。
2. 更规范的方案:用多阶段构建的Dockerfile
虽然你提到Dockerfile增加了复杂度,但这种方式能彻底分离构建和运行环境,而且完全不会污染本地目录,长期来看更靠谱:
第一步:给每个App写Dockerfile
以App1/Dockerfile为例:
# 构建阶段:用SDK镜像完成编译 FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src # 先拷贝csproj单独restore,利用Docker缓存 COPY App1.csproj . RUN dotnet restore # 再拷贝所有代码文件 COPY . . # 编译到指定目录 RUN dotnet build -c Debug -o /app/build # 运行阶段:用轻量的ASP.NET镜像 FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime WORKDIR /app # 从构建阶段拷贝编译好的产物 COPY --from=build /app/build . # 设置启动命令 ENTRYPOINT ["dotnet", "App1.dll"]
App2/Dockerfile只需要把最后一行的App1.dll改成App2.dll就行。
第二步:更新Docker Compose配置
让每个服务基于本地的Dockerfile构建,不用再挂载目录了:
version: '3' services: app1: build: ./App1 # 指向App1目录下的Dockerfile container_name: app1 restart: on-failure ports: - "5002:5001" - "5000:5000" app2: build: ./App2 container_name: app2 restart: on-failure ports: - "5003:5001" - "5001:5000"
这样Docker会在容器内部完成所有构建操作,本地项目目录完全不受影响,你可以正常用IDE编译运行,Docker也能独立构建镜像启动服务。
3. 临时 workaround:调整本地IDE的构建路径
如果暂时不想改Docker配置,也可以让本地IDE的构建输出和Docker分开:
- 在Visual Studio里,右键项目→属性→生成→高级,把“输出路径”改成
./LocalBin/Debug/netcoreapp2.2这类和Docker的bin目录不同的路径 - 在Rider或VS Code里,直接修改
.csproj文件的<OutputPath>属性,指定本地构建的输出目录。
不过这种方式比较繁琐,每个项目都要改,不如前两种方法彻底。
内容的提问来源于stack exchange,提问作者AppDeveloper

