Efficient Microservices Management with Azure DevOps: A Complete Guide

Efficient Microservices Management with Azure DevOps: A Complete Guide

Azure DevOps Voting App

In the world of DevOps, Continuous Integration (CI) is crucial for automating the testing and deployment of applications. Azure DevOps is a popular platform for managing these tasks. In this technical blog, we will guide you through the steps to migrate a GitHub-based CI pipeline to Azure DevOps, using Docker for containerization and Azure Container Registry (ACR) to store Docker images. This project will help streamline your development workflows by automating the build and push stages, ensuring consistency across different environments.

Prerequisites

Before diving into this project, here are some skills and tools you should be familiar with:

  • [x] Clone repository for terraform code
    Note: Replace resource names and variables as per your requirement in terraform code

    • Generate the ssh key in id_rsa format.

    • Update terraform.tfvars.

  • [x] App Repo (Example Voting App)

  • [x] Azure Account: You’ll need an Azure account to create resources like virtual Machines, and AKS clusters, and manage pipelines.

  • [x] Terraform Knowledge: Familiarity with Terraform is needed to set up, manage, and remove infrastructure..

  • [x] Basic Kubernetes (AKS): A basic understanding of Kubernetes, especially Azure AKS, to deploy and manage containers.

  • [x] Docker Knowledge: Basic knowledge of Docker for containerizing applications.

  • [x] GitHub: Experience with GitHub for version control and managing repositories.

  • [x] Command-Line Tools: Basic comfort with using the command line for managing infrastructure and services.

  • [x] Basic CI/CD Knowledge: Some understanding of Continuous Integration and Deployment is recommended.

  • [x] Azure Container Registry (ACR): Set up ACR to store your Docker images.

  • [x] Linux VM: Docker must be installed on a Linux virtual machine to run containers.

Setting Up the Infrastructure

I have developed Terraform code to set up the entire infrastructure. This includes automatically installing the necessary applications, and tools, and creating the AKS cluster.

NoteAKS cluster creation will take approx. 10 to 15 minutes.

  • ⇒ Virtual machines will be created named as "devopsdemovm"

  • ⇒ Docker Install

  • ⇒ Azure Cli Install

  • ⇒ KubeCtl Install

  • ⇒ AKS Cluster Setup

Virtual Machine creation

First, we'll create the necessary virtual machines using terraform code.

Below is a terraform Code:

Once you clone repo and run the terraform command.

$ ls -l
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
dar--l          26/12/24   7:16 PM                pipeline
dar--l          23/12/24   3:38 PM                scripts
-a---l          25/12/24   2:31 PM            600 .gitignore
-a---l          26/12/24   9:29 PM           6571 EC2.tf
-a---l          26/12/24   9:29 PM            892 main.tf
-a---l          26/12/24   9:29 PM            567 output.tf
-a---l          27/12/24   4:00 PM          26624 Project_Voting Apps.md
-a---l          26/12/24   9:29 PM            269 provider.tf
-a---l          26/12/24   9:30 PM            223 terraform.tfvars
-a---l          26/12/24   9:30 PM            615 variable.tf

You need to run the following terraform command.

Now, run the following command.

terraform init
terraform fmt
terraform validate
terraform plan
terraform apply 
# Optional <terraform apply --auto-approve>

After running the Terraform command, we will verify the following to ensure everything is set up correctly using Terraform.

Inspect the Cloud-Init logs:

Once connected to VM then you can check the status of the user_data script by inspecting the log files

# Primary log file for cloud-init
sudo tail -f /var/log/cloud-init-output.log
                    or 
sudo cat /var/log/cloud-init-output.log | more
  • If the user_data script runs successfully, you will see output logs and any errors encountered during execution.

  • If there’s an error, this log will provide clues about what failed.

Verify the Installation

  • [x] Docker version
ubuntu@ip-172-31-95-197:~$ docker --version
Docker version 24.0.7, build 24.0.7-0ubuntu4.1


docker ps -a
ubuntu@ip-172-31-94-25:~$ docker ps
  • [x] kubectl version
ubuntu@ip-172-31-89-97:~$ kubectl version
Client Version: v1.31.1
Kustomize Version: v5.4.2
  • [x] Azure cli version
azureuser@devopsdemovm:~$ az version
{
  "azure-cli": "2.67.0",
  "azure-cli-core": "2.67.0",
  "azure-cli-telemetry": "1.1.0",
  "extensions": {}
}

Step-by-Step Process:

1. Setting up Azure DevOps Pipeline:

  • Create an Azure DevOps Organization: If you don’t have one already, create a new Azure DevOps organization where your repositories and pipelines will reside.

  • Set up Agent Pool: Define an agent pool in Azure DevOps, which will handle the execution of your pipelines.

  • Install Docker on the Agent: Docker needs to be installed on the machine that will run the pipeline. Without Docker, you won’t be able to build or push Docker images.

2. Creating the Pipeline for Build and Push:

  • Define Pipeline YAML: Write a YAML configuration file to define the pipeline. This file specifies stages like build and push, and Docker is used to automate the creation of images.

  • Configure Docker Build: In the build stage, Docker is used to build images from your Dockerfile. The push stage uploads these images to the Azure Container Registry (ACR).

  • Configure Pipeline Triggers: Set up the pipeline to trigger automatically when changes are made in specific directories, like results or worker. This helps in managing microservices by triggering pipelines only for the relevant components.

3. Testing the Pipeline:

  • Make Changes and Test: After setting up the pipeline, make minor changes to test whether the pipeline triggers as expected. For instance, adding a space to a Dockerfile or modifying a JavaScript file in the results directory should trigger a build for that microservice only.

  • Verify Docker Image Creation: Ensure the Docker images are being built and pushed to ACR without issues.

  • Handle Platform-Specific Builds: If the pipeline fails due to architecture issues, ensure the correct platform (Linux/ARM64) is specified in the Dockerfile.

  • Step-01: Create project in Azure DevOps

  • Step-02: Clone the Repo in Azure DevOps

    • Clone the repo ::

      • click on Imort a repository

        image

        image-1

        image-82

  • Create/Configure a pipeline in Azure DevOps.

    • Click on Pipeline:

    • follow the below instruction

      image-2

      image-3

      image-4

      image-5

      image-6

      It will ask you to log in with your Azure account. Please use the same login credentials that you have set up for the Azure portal.

Select the container registry

image-7

  • Note: Key concept overview of pipeline.

    image-9

you will see the following pipeline yaml and we have to modify accordingly.

image-8

  • We have to update the CI_pipeline as below

  • First, we will create a folder in the repo called 'scripts' and update the .sh file as shown below. We will create a shell script to get an updated image tag in case a new image is being created.

  • Don't forget to update the container registry name in the script file.

  • Step-03: To configure Self-hosted Linux agents in Azure DevOps

    • Built the pipeline but I got the below error message

      image-10

Solution:

  • Need to configure Self-hosted Linux agents/integrate to Azure DevOps

    image-11

    image-12

    image-13

    image-14

Select the agent pools name, you can choose any name

  • devops-demo_vm

    image-15

    image-16

run the following command to install the agent.

mkdir myagent && cd myagent
sudo wget https://vstsagentpackage.azureedge.net/agent/4.248.0/vsts-agent-linux-x64-4.248.0.tar.gz
tar zxvf vsts-agent-linux-x64-4.248.0.tar.gz
azureuser@devopsdemovm:~/myagent$ ls -l
total 144072
drwxrwxr-x 26 azureuser azureuser     20480 Nov 13 10:54 bin
-rwxrwxr-x  1 azureuser azureuser      3173 Nov 13 10:45 config.sh
-rwxrwxr-x  1 azureuser azureuser       726 Nov 13 10:45 env.sh
drwxrwxr-x  7 azureuser azureuser      4096 Nov 13 10:46 externals
-rw-rw-r--  1 azureuser azureuser      9465 Nov 13 10:45 license.html
-rw-rw-r--  1 azureuser azureuser      3170 Nov 13 10:45 reauth.sh
-rw-rw-r--  1 azureuser azureuser      2753 Nov 13 10:45 run-docker.sh
-rwxrwxr-x  1 azureuser azureuser      2014 Nov 13 10:45 run.sh
-rw-r--r--  1 root      root      147471638 Nov 13 12:22 vsts-agent-linux-x64-4.248.0.tar.gz
  • Configure the agent
~/myagent$ ./config.sh

Type 'Y'

#Server URL
Azure Pipelines: https://dev.azure.com/{your-organization}
https://dev.azure.com/mrbalraj
  • Need to create a PAT Access Token-

    • Go to azure devops user setting and click on PAT.

image-17

image-18

image-19

  • Give any name for agent
devops-demo_vm

image-20

Agent is still offline.

image-21

  • We have to Optionally run the agent interactively. If you didn't run as a service above:
~/myagent$ ./run.sh

Now, Agent is online ;-)

image-22

  • Rerun the pipeline and it works.

  • Rename the pipeline

  • Will create two more pipeline (microservices).

    • result

    • worker

      image-28

  • Repository status in Container Registry.

Step 04: Set Up ArgoCD

  • K8s Cluster login

  • Argocd install

  • Configure Argocd

  • Go Azure UI and select the AKS cluster

Take putty session of Azure VM and perform the following instruction to login into auzre and K8s

  • Azure login:

Missing Browser on Headless Servers, Use the --use-device-code flag to authenticate without a browser:

az login --use-device-code

image-30

az account list --output table

image-33

  • To get resource details
