Configure continuous deployment for a Python web app in Azure Container Apps

This article is part of a tutorial about how to containerize and deploy a Python web app to Azure Container Apps. Container Apps enables you to deploy containerized apps without managing complex infrastructure.

In this part of the tutorial, you learn how to configure continuous deployment or delivery (CD) for the container app. CD is part of the DevOps practice of continuous integration / continuous delivery (CI/CD), which is automation of your app development workflow. Specifically, you use GitHub Actions for continuous deployment.

This service diagram highlights the components covered in this article: configuration of CI/CD.

A screenshot of the services in the Tutorial - Deploy a Python App on Azure Container Apps. Sections highlighted are parts related to continuous integration - continuous delivery (CI/CD).

Prerequisites

To set up continuous deployment, you need:

  • The resources and their configuration created in the previous article of this tutorial series, which includes an Azure Container Registry and a container app in Azure Container Apps.

  • A GitHub account where you forked the sample code (Django or Flask) and you can connect to from Azure Container Apps. (If you downloaded the sample code instead of forking, make sure you push your local repo to your GitHub account.)

  • Optionally, Git installed in your development environment to make code changes and push to your repo in GitHub. Alternatively, you can make the changes directly in GitHub.

Configure CD for a container

In a previous article of this tutorial, you created and configured a container app in Azure Container Apps. Part of the configuration was pulling a Docker image from an Azure Container Registry. The container image is pulled from the registry when creating a container revision, such as when you first set up the container app.

In this section, you set up continuous deployment using a GitHub Actions workflow. With continuous deployment, a new Docker image and container revision are created based on a trigger. The trigger in this tutorial is any change to the main branch of your repository, such as with a pull request (PR). When triggered, the workflow creates a new Docker image, pushes it to the Azure Container Registry, and updates the container app to a new revision using the new image.

Azure CLI commands can be run in the Azure Cloud Shell or on a workstation with the Azure CLI installed.

If you're running commands in a Git Bash shell on a Windows computer, enter the following command before proceeding:

export MSYS_NO_PATHCONV=1

Step 1. Create a service principal with the az ad sp create-for-rbac command.

az ad sp create-for-rbac \
--name <app-name> \
--role Contributor \
--scopes "/subscriptions/<subscription-ID>/resourceGroups/<resource-group-name>"

Where:

  • <app-name> is an optional display name for the service principal. If you leave off the --name option, a GUID is generated as the display name.
  • <subscription-ID> is the GUID that uniquely identifies your subscription in Azure. If you don't know your subscription ID, you can run the az account show command and copy it from the id property in the output.
  • <resource-group-name> is the name of a resource group that contains the Azure Container Apps container. Role-based access control (RBAC) is on the resource group level. If you followed the steps in the previous article in this tutorial, the resource group name is pythoncontainer-rg.

Save the output of this command for the next step, in particular, the client ID (appId property), client secret (password property), and tenant ID (tenant property).

Step 2. Configure GitHub Actions with az containerapp github-action add command.

az containerapp github-action add \
--resource-group <resource-group-name> \
--name python-container-app \
--repo-url <https://github.com/userid/repo> \
--branch main \
--registry-url <registry-name>.azurecr.io \
--service-principal-client-id <client-id> \
--service-principal-tenant-id <tenant-id> \
--service-principal-client-secret <client-secret> \
--login-with-github

Where:

  • <resource-group-name> is the name of the resource group. If you are following this tutorial, it is "pythoncontainer-rg".
  • <https://github.com/userid/repo> is the URL of your GitHub repository. If you're following the steps in this tutorial, it will be either https://github.com/userid/msdocs-python-django-azure-container-apps or https://github.com/userid/msdocs-python-flask-azure-container-apps; where userid is your GitHub user ID.
  • <registry-name> is the existing Container Registry you created for this tutorial, or one that you can use.
  • <client-id> is the value of the appId property from the previous az ad sp create-for-rbac command. The ID is a GUID of the form 00000000-0000-0000-0000-00000000.
  • <tenant-id> is the value of the tenant property from the previous az ad sp create-for-rbac command. The ID is also a GUID similar to the client ID.
  • <client-secret> is the value of the password property from the previous az ad sp create-for-rbac command.

In the configuration of continuous deployment, a service principal is used to enable GitHub Actions to access and modify Azure resources. Access to resources is restricted by the roles assigned to the service principal. The service principal was assigned the built-in Contributor role on the resource group containing the container app.

If you followed the steps for the portal, the service principal was automatically created for you. If you followed the steps for the Azure CLI, you explicitly created the service principal first before configuring continuous deployment.

