diff --git a/content/guides/dotnet/containerize.md b/content/guides/dotnet/containerize.md index 146874ca9d8c..6a42571c1477 100644 --- a/content/guides/dotnet/containerize.md +++ b/content/guides/dotnet/containerize.md @@ -39,12 +39,18 @@ $ git clone https://github.com/docker/docker-dotnet-sample ## Initialize Docker assets -Now that you have an application, you can use `docker init` to create the -necessary Docker assets to containerize your application. Inside the -`docker-dotnet-sample` directory, run the `docker init` command in a terminal. -`docker init` provides some default configuration, but you'll need to answer a -few questions about your application. Refer to the following example to answer -the prompts from `docker init` and use the same answers for your prompts. +Now that you have an application, you can create the necessary Docker assets to containerize it. You can choose between using the official .NET images or Docker Hardened Images (DHI). + +> [Docker Hardened Images (DHIs)](https://docs.docker.com/dhi/) are minimal, secure, and production-ready container base and application images maintained by Docker. DHI images are recommended for better security—they are designed to reduce vulnerabilities and simplify compliance. + +{{< tabs >}} +{{< tab name="Using Docker Hardened Images" >}} + +Docker Hardened Images (DHIs) for .NET are available on [Docker Hub](https://hub.docker.com/hardened-images/catalog/dhi/aspnetcore). Unlike using the Docker Official Image, you must first mirror the image into your organization. Follow the instructions in the [DHI quickstart](/dhi/get-started/) to create a mirrored repository. + +Mirrored repositories must start with `dhi-`, for example: `FROM /dhi-aspnetcore:`. + +You can use `docker init` to generate Docker assets, then modify the Dockerfile to use DHI images: ```console $ docker init @@ -60,10 +66,86 @@ Let's get started! ? What application platform does your project use? ASP.NET Core ? What's the name of your solution's main project? myWebApp -? What version of .NET do you want to use? 8.0 +? What version of .NET do you want to use? 10.0 ? What local port do you want to use to access your server? 8080 ``` +Then update your Dockerfile to use DHI images: + +```dockerfile {title=Dockerfile} +# syntax=docker/dockerfile:1 + +FROM --platform=$BUILDPLATFORM /dhi-dotnet:10-sdk AS build +ARG TARGETARCH +COPY . /source +WORKDIR /source/src +RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ + dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app + +FROM /dhi-aspnetcore:10 +WORKDIR /app +COPY --from=build /app . +ENTRYPOINT ["dotnet", "myWebApp.dll"] +``` + +> [!NOTE] +> +> DHI runtime images already run as a non-root user (`nonroot`), so there's no need to create a user or specify `USER` in your Dockerfile. This reduces the attack surface and simplifies your configuration. + +{{< /tab >}} +{{< tab name="Using the official .NET 10 image" >}} + +You can use `docker init` to create the necessary Docker assets. Inside the `docker-dotnet-sample` directory, run the `docker init` command in a terminal. `docker init` provides some default configuration, but you'll need to answer a few questions about your application. Refer to the following example to answer the prompts from `docker init` and use the same answers for your prompts. + +```console +$ docker init +Welcome to the Docker Init CLI! + +This utility will walk you through creating the following files with sensible defaults for your project: + - .dockerignore + - Dockerfile + - compose.yaml + - README.Docker.md + +Let's get started! + +? What application platform does your project use? ASP.NET Core +? What's the name of your solution's main project? myWebApp +? What version of .NET do you want to use? 10.0 +? What local port do you want to use to access your server? 8080 +``` + +This generates a Dockerfile using the official .NET 10 images from Microsoft Container Registry: + +```dockerfile {title=Dockerfile} +# syntax=docker/dockerfile:1 + +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build +ARG TARGETARCH +COPY . /source +WORKDIR /source/src +RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ + dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app + +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final +WORKDIR /app +COPY --from=build /app . +ARG UID=10001 +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser +USER appuser +ENTRYPOINT ["dotnet", "myWebApp.dll"] +``` + +{{< /tab >}} +{{< /tabs >}} + You should now have the following contents in your `docker-dotnet-sample` directory. @@ -78,7 +160,7 @@ directory. │ └── README.md ``` -To learn more about the files that `docker init` added, see the following: +To learn more about the files, see the following: - [Dockerfile](/reference/dockerfile.md) - [.dockerignore](/reference/dockerfile.md#dockerignore-file) - [compose.yaml](/reference/compose-file/_index.md) @@ -126,6 +208,7 @@ Related information: - [Dockerfile reference](/reference/dockerfile.md) - [.dockerignore file reference](/reference/dockerfile.md#dockerignore-file) - [Docker Compose overview](/manuals/compose/_index.md) + - [Docker Hardened Images](/dhi/) ## Next steps diff --git a/content/guides/dotnet/develop.md b/content/guides/dotnet/develop.md index d5bea5491fd1..a30122176741 100644 --- a/content/guides/dotnet/develop.md +++ b/content/guides/dotnet/develop.md @@ -288,11 +288,11 @@ immediately reflected in the running container. Open `docker-dotnet-sample/src/Pages/Index.cshtml` in an IDE or text editor and update the student name text on line 13 from `Student name is` to `Student name:`. ```diff --

Student Name is @Model.StudentName

+-

Student name is @Model.StudentName

+

Student name: @Model.StudentName

``` -Save the changes to `Index.cshmtl` and then wait a few seconds for the application to rebuild. Refresh [http://localhost:8080](http://localhost:8080) in your browser and verify that the updated text appears. +Save the changes to `Index.cshtml` and then wait a few seconds for the application to rebuild. Refresh [http://localhost:8080](http://localhost:8080) in your browser and verify that the updated text appears. Press `ctrl+c` in the terminal to stop your application. @@ -304,22 +304,49 @@ Add a new development stage to your Dockerfile and update your `compose.yaml` fi The following is the updated Dockerfile. +{{< tabs >}} +{{< tab name="Using Docker Hardened Images" >}} + ```Dockerfile {hl_lines="10-13"} # syntax=docker/dockerfile:1 -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build +FROM --platform=$BUILDPLATFORM /dhi-dotnet:10-sdk AS build ARG TARGETARCH COPY . /source WORKDIR /source/src RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app -FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS development +FROM /dhi-dotnet:10-sdk AS development COPY . /source WORKDIR /source/src CMD dotnet run --no-launch-profile -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final +FROM /dhi-aspnetcore:10 +WORKDIR /app +COPY --from=build /app . +ENTRYPOINT ["dotnet", "myWebApp.dll"] +``` + +{{< /tab >}} +{{< tab name="Using the official .NET 10 image" >}} + +```Dockerfile {hl_lines="10-13"} +# syntax=docker/dockerfile:1 + +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build +ARG TARGETARCH +COPY . /source +WORKDIR /source/src +RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ + dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app + +FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS development +COPY . /source +WORKDIR /source/src +CMD dotnet run --no-launch-profile + +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final WORKDIR /app COPY --from=build /app . ARG UID=10001 @@ -335,6 +362,9 @@ USER appuser ENTRYPOINT ["dotnet", "myWebApp.dll"] ``` +{{< /tab >}} +{{< /tabs >}} + The following is the updated `compose.yaml` file. ```yaml {hl_lines=[5,15,16]} @@ -379,7 +409,7 @@ secrets: file: db/password.txt ``` -Your containerized application will now use the `mcr.microsoft.com/dotnet/sdk:8.0-alpine` image, which includes development tools like `dotnet test`. Continue to the next section to learn how you can run `dotnet test`. +Your containerized application will now use the SDK image (either `/dhi-dotnet:10-sdk` for DHI or `mcr.microsoft.com/dotnet/sdk:10.0-alpine` for official images), which includes development tools like `dotnet test`. Continue to the next section to learn how you can run `dotnet test`. ## Summary diff --git a/content/guides/dotnet/run-tests.md b/content/guides/dotnet/run-tests.md index 1e404c345965..b96442123196 100644 --- a/content/guides/dotnet/run-tests.md +++ b/content/guides/dotnet/run-tests.md @@ -36,7 +36,7 @@ You should see output that contains the following. Starting test execution, please wait... A total of 1 test files matched the specified pattern. -Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net8.0/tests.dll (net8.0) +Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net10.0/tests.dll (net10.0) ``` To learn more about the command, see [docker compose run](/reference/cli/docker/compose/run/). @@ -47,10 +47,38 @@ To run your tests when building, you need to update your Dockerfile. You can cre The following is the updated Dockerfile. +{{< tabs >}} +{{< tab name="Using Docker Hardened Images" >}} + +```dockerfile {hl_lines="9"} +# syntax=docker/dockerfile:1 + +FROM --platform=$BUILDPLATFORM /dhi-dotnet:10-sdk AS build +ARG TARGETARCH +COPY . /source +WORKDIR /source/src +RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ + dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app +RUN dotnet test /source/tests + +FROM /dhi-dotnet:10-sdk AS development +COPY . /source +WORKDIR /source/src +CMD dotnet run --no-launch-profile + +FROM /dhi-aspnetcore:10 +WORKDIR /app +COPY --from=build /app . +ENTRYPOINT ["dotnet", "myWebApp.dll"] +``` + +{{< /tab >}} +{{< tab name="Using the official .NET 10 image" >}} + ```dockerfile {hl_lines="9"} # syntax=docker/dockerfile:1 -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build ARG TARGETARCH COPY . /source WORKDIR /source/src @@ -58,12 +86,12 @@ RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app RUN dotnet test /source/tests -FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS development +FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS development COPY . /source WORKDIR /source/src CMD dotnet run --no-launch-profile -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final WORKDIR /app COPY --from=build /app . ARG UID=10001 @@ -79,6 +107,9 @@ USER appuser ENTRYPOINT ["dotnet", "myWebApp.dll"] ``` +{{< /tab >}} +{{< /tabs >}} + Run the following command to build an image using the build stage as the target and view the test results. Include `--progress=plain` to view the build output, `--no-cache` to ensure the tests always run, and `--target build` to target the build stage. ```console @@ -92,16 +123,16 @@ You should see output containing the following. #11 1.564 Determining projects to restore... #11 3.421 Restored /source/src/myWebApp.csproj (in 1.02 sec). #11 19.42 Restored /source/tests/tests.csproj (in 17.05 sec). -#11 27.91 myWebApp -> /source/src/bin/Debug/net8.0/myWebApp.dll -#11 28.47 tests -> /source/tests/bin/Debug/net8.0/tests.dll -#11 28.49 Test run for /source/tests/bin/Debug/net8.0/tests.dll (.NETCoreApp,Version=v8.0) +#11 27.91 myWebApp -> /source/src/bin/Debug/net10.0/myWebApp.dll +#11 28.47 tests -> /source/tests/bin/Debug/net10.0/tests.dll +#11 28.49 Test run for /source/tests/bin/Debug/net10.0/tests.dll (.NETCoreApp,Version=v10.0) #11 28.67 Microsoft (R) Test Execution Command Line Tool Version 17.3.3 (x64) #11 28.67 Copyright (c) Microsoft Corporation. All rights reserved. #11 28.68 #11 28.97 Starting test execution, please wait... #11 29.03 A total of 1 test files matched the specified pattern. #11 32.07 -#11 32.08 Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net8.0/tests.dll (net8.0) +#11 32.08 Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net10.0/tests.dll (net10.0) #11 DONE 32.2s ```