top of page
  • Writer's pictureGowtham

Building a Sitecore Solution from Scratch with Docker: Part 4

In our previous blog post, we successfully installed SXA and SPE on our Docker image. In this upcoming post, we will explore the process of creating a solution using Helix architecture and implementing changes on Docker.


Creating Sitecore Docker Solution with Helix Architecture

We can now begin the process of developing the code and establishing the solution structure. To do so, let us create a folder named "src" at the root level, and within that, create the Foundation, Feature, and Project folders using Helix architecture, as illustrated below.

helix

To begin, launch Visual Studio and create a new blank solution at the root level, naming it "SitecoreDocker". By default, Visual Studio generates a folder for your solution. You can relocate the contents of this folder to the root directory and delete the empty folder.


After creating the solution, proceed to create new solution folders named Feature, Foundation, and Project.

solution

Creating the Projects

Now we can create new projects, and as an example, I will create a project called "SitecoreDocker.Website" in the "\src\Project" folder as shown below screenshot.

creating-new-project

After creating the project, we will generate several sample files which will be deployed to our container image, and we will observe the resulting modifications on the site. Prior to doing so, we will rename the website project, assembly name, and default namespace in Visual Studio to "SitecoreDocker.Website".

assembly-name-chages

To proceed, we will generate an example view file by creating a view folder and configuring it with a sample configuration obtained from the App_Config file, as depicted below.

views-and-config

Creating Publish Profile and Deploy folder

After completing the creation of our solution, which includes the essential files, our next step is to generate a publish profile. This profile will enable us to push our changes to the Docker Deploy folder, where they will be made available for consumption by Docker.


Within our docker folder, we establish subfolders named "deploy" and "website". These subfolders will serve as our local deployment target, where we will place our code for execution and testing purposes.

Ex: SitcoreDocker\docker\deploy\website


To create a publish folder right click on the SitecoreDocker.Websiteproject and click on Publish option and select target as Folder.

Creating_Publish_Folder

To publish our changes, we will specify the folder location as "SitcoreDocker\docker\deploy\website". This folder, which we have previously created, will serve as the destination for our published files.

publish-profile

Click the "Finish" button to initiate the project publishing process. Once the publishing is complete, you can open the "SitcoreDocker\docker\deploy\website" folder to verify that the changes have been successfully reflected within the folder.

publishing-files

Mounting Deployment folder to Docker

Currently, we have a folder on our filesystem where our website is published, but it is not being utilized by our Docker instances. To enable coding for our new solution, we need to deploy our published code to the CM and CD Docker instances.


To achieve this, we will begin by mounting our deployment folder to the CD and CM instances. We will create a new variable in our .env file and set its value to the path of our deploy folder. This will ensure that our Docker instances utilize the code from the specified deployment folder.

LOCAL_DEPLOY_PATH=.\docker\deploy

To mount the folder to the CD and CM instances, we will add a new volume entry in the `docker-compose.override.yml` file. This entry will associate the path of the deployment folder with the appropriate directories in the Docker containers. By doing so, the CD and CM instances will have access to the published code.

  cd:
    image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-xm1-cd:${VERSION:-latest}
    build:
      context: ./docker/build/cd
      args:
        BASE_IMAGE: ${SITECORE_DOCKER_REGISTRY}sitecore-xm1-cd:${SITECORE_VERSION}
        SXA_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-sxa-xm1-assets:${SXA_VERSION}
    volumes:
        - ${LOCAL_DATA_PATH}\cd:C:\inetpub\wwwroot\App_Data\logs
        - ${LOCAL_DEPLOY_PATH}\website:C:\deploy

  cm:
    image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-xm1-cm:${VERSION:-latest}
    build:
      context: ./docker/build/cm
      args:
        BASE_IMAGE: ${SITECORE_DOCKER_REGISTRY}sitecore-xm1-cm:${SITECORE_VERSION}
        SPE_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-spe-assets:${SPE_VERSION}
        SXA_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-sxa-xm1-assets:${SXA_VERSION}
    volumes:
        - ${LOCAL_DATA_PATH}\cm:C:\inetpub\wwwroot\App_Data\logs
        - ${LOCAL_DEPLOY_PATH}\website:C:\deploy

