CI/CD with Github Actions, Sonarqube and Ansible

CI/CD with Github Actions, Sonarqube and Ansible

Objectives

  • Target the code at: https://github.com/melvincv/java-basic-practice-app

  • Switch to main-v2 branch.

  • Build the code using GitHub Actions.

  • Deploy the code on two AWS EC2 servers. (independently)

    • Implement approval process for deployment on EC2 servers.

    • Once the application is stable, try implementing the blue-green.

      • Make a change in the codebase and deploy the updated code to ONLY one of the EC2 instances.

      • Upon deployment, your load balancer should serve from both the EC2 instances and show your updated page when refreshed (randomly)


Architecture

Workflow

Complete Github Workflow: .github/workflows/main-pipeline.yml

https://github.com/melvincv/java-basic-practice-app/blob/main-v2/.github/workflows/main-pipeline.yml

Jobs

  1. build job
  • set up JDK 8 Temurin

  • build using maven

  • cache dependencies using the same action

  • upload WAR file as an artifact

  1. code-quality job
  • set up JDK 11 Temurin

  • cache sonarqube packages

  • build and analyze using Maven and sonar-maven-plugin

  1. test job
  • runs maven test

  • uses JDK 8

  • requires build and code-quality jobs

  1. deploy-a job
  • is triggered on the main-v2 branch only + manual input

  • is part of the production environment

  • requires the test job

  • downloads the artifact uploaded by the build job

  • renames WAR file to ROOT.war

  • scp action copies it to /tmp

  • ssh action deploys it to the tomcat webapps dir

  1. deploy-b job
  • similar to deploy-a

Sonarqube Setup

Start an EC2 instance with the following parameters:

Be sure to set the DNS A record to the IP address of your Sonarqube instance asap.

SSH to the sonarqube instance

Check for instance init progress

tail -f /var/log/cloud-init-output.log
Cloud-init v. 23.4.4-0ubuntu0~22.04.1 finished at Sat, 20 Apr 2024 09:33:31

and lookup your domain name. Confirm that it is pointing to your instance before starting the Caddy service.

nslookup jenkins.aws.melvincv.com
sudo systemctl start caddy
sudo systemctl status caddy

Check for the certificate issue in the logs of the status command.

But Sonarqube is not ready yet and you will get an HTTP error 502 (Bad gateway). This is because the upstream server is not available.

Install Sonarqube using Docker Compose

Create a folder for the Docker files

mkdir sonar; cd sonar

Create a .env file with a random, strong PostgreSQL Password.

cat > .env
POSTGRES_PASSWORD=xxxxxxxxxxxxxxxxxxxx

Create a compose.yml file

cat > compose.yml

My compose file: https://github.com/melvincv/java-basic-practice-app/blob/main/scripts/sonarqube/compose.yml

Start Sonarqube using Compose:

docker compose pull
docker compose up -d

The Compose file starts 3 containers:

  • sonarqube lts version

  • postgres 12

  • adminer (optional, for DB management)

$ docker compose ps
NAME                IMAGE                     COMMAND                  SERVICE     CREATED              STATUS              PORTS
sonar-adminer-1     adminer                   "entrypoint.sh php -..."   adminer     About a minute ago   Up About a minute   0.0.0.0:10000->8080/tcp, :::10000->8080/tcp
sonar-db-1          postgres:12               "docker-entrypoint.s..."   db          About a minute ago   Up About a minute   5432/tcp
sonar-sonarqube-1   sonarqube:lts-community   "/opt/sonarqube/dock..."   sonarqube   About a minute ago   Up About a minute   0.0.0.0:9000->9000/tcp, :::9000->9000/tcp

Wait for a minute or two...

... and Sonarqube is ready

Eg: https://sonar.aws.melvincv.com/

Login with the default credentials and change your password asap.

user: admin

pass: admin

Create a Project

Sonarqube > Projects > Manually

Project display name = Java Practice App v2

Project key = Java-Practice-App-v2

Main branch name = main-v2

Follow the given instructions:

  1. Create 2 GitHub Repo Secrets- SONAR_TOKEN and SONAR_HOST_URL

Update workflow. Sample code will be shown. (already updated)

CD Target Servers

Start 2 target ec2 instance - server-a and server-b

  • ami: Ubuntu 22.04 LTS 64 bit

  • type: t3.micro

  • security group: open ports 8080, 22

  • spot instance: true

Ansible Playbook

Write and execute an Ansible playbook to install Tomcat 9 on the Target servers.

Code: https://github.com/melvincv/java-basic-practice-app/tree/main-v2/ansible

Test connectivity to the instances using the ping module:

Run the playbook:

Add Github Variables and Secrets

Settings > Secrets and Variables > Actions > Variables

Add HOST_A and HOST_B as repo variables. In the value box, add the IP of the target server A / B.

Settings > Secrets and Variables > Actions > Secrets

Add repo secrets:

  • SONAR_HOST_URL (URL of the Sonarqube instance)

  • SONAR_TOKEN (Token obtained from the sonarqube project)

  • USERNAME (ubuntu)

  • PRIVATE_KEY (private key for the target servers)

Add Branch Protection for the main and main-v2 branches

Add a production environment for the workflow

Add a reviewer to check and confirm the deployment to production. You may create a new Github account to try this out...

Approval Process

  1. I make a commit to the devel branch

  2. Raise a PR to the main-v2 branch

  3. PR workflow runs

  4. Reviewer approves the PR and code is merged from devel to main-v2

  5. the workflow runs for main-v2

On the reviewer's account, approve the PR:

Production environment deployment approval prompt will be displayed when the workflow reaches the deploy jobs:

The reviewer approves the PR from his account:

The deploy jobs start running and the app is deployed to production.

AWS Setup

Instances

These instances have already been set up:

Create a target group

Create App Load Balancer

Name: akhil-p2-alb

Internet facing, IPv4, Select all subnets

Select security group (akhil-project-1-alb-sg)

Add listeners:

Selecting the wildcard SSL certificate created in the previous project

Create Load Balancer

DNS Name will be like this: akhil-p2-alb-1468339209.ap-northeast-1.elb.amazonaws.com

Add Alias record to Route 53

Redirect HTTP to HTTPS

Edit Health check settings

Since the health checks are reporting that the instances are Unhealthy,

To troubleshoot, check the HTTP status code in your browser's dev tools.

It is giving a 302 redirect. So, change the health check path to /login to get a 200 OK.

Blue Green Deployment

Note: The workflow has to be present in the default branch for Manual triggering to work via workflow_dispatch. I changed the default branch to main-v2 for this project.

Added boolean inputs to the workflow for selective deployment...

on:
  workflow_dispatch:
    inputs:
      deploy-a:
        type: boolean
        description: 'Deploy to Server A?'
        required: true
        default: true
      deploy-b:
        type: boolean
        description: 'Deploy to Server B?'
        required: true
        default: true

Raised a PR to the main-v2 branch and merged the PR.

Now, there is an option to trigger the workflow manually with the inputs.

Check one of the servers (B) > Run

The workflow will run the deploy-b job only:

Results

After the Blue/Green deployment, the app is available at the below URL:

https://javaappv2.aws.melvincv.com/login