Tutorial: Automate container image builds when a base image is updated in an Azure container registry

ACR Tasks supports automated build execution when a container's base image is updated, such as when you patch the OS or application framework in one of your base images. In this tutorial, you learn how to create a task in ACR Tasks that triggers a build in the cloud when a container's base image has been pushed to your registry.

In this tutorial, the last in the series:

  • Build the base image
  • Create an application image build task
  • Update the base image to trigger an application image task
  • Display the triggered task
  • Verify updated application image

Use Azure Cloud Shell

Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. You can use either Bash or PowerShell with Cloud Shell to work with Azure services. You can use the Cloud Shell preinstalled commands to run the code in this article without having to install anything on your local environment.

To start Azure Cloud Shell:

Option Example/Link
Select Try It in the upper-right corner of a code block. Selecting Try It doesn't automatically copy the code to Cloud Shell. Example of Try It for Azure Cloud Shell
Go to https://shell.azure.com, or select the Launch Cloud Shell button to open Cloud Shell in your browser. Launch Cloud Shell in a new window
Select the Cloud Shell button on the top-right menu bar in the Azure portal. Cloud Shell button in the Azure portal

To run the code in this article in Azure Cloud Shell:

  1. Start Cloud Shell.

  2. Select the Copy button on a code block to copy the code.

  3. Paste the code into the Cloud Shell session by selecting Ctrl+Shift+V on Windows and Linux or by selecting Cmd+Shift+V on macOS.

  4. Select Enter to run the code.

If you'd like to use the Azure CLI locally, you must have the Azure CLI version 2.0.46 or later installed. Run az --version to find the version. If you need to install or upgrade the CLI, see Install Azure CLI.

Prerequisites

Complete the previous tutorials

This tutorial assumes you've already completed the steps in the first two tutorials in the series, in which you:

  • Create Azure container registry
  • Fork sample repository
  • Clone sample repository
  • Create GitHub personal access token

If you haven't already done so, complete the first two tutorials before proceeding:

Build container images in the cloud with Azure Container Registry Tasks

Automate container image builds with Azure Container Registry Tasks

Configure the environment

Populate these shell environment variables with values appropriate for your environment. This step isn't strictly required, but makes executing the multiline Azure CLI commands in this tutorial a bit easier. If you don't populate these environment variables, you must manually replace each value wherever they appear in the example commands.

ACR_NAME=<registry-name>        # The name of your Azure container registry
GIT_USER=<github-username>      # Your GitHub user account name
GIT_PAT=<personal-access-token> # The PAT you generated in the second tutorial

Base images

Dockerfiles defining most container images specify a parent image from which it is based, often referred to as its base image. Base images typically contain the operating system, for example Alpine Linux or Windows Nano Server, on which the rest of the container's layers are applied. They might also include application frameworks such as Node.js or .NET Core.

Base image updates

A base image is often updated by the image maintainer to include new features or improvements to the OS or framework in the image. Security patches are another common cause for a base image update.

When a base image is updated, you're presented with the need to rebuild any container images in your registry based on it to include the new features and fixes. ACR Tasks includes the ability to automatically build images for you when a container's base image is updated.

Tasks triggered by a base image update

  • For image builds from a Dockerfile, an ACR task detects dependencies on base images in the following locations:

    • The same Azure container registry where the task runs
    • Another Azure container registry in the same region
    • A public repo in Docker Hub
    • A public repo in Microsoft Container Registry

    If the base image specified in the FROM statement resides in one of these locations, the ACR task adds a hook to ensure the image is rebuilt any time its base is updated.

  • Currently, an ACR tasks only tracks base image updates for application (runtime) images. It doesn't track base image updates for intermediate (buildtime) images used in multi-stage Dockerfiles.

  • When you create an ACR task with the az acr task create command, by default the task is enabled for trigger by a base image update. That is, the base-image-trigger-enabled property is set to True. If you want to disable this behavior in a task, update the property to False. For example, run the following az acr task update command:

    az acr task update --myregistry --name mytask --base-image-trigger-enabled False
    
  • To enable an ACR task to determine and track a container image's dependencies -- which include its base image -- you must first trigger the task at least once. For example, trigger the task manually using the az acr task run command.

  • To trigger a task on base image update, the base image must have a stable tag, such as node:9-alpine. This tagging is typical for a base image that is updated with OS and framework patches to a latest stable release. If the base image is updated with a new version tag, it does not trigger a task. For more information about image tagging, see the best practices guidance.

