Search code examples
dockerimagebuildazure-pipelinesazure-container-registry

Seperate build and push without defining containerRegistry in Devops docker build task


I am having trouble with a docker deployment to Azure Container Registry (ACR). My goal is to build my docker image once, and deploy it over 4 environments (dev, test, acc, prd). The reason I want to only build the docker image once is that the docker build process takes about 1 hour, which I would rather not repeat 4 times

My question is: How can I do this within azure devops without having to define the "containerRegistry" in the build task?

The problem I run into is that I have to specify a containerRegistry variable when building using the Docker@2 build task. Else the Docker@2 push step will fail with an error like:

An image does not exist locally with the tag: ***.azurecr.io/repository_name

I do not want to specify this value in the build process, because each environment has its own container registry. So specifying this would still require me to create a build for each individual environment.

Build step

- task:                                       Docker@2
  inputs:
    repository:                              "${{ parameters.dockerImgName }}"
    command:                                  build
    Dockerfile:                              "${{ parameters.dockerBuildDockerfile }}"
    tags: |                                  
                                              ${{ parameters.dockerImageTag }}
                                              latest
    buildContext:                            "${{ parameters.dockerBuildContext }}"
    containerRegistry:                        ${{ parameters.containerRegistry}}**
  displayName:                               "Build the image"

Push step (in another stage)

- task:                                       Docker@2
  inputs:
    repository:                              "${{ parameters.dockerImgName }}"
    command:                                  push
    Dockerfile:                              "${{ parameters.dockerBuildDockerfile }}"
    tags:                                    "latest"
    buildContext:                            "${{ parameters.dockerBuildContext }}" 
    containerRegistry:                        ${{ parameters.acrConnection }}
  displayName:                               "Push the image with latest tag"

Solution

  • So as it turns out this is possible by manually retagging the docker build before pushing it, as described in the link below:

    https://dev.to/n3wt0n/container-image-promotion-across-environments-build-artifact-5887

    Summary: After building the image I had to save the image as a .TAR file, after which I could publish it as an Artifact:

    - task:                                       Docker@2
      inputs:
        command:                                 "save"
        arguments:                               "--output $(Build.ArtifactStagingDirectory)/${{ parameters.dockerImgName }}.image.tar ${{ parameters.dockerImgName }}:${{ parameters.dockerImageTag }}"
        displayName:                             "Save the image as a .tar"
    
    - task:                                       PublishBuildArtifacts@1
      inputs:
        PathtoPublish:                           "$(Build.ArtifactStagingDirectory)"
        ArtifactName:                            "drop"
        publishLocation:                         "Container"
        displayName:                             "Publish image as an artifact"                      "Save the image as a .tar"
    

    Then in a different stage I could collect the stored artifact, load the .tar file and by manually retagging it through the docker@2 step I was able to make the build accessible for pushing it.

        # collect the docker artifact from azure devops
    - task:                                       DownloadBuildArtifacts@1
      displayName:                               "Collect docker artifact"
      inputs:
        buildType:                               "current"
        downloadType:                            "single"
        artifactName:                            "drop"
        downloadPath:                            "$(System.ArtifactsDirectory)"
    
    - task:                                       Docker@2
      inputs:
        command:                                 "load"
        arguments:                               "--input $(System.ArtifactsDirectory)/drop/${{ parameters.dockerImgName }}.image.tar"
    
    - task:                                       Docker@2
      inputs:
        command:                                 "tag"
        containerRegistry:                        ${{ parameters.acrConnection }}
        arguments:                               "${{ parameters.dockerImgName }}:${{ parameters.dockerImageTag }} ${{parameters.acrName}}.azurecr.io/${{ parameters.dockerImgName }}:${{ parameters.dockerImageTag }}"
    
    - task:                                       Docker@2
      inputs:
        repository:                              "${{ parameters.dockerImgName }}"
        command:                                  push
        Dockerfile:                              "${{ parameters.dockerBuildDockerfile }}"
        tags:                                    "${{ parameters.dockerImageTag }}"
        buildContext:                            "${{ parameters.dockerBuildContext }}" 
        containerRegistry:                        ${{ parameters.acrConnection }}
      displayName:                               "Push the image with id tag"