I just wanted to write this information down since it helped me understand better what happens when you make changes to a Dockerfile.

A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.

Short overview of the commands that this article uses/has info on:

  • docker run -it scr.sitecore.com/sxp/modules/sxa-xp1-assets:10.1-1809
  • docker run -it scr.sitecore.com/sxp/sitecore-xp0-solr-init:10.1-ltsc2019
  • docker run -it --entrypoint powershell scr.sitecore.com/sxp/sitecore-xp0-solr-init:10.1-ltsc2019
  • docker-compose run --entrypoint powershell solr-init

You often see copy statements like this in the Sitecore Dockerfiles COPY --from=tooling \tools\ \tools\

Meaning that from the tooling image, the folder \tools will be copied to a location on the image you are creating. But what is exactly being copied?
I want to look at the files and functionalities that are actually being copied!

If you don't know what a Docker Image is or how it's related to layers, you can read more on the quick overview page of Sitecore

To have a look at the contents of an image, I could find 2 ways that worked.

  • running a container in interactive mode
  • overwriting the entrypoint for a container defined in docker-compose files

Running a container in interactive mode

You can force a container to run in interactive mode which will then open up a terminal into the running container:

Example for the SXA image: docker run -it scr.sitecore.com/sxp/modules/sxa-xp1-assets:10.1-1809

Docker run reference
Configure containers at runtime

Making it easy to check what is in that image/container:

Additionally, you can also navigate the individual container in Visual Studio Code (with the Docker extension):

Overwriting the entrypoint for a container

Sometimes the first example will not work since an entrypoint was defined for the image, making it actually do stuff. Such as the solr-init image, which will run the Start.ps1 script automatically. And that script is dependant on some variables, which aren't available when running it with the docker run command.

An entrypoint is the default command that gets executed when the container starts.
https://docs.docker.com/engine/reference/run/#entrypoint-default-command-to-execute-at-runtime

The solr-init image has an entrypoint defined, so running it will also trigger the entrypoint. And this will result in the error below since it is dependant on variables that we didn't include.
docker run -it scr.sitecore.com/sxp/sitecore-xp0-solr-init:10.1-ltsc2019

As you can see, the error originates in Start.ps1, which is the script that is defined in the Entrypoint for that image.  
The easy fix is to overwrite the entrypoint: docker run -it --entrypoint powershell scr.sitecore.com/sxp/sitecore-xp0-solr-init:10.1-ltsc2019

Always define --entrypoint before the image name, otherwise it will be ignored!

Third option - docker-compose

Another option is to start a named container in your docker-compose file. This will start all containers that this image has a dependency on - so this will not always be what you want.

Eg. the solr-init image has a dependency on solr. So when starting solr-init, you'll also start the solr container.

  solr:
    isolation: ${ISOLATION}
    image: ${SITECORE_DOCKER_REGISTRY}nonproduction/solr:8.4.0-${SITECORE_VERSION}
    ports:
      - "8984:8983"
    volumes:
      - type: bind
        source: .\solr-data
        target: c:\data
    environment:
      SOLR_MODE: solrcloud
  solr-init:
    isolation: ${ISOLATION}
    image: ${SITECORE_DOCKER_REGISTRY}sitecore-xp0-solr-init:${SITECORE_VERSION}
    environment:
      SITECORE_SOLR_CONNECTION_STRING: http://solr:8983/solr
      SOLR_CORE_PREFIX_NAME: ${SOLR_CORE_PREFIX_NAME}
    depends_on:
      solr:
        condition: service_healthy

To start the solr-init use this command: docker-compose run --entrypoint powershell solr-init

Also notice the --entrypoint powershell parameter, this will force docker-compose to use powershell as an entrypoint. Other entrypoints defined on the image or the docker-compose will get ignored by this.

In the terminal that opens, you can navigate for example to the C:\data folder and check what is in the cores.json file (you can use the command more cores.json).

{
    "sitecore": [
        "_core_index",
        "_master_index",
        "_web_index",
        "_marketingdefinitions_master",
        "_marketingdefinitions_web",
        "_marketing_asset_index_master",
        "_marketing_asset_index_web",
        "_testing_index",
        "_suggested_test_index",
        "_fxm_master_index",
        "_fxm_web_index",
        "_personalization_index"
    ],
    "xdb": [
        "_xdb_internal",
        "_xdb_rebuild_internal"
    ]
}

Or you can check out what is in C:\Start.ps1 for example which is called by the default entrypoint for this image.  
Since we overwrote the entrypoint with our docker-compose run command, this isn't executed!

param(
    [Parameter(Mandatory)]
    [string]$SitecoreSolrConnectionString,

    [Parameter(Mandatory)]
    [string]$SolrSitecoreConfigsetSuffixName,

    [Parameter(Mandatory)]
    [string]$SolrCorePrefix,

    [Parameter(Mandatory)]
    [string]$SolrReplicationFactor,

    [Parameter(Mandatory)]
    [int]$SolrNumberOfShards,
    
    [Parameter(Mandatory)]
    [int]$SolrMaxShardsPerNodes,

    [string]$SolrXdbSchemaFile,
    
    [string]$SolrCollectionsToDeploy
)

