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
- build job
set up JDK 8 Temurin
build using maven
cache dependencies using the same action
upload WAR file as an artifact
- code-quality job
set up JDK 11 Temurin
cache sonarqube packages
build and analyze using Maven and sonar-maven-plugin
- test job
runs maven test
uses JDK 8
requires build and code-quality jobs
- 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
- deploy-b job
- similar to deploy-a
Sonarqube Setup
Start an EC2 instance with the following parameters:
name: sonarqube
ami: Ubuntu 22.04 LTS 64 bit
type: t3.medium
security group: open ports 80, 443, 22
spot instance: true
user data: (check and modify shell script variables first)
https://github.com/melvincv/java-basic-practice-app/blob/main/scripts/sonarqube/prereq.sh
The script configures kernel settings, installs Docker and Caddy reverse proxy.
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:
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
I make a commit to the devel branch
Raise a PR to the main-v2 branch
PR workflow runs
Reviewer approves the PR and code is merged from devel to main-v2
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



