How To Deploy Azure Container Apps With GitLab CI/CD
Do you want to learn how to set up a pipeline to deploy an azure container app with GitLab CI/CD? In this post, I will show you how to use the GitLab CI to build an image, store it in the GitLab Registry and then deploy it to Azure Container Apps.
For that, we will first look at some requirements, either what’s needed for the automation to work or things that are required by the different tools we are using. And then, we will build up the CI and the different jobs inside it.
Requirements
Now let’s first look at some general requirements. First of all, we need a repository in GitLab which contains a Dockerfile that builds to an Image. Preferably to some sort of web application so we can quickly check if it is running successfully (for an example and step-by-step instructions, check this post here). In addition to that, you also need an Azure Account.
One requirement created through azure container apps is that you should use versioning of your images. Azure Container Apps will not pull the image every time we update the app, only when it sees that the version number changed! Meaning if we only use latest
the automatic deployment will only work once. Because of that, we will use semantic-release, a tool that automatically generates a semantic version number from your commit messages. You do not have to use semantic release. There is also the possibility to do it manually or in whatever way you want.
In the end, the technologies we will use are the GitLab Registry, semantic-release, and Azure. To use these three technologies, we have to give them the access rights they need. Therefore we need to create:
- GitLab Deploy Token (for the Registry -> read_registry and write_registry scopes)
- GitLab Personal Access Token (for semantic-release -> api and write_repository scopes )
- Azure Service Principal (for access to Azure -> with Contributor role)
For Azure, you could also use your own Account credentials, but I would recommend using a service principal inside of CIs and similar mechanics.
Need help or want to share feedback? Join my discord community!
Before going to the next section, please create the three access methods with the linked tutorials.
GitLab CI to deploy to Azure Container Apps
After discussing the requirements, let’s create the CI and everything needed for it. The basic idea of the pipeline is simple and consists of three steps:
If this guide is helpful to you and you like what I do, please support me with a coffee!
- Get version number
- Build image and push it to the registry
- Deploy image to container apps
For the first step, we need to add a semantic-release configuration file with the name .releaserc
in the root of our project. Its contents should look something like this:
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
["@semantic-release/gitlab", {
"gitlabUrl": "https://gitlab.com",
"repositoryUrl": "<your-repository-url>"
}]
],
"tagFormat": "${version}"
}
To learn more about the different configuration options, check here.
Now that we are able to run semantic-release in our GitLab CI, we will first create the three needed jobs and then add the environment variables:
stages:
- release
- build
- deploy
# automatically retrieve version of the repository and create a new tag with repository settings this should occur on a merge
# semantic-release automates the whole package release workflow including: determining the next version number, generating the release notes, and publishing the package.
# It is required to set the GL_TOKEN environment variable
release:
image: node:16-buster-slim
stage: release
before_script:
- apt-get update && apt-get install -y --no-install-recommends git-core ca-certificates
- npm install -g semantic-release @semantic-release/gitlab
script:
- semantic-release
only:
- main
# automatically build the docker image after the new tag is created
build:
image: docker:latest
stage: build
only:
- tags
services:
- docker:dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
# get the version tag
- tag=$CI_COMMIT_REF_NAME
- docker build --pull -t "$CI_REGISTRY_IMAGE:${tag}" .
- docker push "$CI_REGISTRY_IMAGE:${tag}"
# after the image build is finished use it for the containerapps and automatically deploy it
# container apps quickstart: https://docs.microsoft.com/de-de/azure/container-apps/get-started?tabs=bash
deploy:
image: mcr.microsoft.com/azure-cli
stage: deploy
before_script:
- az login --service-principal -u $AZURE_APP_ID -p $AZURE_PASSWORD --tenant $AZURE_TENANT
- az extension add --name containerapp --upgrade
script:
- tag=$CI_COMMIT_REF_NAME
# az containerapp up documentation: https://docs.microsoft.com/en-us/cli/azure/containerapp?view=azure-cli-latest#az-containerapp-up
- az containerapp up --name $CONTAINER_APP_NAME --resource-group $RESOURCE_GROUP --location $LOCATION --environment $APP_ENV
--registry-password $CI_REGISTRY_PASSWORD --registry-server $CI_REGISTRY --registry-username $CI_REGISTRY_USER
--target-port $TARGET_PORT --ingress external
--image "$CI_REGISTRY_IMAGE:${tag}"
- az containerapp secret set --name $CONTAINER_APP_NAME --resource-group $RESOURCE_GROUP
--secrets
secret-key=$SECRET_KEY
- az containerapp update --name $CONTAINER_APP_NAME --resource-group $RESOURCE_GROUP
# --command <your-start-command>
--cpu 1 --memory 2Gi
--min-replicas 1 --max-replicas 10
# --set-env-vars
# SECRET_KEY=secretref:secret-key
# ENV_VAR=$ENV_VAR
only:
- tags
As an explanation for the different jobs:
- release: install needed packages and the run semantic-release -> semantic-release creates a git tag with the version number -> other jobs run based on this tag
- build: log into the GitLab Registry with the deploy token -> get the tag, build the image and push it to the registry
- deploy: log into azure with the service principal and install needed packages -> create a plain container app with up (creates or updates the specified values) -> set the secrets -> update the container app with env variables and other settings
In the deployment, we have a three-step process because we first need to ensure a running app (up
). Then we can add the secrets to the container app, and then we can update the other configurations. Especially the environment variables need the secrets to be set first. Otherwise, they can not be used.
To learn about all the configurations you can set with the last command, check here.
Now before we can run the pipeline, we need to set the following GitLab Variables:
- GL_TOKEN: Your GitLab PAT, needed for semantic-release
- CI_REGISTRY_USER: Deploy Token User
- CI_REGISTRY_PASSWORD: Deploy Token Password
- CI_REGISTRY: GitLab Registry URL (eg: registry.gitlab.com)
- CI_REGISTRY_IMAGE: Image name you want to use
- AZURE_APP_ID: Azure app id (service principal)
- AZURE_PASSWORD: Azure password (service principal)
- AZURE_TENANT: Azure tenant (service principal)
- CONTAINER_APP_NAME: Container app name you want to use
- RESOURCE_GROUP: Resource Group you want to use or create
- LOCATION: Location you want to create the resources in
- APP_ENV: Container app environment you want to use or create
- TARGET_PORT: Port of your app you want to reach with ingress (if your container has no outbound port, remove the ingress and target-port part from the
az containerapp up
command) - All secrets and environment variables you need for your container app
With that, the CI/CD Variables should be specified, and you should be able to deploy your GitLab repository to azure container apps on a commit to the main branch.
Conclusion
In this post, you learned how to create a GitLab pipeline to automatically deploy an application to Azure Containerapps! I hope that it helped you, and in case there are any questions, feel free to leave a comment or send me an email at mail@programonaut.com.
If you liked this post, consider subscribing to my newsletter to get monthly updates for all my posts!
[convertkit form=2303042]