CI/CD for a web application using Azure DevOps

Introduction

Azure DevOps platform was shown for the first time at the end of September 2018. To check what this new tool from Microsoft has to offer, we have decided to develop continuous integration and continuous deployment processes (CI/CD) for a simple (monolithic) web application. For that purpose, we are going to use PWA application for claim reporting, which we have developed beforehand.

The solution, which we would like to construct, will look as follows:

Azure DevOps

The continuous integration process downloads the source code from GitHub repository, compiles the application and packages it into a container which then is uploaded to DockerHub image repository. The continuous deployment process downloads a container’s image from DockerHub and runs it in an already prepared environment. In our case, it will be the Azure App Service.

Preparation

Before we start to develop CI/CD processes, we must prepare a few things:

  • We create an account https://github.com/ and repository to which we put the code of our application. The application is containerized (it has Dockerfile prepared).
  • We create an account at https://hub.docker.com/ and define a team together with the namespace assigned to it.
  • We create an account at http://dev.azure.com and start a new project.
  • We create an account at https://portal.azure.com, install Azure CLI, authorize (issuing: az login) and prepare runtime environment for our application:
    • we select the name of our application, Azure Resource Group and image repository in DockerHub, for example:

RG_NAME=claim-reporter
APP_NAME=claim-reporter-app
IMAGE=altkom/claim-reporter-app

    • we create the Resource Group:

az group create \
    --location westeurope \
    --name ${RG_NAME}

    • we create the Service Plan:

az appservice plan create \
    --name ${APP_NAME} \
    --resource-group ${RG_NAME} \
    --is-linux

    • we create a new “App Service” type resource:

az webapp create \
    --plan ${APP_NAME} \
    --resource-group ${RG_NAME} \
    --name ${APP_NAME} \
    --deployment-container-image-name ${IMAGE}

Continuous Integration process

Defining the continuous integration process in Azure DevOps requires 11 simple steps:

1) On the main screen of the selected project, we choose the “Builds” tab and click the button “New Pipeline”:

Azure DevOps

2) We choose the platform on which we are going to host our code repository (Github or Azure):

Azure DevOps

3) We choose a method of integration with GitHub:

Azure DevOps

4) We install Azure Pipelines plug-in on GitHub account:

Azure DevOps

5) We grant necessary permissions to a plug-in:

Azure DevOps

6) We select the code repository:

Azure DevOps

7) We choose a template for a new pipeline:

Azure DevOps

Available templates include simple examples of build pipelines for several popular technological stacks: Java (ant, maven, gradle), JavaScript (npm, grunt, gulp, angular, react, vue), .NET (Full, Core), UWP, Python, Ruby, Go, PHP, Xamarin, Xcode, C/C++, Docker, etc.

8) We look it through and save with no modifications :

Azure DevOps

The definition of the pipeline has been saved in code’s repository in the azure-pipelines.yml file.

A new commit causes initiation of build process which (for obvious reasons) ends up with a failure:

Azure DevOps

9) To make the build process working, we have to adopt the definition to the specifics of our project. We start from defining three variables in a pipeline:

  • dockerId – login for DockerHub account
  • dockerNamespace – namespace created on DockerHub
  • dockerPass – a necessary password for authentication (you must remember to mark the variable as storing the password in order to avoid displaying it in the console and logs)

Azure DevOps

10) We adjust pipeline’s definition, which is saved in the azure-pipelines.yml file:

# azure-pipelines.yml
pool:
vmImage: 'Ubuntu 16.04'

variables:
app: 'claim-reporter-app'
image: '$(dockerNamespace)/claim-reporter-app'
tag: '$(build.buildId)'

steps:
- script: docker login -u $(dockerId) -p $(dockerPass)
displayName: 'docker login'
- script: docker build -t $(image) $(app)
displayName: 'docker build $(app)'
- script: docker tag $(image) $(image):$(tag)
displayName: 'docker tag $(image):$(tag)'
- script: docker push $(image):$(tag)
displayName: 'docker push $(image):$(tag)'
- script: docker push $(image):latest
displayName: 'docker push $(image):latest'

11) We save the changes in the repository which initiates the build process:

Azure DevOps

We may also see the build status on GitHub:

Azure DevOps

Created container images have appeared on DockerHub:

Azure DevOps

Continuous Deployment process

To define a simple Continuous Deployment process, we must take eight steps:

  1. On the project’s screen we move to “Releases” tab and select an option “New Pipeline”:

Azure DevOps

2) We select “Azure App Service deployment” template and click “Apply”:

Azure DevOps

3) We specify the name of the pipeline and its first step:

Azure DevOps

4) We open the deployment artifact selection screen and choose “Docker Hub” as a source:

Azure DevOps

5) We configure the connection with an account on DockerHub:

Azure DevOps

6) Next, we select namespace as well as a repository and confirm by clicking the button “Add”:

Azure DevOps

7) We move to the “Tasks” tab:

Azure DevOps

We select Azure subscription, click “Authorize” and log into Azure account (warning: make sure that pop-up blockers are switched off). Then we choose the target location by indicating “App Service” created beforehand and click “Save”:

Azure DevOps

8) We come back to the pipeline’s configuration screen and add the deployment process trigger:

Azure DevOps

The process, which we developed, looks as follows:

Azure DevOps

We initiate the first deployment manually (Release -> Create a release), by choosing the version of artifact, which is supposed to be used:

Azure DevOps

After a while, the installation process will be complete:

Azure DevOps

and our application is going to appear under the desired address:

Azure DevOps

Summary

A few experiments, which we have carried out using Azure DevOps platform, allowed us to make the following observations:

  • it is not a new solution, but graphically refreshed VSTS service – main changes have occurred in pricing, which I’m going to mention in the following points;
  • Azure DevOps platform allows to quickly and easily construct CI/CD pipelines – for simple cases;
  • configuring the integration with GitHub and DockerHub is painless;
  • the integration with GitHub works very well in both ways: on GitHub, for every commit, we may see the status of compilation triggered by that particular change, while in the Azure DevOps we have a convenient view of the changelist which have been included in a given build;
  • lack of proper CLI is the reason why all operations have to be done via GUI, which most engineers consider as a disadvantage; official CLI (in 0.1.3 version) does not allow defining pipelines; there is also alternative solution created by community: VSTeam, however, it is only available from Powershell, and has its limitations as well;
  • a platform offers HTTP REST API and Client’s libraries for a few programming languages; however, not all functionalities are supported this way;
  • the tool is not stable yet – we had some issues with commits that failed to trigger builds (silently, without any error messages);
  • we can see some conceptual inconsistencies between Azure and Azure DevOps platforms – although we would like to perform deployment using Azure DevOps, we still have to select docker image repository while creating App Service container (we cannot create an empty App Service instance);
  • a wizard for creating build pipelines is very simplified – for example, it does not allow to define environment variables which build process is going to use, and cannot be skipped; it means that if we want to use variables, we must set the pipeline first (which will trigger its execution), then edit it, configure variables and re-run the build process;
  • platform’s graphic interface is inconsistent in some places – for example, YAML file, which includes the definition of the build pipeline, can be edited from visual interface only during its creation; all further changes have to be introduced by performing commits directly to code’s repository;
  • current service tarification makes it a perfect solution for small teams and small, Open Source projects (proof-of-concept for example).

To sum it all up – Azure DevOps platform offers a wide range of features. We are hoping that few shortcomings, which we have discovered, will soon be removed.

Robert Kuśmierek
Lead Software Engineer, ASC LAB