By mounting our local path to the `C:\deploy` path of the CD and CM instances, we establish a connection between the two. However, it's important to note that the current code is not yet included in the CM and CD websites. As a result, it has not been deployed to our website.


To address this, the next step involves transferring the deployed code from the mounted folder to our website. This process will ensure that the code becomes part of the CM and CD websites and is deployed accordingly.


Deploying files from mounted folder to container image

Sitecore Docker Tools, offered by Sitecore, are a collection of utilities designed to support Sitecore developers in the setup and operation of containerized Sitecore environments. These tools serve as helpful utilities that simplify the process of initializing and managing Sitecore instances within Docker containers.


To facilitate the monitoring of our deployment folder on both instances, we will start by using the "sitecore-docker-tools-assets" image. We'll then create an entrypoint that executes a PowerShell command. This command will continuously observe the deployment folder, ensuring that any modifications or additions are detected and appropriately managed in both the CD and CM instances.

Within the .env file, we utilize the following parameters:

SITECORE_TOOLS_REGISTRY=scr.sitecore.com/tools/
TOOLS_VERSION=10.2-1809

We incorporate the new TOOLING_IMAGE and define the entry point within the docker-compose.override.yml file.


  cd:
    image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-xm1-cd:${VERSION:-latest}
    build:
      context: ./docker/build/cd
      args:
        BASE_IMAGE: ${SITECORE_DOCKER_REGISTRY}sitecore-xm1-cd:${SITECORE_VERSION}
        SXA_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-sxa-xm1-assets:${SXA_VERSION}
        TOOLING_IMAGE: ${SITECORE_TOOLS_REGISTRY}sitecore-docker-tools-assets:${TOOLS_VERSION}
    volumes:
        - ${LOCAL_DATA_PATH}\cd:C:\inetpub\wwwroot\App_Data\logs
        - ${LOCAL_DEPLOY_PATH}\website:C:\deploy
    entrypoint: powershell -Command "& C:\\tools\\entrypoints\\iis\\Development.ps1"

  cm:
    image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-xm1-cm:${VERSION:-latest}
    build:
      context: ./docker/build/cm
      args:
        BASE_IMAGE: ${SITECORE_DOCKER_REGISTRY}sitecore-xm1-cm:${SITECORE_VERSION}
        SPE_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-spe-assets:${SPE_VERSION}
        SXA_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-sxa-xm1-assets:${SXA_VERSION}
        TOOLING_IMAGE: ${SITECORE_TOOLS_REGISTRY}sitecore-docker-tools-assets:${TOOLS_VERSION}
    volumes:
        - ${LOCAL_DATA_PATH}\cm:C:\inetpub\wwwroot\App_Data\logs
        - ${LOCAL_DEPLOY_PATH}\website:C:\deploy
    entrypoint: powershell -Command "& C:\\tools\\entrypoints\\iis\\Development.ps1"

To include the Tooling Image in our custom image, you need to make modifications to both the CM and CD Dockerfiles as follows:


CM:

# escape=`

ARG BASE_IMAGE
ARG SXA_IMAGE
ARG SPE_IMAGE
ARG TOOLING_IMAGE

FROM ${SPE_IMAGE} as spe
FROM ${SXA_IMAGE} as sxa
FROM ${TOOLING_IMAGE} as tooling
FROM ${BASE_IMAGE}

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Copy development tools and entrypoint
COPY --from=tooling \tools\ \tools\

WORKDIR C:\inetpub\wwwroot

COPY --from=spe C:\module\cm\content C:\inetpub\wwwroot

