From 171d81c081c10f6d16c9407743b74c54d7117a22 Mon Sep 17 00:00:00 2001 From: Logan Bussell Date: Thu, 26 Sep 2024 11:00:19 -0700 Subject: [PATCH 1/5] Update build-in-sdk-container documentation --- samples/build-in-sdk-container.md | 88 +++++++++++++++++++++----- samples/dotnetapp/Dockerfile.sdk-build | 18 ++++++ 2 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 samples/dotnetapp/Dockerfile.sdk-build diff --git a/samples/build-in-sdk-container.md b/samples/build-in-sdk-container.md index efccc2cc2f..1c343fc2e9 100644 --- a/samples/build-in-sdk-container.md +++ b/samples/build-in-sdk-container.md @@ -1,27 +1,83 @@ # Build in a .NET SDK container -You can use Docker to run your build in an isolated environment using the [.NET SDK Docker image](https://hub.docker.com/r/microsoft/dotnet-sdk/). This is useful to either avoid the need to install .NET on the build machine or ensure that your environment is correctly configured (dev, staging, or production). +You can use Docker to run your build in an isolated environment using the [.NET SDK Docker image](https://hub.docker.com/r/microsoft/dotnet-sdk/). +This is useful to avoid the need to install .NET on the build machine and helps ensure that your build environment is correctly configured (dev, staging, or production). -The instructions assume that you have cloned the repository locally, and that you are in the `samples/dotnetapp` directory (due to the volume mounting syntax), as demonstrated by the examples. +## Build using a Dockerfile (Requires buildx) -## Requirements +Docker [buildx](https://docs.docker.com/reference/cli/docker/buildx/) has built-in support for [exporting files from the docker build command](https://docs.docker.com/build/building/export/). +Using a Dockerfile to build .NET apps is advantageous because it allows you to specify all of the apps required dependencies and build instructions in one place. + +The `dotnetapp` sample contains a sample [Dockerfile](Dockerfile.sdk-build) that supports this functionality. +This sample uses a multi-stage Dockerfile with a `FROM scratch` stage. +The Dockerfile copies the build outputs into the that stage using the `COPY` instruction. + +### Build single-platform binary + +From the `samples/dotnetapp` directory: + +```pwsh +docker build --pull -f Dockerfile.sdk-build . --output out +``` + +You can also give it a try without cloning this repository: + +```pwsh +docker build --pull -f Dockerfile.sdk-build 'https://github.com/dotnet/dotnet-docker.git#:samples/dotnetapp' --output out +``` + +### Build binaries for multiple platforms at once + +Taking advantage of Docker buildx, you can cross-build binaries for multiple platforms at once, all without using emulation. +For more info about how this works, see our documentation on [building images for a specific platform](./build-for-a-platform.md). + +```pwsh +docker buildx build -o out --platform linux/amd64,linux/arm64 -f .\samples\dotnetapp\Dockerfile.sdk-build .\samples\dotnetapp\ +``` + +Docker buildx will create a separate sub-directory for each target platform: + +```pwsh +PS> tree /F out +C:\...\dotnetapp\out +├───linux_amd64 +│ dotnetapp +│ dotnetapp.deps.json +│ dotnetapp.dll +│ dotnetapp.pdb +│ dotnetapp.runtimeconfig.json +│ +└───linux_arm64 + dotnetapp + dotnetapp.deps.json + dotnetapp.dll + dotnetapp.pdb + dotnetapp.runtimeconfig.json +``` + +## Build by running Docker container directly + +If you can't use Docker buildx or don't want to use a Dockerfile, you can build your app by running the SDK image directly and volume mounting your app's source code into the container. +These instructions assume that you have cloned the repository locally, and that you are in the `samples/dotnetapp` directory (due to the volume mounting syntax), as demonstrated by the examples. + +### Requirements This scenario relies on [volume mounting](https://docs.docker.com/engine/admin/volumes/volumes/) (that's the `-v` argument) to make source available within the container (to build it). You may need to enable [shared drives (Windows)](https://docs.docker.com/docker-for-windows/#shared-drives) or [file sharing (macOS)](https://docs.docker.com/docker-for-mac/#file-sharing) first. `dotnet publish` (and `build`) produces native executables for applications. If you use a Linux container, you will build a Linux executable that will not run on Windows or macOS. You can use a [runtime argument](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog) (`-r`) to specify the type of assets that you want to publish (if they don't match the SDK container). The following examples assume you want assets that match your host operating system, and use runtime arguments to ensure that. -## Pull SDK image +### Pull SDK image It is recommended to pull the SDK image before running the appropriate command. This ensures that you get the latest patch version of the SDK. Use the following command: ```console -docker pull mcr.microsoft.com/dotnet/sdk:8.0 +docker pull mcr.microsoft.com/dotnet/sdk:9.0 ``` -## Linux +### Linux ```console -docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:8.0 dotnet publish -c Release -o out +docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:9.0 dotnet publish -c Release -o out ``` You can see the built binaries with the following command: @@ -31,10 +87,10 @@ $ ls out dotnetapp dotnetapp.deps.json dotnetapp.dll dotnetapp.pdb dotnetapp.runtimeconfig.json ``` -## macOS +### macOS ```console -docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:8.0 dotnet publish -c Release -o out -r osx-x64 --self-contained false +docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:9.0 dotnet publish -c Release -o out -r osx-x64 --self-contained false ``` You can see the built binaries with the following command: @@ -46,12 +102,12 @@ dotnetapp.deps.json dotnetapp.runtimeconfig.json dotnetapp.dll ``` -## Windows using Linux containers +### Windows using Linux containers The following example uses PowerShell. ```console -docker run --rm -v ${pwd}:/app -w /app mcr.microsoft.com/dotnet/sdk:8.0 dotnet publish -c Release -o out -r win-x64 --self-contained false +docker run --rm -v ${pwd}:/app -w /app mcr.microsoft.com/dotnet/sdk:9.0 dotnet publish -c Release -o out -r win-x64 --self-contained false ``` You can see the built binaries with the following command: @@ -71,12 +127,12 @@ Mode LastWriteTime Length Name -a---- 11/2/2020 10:46 AM 152 dotnetapp.runtimeconfig.json ``` -## Windows using Windows containers +### Windows using Windows containers The following example uses PowerShell. ```console -docker run --rm -v ${pwd}:c:\app -w c:\app mcr.microsoft.com/dotnet/sdk:8.0-nanoserver-ltsc2022 dotnet publish -c Release -o out +docker run --rm -v ${pwd}:c:\app -w c:\app mcr.microsoft.com/dotnet/sdk:9.0-nanoserver-ltsc2022 dotnet publish -c Release -o out ``` > [!WARNING] @@ -100,14 +156,14 @@ Mode LastWriteTime Length Name -a---- 11/2/2020 10:49 AM 160 dotnetapp.runtimeconfig.json ``` -## Building to a separate location +### Building to a separate location You may want the build output to be written to a separate location than the source directory. That's easy to do with a second volume mount. The following example demonstrates doing that on macOS: ```console -docker run --rm -v ~/dotnetapp:/out -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:8.0 dotnet publish -c Release -o /out -r osx-x64 --self-contained false +docker run --rm -v ~/dotnetapp:/out -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:9.0 dotnet publish -c Release -o /out -r osx-x64 --self-contained false ``` You can see the built binaries with the following command: @@ -123,7 +179,7 @@ The following PowerShell example demonstrates doing that on Windows (using Linux ```console mkdir C:\dotnetapp -docker run --rm -v C:\dotnetapp:c:\app\out -v ${pwd}:c:\app -w /app mcr.microsoft.com/dotnet/sdk:8.0 dotnet publish -c Release -o out -r win-x64 --self-contained false +docker run --rm -v C:\dotnetapp:c:\app\out -v ${pwd}:c:\app -w /app mcr.microsoft.com/dotnet/sdk:9.0 dotnet publish -c Release -o out -r win-x64 --self-contained false ``` You can see the built binaries with the following command: diff --git a/samples/dotnetapp/Dockerfile.sdk-build b/samples/dotnetapp/Dockerfile.sdk-build new file mode 100644 index 0000000000..b4c71e7866 --- /dev/null +++ b/samples/dotnetapp/Dockerfile.sdk-build @@ -0,0 +1,18 @@ +# Learn about building .NET container images: +# https://github.com/dotnet/dotnet-docker/blob/main/samples/README.md +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0 AS build +ARG TARGETARCH +WORKDIR /source + +# Copy project file and restore as distinct layers +COPY --link *.csproj . +RUN dotnet restore -a $TARGETARCH + +# Copy source code and publish app +COPY --link . . +RUN dotnet publish -a $TARGETARCH --no-restore -o /out + +# Output stage +# Learn more about exporting binaries with Docker: https://docs.docker.com/build/building/export/ +FROM scratch +COPY --link --from=build /out . From 9706343aac72dca57efe460d0ae5f88caf160851 Mon Sep 17 00:00:00 2001 From: Logan Bussell Date: Thu, 26 Sep 2024 12:48:14 -0700 Subject: [PATCH 2/5] Update sample dockerfile link --- samples/build-in-sdk-container.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/build-in-sdk-container.md b/samples/build-in-sdk-container.md index 1c343fc2e9..9cef7d13b1 100644 --- a/samples/build-in-sdk-container.md +++ b/samples/build-in-sdk-container.md @@ -8,7 +8,7 @@ This is useful to avoid the need to install .NET on the build machine and helps Docker [buildx](https://docs.docker.com/reference/cli/docker/buildx/) has built-in support for [exporting files from the docker build command](https://docs.docker.com/build/building/export/). Using a Dockerfile to build .NET apps is advantageous because it allows you to specify all of the apps required dependencies and build instructions in one place. -The `dotnetapp` sample contains a sample [Dockerfile](Dockerfile.sdk-build) that supports this functionality. +The `dotnetapp` sample contains a sample [Dockerfile](./dotnetapp/Dockerfile.sdk-build) that supports this functionality. This sample uses a multi-stage Dockerfile with a `FROM scratch` stage. The Dockerfile copies the build outputs into the that stage using the `COPY` instruction. From 343ed279c2806ddfb386f20dfd7708ef4202bdce Mon Sep 17 00:00:00 2001 From: Logan Bussell Date: Mon, 30 Sep 2024 09:40:29 -0700 Subject: [PATCH 3/5] Apply multiple suggestions from code review Co-authored-by: Matt Thalman --- samples/build-in-sdk-container.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/build-in-sdk-container.md b/samples/build-in-sdk-container.md index 316974db96..e64a96aeda 100644 --- a/samples/build-in-sdk-container.md +++ b/samples/build-in-sdk-container.md @@ -10,20 +10,20 @@ Using a Dockerfile to build .NET apps is advantageous because it allows you to s The `dotnetapp` sample contains a sample [Dockerfile](./dotnetapp/Dockerfile.sdk-build) that supports this functionality. This sample uses a multi-stage Dockerfile with a `FROM scratch` stage. -The Dockerfile copies the build outputs into the that stage using the `COPY` instruction. +The Dockerfile copies the build outputs into that stage using the `COPY` instruction. ### Build single-platform binary From the `samples/dotnetapp` directory: ```pwsh -docker build --pull -f Dockerfile.sdk-build . --output out +docker build --pull -f Dockerfile.sdk-build --output out . ``` You can also give it a try without cloning this repository: ```pwsh -docker build --pull -f Dockerfile.sdk-build 'https://github.com/dotnet/dotnet-docker.git#:samples/dotnetapp' --output out +docker build --pull -f Dockerfile.sdk-build --output out 'https://github.com/dotnet/dotnet-docker.git#:samples/dotnetapp' ``` ### Build binaries for multiple platforms at once From c6f54110bb7ae60f08ed79879d5e5ee7711b3179 Mon Sep 17 00:00:00 2001 From: Logan Bussell Date: Mon, 30 Sep 2024 09:52:23 -0700 Subject: [PATCH 4/5] Make commands more consistent and add a blurb about how Docker build outputs work --- samples/build-in-sdk-container.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/samples/build-in-sdk-container.md b/samples/build-in-sdk-container.md index e64a96aeda..d5bf6759bc 100644 --- a/samples/build-in-sdk-container.md +++ b/samples/build-in-sdk-container.md @@ -11,19 +11,21 @@ Using a Dockerfile to build .NET apps is advantageous because it allows you to s The `dotnetapp` sample contains a sample [Dockerfile](./dotnetapp/Dockerfile.sdk-build) that supports this functionality. This sample uses a multi-stage Dockerfile with a `FROM scratch` stage. The Dockerfile copies the build outputs into that stage using the `COPY` instruction. +Then, when you provide the `--output/-o ` argument to the Docker build command, Docker will copy the entire filesystem of the final stage of the image to the specified directory. +Since the sample Dockerfile's final stage is a `FROM scratch` stage, the result is that the build outputs are placed in the specified directory on the host machine's disk. ### Build single-platform binary From the `samples/dotnetapp` directory: ```pwsh -docker build --pull -f Dockerfile.sdk-build --output out . +docker build --pull -f Dockerfile.sdk-build --output ./out . ``` You can also give it a try without cloning this repository: ```pwsh -docker build --pull -f Dockerfile.sdk-build --output out 'https://github.com/dotnet/dotnet-docker.git#:samples/dotnetapp' +docker build --pull -f Dockerfile.sdk-build --output ./out 'https://github.com/dotnet/dotnet-docker.git#:samples/dotnetapp' ``` ### Build binaries for multiple platforms at once @@ -32,7 +34,7 @@ Taking advantage of Docker buildx, you can cross-build binaries for multiple pla For more info about how this works, see our documentation on [building images for a specific platform](./build-for-a-platform.md). ```pwsh -docker buildx build -o out --platform linux/amd64,linux/arm64 -f .\samples\dotnetapp\Dockerfile.sdk-build .\samples\dotnetapp\ +docker buildx build --pull --platform linux/amd64,linux/arm64 -f ./samples/dotnetapp/Dockerfile.sdk-build --output out ./samples/dotnetapp/ ``` Docker buildx will create a separate sub-directory for each target platform: @@ -46,7 +48,6 @@ C:\...\dotnetapp\out │ dotnetapp.dll │ dotnetapp.pdb │ dotnetapp.runtimeconfig.json -│ └───linux_arm64 dotnetapp dotnetapp.deps.json From 3562f21dc388f9dc07e4e2aab5351a1d24b813a5 Mon Sep 17 00:00:00 2001 From: Logan Bussell Date: Mon, 30 Sep 2024 14:27:18 -0700 Subject: [PATCH 5/5] Fix two CONTRIBUTING links that were failing markdown link check --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e2a2682e3..f6a09da4e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,11 +71,11 @@ The [`build-and-test.ps1`](https://github.com/dotnet/dotnet-docker/blob/main/bui #### Editing Dockerfiles -The [Dockerfiles](https://github.com/search?q=repo%3Adotnet%2Fdotnet-docker+filename%3ADockerfile&type=Code&ref=advsearch&l=&l=) contained in this repo are generated from a set of [Cottle](https://cottle.readthedocs.io/en/stable/page/01-overview.html) based [templates](https://github.com/dotnet/dotnet-docker/tree/main/eng/dockerfile-templates). A single template generates the set of Dockerfiles that are similar (e.g. all Windows sdk Dockerfiles for a particular .NET version). This ensures consistency across the various Dockerfiles and eases the burden of making changes to the Dockerfiles. Instead of editing the Dockerfiles directly, the templates should be updated and then the Dockerfiles should get regenerated by running the [generate Dockerfiles script](https://github.com/dotnet/dotnet-docker/blob/main/eng/dockerfile-templates/Get-GeneratedDockerfiles.ps1). +The [Dockerfiles](https://github.com/search?q=repo%3Adotnet%2Fdotnet-docker+path%3Asrc%2F**%2FDockerfile&type=code&ref=advsearch) contained in this repo are generated from a set of [Cottle](https://cottle.readthedocs.io/en/stable/page/01-overview.html) based [templates](https://github.com/dotnet/dotnet-docker/tree/main/eng/dockerfile-templates). A single template generates the set of Dockerfiles that are similar (e.g. all Windows sdk Dockerfiles for a particular .NET version). This ensures consistency across the various Dockerfiles and eases the burden of making changes to the Dockerfiles. Instead of editing the Dockerfiles directly, the templates should be updated and then the Dockerfiles should get regenerated by running the [generate Dockerfiles script](https://github.com/dotnet/dotnet-docker/blob/main/eng/dockerfile-templates/Get-GeneratedDockerfiles.ps1). #### Editing READMEs -The [READMEs](https://github.com/search?q=repo%3Adotnet%2Fdotnet-docker+filename%3AREADME+path%3A%2F&type=Code&ref=advsearch&l=&l=) contained in this repo are used as the descriptions for the Docker repositories the images are published to. Just like the Dockerfiles, the READMEs are generated from a set of [Cottle](https://cottle.readthedocs.io/en/stable/page/01-overview.html) based [templates](https://github.com/dotnet/dotnet-docker/tree/main/eng/readme-templates). This ensures consistency across the various READMEs and eases the burden of making changes. Instead of editing the READMEs directly, the templates should be updated and then the READMEs should get regenerated by running the [generate READMEs script](https://github.com/dotnet/dotnet-docker/blob/main/eng/readme-templates/Get-GeneratedReadmes.ps1). +The [READMEs](https://github.com/search?q=repo%3Adotnet%2Fdotnet-docker+path%3A**%2FREADME*+-path%3Aeng+-path%3Asamples&type=code&ref=advsearch&p=1) contained in this repo are used as the descriptions for the Docker repositories the images are published to. Just like the Dockerfiles, the READMEs are generated from a set of [Cottle](https://cottle.readthedocs.io/en/stable/page/01-overview.html) based [templates](https://github.com/dotnet/dotnet-docker/tree/main/eng/readme-templates). This ensures consistency across the various READMEs and eases the burden of making changes. Instead of editing the READMEs directly, the templates should be updated and then the READMEs should get regenerated by running the [generate READMEs script](https://github.com/dotnet/dotnet-docker/blob/main/eng/readme-templates/Get-GeneratedReadmes.ps1). #### Tests