docker

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Docker

Docker

Core Principles

核心原则

  1. Multi-stage builds always — Separate build and runtime stages. Build in the SDK image, run in the ASP.NET runtime image.
  2. Non-root by default — .NET container images support
    USER app
    by default since .NET 8. Never run as root in production.
  3. Layer caching matters — Copy
    .csproj
    files and restore before copying source code. This caches NuGet dependencies across builds.
  4. Health probes at the orchestrator level — Expose a
    /health/live
    endpoint and let Kubernetes/Compose probe it. Chiseled and default aspnet images have no shell or curl, so in-image
    HEALTHCHECK
    commands have nothing to run with.
  1. 始终使用多阶段构建 —— 分离构建阶段与运行时阶段。在SDK镜像中构建,在ASP.NET运行时镜像中运行。
  2. 默认使用非根用户 —— 自.NET 8起,.NET容器镜像默认支持
    USER app
    。生产环境中绝不要以根用户身份运行。
  3. 分层缓存至关重要 —— 在复制源代码前,先复制
    .csproj
    文件并执行还原操作。这样可以在多次构建间缓存NuGet依赖。
  4. 在编排器层面配置健康探针 —— 暴露
    /health/live
    端点,让Kubernetes/Compose对其进行探测。精简版(Chiseled)和默认aspnet镜像不包含shell或curl,因此镜像内的
    HEALTHCHECK
    命令没有可执行的工具。

Patterns

实践模式

Multi-Stage Dockerfile for Web API

Web API的多阶段Dockerfile

dockerfile
undefined
dockerfile
undefined

Stage 1: Build

Stage 1: Build

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src

Copy project files and restore (cached layer)

Copy project files and restore (cached layer)

COPY ["src/MyApp.Api/MyApp.Api.csproj", "src/MyApp.Api/"] COPY ["src/MyApp.Domain/MyApp.Domain.csproj", "src/MyApp.Domain/"] COPY ["Directory.Build.props", "."] COPY ["Directory.Packages.props", "."] RUN dotnet restore "src/MyApp.Api/MyApp.Api.csproj"
COPY ["src/MyApp.Api/MyApp.Api.csproj", "src/MyApp.Api/"] COPY ["src/MyApp.Domain/MyApp.Domain.csproj", "src/MyApp.Domain/"] COPY ["Directory.Build.props", "."] COPY ["Directory.Packages.props", "."] RUN dotnet restore "src/MyApp.Api/MyApp.Api.csproj"

Copy everything and build

Copy everything and build

COPY . . RUN dotnet publish "src/MyApp.Api/MyApp.Api.csproj"
-c Release
-o /app/publish
--no-restore
COPY . . RUN dotnet publish "src/MyApp.Api/MyApp.Api.csproj"
-c Release
-o /app/publish
--no-restore

Stage 2: Runtime

Stage 2: Runtime

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime WORKDIR /app
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime WORKDIR /app

Non-root user (default in .NET 8+ images)

Non-root user (default in .NET 8+ images)

USER app
COPY --from=build /app/publish .
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
undefined
USER app
COPY --from=build /app/publish .
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
undefined

Container Health Probes

容器健康探测

Prefer orchestrator-level probes (Kubernetes
livenessProbe
, Compose
healthcheck
) over a Dockerfile
HEALTHCHECK
— the standard
aspnet
and chiseled images ship no shell, no curl, and no wget, so there is nothing inside the container to run the probe with. Point the orchestrator at
/health/live
:
yaml
undefined
优先使用编排器层面的探测(Kubernetes
livenessProbe
、Compose
healthcheck
),而非Dockerfile中的
HEALTHCHECK
——标准
aspnet
和 精简版镜像不包含shell、curl和wget,因此容器内没有可执行探测命令的工具。将编排器指向
/health/live
yaml
undefined

docker-compose — probe from outside the app process

docker-compose — probe from outside the app process

services: api: healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health/live || exit 1"] interval: 30s timeout: 3s retries: 3
services: api: healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health/live || exit 1"] interval: 30s timeout: 3s retries: 3

Note: CMD-SHELL requires a shell + wget in the image. Use a non-chiseled

Note: CMD-SHELL requires a shell + wget in the image. Use a non-chiseled

variant for this, or better: let Kubernetes httpGet probes do it —

variant for this, or better: let Kubernetes httpGet probes do it —

they run from the kubelet, needing nothing inside the image.

they run from the kubelet, needing nothing inside the image.


If you must have an in-image HEALTHCHECK, base the runtime stage on a
non-chiseled image that includes `wget` — never re-run the app binary as the
probe command; that starts a second instance instead of checking the first.

如果必须在镜像内配置HEALTHCHECK,请将运行时阶段基于包含`wget`的非精简版镜像——绝不要将应用二进制文件作为探测命令重新运行;这会启动第二个实例,而非检查第一个实例的状态。

.dockerignore

.dockerignore配置

**/.git
**/.vs
**/bin
**/obj
**/node_modules
**/Dockerfile*
**/docker-compose*
**/tests
**/.git
**/.vs
**/bin
**/obj
**/node_modules
**/Dockerfile*
**/docker-compose*
**/tests

Docker Compose for Local Development

本地开发的Docker Compose配置