COPY --from=sxa C:\module\cm\content C:\inetpub\wwwroot
COPY --from=sxa C:\module\tools C:\module\tools
RUN C:\module\tools\Initialize-Content.ps1 -TargetPath C:\inetpub\wwwroot; `
  Remove-Item -Path C:\module -Recurse -Force;

CD:

# escape=`

ARG BASE_IMAGE
ARG SXA_IMAGE
ARG TOOLING_IMAGE

FROM ${SXA_IMAGE} as sxa
FROM ${TOOLING_IMAGE} as tooling
FROM ${BASE_IMAGE}

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Copy development tools and entrypoint
COPY --from=tooling \tools\ \tools\

WORKDIR C:\inetpub\wwwroot

COPY --from=sxa C:\module\cd\content C:\inetpub\wwwroot
COPY --from=sxa C:\module\tools C:\module\tools
RUN C:\module\tools\Initialize-Content.ps1 -TargetPath C:\inetpub\wwwroot; `
  Remove-Item -Path C:\module -Recurse -Force;

Adding Docker Build

Up to this point, we have successfully incorporated the required folders and configurations to facilitate the publishing of solution files. Now, the next step is to transform our solution into a container image. To achieve this, we need a dedicated Dockerfile that primarily focuses on building the solution and preserving the resulting output as structured build artifacts within the image.


To enable the conversion of our solution into a container image, we must add the following parameters to the .env file:

SOLUTION_BUILD_IMAGE=mcr.microsoft.com/dotnet/framework/sdk:4.8
SOLUTION_BASE_IMAGE=mcr.microsoft.com/windows/nanoserver:1089
BUILD_CONFIGURATION=debug

In the docker-compose.override.yml file, when adding the Docker.build image, we include the following configuration:

  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

To begin, we will create an empty Dockerfile within the root folder. Afterwards, we will populate it with the following content:

# escape=`

ARG BASE_IMAGE
ARG BUILD_IMAGE

FROM ${BUILD_IMAGE} AS prep
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Gather only artifacts necessary for NuGet restore, retaining directory structure
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'

FROM ${BUILD_IMAGE} AS builder

ARG BUILD_CONFIGURATION

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Create an empty working directory
WORKDIR C:\build

# Copy prepped NuGet artifacts, and restore as distinct layer to take better advantage of caching
COPY --from=prep .\nuget .\
RUN nuget restore

# Copy remaining source code
COPY src\ .\src\

# Copy transforms, retaining directory structure
RUN Invoke-Expression 'robocopy C:\build\src C:\out\transforms /s /ndl /njh /njs *.xdt'

# Build website with file publish
RUN msbuild .\src\Project\website\SitecoreDocker.Website.csproj /p:Configuration=$env:BUILD_CONFIGURATION /p:DeployOnBuild=True /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:PublishUrl=C:\out\website

FROM ${BASE_IMAGE}

WORKDIR C:\artifacts

# Copy final build artifacts
COPY --from=builder C:\out\website .\website\

This newly added Dockerfile, located in the root folder, serves the purpose of creating our custom image. It not only builds the solution but also transfers the generated artifacts to their designated locations within the container.