Base image update scenario

This tutorial walks you through a base image update scenario. The code sample includes two Dockerfiles: an application image, and an image it specifies as its base. In the following sections, you create an ACR task that automatically triggers a build of the application image when a new version of the base image is pushed to the same container registry.

Dockerfile-app: A small Node.js web application that renders a static web page displaying the Node.js version on which it's based. The version string is simulated: it displays the contents of an environment variable, NODE_VERSION, that's defined in the base image.

Dockerfile-base: The image that Dockerfile-app specifies as its base. It is itself based on a Node image, and includes the NODE_VERSION environment variable.

In the following sections, you create a task, update the NODE_VERSION value in the base image Dockerfile, then use ACR Tasks to build the base image. When the ACR task pushes the new base image to your registry, it automatically triggers a build of the application image. Optionally, you run the application container image locally to see the different version strings in the built images.

In this tutorial, your ACR task builds and pushes an application container image specified in a Dockerfile. ACR Tasks can also run multi-step tasks, using a YAML file to define steps to build, push, and optionally test multiple containers.

Build the base image

Start by building the base image with an ACR Tasks quick task. As discussed in the first tutorial in the series, this process not only builds the image, but pushes it to your container registry if the build is successful.

az acr build --registry $ACR_NAME --image baseimages/node:9-alpine --file Dockerfile-base .

Create a task

Next, create a task with az acr task create:

az acr task create \
    --registry $ACR_NAME \
    --name taskhelloworld \
    --image helloworld:{{.Run.ID}} \
    --arg REGISTRY_NAME=$ACR_NAME.azurecr.io \
    --context https://github.com/$GIT_USER/acr-build-helloworld-node.git \
    --file Dockerfile-app \
    --git-access-token $GIT_PAT

Important

If you previously created tasks during the preview with the az acr build-task command, those tasks need to be re-created using the az acr task command.

This task is similar to the quick task created in the previous tutorial. It instructs ACR Tasks to trigger an image build when commits are pushed to the repository specified by --context. While the Dockerfile used to build the image in the previous tutorial specifies a public base image (FROM node:9-alpine), the Dockerfile in this task, Dockerfile-app, specifies a base image in the same registry:

FROM ${REGISTRY_NAME}/baseimages/node:9-alpine

This configuration makes it easy to simulate a framework patch in the base image later in this tutorial.

Build the application container

Use az acr task run to manually trigger the task and build the application image. This step ensures that the task tracks the application image's dependency on the base image.

az acr task run --registry $ACR_NAME --name taskhelloworld

Once the task has completed, take note of the Run ID (for example, "da6") if you wish to complete the following optional step.

Optional: Run application container locally

If you're working locally (not in the Cloud Shell), and you have Docker installed, run the container to see the application rendered in a web browser before you rebuild its base image. If you're using the Cloud Shell, skip this section (Cloud Shell does not support az acr login or docker run).

First, authenticate to your container registry with az acr login:

az acr login --name $ACR_NAME

Now, run the container locally with docker run. Replace <run-id> with the Run ID found in the output from the previous step (for example, "da6"). This example names the container myapp and includes the --rm parameter to remove the container when you stop it.

docker run -d -p 8080:80 --name myapp --rm $ACR_NAME.azurecr.io/helloworld:<run-id>

Navigate to http://localhost:8080 in your browser, and you should see the Node.js version number rendered in the web page, similar to the following. In a later step, you bump the version by adding an "a" to the version string.

Screenshot of sample application rendered in browser

To stop and remove the container, run the following command:

docker stop myapp

List the builds

Next, list the task runs that ACR Tasks has completed for your registry using the az acr task list-runs command:

az acr task list-runs --registry $ACR_NAME --output table

