|
| 1 | +--- |
| 2 | +title: Visual Studio Container Tools build overview |
| 3 | +author: ghogen |
| 4 | +description: Overview of the Container Tools build process |
| 5 | +ms.author: ghogen |
| 6 | +ms.date: 06/06/2019 |
| 7 | +ms.technology: vs-azure |
| 8 | +ms.topic: conceptual |
| 9 | +--- |
| 10 | +# Building containerized apps using Visual Studio or the command line |
| 11 | + |
| 12 | +Whether you're building from the Visual Studio IDE, or setting up a command-line build, you need to know how Visual Studio builds uses the Dockerfile to build your projects. For performance reasons, Visual Studio follows a special process for containerized apps. Understanding how Visual Studio builds your projects is especially important when you customize your build process by modifying the Dockerfile. |
| 13 | + |
| 14 | +When Visual Studio builds a project that doesn't use Docker containers, it invokes MSBuild on the local machine and generates the output files in a folder (typically `bin`) under your local solution folder. For a containerized project, however, the build process takes account of the Dockerfile's instructions for building the containerized app. The Dockerfile that Visual Studio uses is divided into multiple stages. This process relies on Docker's *multistage build* feature. |
| 15 | + |
| 16 | +## Multistage build |
| 17 | + |
| 18 | +The multistage build feature helps make the process of building containers more efficient, and makes containers smaller by allowing them to contain only the bits that your app needs at runtime. |
| 19 | + |
| 20 | +The multistage build allows container images to be created in stages that produce intermediate images. As an example, consider a typical Dockerfile generated by Visual Studio - the first stage is `base`: |
| 21 | + |
| 22 | +``` |
| 23 | +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base |
| 24 | +WORKDIR /app |
| 25 | +EXPOSE 80 |
| 26 | +EXPOSE 443 |
| 27 | +``` |
| 28 | + |
| 29 | +The lines in the Dockerfile begin with the Nanoserver image from Microsoft Container Registry (mcr.microsoft.com) and create an intermediate image `base` that exposes ports 80 and 443, and sets the working directory to `/app`. |
| 30 | + |
| 31 | +The next stage is `build`, which appears as follows: |
| 32 | + |
| 33 | +``` |
| 34 | +FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build |
| 35 | +WORKDIR /src |
| 36 | +COPY ["WebApplication43/WebApplication43.csproj", "WebApplication43/"] |
| 37 | +RUN dotnet restore "WebApplication43/WebApplication43.csproj" |
| 38 | +COPY . . |
| 39 | +WORKDIR "/src/WebApplication43" |
| 40 | +RUN dotnet build "WebApplication43.csproj" -c Release -o /app |
| 41 | +``` |
| 42 | + |
| 43 | +You can see that the `build` stage starts from a different original image from the registry (`sdk` rather than `aspnet`), rather than continuing from base. The `sdk` image has all the build tools, and for that reason it's a lot bigger than the aspnet image, which only contains runtime components. The reason for using a separate image becomes clear when you look at the rest of the Dockerfile: |
| 44 | + |
| 45 | +``` |
| 46 | +FROM build AS publish |
| 47 | +RUN dotnet publish "WebApplication43.csproj" -c Release -o /app |
| 48 | +
|
| 49 | +FROM base AS final |
| 50 | +WORKDIR /app |
| 51 | +COPY --from=publish /app . |
| 52 | +ENTRYPOINT ["dotnet", "WebApplication43.dll"] |
| 53 | +``` |
| 54 | + |
| 55 | +The final stage starts again from `base`, and includes the `COPY --from=publish` to copy the published output to the final image. This process makes it possible for the final image to be a lot smaller, since it doesn't need to include all of the build tools that were in the `sdk` image. |
| 56 | + |
| 57 | +## Faster builds for the Debug configuration |
| 58 | + |
| 59 | +For performance reasons, the build process for containerized apps is not as straightforward as simply following the steps outlined in the Dockerfile. Building in a container is much slower than building on the local machine. So, when you build in the **Debug** configuration, Visual Studio actually builds your projects on the local machine, and then shares the output folder to the container using volume mounting. A build with this optimization enabled is called a *Fast* mode build. |
| 60 | + |
| 61 | +In **Fast** mode, Visual Studio calls `docker build` with an argument that tells Docker to build only the `base` stage. Visual Studio handles the rest of the process without regard to the contents of the Dockerfile. So, when you modify your Dockerfile, such as to customize the container environment or install additional dependencies, you should put your modifications in the first stage. Any custom steps placed in the Dockerfile's `build`, `publish`, or `final` stages will not be executed. |
| 62 | + |
| 63 | +This performance optimization only occurs when you build in the **Debug** configuration. In the **Release** configuration, the build occurs in the container as specified in the Dockerfile. |
| 64 | + |
| 65 | +If you want to disable the performance optimization and build as the Dockerfile specifies, then set the **ContainerDevelopmentMode** property to **Regular** in the project file as follows: |
| 66 | + |
| 67 | +```xml |
| 68 | +<PropertyGroup> |
| 69 | + <ContainerDevelopmentMode>Regular</ContainerDevelopmentMode> |
| 70 | +</PropertyGroup> |
| 71 | +``` |
| 72 | + |
| 73 | +To restore the performance optimization, set the value to **Fast**. |
| 74 | + |
| 75 | +## Building from the command line |
| 76 | + |
| 77 | +You can use `docker build` or `MSBuild` to build from the command line. |
| 78 | + |
| 79 | +### docker build |
| 80 | + |
| 81 | +To build a containerized solution from the command line, you can usually use the command `docker build <context>` for each project in the solution. You provide the *build context* argument. The *build context* for a Dockerfile is the folder on the local machine that's used as the working folder to generate the image. For example, it's the f`older that you copy files from when you copy to the container. In .NET Core projects, use the folder that contains the solution file (.sln). Expressed as a relative path, this argument is typically ".." for a Dockerfile in a project folder, and the solution file in its parent folder. For .NET Framework projects, the build context is the project folder, not the solution folder. |
| 82 | + |
| 83 | +> [!NOTE] |
| 84 | +> For .NET Framework projects, and for .NET Core projects created with versions of Visual Studio prior to Visual Studio 2017 Update 4, the Dockerfile did not use multistage builds. When Visual Studio builds with these Dockerfiles, instead of building the project in the Dockerfile, Visual Studio builds each project and then copies the results to the container. Because the build steps aren't included in the Dockerfile, you can't build such projects using `docker build` from the command line. You should use MSBuild to build these projects. |
| 85 | +
|
| 86 | +### MSBuild |
| 87 | + |
| 88 | +To build a project or solution for single docker container, you can use MSBuild with the `/t:ContainerBuild` command option. |
| 89 | + |
| 90 | +```cmd |
| 91 | +MSBuild <solution_name>.sln /t:ContainerBuild /p:Configuration=Debug |
| 92 | +``` |
| 93 | + |
| 94 | +You'll see output similar to what you see in the **Output** window when you build your solution from the Visual Studio IDE. |
| 95 | + |
| 96 | +When the **Debug** configuration is specified (and the **ContainerDevelopmentMode** property is set to **Fast**), Visual Studio uses the build optimization described in this article, so your project builds more quickly on the local machine and is copied to the container. When the **Release** configuration is specified, the build occurs in the container and might be slower. |
| 97 | + |
| 98 | +If you are using a Docker Compose project, use the command: |
| 99 | + |
| 100 | +``` |
| 101 | +msbuild /t:DockerPackageService /p:DockerAppType=AspNet /p:Configuration=Release <project_name>\<project_name>.csproj |
| 102 | +msbuild /p:Configuration=Release docker-compose.dcproj |
| 103 | +``` |
| 104 | + |
| 105 | +## Scripting a containerized build using Azure DevOps |
| 106 | + |
| 107 | +You can use Azure Pipelines to build your containerized apps and publish to Azure Container Registry or Docker Hub. Follow the instructions for setting up an Azure Pipeline in this article: [Build, test, and push Docker container app](/azure/devops/pipelines/languages/docker?view=azure-devops). However, to build your solution, add an MSBuild task to your pipeline. Internally, MSBuild uses `docker build` to build your containers. |
| 108 | + |
| 109 | +Add an MSBuild task to your pipeline as follows: |
| 110 | + |
| 111 | +``` |
| 112 | +- task: MSBuild@1 |
| 113 | + displayName: 'Build Application and main Docker Image' |
| 114 | + inputs: |
| 115 | + solution: '**/*.sln' |
| 116 | + msbuildArguments: '/t:ContainerBuild /p:Configuration=$(buildConfiguration)' |
| 117 | +``` |
| 118 | + |
| 119 | +For more information, see [MSBuild task](/azure/devops/pipelines/tasks/build/msbuild?view=azure-devops). |
| 120 | + |
| 121 | +## Next steps |
| 122 | + |
| 123 | +Learn how to further customize your builds by setting additional MSBuild properties in your project files. See [Container Tools build properties](container-msbuild-properties.md). |
| 124 | + |
| 125 | +## See also |
| 126 | + |
| 127 | +[MSBuild](../msbuild/msbuild.md) |
| 128 | +[Dockerfile on Windows](/virtualization/windowscontainers/manage-docker/manage-windows-dockerfile) |
| 129 | +[Linux containers on Windows](/virtualization/windowscontainers/deploy-containers/linux-containers) |
0 commit comments