Build Sitecore solutions
This topic describes the Dockerfile and the Docker image build process and how you use it to build your Sitecore solution.
If you have not already done so, clone the Docker Examples repository to a location on your machine. In this topic, we use the custom-images folder. This topic assumes that you have gone through the documentation on how to run an out-of-the-box Sitecore instance and that you can successfully start a Sitecore instance.
This topic shows how you build container images with your solution code. To ensure efficient feedback loops during development, you must also configure your development environment for deploying files into running containers.
About Dockerfiles
You write a Dockerfile as the first step in containerizing your application. A Dockerfile contains instructions that Docker uses to assemble a Docker image and run it. The Dockerfile commands are a step-by-step recipe for how to build up your image.
The following is a Dockerfile example for a simple (non-Sitecore) ASP.NET MVC app:
FROM mcr.microsoft.com/dotnet/framework/sdk:4.8 AS build
WORKDIR /app
COPY *.sln .
COPY aspnetmvcapp/*.csproj ./aspnetmvcapp/
RUN nuget restore
COPY aspnetmvcapp/. ./aspnetmvcapp/
WORKDIR /app/aspnetmvcapp
RUN msbuild /p:Configuration=Release
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8 AS runtime
WORKDIR /inetpub/wwwroot
COPY --from=build /app/aspnetmvcapp/. ./
This example uses a multi-stage build: first the code is built with the mcr.microsoft.com/dotnet/framework/sdk
image (the build
stage), and then the output is copied to the mcr.microsoft.com/dotnet/framework/aspnet
image (the runtime
stage).
The Dockerfile is then used by the Docker build command and this command builds and creates the image.
An optional .dockerignore
can be used to exclude files and folders from the build context, reducing the size or skipping sensitive data in COPY
and ADD
commands.
The example shows that code compilation and build are part of a Dockerfile's instructions. In .NET, this could include a NuGet restore, followed by invoking MSBuild. Building your application directly in a Dockerfile like this has advantages over a more traditional build, including:
-
Portability - The entire build process is containerized. Build agents and local environments do not need to have anything installed other than Docker.
-
Control over the build environment - The solution owns which build dependencies (and which versions) are used.
-
Efficiency - Even if build steps are very verbose, use of the Docker build cache optimizes rebuilds.
-
Plays nice in Docker ecosystem - Builds can be triggered using Docker commands and retrieving build artifacts for use in other images is simple with Dockerfile
FROM
instructions.
This topic focuses on using a Dockerfile. Although building in a Dockerfile is preferred, but you might have to rely on traditional means to build your solution (for example because of the limitations of a legacy codebase or build process).
Solution build Dockerfile and image
With a typical Sitecore implementation, a single Visual Studio solution usually creates build artifacts for multiple roles, for example, Website and XConnect assemblies, and some artifacts must be deployed to multiple roles (for example CM/CD). For containers, this means that the same build output must be layered on top of multiple base Sitecore runtime images. You could instead duplicate the build instructions for each Sitecore image, but this would be very inefficient.
What you need is a Dockerfile that is focused solely on building your solution and storing the output as structured build artifacts on the resulting image.
Build Dockerfile
The root Dockerfile
in the example is such a Dockerfile. The Docker community often name this type of build Dockerfile Dockerfile.build
.
Navigate to the custom-images folder, and look at the Dockerfile
located there. Note that for simplicity, the BASE_IMAGE
and BUILD_IMAGE
ARG
s have been expanded:
-
The file starts with an escape directive to set the escape character to a backtick (`), instead of the default backslash:
RequestResponse# escape=`
-
A
prep
stage is used to gather only artifacts necessary for NuGet restore. This is a common optimization made in .NET builds (see Dockerfile best practices for an explanation):RequestResponseFROM mcr.microsoft.com/dotnet/framework/sdk:4.8 AS prep COPY *.sln nuget.config Directory.Build.targets Packages.props \nuget\ COPY src\ \temp\ RUN Invoke-Expression 'robocopy C:\temp C:\nuget\src /s /ndl /njh /njs *.csproj *.scproj packages.config'
-
A new
builder
stage initiates the code compilation and build process, based on the .NET Framework SDK image, and aBUILD_CONFIGURATION
ARG
is declared (this is eitherdebug
orrelease
, configured in Docker Compose):RequestResponseFROM mcr.microsoft.com/dotnet/framework/sdk:4.8 AS builder ARG BUILD_CONFIGURATION
-
The
SHELL
instruction switches the default shell to PowerShell (from the default ofcmd
) for all subsequent instructions. This is a common instruction in Windows Dockerfiles:RequestResponseSHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
-
Next, a working directory is created, the NuGet artifacts collected earlier are copied in, and a (optimized)
nuget restore
is performed:RequestResponseWORKDIR C:\build COPY --from=prep .\nuget .\ RUN nuget restore
-
After the restore, the remainder of the source code is copied in, and transform files are collected at
C:\out\transforms
:RequestResponseCOPY src\ .\src\ RUN Invoke-Expression 'robocopy C:\build\src C:\out\transforms /s /ndl /njh /njs *.xdt'
The example includes a
.dockerignore
(located alongside the Dockerfile). This reduces the size of theCOPY
commands and excludes things likebin
andobj
folders. This is the best practice for build Dockerfiles. For more information about config transforms, see Applying configuration transforms. -
Next, the build is performed for the configured
BUILD_CONFIGURATION
usingmsbuild
. The example contains projects targeting both the website/platform (DockerExamples.Website.csproj) and xConnect (DockerExamples.XConnect.csproj) environments. A simple file system publish is used, with the output landing atC:\out\website
andC:\out\xconnect
respectively:RequestResponseRUN msbuild .\src\DockerExamples.Website\DockerExamples.Website.csproj /p:Configuration=$env:BUILD_CONFIGURATION /p:DeployOnBuild=True /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:PublishUrl=C:\out\website RUN msbuild .\src\DockerExamples.XConnect\DockerExamples.XConnect.csproj /p:Configuration=$env:BUILD_CONFIGURATION /p:DeployOnBuild=True /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:PublishUrl=C:\out\xconnect
-
After the build is completed, the build output for the final image is collected. For this stage, the Microsoft Nano Server image is used. Because this image only delivers files and is never run, the base image is selected for its optimized size - much smaller than other runtime images:
RequestResponseFROM mcr.microsoft.com/windows/nanoserver:1809
-
The output files are copied in from the
builder
stage to our final image with the following structure:-
\artifacts\website
-
\artifacts\transforms
-
\artifacts\xconnect
RequestResponseWORKDIR C:\artifacts COPY --from=builder C:\out\website .\website\ COPY --from=builder C:\out\transforms .\transforms\ COPY --from=builder C:\out\xconnect .\xconnect\
-
This example does not do Sitecore item serialization. Depending on your serialization framework and strategy, your solution build Dockerfile can have additional instructions. See Deploying items for details.
Solution image
The solution build Dockerfile produces an image that consists only of build artifacts. The resulting solution image is never intended to be run in a Docker container. Such images are sometimes called asset images.
Configure in Docker Compose
You must send a Dockerfile through the Docker build command to produce an image. You can certainly use build
command directly, but it is mostly configured with Docker Compose.
The custom-images folder contains the following files:
-
.env
-
docker-compose.yml
-
docker-compose.override.yml
These are all types of Docker Compose files.
Understand docker-compose.override.yml
For any Docker Compose command (such as docker-compose up -d
), the docker-compose.override.yml
file is automatically included along with your main docker-compose.yml
file, unless you specify otherwise. When Docker Compose is handed two or more compose files, it merges them together, bringing all the resources and configuration together into a single merged definition.
In this example, the docker-compose.yml
file is the default Sitecore Experience Platform - Single (XP0) Docker Compose file that comes from Sitecore. The docker-compose.override.yml
extends the main file with overrides and extensions necessary for custom Sitecore image build and development purposes.
Configure the solution service
The docker-compose.override.yml
file is where you define the solution image build.
Open the file and look at the solution
service to see how it is configured:
solution:
image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-solution:${VERSION:-latest}
build:
context: .
args:
BASE_IMAGE: ${SOLUTION_BASE_IMAGE}
BUILD_IMAGE: ${SOLUTION_BUILD_IMAGE}
BUILD_CONFIGURATION: ${BUILD_CONFIGURATION}
scale: 0
Note the following points:
-
Variable values (for example,
${SOLUTION_BASE_IMAGE}
) are defined in the environment file (.env
) in this example, but can also be sourced from system environment variables on local development machines or secrets on your build server. -
The image name uses a
-solution
suffix. With the default variable values, the tagged version isdocker-examples-solution:latest
-
The
build
context
is set to.
to tell Docker Compose to use the build Dockerfile at the same location. -
The
build
args
correlate to those found in the build Dockerfile. -
scale
is set to 0 so that a container for thesolution
service is not started during adocker-compose up
.
Build the solution image
To build the solution image:
-
Open a PowerShell prompt and run the following from the same folder as your Compose files:
RequestResponsedocker-compose build solution
This initiates the build process for the solution image and creates your image:
RequestResponseBuilding solution Step 1/21 : ARG BASE_IMAGE Step 2/21 : ARG BUILD_IMAGE Step 3/21 : FROM ${BUILD_IMAGE} AS prep [...] Successfully built 9bb20b2ab6db Successfully tagged docker-examples-solution:latest
-
Confirm the image was created by listing all Docker images:
RequestResponsedocker images docker-examples*
RequestResponseREPOSITORY TAG IMAGE ID CREATED SIZE docker-examples-solution latest 9bb20b2ab6db 2 minutes ago 259MB
Troubleshoot solution image builds
Start by looking at the troubleshooting guide.
One common troubleshooting problem unique to the solution image (or any asset image) is that because it is never run as a container, it might not be obvious how you can explore the resulting file system. You can do this by running the image with an interactive shell.
For example, to open up an interactive command prompt (Nano Server does not have PowerShell) to the solution image example given previously:
docker run -it --rm docker-examples-solution:latest
Type exit
to remove the temporary container and return to your previous PowerShell session.