Redeploy web app with GitHub Actions

In this section, you make a change to your forked copy of the sample repository and confirm that the change is automatically deployed to the web site.

If you haven't already, make a fork of the sample repository (Django or Flask). You can make your code change directly in GitHub or in your development environment from a command line with Git.

Step 1. Go to your fork of the sample repository and start in the main branch.

Step 2. Make a change.

  • Go to the /templates/base.html file. (For Django, the path is: restaurant_review/templates/restaurant_review/base.html.)
  • Select Edit and change the phrase "Azure Restaurant Review" to "Azure Restaurant Review - Redeployed".

Step 3. Commit the change directly to the main branch.

  • On the bottom of the page you editing, select the Commit button.
  • The commit kicks off the GitHub Actions workflow.

Note

We showed making a change directly in the main branch. In typical software workflows, you'll make a change in a branch other than main and then create a pull request (PR) to merge those change into main. PRs also kick off the workflow.

About GitHub Actions

Viewing workflow history

Step 1. On GitHub, go to your fork of the sample repository and open the Actions tab.

Workflow secrets

In the .github/workflows/<workflow-name>.yml workflow file that was added to the repo, you'll see placeholders for credentials that are needed for the build and container app update jobs of the workflow. The credential information is stored encrypted in the repository Settings under Security/Secrets and variables/Actions.

Screenshot showing how to see where GitHub Actions secrets are stored in GitHub.

If credential information changes, you can update it here. For example, if the Azure Container Registry passwords are regenerated, you'll need to update the REGISTRY_PASSWORD value. For more information, see Encrypted secrets in the GitHub documentation.

OAuth authorized apps

When you set up continuous deployment, you authorize Azure Container Apps as an authorized OAuth App for your GitHub account. Container Apps uses the authorized access to create a GitHub Actions YML file in .github/workflows/<workflow-name>.yml. You can see your authorized apps and revoke permissions under Integrations/Applications of your account.

Screenshot showing how to see the authorized apps for a user in GitHub.

Troubleshooting tips

Errors setting up a service principal with the Azure CLI az ad sp create-for-rba command.

  • You receive an error containing "InvalidSchema: No connection adapters were found".

  • You receive an error containing "More than one application have the same display name".

    • This error indicates the name is already taken for the service principal. Choose another name or leave off the --name argument and a GUID will be automatically generated as a display name.

GitHub Actions workflow failed.

  • To check a workflow's status, go to the Actions tab of the repo.
  • If there's a failed workflow, drill into its workflow file. There should be two jobs "build" and "deploy". For a failed job, look at the output of the job's tasks to look for problems.
  • If you see an error message with "TLS handshake timeout", run the workflow manually by selecting Trigger auto deployment under the Actions tab of the repo to see if the timeout is a temporary issue.
  • If you set up continuous deployment for the container app as shown in this tutorial, the workflow file (.github/workflows/<workflow-name>.yml) is created automatically for you. You shouldn't need to modify this file for this tutorial. If you did, revert your changes and try the workflow.

Website doesn't show changes you merged in the main branch.

  • In GitHub: check that the GitHub Actions workflow ran and that you checked the change into the branch that triggers the workflow.
  • In Azure portal: check the Azure Container Registry to see if a new Docker image was created with a timestamp after your change to the branch.
  • In Azure portal: check the logs of the container app. If there's a programming error, you'll see it here.
    • Go to the Container App | Revision Management | <active container> | Revision details | Console logs
    • Choose the order of the columns to show "Time Generated", "Stream_s", and "Log_s". Sort the logs by most-recent first and look for Python stderr and stdout messages in the "Stream_s" column. Python 'print' output will be stdout messages.
  • With the Azure CLI, use the az containerapp logs show command.

What happens when I disconnect continuous deployment?

  • Stopping continuous deployment means disconnecting your container app from your repo. To disconnect:

  • After disconnecting, in your GitHub repo:

    • The .github/workflows/<workflow-name>.yml file is removed from your repo.
    • Secret keys aren't removed.
    • Azure Container Apps remains as an authorized OAuth App for your GitHub account.
  • After disconnecting, in Azure:

    • The container is left with last deployed container. You can reconnect the container app with the Azure Container Registry, so that new container revisions pick up the latest image.
    • Service principals created and used for continuous deployment aren't deleted.

Next steps

If you're done with the tutorial and don't want to incur extra costs, remove the resources used. Removing a resource group removes all resources in the group and is the fastest way to remove resources. For an example of how to remove resource groups, see Containerize tutorial cleanup.

If you plan on building on this tutorial, here are some next steps you can take.