How to build custom images on top of base Sitecore runtime images

When I started working with Sitecore running in Docker, I faced a lot of issues and mainly it was related to lack of my knowledge. I saw a lot of mentions that we need to create an image “on top of Sitecore images”. Let’s figure out what that means. Basically, this definition comes from the Docker terminology. 

Docker images are a result of their build. At the same time, Docker Container is a number of images that are run inside a container. Each Docker image has a Dockerfile behind it. This file provides instructions on how to build the current image.

Docker image is that it’s a layered file system. In other words, if you start out with an image that’s just the operating system (say Windows) and then add an application (say Node.js), you’ll wind up with something like this:

And the final File System of the Image will look as following:

As you can see, the difference between IMAGE1 and IMAGE2 is just the application itself, and then IMAGE4 has the changes made on layers 3. So in order to create an image, you are basically starting with a base image and defining the changes to it, or let's say ON TOP ON IT. So when we see that we need to create an image on top of the Sitecore image, it just means that we will use it as a base image. Simple! Let’s have a look at an example:

Sitecore provides sitecore-xp1-mssql-init (or sitecore-xm1-mssql-init) images with dacpac files to initially create database tables, users, and default items. All this stuff is just for empty Sitecore instances. Now let’s imagine that we also need Sitecore Powershell and Headless Services modules to be installed as well? Once Docker is a stateless system, we can’t install these modules as packages. Because in case we re-run the Docker instance, all items will disappear. So we need to create a new mssql-init image on top of sitecore-xm1-mssql-init.

The first thing that we need to add is Dockerfile:

# escape=`

ARG PARENT_IMAGE
ARG HEADLESS_SERVICES_IMAGE
ARG SPE_IMAGE
ARG SXA_IMAGE

FROM ${HEADLESS_SERVICES_IMAGE} AS headless_services
FROM ${SPE_IMAGE} as spe
FROM ${SXA_IMAGE} as sxa_image
FROM ${PARENT_IMAGE} as final

# Copy and init the SPE
COPY --from=spe C:\module\db C:\resources\spe

# Copy and init the SXA
COPY --from=sxa_image C:\module\db C:\resources\sxa

# Copy and init the JSS / Headless Services Module
COPY --from=headless_services C:\module\db C:\resources\jss

Where ARGs are variables that will be passed to the Dockerfile from the Docker-compose file. PARENT_IMAGE in this case will be sitecore-xm1-mssql-init image. Take into account that only the last FROM command will be assumed as a base image that we extend with all above. In this Dockerfile, we defined three layers for external services and added a COPY command to copy required files from them to the final image.

The Docker-compose.yml file looks like below:

version: "2.4"

services: 
  mssql-init:
    image: corporate-xm1-mssql-init-sxa-headless:${SITECORE_VERSION}
    build:
      context: ./docker/build/mssql-init
      args:
        PARENT_IMAGE: ${SITECORE_DOCKER_REGISTRY}sitecore-xp1-mssql-init:10.1-ltsc2019
        HEADLESS_SERVICES_IMAGE: scr.sitecore.com/sxp/modules/sitecore-headless-services-xp1-assets:16.0.0-1809
        SPE_IMAGE: scr.sitecore.com/sxp/modules/spe-assets:6.2.383-1809
        SXA_IMAGE: scr.sitecore.com/sxp/modules/sxa-xp1-assets:10.1-1809

Where image is the name of our custom image and context is a path to the folder where Dockerfile is located. 

This example explains how to create images ON TOP OF others.