If you completed the previous tutorial (and didn't delete the registry), you should see output similar to the following. Take note of the number of task runs, and the latest RUN ID, so you can compare the output after you update the base image in the next section.

$ az acr task list-runs --registry $ACR_NAME --output table

RUN ID    TASK            PLATFORM    STATUS     TRIGGER     STARTED               DURATION
--------  --------------  ----------  ---------  ----------  --------------------  ----------
da6       taskhelloworld  Linux       Succeeded  Manual      2018-09-17T23:07:22Z  00:00:38
da5                       Linux       Succeeded  Manual      2018-09-17T23:06:33Z  00:00:31
da4       taskhelloworld  Linux       Succeeded  Git Commit  2018-09-17T23:03:45Z  00:00:44
da3       taskhelloworld  Linux       Succeeded  Manual      2018-09-17T22:55:35Z  00:00:35
da2       taskhelloworld  Linux       Succeeded  Manual      2018-09-17T22:50:59Z  00:00:32
da1                       Linux       Succeeded  Manual      2018-09-17T22:29:59Z  00:00:57

Update the base image

Here you simulate a framework patch in the base image. Edit Dockerfile-base, and add an "a" after the version number defined in NODE_VERSION:

ENV NODE_VERSION 9.11.2a

Run a quick task to build the modified base image. Take note of the Run ID in the output.

az acr build --registry $ACR_NAME --image baseimages/node:9-alpine --file Dockerfile-base .

Once the build is complete and the ACR task has pushed the new base image to your registry, it triggers a build of the application image. It may take few moments for the task you created earlier to trigger the application image build, as it must detect the newly built and pushed base image.

List updated build

Now that you've updated the base image, list your task runs again to compare to the earlier list. If at first the output doesn't differ, periodically run the command to see the new task run appear in the list.

az acr task list-runs --registry $ACR_NAME --output table

Output is similar to the following. The TRIGGER for the last-executed build should be "Image Update", indicating that the task was kicked off by your quick task of the base image.

$ az acr task list-runs --registry $ACR_NAME --output table

Run ID    TASK            PLATFORM    STATUS     TRIGGER       STARTED               DURATION
--------  --------------  ----------  ---------  ------------  --------------------  ----------
da8       taskhelloworld  Linux       Succeeded  Image Update  2018-09-17T23:11:50Z  00:00:33
da7                       Linux       Succeeded  Manual        2018-09-17T23:11:27Z  00:00:35
da6       taskhelloworld  Linux       Succeeded  Manual        2018-09-17T23:07:22Z  00:00:38
da5                       Linux       Succeeded  Manual        2018-09-17T23:06:33Z  00:00:31
da4       taskhelloworld  Linux       Succeeded  Git Commit    2018-09-17T23:03:45Z  00:00:44
da3       taskhelloworld  Linux       Succeeded  Manual        2018-09-17T22:55:35Z  00:00:35
da2       taskhelloworld  Linux       Succeeded  Manual        2018-09-17T22:50:59Z  00:00:32
da1                       Linux       Succeeded  Manual        2018-09-17T22:29:59Z  00:00:57

If you'd like to perform the following optional step of running the newly built container to see the updated version number, take note of the RUN ID value for the Image Update-triggered build (in the preceding output, it's "da8").

Optional: Run newly built image

If you're working locally (not in the Cloud Shell), and you have Docker installed, run the new application image once its build has completed. Replace <run-id> with the RUN ID you obtained in the previous step. If you're using the Cloud Shell, skip this section (Cloud Shell does not support docker run).

docker run -d -p 8081:80 --name updatedapp --rm $ACR_NAME.azurecr.io/helloworld:<run-id>

Navigate to http://localhost:8081 in your browser, and you should see the updated Node.js version number (with the "a") in the web page:

Screenshot of sample application rendered in browser

What's important to note is that you updated your base image with a new version number, but the last-built application image displays the new version. ACR Tasks picked up your change to the base image, and rebuilt your application image automatically.

To stop and remove the container, run the following command:

docker stop updatedapp

Clean up resources

To remove all resources you've created in this tutorial series, including the container registry, container instance, key vault, and service principal, issue the following commands:

az group delete --resource-group $RES_GROUP
az ad sp delete --id http://$ACR_NAME-pull

Next steps

In this tutorial, you learned how to use a task to automatically trigger container image builds when the image's base image has been updated. Now, move on to the next tutorial to learn how to trigger tasks on a defined schedule.