Share:

Kishan Gohil and Evdokia Pilavaki demonstrate how to deploy a containerised application using Azure Kubernetes Service.

Containerized cloud applications have become a recent trend and interesting topic within the cloud development community over the last few years. Containers have some considerable advantages compared to some traditional application development practices, such as being lightweight and portable. Within the context of Microsoft’s Azure Cloud – using the Azure Kubernetes Service (AKS) we can manage, orchestrate, and scale these applications. AKS allows developers to develop and deploy their applications faster compared to packaging, deploying, and running applications on a physical or virtual machine.

AKS can be leveraged with Azure DevOps to easily deploy the containerized application and supporting infrastructure natively. Azure DevOps has become a widely adopted SaaS tool for source control and CI/CD given it can easily integrate with Azure, AWS, and GCP.

In this article, we will demonstrate technologies including Azure Kubernetes Service, Azure Container Registry, Azure DevOps and Terraform together with a demo application. This demo will show how we can deploy a containerized application using Kubernetes.

Overall Architecture

In this demo our application is a random number game running on Azure, making use of Azure-native resources and services for deploying the application and infrastructure. We will explore these resources and services in detail in this article. The diagram below shows the overall architecture:

Architecture

  • Azure DevOps is a great, easy to use SaaS product – Azure Repos and Azure Pipelines are two features of Azure DevOps that helped us achieve our source control and CI/CD needs. The source code is in a Git-based repository in Azure Repos (our application, infrastructure, and pipeline code), and our CI/CD pipeline is an Azure YAML Pipeline.
  • Azure Container Registry (ACR) is an Azure-native container registry, much like Docker Hub but it’s Azure’s container registry solution, so it integrates with other Azure resources and uses Azure Active Directory for added security. The Azure Pipeline in this demo is building and pushing the Docker image to the ACR (a new version of the image is created on every successful run of the pipeline execution).
  • Azure Kubernetes Service (AKS) is a serverless, managed container orchestration service. AKS runs directly on Azure as a PaaS service and provides us with a Kubernetes environment to deploy and manage our containerized Docker application. This managed Kubernetes environment is what runs our Kubernetes resources in this demo.
  • Azure Active Directory is the built-in Azure identity management solution. In this demo, it is important for us because we need a Service Principal (an identity based on an Azure AD App Registration). This Service Principal is used to create a secure, identity-based authenticated connection (a Service Connection to the Azure Resource Manager) so we can deploy the resources with the correct permissions to the correct Azure Subscription.
  • Azure Key Vault is a PaaS product in Azure that allows us to manage/store keys and secrets. The Service Principal password (the client secret) is stored in the Azure Key Vault for best practice. We use this Service Principal for two specific cases: the Service Connection and as the AKS identity.
  • Terraform is our tool of choice for infrastructure-as-code to create our AKS and ACR resources.
  • Azure Storage Accounts are ideal for remotely and securely storing the Terraform state file. A bonus of using an Azure Storage Account is that it integrates easily with our Azure DevOps pipeline.

What happens in the Azure Pipeline?

Let’s look at the Azure YAML Pipeline we used to deploy our infrastructure and application into Azure.

The fully automated Azure Pipeline makes use of several different deployment tasks. The tasks used by the Azure Pipeline are:

  • Terraform (Terraform Installer & Terraform Task): Installs Terraform and deploys the AKS and ACR based on Terraform modules in our Azure Repo.
  • Docker Compose (Docker Task): Builds and pushes the Docker image for the application.
  • Kubernetes (Kubectl Task): Deploys the latest Docker image into a cluster in the AKS using a Kubernetes manifest file describing the resources (deployments and services).

Terraform

Our Azure Pipeline uses the Terraform Installer task to install v0.12 of Terraform onto the Azure DevOps pipeline agent. We also use the Terraform tasks to run the terraform init, plan and apply. The code snippets from the YAML pipeline are shown below.

Firstly the terraform init task is using the Service Connection (ARM-DevOps-AKS-DemoApp) to authenticate to Azure, and connects to the Terraform state file which we are storing and managing in an Azure Storage Account (based on the Terraform backend configuration). Next, the terraform plan task is comparing the code with the state file to identify any changes that need to be deployed. Finally, in the terraform apply task the changes are deployed – in this case, the AKS and ACR resources are created for the first time.

steps:
- task: TerraformInstaller@0
  displayName: 'Terraform - Install'
  inputs:
    terraformVersion: '0.12.26'

- task: TerraformTaskV1@0
  displayName: 'Terraform - Init'
  inputs:
    provider: 'azurerm'
    command: 'init'
    commandOptions: '-input=false'
    backendServiceArm: 'ARM-DevOps-AKS-DemoApp'
    backendAzureRmResourceGroupName: '$(TF_STATE_SA_RG)'
    backendAzureRmStorageAccountName: '$(TF_STATE_STORAGE_ACCOUNT)'
    backendAzureRmContainerName: '$(TF_STATE_STORAGE_CONTAINER)'
    backendAzureRmKey: '$(TF_STATE_KEY)'

 

- task: TerraformTaskV1@0
  displayName: 'Terraform - Plan'
  inputs:
    provider: 'azurerm'
    command: 'plan'
    commandOptions: '-input=false -var-file="azure-pipeline.tfvars"'
    environmentServiceNameAzureRM: 'ARM-DevOps-AKS-DemoApp'
