Nordlicht Reisen GmbH, a fictional travel agency, ships its booking portal in three forms: the interface as a container, a small administration portal as a package for App Service, and an Azure SQL database whose schema changes with every release. So far, someone has kicked off the three parts by hand in the right order. You are to turn that into a deployment that a team can follow: which step for which form, in which order and with which conditions. Those are the skills of this lesson: deploying applications (containers, binaries, scripts) and deployment with database tasks.
Prior knowledge: How a pipeline signs in to Azure without a secret is in K3, lesson 3.7.5; releases, revisions and rolling back in K3, lesson 3.7.6. Why a migration must be compatible with the previous version of the app and how to rebuild a schema in several steps is shown in K3, lesson 3.6.2. Environments, checks and approvals in YAML are covered in lesson 4.7, the strategies (blue-green, canary) in lesson 4.8. This lesson asks only: Which building block brings which form of application to Azure, and where does the database stand in the order?
Not run against Azure or GitHub; from the reference of Microsoft: All commands, YAML files and menu paths of this lesson come from Microsoft's documentation and were not run in this course. Names in angle brackets are placeholders that you replace.
Deploying containers
A container is deployed in two separate steps, and the separation is the core of the design: build and store (an image is created and sits in a registry) and roll out (a service pulls exactly this image). What stands between the two is the name of the image, and it must be unique.
Build and store
In Azure Pipelines, the task Docker@2 builds and pushes an image to a registry. This is how Microsoft's template for Azure Container Registry shows it:
- task: Docker@2
displayName: Build and push an image to container registry
inputs:
command: buildAndPush
repository: $(imageRepository)
dockerfile: $(dockerfilePath)
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
containerRegistry is the name of a Docker registry service connection; it holds the sign-in to the registry. Microsoft's template creates a connection based on a service principal. Service connections in detail are covered in lesson 5.1.
In GitHub Actions, docker/build-push-action builds the image. Microsoft's example for Container Apps uses the commit ID as the tag:
- name: Build and push Docker image to Azure Container Registry
uses: docker/build-push-action@v4
with:
push: true
tags: ${{ env.CONTAINER_REGISTRY_LOGIN_SERVER }}/webfrontend:${{ github.sha }}
file: ${{ env.DOCKER_FILEPATH_WEBFRONTEND }}
Both sources from Microsoft warn in the same way: If you build the image in a step of its own, take a unique tag such as the build ID ($(Build.BuildId)) or the commit ID, not a stable one like latest. Microsoft gives the reason for Container Apps: A unique tag makes sure that new revisions are reliably created. A tag that always has the same name also obscures which state is actually running.
Rolling out to Azure Container Apps
Azure Container Apps (the managed container hosting from K3, module 3.7) has a building block of its own for both worlds. It can do three things: build from a Dockerfile and roll out, build from source code without a Dockerfile (supported: .NET, Java, Node.js, PHP, Python) or roll out an existing image.
Azure Pipelines (task AzureContainerApps@1, rolling out an existing image):
steps:
- task: AzureContainerApps@1
inputs:
azureSubscription: 'my-subscription-service-connection'
containerAppName: 'my-container-app'
resourceGroup: 'my-container-app-rg'
imageToDeploy: 'myregistry.azurecr.io/my-container-app:$(Build.BuildId)'
GitHub Actions (action azure/container-apps-deploy-action) has the same inputs: containerAppName, resourceGroup, imageToDeploy, plus appSourcePath and acrName for building. The sign-in comes from azure/login beforehand.
What matters is the question who signs in to the registry. Microsoft separates two directions:
- Uploading (push): Task or action sign in with the identity that you specified for Azure (service connection,
azure/login). If this identity lacks the roleAcrPush, you specify administrator credentials of the registry withacrUsernameandacrPassword. - Downloading (pull): The container app itself fetches the image. Microsoft recommends a managed identity of the app with the role
AcrPullfor the registry; the alternative is administrator credentials.
Microsoft's setup for this consists of three commands (Azure CLI):
az containerapp identity assign --name my-container-app --resource-group my-container-app-rg --system-assigned
az role assignment create --assignee <principal-id> --role AcrPull --scope <acr-resource-id>
az containerapp registry set --name my-container-app --resource-group my-container-app-rg --server <acr-name>.azurecr.io --identity system
Rolling out to App Service (custom container)
For a custom container in Azure App Service, Microsoft uses azure/webapps-deploy in GitHub Actions. Two inputs are required for containers: app-name (the name of the app) and images (the full name of the image, such as myregistry.azurecr.io/nginx:latest). Optional are slot-name (an existing slot instead of production, lesson 4.9) and startup-command. For signing in, Microsoft recommends OpenID Connect. A web app for containers, unlike a normal web app, has no home page as long as no container is published.