Within the docker-compose.override.yml file, we introduce an extra args parameter specifically for CD and CM.

  cd:
    image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-xm1-cd:${VERSION:-latest}
    build:
      context: ./docker/build/cd
      args:
        BASE_IMAGE: ${SITECORE_DOCKER_REGISTRY}sitecore-xm1-cd:${SITECORE_VERSION}
        SXA_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-sxa-xm1-assets:${SXA_VERSION}
        TOOLING_IMAGE: ${SITECORE_TOOLS_REGISTRY}sitecore-docker-tools-assets:${TOOLS_VERSION}
        SOLUTION_IMAGE: ${REGISTRY}${COMPOSE_PROJECT_NAME}-solution:${VERSION:-latest}
    depends_on:
      - solution
    volumes:
      - ${LOCAL_DEPLOY_PATH}\website:C:\deploy
      - ${LOCAL_DATA_PATH}\cd:C:\inetpub\wwwroot\App_Data\logs
      - ${LOCAL_DATA_PATH}\devicedetection:C:\inetpub\wwwroot\App_Data\DeviceDetection
    environment:
      SITECORE_DEVELOPMENT_PATCHES: CustomErrorsOff
    entrypoint: powershell -Command "& C:\\tools\\entrypoints\\iis\\Development.ps1"

  cm:
    image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-xm1-cm:${VERSION:-latest}
    build:
      context: ./docker/build/cm
      args:
        BASE_IMAGE: ${SITECORE_DOCKER_REGISTRY}sitecore-xm1-cm:${SITECORE_VERSION}
        SPE_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-spe-assets:${SPE_VERSION}
        SXA_IMAGE: ${SITECORE_MODULE_REGISTRY}sitecore-sxa-xm1-assets:${SXA_VERSION}
        TOOLING_IMAGE: ${SITECORE_TOOLS_REGISTRY}sitecore-docker-tools-assets:${TOOLS_VERSION}
        SOLUTION_IMAGE: ${REGISTRY}${COMPOSE_PROJECT_NAME}-solution:${VERSION:-latest}
    depends_on:
      - solution
    volumes:
      - ${LOCAL_DEPLOY_PATH}\website:C:\deploy
      - ${LOCAL_DATA_PATH}\cm:C:\inetpub\wwwroot\App_Data\logs
    environment:
      SITECORE_DEVELOPMENT_PATCHES: CustomErrorsOff
    entrypoint: powershell -Command "& C:\\tools\\entrypoints\\iis\\Development.ps1"

In order to incorporate this additional parameter into our build process, we must make adjustments to the Dockerfiles for CM and CD.


CM:

# escape=`

ARG BASE_IMAGE
ARG SXA_IMAGE
ARG SPE_IMAGE
ARG TOOLING_IMAGE

FROM ${TOOLING_IMAGE} as tooling
FROM ${SPE_IMAGE} as spe
FROM ${SXA_IMAGE} as sxa
FROM ${BASE_IMAGE}

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Copy development tools and entrypoint
COPY --from=tooling \tools\ \tools\

WORKDIR C:\inetpub\wwwroot

# Add SPE module
COPY --from=spe \module\cm\content .\

# Add SXA module
COPY --from=sxa \module\cm\content .\
COPY --from=sxa \module\tools \module\tools
RUN C:\module\tools\Initialize-Content.ps1 -TargetPath .\; `
    Remove-Item -Path C:\module -Recurse -Force;

# Copy solution website files
COPY --from=solution \artifacts\website\ .\

CD:

# escape=`

ARG BASE_IMAGE
ARG SXA_IMAGE
ARG TOOLING_IMAGE

FROM ${TOOLING_IMAGE} as tooling
FROM ${SXA_IMAGE} as sxa
FROM ${BASE_IMAGE}

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Copy development tools and entrypoint
COPY --from=tooling \tools\ \tools\

WORKDIR C:\inetpub\wwwroot

# Add SXA module
COPY --from=sxa \module\cd\content .\
COPY --from=sxa \module\tools \module\tools
RUN C:\module\tools\Initialize-Content.ps1 -TargetPath .\; `
    Remove-Item -Path C:\module -Recurse -Force;

# Copy solution website files
COPY --from=solution \artifacts\website\ .\

We have finished setting up the solution, and now it's time to try it out by running our containers. To do this, just use the command below.

docker compose up -d

Once the website is up and operational, you can verify that the files have been successfully deployed to the container image by executing the following command in the Docker Desktop Terminal window on both CD and CM.

cd bin
dir SitecoreDocker*
verifying-deployment

If you see the screen above, it means you have successfully finished creating and deploying a new solution for Sitecore from scratch using Docker with the Helix architecture. I consider this as the final part of the series, and I may use the same repository in the future for any additional extensions if needed. You can find the entire solution at the provided link. If you have any questions, please leave them in the comment box, and I will get back to you soon. Thank you and enjoy working with Sitecore!

166 views0 comments
bottom of page