用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/rudironsoni/Ghost --skill dotnet-containers命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Rulesync CLI tool documentation - unified AI rule file management for various AI coding tools
Publishes .NET artifacts from Azure DevOps. NuGet push, containers to ACR, pipeline artifacts.
Configures artifacts output layout. UseArtifactsOutput, ArtifactsPath, impact on CI and Docker.
基于 SOC 职业分类
正在显示 SKILL.md
| name | dotnet-containers |
| description | Containerizes .NET apps. Multi-stage Dockerfiles, SDK container publish (.NET 8+), rootless. |
| license | MIT |
| targets | ["*"] |
| category | operations |
| subcategory | containers |
| tags | ["devops","dotnet","skill","containers","docker"] |
| version | 1.0.0 |
| author | dotnet-agent-harness |
| invocable | true |
| related_skills | ["dotnet-container-deployment","dotnet-native-aot","dotnet-gha-publish"] |
| claudecode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| codexcli | {"short-description":".NET skill guidance for container tasks"} |
| opencode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| copilot | {} |
| geminicli | {} |
| antigravity | {} |
Best practices for containerizing .NET applications. Covers multi-stage Dockerfile patterns, the dotnet publish
container image feature (.NET 8+), rootless container configuration, optimized layer caching, and container health
checks.
dotnet publish /t:PublishContainer)Cross-references: [skill:dotnet-observability] for health check patterns, [skill:dotnet-container-deployment] for deploying containers to Kubernetes and local dev with Compose, [skill:dotnet-artifacts-output] for Dockerfile path adjustments when using centralized build output layout.
Multi-stage builds separate the build environment from the runtime environment, producing minimal final images.
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy project files first for layer caching
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
COPY ["src/MyApi.Core/MyApi.Core.csproj", "src/MyApi.Core/"]
COPY ["Directory.Build.props", "."]
COPY ["Directory.Packages.props", "."]
RUN dotnet restore "src/MyApi/MyApi.csproj"
# Copy everything else and build
COPY . .
WORKDIR "/src/src/MyApi"
RUN dotnet publish -c Release -o /app/publish --no-restore
# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
EXPOSE 8080
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
```text
### Layer Caching Strategy
Order COPY instructions from least-frequently-changed to most-frequently-changed:
1. **Project files and props** -- change only when dependencies change
2. **`dotnet restore`** -- cached until project files change
3. **Source code** -- changes with every build
4. **`dotnet publish`** -- runs only when source or restore layer changes
```dockerfile
# Good: restore layer is cached when only source changes
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
RUN dotnet restore
COPY . .
RUN dotnet publish
# Bad: restore runs on every source change
COPY . .
RUN dotnet restore
RUN dotnet publish
```text
### Solution-Level Restore
For multi-project solutions, copy all `.csproj` files and the solution file to enable a single restore:
```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy solution and all project files for restore caching
COPY ["MyApp.sln", "."]
COPY ["Directory.Build.props", "."]
COPY ["Directory.Packages.props", "."]
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
COPY ["src/MyApi.Core/MyApi.Core.csproj", "src/MyApi.Core/"]
COPY ["src/MyApi.Infrastructure/MyApi.Infrastructure.csproj", "src/MyApi.Infrastructure/"]
RUN dotnet restore
COPY . .
RUN dotnet publish "src/MyApi/MyApi.csproj" -c Release -o /app/publish --no-restore
```csharp
---
## dotnet publish Container Images (.NET 8+)
Starting with .NET 8, `dotnet publish` can produce OCI container images directly without a Dockerfile. This uses the
`Microsoft.NET.Build.Containers` SDK (included in the .NET SDK).
### Basic Usage
```bash
# Publish as a container image to local Docker daemon
dotnet publish --os linux --arch x64 /t:PublishContainer
# Publish to a remote registry
dotnet publish --os linux --arch x64 /t:PublishContainer \
-p:ContainerRegistry=ghcr.io \
-p:ContainerRepository=myorg/myapi
```text
### MSBuild Configuration
Configure container properties in the `.csproj`:
```xml
<PropertyGroup>
<ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:10.0</ContainerBaseImage>
<ContainerImageName>myapi</ContainerImageName>
<ContainerImageTag>$(Version)</ContainerImageTag>
</PropertyGroup>
<ItemGroup>
<ContainerPort Include="8080" Type="tcp" />
</ItemGroup>
```text
### Advanced Configuration
```xml
<PropertyGroup>
<!-- Use chiseled (distroless) base image for smaller attack surface -->
<ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled</ContainerBaseImage>
<!-- Run as non-root user (default for chiseled images) -->
<ContainerUser>app</ContainerUser>
</PropertyGroup>
<ItemGroup>
<!-- Environment variables -->
<ContainerEnvironmentVariable Include="ASPNETCORE_URLS" Value="http://+:8080" />
<ContainerEnvironmentVariable Include="DOTNET_RUNNING_IN_CONTAINER" Value="true" />
<!-- Labels -->
<ContainerLabel Include="org.opencontainers.image.source" Value="https://github.com/myorg/myapi" />
</ItemGroup>
```text
### When to Use dotnet publish vs Dockerfile
| Scenario | Recommendation |
| ------------------------------------------------ | ---------------------------------------------------------------- |
| Simple single-project API | `dotnet publish /t:PublishContainer` -- less boilerplate |
| Multi-stage build with native dependencies | Dockerfile -- full control over build environment |
| Need to install OS packages (e.g., `libgdiplus`) | Dockerfile -- `RUN apt-get install` not available in SDK publish |
| CI/CD with complex build steps | Dockerfile -- explicit, reproducible |
| Quick local container testing | `dotnet publish /t:PublishContainer` -- fastest iteration |
---
## Base Image Selection
### Official .NET Container Images
| Image | Use Case | Size |
| ----------------------------------------------------------- | -------------------------------------------- | ------- |
| `mcr.microsoft.com/dotnet/aspnet:10.0` | ASP.NET Core apps (Ubuntu) | ~220 MB |
| `mcr.microsoft.com/dotnet/aspnet:10.0-alpine` | ASP.NET Core apps (Alpine, smaller) | ~110 MB |
| `mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled` | Distroless (no shell, no package manager) | ~110 MB |
| `mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled-extra` | Chiseled + globalization + time zones | ~130 MB |
| `mcr.microsoft.com/dotnet/runtime:10.0` | Console apps, worker services | ~190 MB |
| `mcr.microsoft.com/dotnet/runtime-deps:10.0` | Self-contained/AOT apps (runtime not needed) | ~30 MB |
### Choosing a Base Image
- **Default:** Use `aspnet` for web apps, `runtime` for worker services
- **Minimal footprint:** Use `chiseled` variants (no shell, no root user, no package manager)
- **Globalization needed:** Use `chiseled-extra` if your app uses culture-specific formatting or time zones
- **Self-contained or AOT:** Use `runtime-deps` -- the runtime is bundled in your app
- **Alpine:** Smaller than Ubuntu but uses musl libc; test for compatibility with native dependencies
---
## Rootless Containers
Running containers as non-root reduces the attack surface. .NET 8+ chiseled images run as non-root by default.
### Non-Root with Standard Images
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
# Create non-root user and switch to it
RUN adduser --disabled-password --gecos "" --uid 1001 appuser
USER appuser
COPY --from=build --chown=appuser:appuser /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
```text
### Non-Root with Chiseled Images
Chiseled images include a pre-configured `app` user (UID 1654). No additional configuration needed:
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS runtime
WORKDIR /app
# Already runs as non-root 'app' user (UID 1654)
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
```text
### Port Configuration
Non-root users cannot bind to ports below 1024. ASP.NET Core defaults to port 8080 in containers (set via
`ASPNETCORE_HTTP_PORTS`):
```dockerfile
# Default in .NET 8+ container images -- no explicit config needed
# ASPNETCORE_HTTP_PORTS=8080
# If you need a different port:
ENV ASPNETCORE_HTTP_PORTS=5000
EXPOSE 5000
```text
---
## Container Health Checks
Health checks allow container runtimes to monitor application readiness. The application-level health check endpoints
(see [skill:dotnet-observability]) are consumed by Docker and Kubernetes probes.
### Docker HEALTHCHECK
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
# Health check using curl (not available in chiseled images)
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/health/live || exit 1
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
```text
For chiseled images (no `curl`), use a dedicated health check binary or rely on orchestrator-level probes (Kubernetes
`httpGet`, Docker Compose `test`):
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS runtime
WORKDIR /app
# No HEALTHCHECK directive -- use orchestrator probes instead
# See [skill:dotnet-container-deployment] for Kubernetes probe configuration
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
```text
### Health Check Endpoints
Register health check endpoints in your application (see [skill:dotnet-observability] for full guidance):
```csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), tags: ["live"])
.AddNpgSql(
builder.Configuration.GetConnectionString("DefaultConnection")!,
name: "database",
tags: ["ready"]);
var app = builder.Build();
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("live")
});
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
```text
---
## Container Optimization
### .dockerignore
Always include a `.dockerignore` to exclude unnecessary files from the build context:
```text
**/.git
**/.vs
**/.vscode
**/bin
**/obj
**/node_modules
**/*.user
**/*.suo
**/Dockerfile*
**/docker-compose*
**/.dockerignore
**/README.md
**/LICENSE
```markdown
### Globalization and Time Zones
If your app needs globalization support (culture-specific formatting, time zones), configure ICU:
```dockerfile
# Option 1: Use the chiseled-extra image (includes ICU + tzdata)
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled-extra
# Option 2: Disable globalization for smaller images (if not needed)
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true
```text
### Memory Limits
Configure .NET to respect container memory limits:
```dockerfile
# .NET automatically detects container memory limits and adjusts GC heap size.
# Override only if needed:
ENV DOTNET_GCHeapHardLimit=0x10000000 # 256 MB hard limit
```dockerfile
.NET automatically reads cgroup memory limits. The GC adjusts its heap size to stay within the container memory budget.
Avoid setting `DOTNET_GCHeapHardLimit` unless you have a specific reason.
### ReadOnlyRootFilesystem
For defense-in-depth, run with a read-only root filesystem. Ensure writable paths for temp files:
```dockerfile
ENV DOTNET_EnableDiagnostics=0
# Or mount a tmpfs at /tmp for diagnostics support
```dockerfile
---
## Key Principles
- **Use multi-stage builds** -- keep build tools out of the final image
- **Order COPY for layer caching** -- project files and restore before source code
- **Prefer chiseled images** for production -- no shell, no root, minimal attack surface
- **Use `dotnet publish /t:PublishContainer`** for simple projects -- skip Dockerfile boilerplate
- **Run as non-root** -- use `USER` directive or chiseled images (non-root by default)
- **Set health check endpoints** -- enable orchestrators to monitor application state (see [skill:dotnet-observability])
- **Include `.dockerignore`** -- keep build context small and exclude secrets
---
## Agent Gotchas
1. **Do not use `mcr.microsoft.com/dotnet/sdk` as the final image** -- SDK images are 800+ MB and include build tools.
Always use `aspnet`, `runtime`, or `runtime-deps` for the final stage.
2. **Do not hardcode image tags to a patch version** (e.g., `10.0.1`) -- use `10.0` to receive security patches. Pin to
patch versions only if you have a specific compatibility requirement.
3. **Do not use `HEALTHCHECK` with chiseled images** -- chiseled images have no `curl` or shell. Use orchestrator-level
probes (Kubernetes `httpGet`, Docker Compose `test`) instead.
4. **Do not forget `--no-restore` on `dotnet publish` after a separate `dotnet restore` step** -- without it, restore
runs again and breaks layer caching.
5. **Do not bind to ports below 1024 in non-root containers** -- .NET defaults to port 8080 in container images. If you
override `ASPNETCORE_HTTP_PORTS`, ensure the port is >= 1024.
6. **Do not omit `.dockerignore`** -- without it, the build context includes `.git`, `bin/obj`, and potentially secrets,
increasing build time and image size.
---
## Code Navigation (Serena MCP)
**Primary approach:** Use Serena symbol operations for efficient code navigation:
1. **Find definitions**: `serena_find_symbol` instead of text search
2. **Understand structure**: `serena_get_symbols_overview` for file organization
3. **Track references**: `serena_find_referencing_symbols` for impact analysis
4. **Precise edits**: `serena_replace_symbol_body` for clean modifications
**When to use Serena vs traditional tools:**
- ✅ **Use Serena**: Navigation, refactoring, dependency analysis, precise edits
- ✅ **Use Read/Grep**: Reading full files, pattern matching, simple text operations
- ✅ **Fallback**: If Serena unavailable, traditional tools work fine
**Example workflow:**
```text
# Instead of:
Read: src/Services/OrderService.cs
Grep: "public void ProcessOrder"
# Use:
serena_find_symbol: "OrderService/ProcessOrder"
serena_get_symbols_overview: "src/Services/OrderService.cs"
```
## References
- [.NET container images](https://learn.microsoft.com/en-us/dotnet/core/docker/build-container)
- [Containerize a .NET app with dotnet publish](https://learn.microsoft.com/en-us/dotnet/core/docker/publish-as-container)
- [.NET container image variants](https://learn.microsoft.com/en-us/dotnet/core/docker/container-images)
- [Chiseled Ubuntu containers for .NET](https://devblogs.microsoft.com/dotnet/dotnet-6-is-now-in-ubuntu-2204/#chiseled-ubuntu-containers)
- [ASP.NET Core health checks](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks)
- [Docker multi-stage builds](https://docs.docker.com/build/building/multi-stage/)