Key .NET-specific concerns — pass connection strings via environment, use
depends_on
with health checks:
yaml
services:
  api:
    build:
      context: .
      dockerfile: src/MyApp.Api/Dockerfile
    ports:
      - "5000:8080"
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ConnectionStrings__Default=Host=postgres;Database=myapp;Username=postgres;Password=postgres
      - ConnectionStrings__Redis=redis:6379
    depends_on:
      postgres:
        condition: service_healthy
  # Add postgres/redis services with healthcheck — standard boilerplate
.NET特有的关键注意事项——通过环境变量传递连接字符串,结合健康检查使用
depends_on
yaml
services:
  api:
    build:
      context: .
      dockerfile: src/MyApp.Api/Dockerfile
    ports:
      - "5000:8080"
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ConnectionStrings__Default=Host=postgres;Database=myapp;Username=postgres;Password=postgres
      - ConnectionStrings__Redis=redis:6379
    depends_on:
      postgres:
        condition: service_healthy
  # Add postgres/redis services with healthcheck — standard boilerplate

Optimized Build with .slnx

使用.slnx的优化构建

For solutions with multiple projects, restore only the necessary projects.
dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
对于包含多个项目的解决方案,仅还原必要的项目。
dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

Copy solution and all project files

Copy solution and all project files

COPY .slnx . COPY Directory.Build.props . COPY Directory.Packages.props . COPY src/**/.csproj ./src/
COPY .slnx . COPY Directory.Build.props . COPY Directory.Packages.props . COPY src/**/.csproj ./src/

Restore project structure

Restore project structure

RUN for file in src/**/*.csproj; do
mkdir -p $(dirname $file) && mv $file $(dirname $file)/;
done RUN dotnet restore
COPY . . RUN dotnet publish src/MyApp.Api -c Release -o /app/publish --no-restore
undefined
RUN for file in src/**/*.csproj; do
mkdir -p $(dirname $file) && mv $file $(dirname $file)/;
done RUN dotnet restore
COPY . . RUN dotnet publish src/MyApp.Api -c Release -o /app/publish --no-restore
undefined

Health Check Endpoint

健康检查端点

csharp
// In Program.cs — lightweight health endpoint for Docker
app.MapGet("/health/live", () => Results.Ok("healthy"))
    .ExcludeFromDescription();
csharp
// In Program.cs — lightweight health endpoint for Docker
app.MapGet("/health/live", () => Results.Ok("healthy"))
    .ExcludeFromDescription();

Anti-patterns

反模式

Don't Use SDK Image for Runtime

不要将SDK镜像用于运行时

dockerfile
undefined
dockerfile
undefined

BAD — SDK image is 900MB+, includes compilers

BAD — SDK image is 900MB+, includes compilers

FROM mcr.microsoft.com/dotnet/sdk:10.0 COPY . . RUN dotnet run
FROM mcr.microsoft.com/dotnet/sdk:10.0 COPY . . RUN dotnet run

GOOD — separate build and runtime, runtime image is ~200MB

GOOD — separate build and runtime, runtime image is ~200MB

FROM mcr.microsoft.com/dotnet/aspnet:10.0
undefined
FROM mcr.microsoft.com/dotnet/aspnet:10.0
undefined

Don't Copy Everything Before Restore

不要在还原前复制所有内容

dockerfile
undefined
dockerfile
undefined

BAD — any source change invalidates the NuGet cache

BAD — any source change invalidates the NuGet cache

COPY . . RUN dotnet restore
COPY . . RUN dotnet restore

GOOD — copy only project files first, then restore

GOOD — copy only project files first, then restore

COPY ["src/MyApp.Api/MyApp.Api.csproj", "src/MyApp.Api/"] RUN dotnet restore "src/MyApp.Api/MyApp.Api.csproj" COPY . .
undefined
COPY ["src/MyApp.Api/MyApp.Api.csproj", "src/MyApp.Api/"] RUN dotnet restore "src/MyApp.Api/MyApp.Api.csproj" COPY . .
undefined

Don't Run as Root

不要以根用户身份运行

dockerfile
undefined
dockerfile
undefined

BAD — running as root (security risk)

BAD — running as root (security risk)

FROM mcr.microsoft.com/dotnet/aspnet:10.0 COPY --from=build /app . ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
FROM mcr.microsoft.com/dotnet/aspnet:10.0 COPY --from=build /app . ENTRYPOINT ["dotnet", "MyApp.Api.dll"]

GOOD — use the built-in non-root user

GOOD — use the built-in non-root user

FROM mcr.microsoft.com/dotnet/aspnet:10.0 USER app COPY --from=build /app . ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
undefined
FROM mcr.microsoft.com/dotnet/aspnet:10.0 USER app COPY --from=build /app . ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
undefined

Decision Guide

决策指南

ScenarioRecommendation
Web API containerMulti-stage build with aspnet runtime image
Worker serviceMulti-stage build with dotnet/runtime image
Local developmentDocker Compose with service dependencies
CI buildsMulti-stage build (self-contained)
Image size optimizationUse Alpine variant + trimming for small images
Health monitoring
/health
endpoint + orchestrator probe (K8s
httpGet
/ Compose healthcheck)
SecretsEnvironment variables or mounted secrets, never in image
场景推荐方案
Web API容器使用aspnet运行时镜像的多阶段构建
工作服务使用dotnet/runtime镜像的多阶段构建
本地开发包含服务依赖的Docker Compose
CI构建多阶段构建(自包含模式)
镜像大小优化使用Alpine变体+裁剪功能生成小体积镜像
健康监控
/health
端点 + 编排器探测(K8s
httpGet
/ Compose healthcheck)
密钥管理环境变量或挂载密钥,绝不要存入镜像