- task: TerraformTaskV1@0
  displayName: 'Terraform - Apply'
  inputs:
    provider: 'azurerm'
    command: 'apply'
    commandOptions: '-input=false -auto-approve -var-file="azure-pipeline.tfvars"'
    environmentServiceNameAzureRM: 'ARM-DevOps-AKS-DemoApp'

 

Docker

Once Terraform has successfully created the AKS and ACR resources, the Azure Pipeline moves onto the Docker tasks. Using the docker-compose.yaml file, the Docker services will be built and the resulting Docker image will be pushed from the Azure Pipelines agent to the ACR. The latest version tag of the image will also be updated with the newly built image.

- task: DockerCompose@0
  displayName: 'Docker - Build'
  inputs:
    containerRegistryType: 'Azure Container Registry'
    azureSubscription: 'ARM-DevOps-AKS-DemoApp'
    azureContainerRegistry: '{"loginServer":"$(ACR_LOGIN_SERVER)", "id" : "$(ACR_ID)"}'
    dockerComposeFile: 'docker-compose.yaml'
    dockerComposeFileArgs: 'DOCKER_BUILD_SOURCE='
    action: 'Build services'
    additionalImageTags: '$(Build.BuildId)'

 

- task: DockerCompose@0
  displayName: 'Docker - Push'
  inputs:
    containerRegistryType: 'Azure Container Registry'
    azureSubscription: 'ARM-DevOps-AKS-DemoApp'
    azureContainerRegistry: '{"loginServer":"$(ACR_LOGIN_SERVER)", "id" : "$(ACR_ID)"}'
    dockerComposeFile: 'docker-compose.yaml'
    dockerComposeFileArgs: 'DOCKER_BUILD_SOURCE='
    action: 'Push services'
    additionalImageTags: '$(Build.BuildId)'

Kubernetes

Once Docker has completed building and pushing the image, the Azure pipeline will move onto the Kubernetes tasks, which under the covers uses kubectl. Firstly, the Kubernetes task will create the resources (deployments and services) based on the Kubernetes manifest file azure-random-game-app.yaml. Then, the last step is to pull the latest image version from the ACR and update the resources using Kubernetes.

- task: Kubernetes@1
  displayName: 'AKS Deployments & Services'
  inputs:
    connectionType: 'Azure Resource Manager'
    azureSubscriptionEndpoint: 'ARM-DevOps-AKS-DemoApp'
    azureResourceGroup: '$(AKS_RG_NAME)'
    kubernetesCluster: '$(AKS_NAME)'
    command: 'apply'
    useConfigurationFile: true
    configuration: '$(System.DefaultWorkingDirectory)/azure-random-game-app.yaml'
    secretType: 'dockerRegistry'
    containerRegistryType: 'Azure Container Registry'
    azureSubscriptionEndpointForSecrets: 'ARM-DevOps-AKS-DemoApp'
    azureContainerRegistry: '$(ACR_LOGIN_SERVER)'
    secretName: 'akssecret'
    versionSpec: '1.8.1'
    checkLatest: true

- task: Kubernetes@1
  displayName: 'AKS - Update Image'
  inputs:
    connectionType: 'Azure Resource Manager'
    azureSubscriptionEndpoint: 'ARM-DevOps-AKS-DemoApp'
    azureResourceGroup: '$(AKS_RG_NAME)'
    kubernetesCluster: '$(AKS_NAME)'
    command: 'set'
    arguments: 'image deployment azure-game-front azure-game-front=$(ACR_LOGIN_SERVER)/azure-game-front:$(Build.BuildId)'
    secretType: 'dockerRegistry'
    containerRegistryType: 'Azure Container Registry'
    azureSubscriptionEndpointForSecrets: 'ARM-DevOps-AKS-DemoApp'
    azureContainerRegistry: '$(ACR_LOGIN_SERVER)'
    secretName: 'akssecret'
    versionSpec: '1.8.1'
    checkLatest: true

Application is Deployed!

After the Azure Pipeline has successfully completed, the end result shows that the resources are deployed on Azure. The application is available with two services running: azure-game-back and azure-game-front in the Kubernetes cluster. The backend of the application is a Redis cache and the front-end is a web application built with Python and HTML.

Great, so now we can use the kubectl commands (shown below) to validate availability and to find the External IP address of the azure-game-front service so we open the application.

$ kubectl get deployments

 

$ kubectl get pods

 

$ kubectl get services

 

The application can now be used to generate a random number. The number of each player will be stored in the Redis cache until the reset button is pressed. Below are some screenshots of the game.

Random Number Game

Random Number Game

Random Number Game

The End… Or is it?

We’ve now seen the end-to-end deployment of the infrastructure and application using AKS, Azure DevOps, and various other services. As we highlighted earlier – compared to traditional deployments of an application we did not buy any hosting plans, Azure App Service plans, virtual/physical machines and therefore we had a faster deployment. We can easily roll back to previous versions of the application, infrastructure, and the YAML pipeline because we used Azure DevOps for our Azure Repo and Azure Pipeline. In the future, we can scale the resources with minimal effort because everything exists as code.

We’d encourage you to try a hands-on lab with Azure DevOps and Kubernetes, so please take a look at these free resources:

Deploying containers with Azure DevOps:

https://azuredevopslabs.com/labs/vstsextend/kubernetes/

Creating an Azure YAML Pipeline: 

https://azuredevopslabs.com/labs/azuredevops/yaml/

For more information on how we use Azure DevOps and Kubernetes services, check out our Microsoft Azure Practice.