function GetCoreNames {
    param (
        [ValidateSet("sitecore", "xdb")]
        [string]$CoreType,
        
        [string]$SolrCollectionsToDeploy
    )

    $resultCoreNames = @()
    $SolrCollectionsToDeploy.Split(',') | ForEach-Object {
        $solrCollectionToDeploy = $_
        Get-ChildItem C:\data -Filter "cores*$solrCollectionToDeploy.json" | ForEach-Object {
            $coreNames = (Get-Content $_.FullName | Out-String | ConvertFrom-Json).$CoreType
            if ($coreNames) {
                $resultCoreNames += $coreNames
            }
        }
    }

    return $resultCoreNames
}

. .\Get-SolrCredential.ps1

$solrContext = .\Parse-ConnectionString.ps1 -SitecoreSolrConnectionString $SitecoreSolrConnectionString

$SolrEndpoint = $solrContext.SolrEndpoint
$env:SOLR_USERNAME = $solrContext.SolrUsername
$env:SOLR_PASSWORD = $solrContext.SolrPassword

$solrSitecoreCoreNames = GetCoreNames -CoreType "sitecore" -SolrCollectionsToDeploy $SolrCollectionsToDeploy
$solrXdbCoreNames = GetCoreNames -CoreType "xdb" -SolrCollectionsToDeploy $SolrCollectionsToDeploy

$solrCollections = (Invoke-RestMethod -Uri "$SolrEndpoint/admin/collections?action=LIST&omitHeader=true" -Method Get -Credential (Get-SolrCredential)).collections
foreach ($solrCoreName in ($solrSitecoreCoreNames + $solrXdbCoreNames)) {
    if ($solrCollections -contains ('{0}{1}' -f $SolrCorePrefix, $solrCoreName)) {
        Write-Information -MessageData "Sitecore collections are already exist. Use collection name prefix different from '$SolrCorePrefix'." -InformationAction:Continue
        return
    }    
}

$solrConfigDir = "C:\temp\sitecore_content_config"
$solrBaseConfigDir = "C:\temp\default"
.\Download-SolrConfig.ps1 -SolrEndpoint $SolrEndpoint -OutPath $solrBaseConfigDir
.\Patch-SolrConfig.ps1 -SolrConfigPath $solrBaseConfigDir -XsltPath "C:\data\xslt" -OutputPath $solrConfigDir

$collectionAliases = $null
if(Test-Path -Path "C:\data\aliases.json") {
    $collectionAliases = ((Get-Content C:\data\aliases.json | Out-String | ConvertFrom-Json).aliases)
}

$solrBaseConfigsetName = $null
foreach ($solrCoreName in $solrSitecoreCoreNames) {
    $solrConfigsetName = ('{0}{1}{2}' -f $SolrCorePrefix, $solrCoreName, $SolrSitecoreConfigsetSuffixName)

    if ([string]::IsNullOrEmpty($solrBaseConfigsetName)) {
        .\New-SolrConfig.ps1 -SolrEndpoint $SolrEndpoint -SolrConfigName $solrConfigsetName -SolrConfigDir $solrConfigDir
        $solrBaseConfigsetName = $solrConfigsetName
    }
    else {
        .\Copy-SolrConfig.ps1 -SolrEndpoint $SolrEndpoint -SolrConfigName $solrConfigsetName -SolrBaseConfigName $solrBaseConfigsetName
    }

    .\New-SolrCore.ps1 -SolrCoreNames $solrCoreName -SolrEndpoint $SolrEndpoint -SolrCorePrefix $SolrCorePrefix -SolrConfigsetName $solrConfigsetName -SolrReplicationFactor $SolrReplicationFactor -SolrNumberOfShards $SolrNumberOfShards -SolrMaxShardNumberPerNode $SolrMaxShardsPerNodes
}

if($solrXdbCoreNames) 
{
    foreach ($solrCoreName in $solrXdbCoreNames) {
        $SolrXdbConfigsetName = ('{0}{1}{2}' -f $SolrCorePrefix, $solrCoreName, $SolrSitecoreConfigsetSuffixName)
        $SolrCollectionName = ('{0}{1}' -f $SolrCorePrefix, $solrCoreName)

        if($solrconfigs -notcontains $SolrXdbConfigsetName)
        {
            .\Copy-SolrConfig.ps1 -SolrEndpoint $SolrEndpoint -SolrConfigName $SolrXdbConfigsetName
        }
        .\New-SolrCore.ps1 -SolrCoreNames $solrCoreName -SolrEndpoint $SolrEndpoint -SolrCorePrefix $SolrCorePrefix -SolrConfigsetName $SolrXdbConfigsetName -SolrReplicationFactor $SolrReplicationFactor -SolrNumberOfShards $SolrNumberOfShards -SolrMaxShardNumberPerNode $SolrMaxShardsPerNodes -SolrCollectionAliases $collectionAliases
        
        .\Update-Schema.ps1 -SolrCollectionName $SolrCollectionName -SolrEndpoint $SolrEndpoint -SchemaPath $SolrXdbSchemaFile
    }
}

Bonus - docker image inspect

To find out if a docker image has an entrypoint defined, you can inspect it:  
docker image inspect scr.sitecore.com/sxp/sitecore-xp0-solr-init:10.1-ltsc2019

This will show all information available on the image. And for example also show the Entrypoint: