Container Publishing (No Dockerfile)
容器发布(无需Dockerfile)
- No Dockerfile needed — The .NET 10 SDK builds OCI-compliant container images directly from
dotnet publish /t:PublishContainer
. No Dockerfile to write or maintain.
- Chiseled images for production — Use base images: no shell, no package manager, 7 Linux components vs 100+. Smallest attack surface.
- Non-root by default — .NET 10 container images run as the user automatically. Never override to root in production.
- Configuration in the .csproj — All container settings are MSBuild properties, versioned with your project. No separate files to drift.
- 无需编写Dockerfile — .NET 10 SDK可直接通过
dotnet publish /t:PublishContainer
构建符合OCI标准的容器镜像,无需编写或维护Dockerfile。
- 面向生产的chiseled镜像 — 使用基础镜像:无Shell、无包管理器,仅包含7个Linux组件(常规镜像包含100+),攻击面最小。
- 默认以非root用户运行 — .NET 10容器镜像自动以用户运行,生产环境中绝不要切换为root用户。
- 在.csproj中配置 — 所有容器设置均为MSBuild属性,与项目版本一同管理,不会出现配置文件不一致的问题。
Minimal Container Publish
极简容器发布
No project file changes needed. Just publish:
bash
dotnet publish /t:PublishContainer --os linux --arch x64
This creates a container image in your local Docker daemon using the default
base image.
无需修改项目文件,直接执行发布命令:
bash
dotnet publish /t:PublishContainer --os linux --arch x64
此命令会使用默认的
基础镜像,在本地Docker守护进程中创建容器镜像。
Production-Ready .csproj Configuration
生产就绪的.csproj配置
xml
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ContainerRepository>mycompany/myapp-api</ContainerRepository>
<ContainerFamily>noble-chiseled</ContainerFamily>
</PropertyGroup>
<ItemGroup>
<ContainerPort Include="8080" Type="tcp" />
<ContainerEnvironmentVariable Include="ASPNETCORE_HTTP_PORTS" Value="8080" />
<ContainerEnvironmentVariable Include="DOTNET_EnableDiagnostics" Value="0" />
<ContainerLabel Include="org.opencontainers.image.vendor" Value="MyCompany" />
</ItemGroup>
</Project>
xml
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ContainerRepository>mycompany/myapp-api</ContainerRepository>
<ContainerFamily>noble-chiseled</ContainerFamily>
</PropertyGroup>
<ItemGroup>
<ContainerPort Include="8080" Type="tcp" />
<ContainerEnvironmentVariable Include="ASPNETCORE_HTTP_PORTS" Value="8080" />
<ContainerEnvironmentVariable Include="DOTNET_EnableDiagnostics" Value="0" />
<ContainerLabel Include="org.opencontainers.image.vendor" Value="MyCompany" />
</ItemGroup>
</Project>
Publishing to a Registry
发布到镜像仓库
Authenticate with
first, then specify the registry:
GitHub Container Registry
GitHub Container Registry
docker login ghcr.io
dotnet publish /t:PublishContainer --os linux --arch x64
-p ContainerRegistry=ghcr.io
-p ContainerImageTag=1.0.0
docker login ghcr.io
dotnet publish /t:PublishContainer --os linux --arch x64
-p ContainerRegistry=ghcr.io
-p ContainerImageTag=1.0.0
Azure Container Registry
Azure Container Registry
az acr login --name myregistry
dotnet publish /t:PublishContainer --os linux --arch x64
-p ContainerRegistry=myregistry.azurecr.io
az acr login --name myregistry
dotnet publish /t:PublishContainer --os linux --arch x64
-p ContainerRegistry=myregistry.azurecr.io
Docker Hub (requires username prefix in repository)
Docker Hub(仓库名需包含用户名前缀)
dotnet publish /t:PublishContainer --os linux --arch x64
-p ContainerRegistry=docker.io
-p ContainerRepository=myuser/myapp
dotnet publish /t:PublishContainer --os linux --arch x64
-p ContainerRegistry=docker.io
-p ContainerRepository=myuser/myapp
Multi-Architecture Images
多架构镜像
Build images for multiple platforms with a single publish:
xml
<PropertyGroup>
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
<ContainerRuntimeIdentifiers>linux-x64;linux-arm64</ContainerRuntimeIdentifiers>
</PropertyGroup>
bash
dotnet publish /t:PublishContainer
This produces an OCI Image Index — registries serve the correct architecture automatically.
通过一次发布命令构建多平台镜像:
xml
<PropertyGroup>
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
<ContainerRuntimeIdentifiers>linux-x64;linux-arm64</ContainerRuntimeIdentifiers>
</PropertyGroup>
bash
dotnet publish /t:PublishContainer
此命令会生成OCI镜像索引——镜像仓库会自动为不同架构提供对应的镜像。
Bash — note the quoting for semicolons
Bash — 注意分号需要加引号
dotnet publish /t:PublishContainer --os linux --arch x64
-p ContainerImageTags='"1.0.0;latest"'
Or in the project file:
```xml
<ContainerImageTags>1.0.0;latest</ContainerImageTags>
dotnet publish /t:PublishContainer --os linux --arch x64
-p ContainerImageTags='"1.0.0;latest"'
也可在项目文件中配置:
```xml
<ContainerImageTags>1.0.0;latest</ContainerImageTags>
Save as Tarball (No Docker Required)
保存为Tar包(无需Docker)
No container runtime needed on the build machine. Useful for CI scanning:
bash
dotnet publish /t:PublishContainer --os linux --arch x64 \
-p ContainerArchiveOutputPath=./images/myapp.tar.gz
构建机器无需安装容器运行时,适用于CI扫描场景:
bash
dotnet publish /t:PublishContainer --os linux --arch x64 \
-p ContainerArchiveOutputPath=./images/myapp.tar.gz
Scan with Trivy before pushing
推送前用Trivy扫描
trivy image --input ./images/myapp.tar.gz
trivy image --input ./images/myapp.tar.gz
Chiseled Image Variants
Chiseled镜像变体
| ContainerFamily | Use Case | Shell | Size |
|---|
| (default) | General purpose (Debian) | Yes | ~220 MB |
| Production (no shell) | No | ~110 MB |
| Production with localization (ICU) | No | ~120 MB |
| Small size, has shell | Yes | ~112 MB |
xml
<!-- Standard chiseled (InvariantGlobalization=true) -->
<ContainerFamily>noble-chiseled</ContainerFamily>
<!-- Chiseled with ICU for localization -->
<ContainerFamily>noble-chiseled-extra</ContainerFamily>
For Native AOT, the SDK auto-selects
:
xml
<PublishAot>true</PublishAot>
<!-- SDK picks runtime-deps:10.0-noble-chiseled-aot automatically -->
| ContainerFamily | 使用场景 | 是否包含Shell | 大小 |
|---|
| (默认) | 通用场景(Debian) | 是 | ~220 MB |
| 生产环境(无Shell) | 否 | ~110 MB |
| 带本地化支持的生产环境(ICU) | 否 | ~120 MB |
| 轻量场景,包含Shell | 是 | ~112 MB |
xml
<!-- 标准chiseled镜像(启用InvariantGlobalization=true) -->
<ContainerFamily>noble-chiseled</ContainerFamily>
<!-- 带ICU本地化支持的chiseled镜像 -->
<ContainerFamily>noble-chiseled-extra</ContainerFamily>
xml
<PublishAot>true</PublishAot>
<!-- SDK会自动选择runtime-deps:10.0-noble-chiseled-aot作为基础镜像 -->
CI/CD with GitHub Actions
GitHub Actions CI/CD配置
yaml
jobs:
publish:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: |
dotnet publish src/MyApp.Api/MyApp.Api.csproj \
/t:PublishContainer --os linux --arch x64 \
-p ContainerRegistry=ghcr.io \
-p ContainerRepository=${{ github.repository_owner }}/myapp \
-p ContainerImageTag=${{ github.sha }}
yaml
jobs:
publish:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: |
dotnet publish src/MyApp.Api/MyApp.Api.csproj \
/t:PublishContainer --os linux --arch x64 \
-p ContainerRegistry=ghcr.io \
-p ContainerRepository=${{ github.repository_owner }}/myapp \
-p ContainerImageTag=${{ github.sha }}
Don't Use the Deprecated Property Names
不要使用已废弃的属性名
xml
<!-- BAD — ContainerImageName is deprecated -->
<ContainerImageName>myapp</ContainerImageName>
<!-- GOOD — use ContainerRepository -->
<ContainerRepository>myapp</ContainerRepository>
xml
<!-- 错误用法 — ContainerImageName已废弃 -->
<ContainerImageName>myapp</ContainerImageName>
<!-- 正确用法 — 使用ContainerRepository -->
<ContainerRepository>myapp</ContainerRepository>
Don't Use PublishProfile=DefaultContainer
不要使用PublishProfile=DefaultContainer
BAD — old approach, inconsistent across project types
错误用法 — 旧方案,不同项目类型表现不一致
dotnet publish -p:PublishProfile=DefaultContainer
dotnet publish -p:PublishProfile=DefaultContainer
GOOD — use the MSBuild target directly
正确用法 — 直接使用MSBuild目标
dotnet publish /t:PublishContainer
dotnet publish /t:PublishContainer
Don't Forget to Target Linux
不要忘记指定Linux目标
BAD on Windows — may produce a Windows container
Windows环境下的错误用法 — 可能生成Windows容器
dotnet publish /t:PublishContainer
dotnet publish /t:PublishContainer
GOOD — explicitly target Linux
正确用法 — 明确指定Linux目标
dotnet publish /t:PublishContainer --os linux --arch x64
dotnet publish /t:PublishContainer --os linux --arch x64
Don't Skip Authentication Before Push
推送前不要跳过认证
BAD — fails with CONTAINER1013 error
错误用法 — 会触发CONTAINER1013错误
dotnet publish /t:PublishContainer -p ContainerRegistry=ghcr.io
dotnet publish /t:PublishContainer -p ContainerRegistry=ghcr.io
GOOD — authenticate first
正确用法 — 先完成认证
docker login ghcr.io
dotnet publish /t:PublishContainer -p ContainerRegistry=ghcr.io
docker login ghcr.io
dotnet publish /t:PublishContainer -p ContainerRegistry=ghcr.io
Don't Use SDK Publishing When You Need OS Packages
需要安装系统包时不要使用SDK发布
xml
<!-- BAD — SDK container publish cannot run apt-get or install native packages -->
<!-- There is no RUN equivalent -->
<!-- GOOD — create a custom base image with a Dockerfile first, then reference it -->
<ContainerBaseImage>myregistry/custom-base:1.0</ContainerBaseImage>
xml
<!-- 错误用法 — SDK容器发布无法执行apt-get或安装原生包 -->
<!-- 没有对应的RUN指令 -->
<!-- 正确用法 — 先用Dockerfile创建自定义基础镜像,再引用它 -->
<ContainerBaseImage>myregistry/custom-base:1.0</ContainerBaseImage>
| Scenario | Recommendation |
|---|
| Standard ASP.NET Core API | SDK container publishing with |
| Worker service / console app | SDK container publishing (native .NET 10 support) |
| Needs native OS packages | Dockerfile (or custom base image + SDK publishing) |
| Azure Functions | Dockerfile (not supported by SDK publishing) |
| CI without Docker daemon | Tarball output with ContainerArchiveOutputPath
|
| Multi-arch deployment (x64 + arm64) | ContainerRuntimeIdentifiers
property |
| Production image size | (~110 MB) or Native AOT (~10 MB) |
| Local development | dotnet publish /t:PublishContainer --os linux --arch x64
|
| Registry push | + |
| 场景 | 推荐方案 |
|---|
| 标准ASP.NET Core API | 使用SDK容器发布 + 镜像 |
| Worker服务/控制台应用 | 使用SDK容器发布(.NET 10原生支持) |
| 需要安装原生系统包 | 使用Dockerfile(或自定义基础镜像 + SDK发布) |
| Azure Functions | 使用Dockerfile(SDK发布暂不支持) |
| 无Docker守护进程的CI环境 | 通过ContainerArchiveOutputPath
输出Tar包 |
| 多架构部署(x64 + arm64) | 使用ContainerRuntimeIdentifiers
属性 |
| 生产镜像轻量化 | 使用(约110 MB)或Native AOT(约10 MB) |
| 本地开发 | 执行dotnet publish /t:PublishContainer --os linux --arch x64
|
| 推送至镜像仓库 | 使用 + |