az aks list --resource-group "resourceGroupName" --output table

image-34

image-35

  • [x] Verify the AKS cluster.

  • To get credentials for AKS

az aks get-credentials --name "Clustername" --resource-group "ResourceGroupName" --overwrite-existing

kubectl config current-context
kubectl get nodes
kubectl get nodes -o wide

image-36

Install ArgoCD

kubectl create namespace argocd

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

Configure ArgoCD

  • Run the following commands to verify the Pods and services type
kubectl get pods -n argocd
kubectl get svc -n argocd
  • To get details of Pods in namespace "argocd"
kubectl get pods -n argocd

image-37

  • To get the secrets for argoCd
kubectl get secrets -n argocd

image-38

  • To get service details in argocd
kubectl get svc -n argocd
azureuser@devopsdemovm:~/myagent$ kubectl get svc -n argocd
NAME                                      TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
argocd-applicationset-controller          ClusterIP   10.0.199.83    <none>        7000/TCP,8080/TCP            6m17s
argocd-dex-server                         ClusterIP   10.0.236.32    <none>        5556/TCP,5557/TCP,5558/TCP   6m17s
argocd-metrics                            ClusterIP   10.0.231.144   <none>        8082/TCP                     6m17s
argocd-notifications-controller-metrics   ClusterIP   10.0.54.255    <none>        9001/TCP                     6m16s
argocd-redis                              ClusterIP   10.0.38.40     <none>        6379/TCP                     6m16s
argocd-repo-server                        ClusterIP   10.0.29.153    <none>        8081/TCP,8084/TCP            6m16s
argocd-server                             ClusterIP   10.0.216.42    <none>        80/TCP,443/TCP               6m16s
argocd-server-metrics                     ClusterIP   10.0.201.27    <none>        8083/TCP                     6m16s

image-40

  • Currently, it is set to clusterIP and we will change it to NodePort
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'
  • Review the service again
kubectl get svc -n argocd

azureuser@devopsdemovm:~$ kubectl get svc -n argocd
NAME                                      TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGE
argocd-applicationset-controller          ClusterIP   10.0.98.49    <none>        7000/TCP,8080/TCP            49m
argocd-dex-server                         ClusterIP   10.0.130.97   <none>        5556/TCP,5557/TCP,5558/TCP   49m
argocd-metrics                            ClusterIP   10.0.91.113   <none>        8082/TCP                     49m
argocd-notifications-controller-metrics   ClusterIP   10.0.83.161   <none>        9001/TCP                     49m
argocd-redis                              ClusterIP   10.0.241.99   <none>        6379/TCP                     49m
argocd-repo-server                        ClusterIP   10.0.38.142   <none>        8081/TCP,8084/TCP            49m
argocd-server                             NodePort    10.0.228.33   <none>        80:32648/TCP,443:31181/TCP   49m
argocd-server-metrics                     ClusterIP   10.0.124.90   <none>        8083/TCP                     49m

image-41

  • To get URL/IP address details
kubectl get nodes -o wide
azureuser@devopsdemovm:~/myagent$ kubectl get nodes -o wide
NAME                                STATUS   ROLES    AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
aks-agentpool-23873620-vmss000000   Ready    <none>   54m   v1.30.6   10.224.0.4    <none>        Ubuntu 22.04.5 LTS   5.15.0-1075-azure   containerd://1.7.23-1

image-42

Verify Kubernetes Config: Confirm that the argocd-server service has the correct NodePort and is not misconfigured:

kubectl describe svc argocd-server -n argocd
azureuser@devopsdemovm:~$ kubectl get svc -n argocd
NAME                                      TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGE
argocd-applicationset-controller          ClusterIP   10.0.98.49    <none>        7000/TCP,8080/TCP            2m31s
argocd-dex-server                         ClusterIP   10.0.130.97   <none>        5556/TCP,5557/TCP,5558/TCP   2m31s
argocd-metrics                            ClusterIP   10.0.91.113   <none>        8082/TCP                     2m31s
argocd-notifications-controller-metrics   ClusterIP   10.0.83.161   <none>        9001/TCP                     2m31s
argocd-redis                              ClusterIP   10.0.241.99   <none>        6379/TCP                     2m31s
argocd-repo-server                        ClusterIP   10.0.38.142   <none>        8081/TCP,8084/TCP            2m31s
argocd-server                             NodePort    10.0.228.33   <none>        80:32648/TCP,443:31181/TCP   2m31s
argocd-server-metrics                     ClusterIP   10.0.124.90   <none>        8083/TCP                     2m31s

azureuser@devopsdemovm:~$ kubectl get nodes -o wide
NAME                             STATUS   ROLES    AGE   VERSION   INTERNAL-IP   EXTERNAL-IP     OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
aks-system-29381837-vmss000000   Ready    <none>   26m   v1.30.6   10.224.0.4    52.148.171.58   Ubuntu 22.04.5 LTS   5.15.0-1075-azure   containerd://1.7.23-1

Then access it at http://52.148.171.58:32648.

If page is not opening then we have to open a port in NSG.

  • On the Azure portal, go to the server with VMSS and select the VMSS.

    image-72

    image-74

    image-75

    image-76

    image-77

Now, we need to try to access it again http://PublicIPAddress:32648.

image-78

  • To retrive the password for argocd
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo

image-39

  • To configure repo in argocd with token base.
(http://PublicIPAddress:32648)

image-44

Go to azure pipeline and click on setting and select "Personal Access Token"

image-45

image-46

image-47

image-48

https://<Accesstoken>@dev.azure.com/mrbalraj/balraj-devops/_git/balraj-devops

image-49

  • To create a application in argocd Once you access the ArgoCD URL and create an application

image-50

image-51

by default 3 min to sync with argocd but it can be changed as below.

image-52

image-53

  • Now, we will change the argocd default time (3 min) to 10 sec.
kubectl edit cm argocd-cm -n argocd

edit as below

image-57

Now, try to get all resources, and you will notice there is an error related to ImagePullBackoff.

kubectl get all

I am getting the below error message.

image-58

image-59

Solution: As we are using private registory and we need to use 'imagepullsecrets'

Go to the Azure registry and get the password, which will be used in the command below.

image-60

  • command to create ACRImagePullSecret
kubectl create secret docker-registry <secret-name> \
    --namespace <namespace> \
    --docker-server=<container-registry-name>.azurecr.io \
    --docker-username=<service-principal-ID> \
    --docker-password=<service-principal-password>

Explanation of the Command:

kubectl create secret docker-registry:

- This creates a new Kubernetes secret of type docker-registry.
<secret-name>:

- The name of the secret being created. For example, acr-credentials.
--namespace <namespace>:

- Specifies the namespace in which the secret will be created. If omitted, it defaults to the default namespace.
Replace <namespace> with the desired namespace name.
--docker-server=<container-registry-name>.azurecr.io:

- The URL of your container registry. For Azure Container Registry (ACR), the format is <container-registry-name>.azurecr.io.
Replace <container-registry-name> with your ACR name.
--docker-username=<service-principal-ID>:

- The username to authenticate with the container registry. For Azure, this is typically a service principal's application (client) ID.
--docker-password=<service-principal-password>:

- The password (or secret) associated with the service principal used for authentication.

To get token, click on container registory

image-79

kubectl create secret docker-registry acr-credentials \
    --namespace default \
    --docker-server=aconregee7b05ba.azurecr.io \
    --docker-username=aconregee7b05ba \
    --docker-password=<token>

image-61

  • Command to delete secret
kubectl delete secret acr-credential --namespace default

now, we will update the vote-deployment.yaml as below:

image-62

Need to update in all other deployment as below:

image-43

image-73

here is the updated service status

image-63

Try updating anything in the vote folder in "app.py" as shown below, and the pipeline should automatically trigger.

image-64

image-65

  • To check the deployment
kubectl get deploy vote -o yaml
  • Verify services and try to access the application
kubectl get svc
kubectl get node -o wide
kubectl describe svc argocd-server -n argocd
  • Now, we will open one more port in VMSS

  • Vote application is accessible now.

image-80

image-81

Congratulations :-) the application is working and accessible.

Step-05: Clean up the images and container registry using the pipeline.

  • First create Service Connection in Azure Devops.

  • Once you create a connection then note it down the connection ID, because that ID would be used in pipeline.

  • On agent machine, make sure login with azure login and connection is active, if not then login with following.

      az login --use-device-code
    
  • Delete all the images along with repogitory.

Environment Cleanup:

  • As we are using Terraform, we will use the following command to delete

  • Delete all deployment/Service first

         kubectl delete service/redis
         kubectl delete service/db
         kubectl delete service/resut
         kubectl delete service/result
         kubectl delete service/vote
         kubectl delete deployment.apps/db
         kubectl delete deployment.apps/redis
         kubectl delete deployment.apps/vote
         kubectl delete deployment.apps/worker
         kubectl delete deployment.apps/db
         kubectl delete deployment.apps/result
         kubectl delete service/db
    

image-67

Now, time to delete the AKS Cluster and Virtual machine.

run the terraform command.

Terraform destroy --auto-approve

Conclusion

Migrating CI pipelines from GitHub to Azure DevOps can significantly improve your development process by automating the build and deployment stages. This blog covered the step-by-step process of setting up an Azure DevOps pipeline to build Docker images and push them to Azure Container Registry. It also provided insights into handling pipeline triggers for different microservices, ensuring that the correct pipelines run when changes are made. By adopting this approach, you’ll streamline your CI workflows and enhance your development productivity.

Ref Link: