We will deploy SonarQube Self-Host on the Build Instance. You can read more here.
First, create a working directory:
cd && mkdir -p /root/tools/sonar && vi /root/tools/sonar/docker-compose.yml
Enter the following content:
version: "3"
services:
sonarqube:
image: sonarqube:community
depends_on:
- db
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
ports:
- "9000:9000"
db:
image: postgres:12
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar
volumes:
- postgresql:/var/lib/postgresql
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
postgresql:
postgresql_data:
Run the docker-compose.yml
file:
docker-compose -f tools/sonar/docker-compose.yml up -d
When you run docker ps -a
, you might see the sonar-sonarqube-1
container exited:
Use docker logs
to check the error:
Elasticsearch uses many virtual memory areas to map large data files into memory. If the value of vm.max_map_count
is too low, Elasticsearch will not be able to map enough memory areas, leading to startup or runtime errors. This is the cause of the “bootstrap checks failed” error.
Fix this by increasing max_map_count
:
sysctl vm.max_map_count
sudo sysctl -w vm.max_map_count=262144
Edit the file /etc/sysctl.conf
:
sudo nano /etc/sysctl.conf
Add this line to the end of the file:
vm.max_map_count=262144
Restart docker-compose
in /root/tools/sonar/
:
cd /root/tools/sonar/
docker-compose down
docker-compose up -d
The containers should now be running:
You need to configure port 9000 to access SonarQube:
Check port 9000 to access the SonarQube main interface:
For more information on connecting, see: (2) GitLab Integration | Mapping your organization into SonarQube - YouTube
Log in with:
After logging in, update the password:
This is the main interface of SonarQube:
Personal Access Token: You will create a new token in Edit Profile → Access Tokens → Add new token
Copy this token and paste it into Personal Access Token in SonarQube, then save the configuration:
In GitLab project onboarding, enter the token:
Select the frontend and backend projects to import:
In the Set up 2 projects for Clean as You Code section, select Use the global setting:
We will select the Backend section first:
Follow the instructions:
Create a token for the project:
Click create:
Then copy the token:
Go to the backend project to create a new variable:
Create as instructed in step 1:
You will have 2 variables, SONAR_TOKEN
and SONAR_HOST_URL
:
Create a new branch named pipeline-be-7.1-sonarqube
from the previous pipeline-be-codelimate
:
Edit the gitlab-ci.yaml
file in the newly created branch:
Enter the following commands and then commit:
variables:
USER_PROJECT: "ecommerce"
PATH_PROJECT: "/home/${USER_PROJECT}/${CI_PROJECT_NAME}"
IMAGE_VERSION: "${CI_REGISTRY_USER}/${CI_PROJECT_NAME}-${USER_PROJECT}:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}"
CODECLIMATE_FILE: "${CI_PROJECT_NAME}:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}.html"
stages:
- clone
- SAST
- build
- push registry
- deploy
before_script:
- sudo mkdir -p $PATH_PROJECT
clone repository:
stage: clone
script:
- echo "Repository cloned."
tags:
- group-ecommerce-shell-runner-build
codelimate testing:
stage: SAST
variables:
GIT_STRATEGY: none
script:
- sudo docker run --rm --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze -f html > $CODECLIMATE_FILE
allow_failure: true
tags:
- group-ecommerce-shell-runner-build
only:
- tags
artifacts:
paths:
- $CODECLIMATE_FILE
expire_in: 1 week
sonarqube testing:
stage: SAST
cache:
policy: pull
key: "${CI_COMMIT_SHORT_SHA}"
paths:
- sonar-scanner/
script:
- "sudo apt-get update"
- "sudo apt-get install --yes --no-install-recommends openjdk-17-jre"
- |
if ! dotnet tool list -g | grep -q 'dotnet-sonarscanner'; then
dotnet tool install --global dotnet-sonarscanner
else
echo "dotnet-sonarscanner is already installed"
fi
- "export PATH=\"$PATH:$HOME/.dotnet/tools\""
- echo "$SONAR_TOKEN"
- echo "$SONAR_HOST_URL"
- "dotnet sonarscanner begin /k:\"ecommerce-fsn_backend_870b5bec-7663-4e18-b150-2f75835d4455\" /d:sonar.token=\"$SONAR_TOKEN\" /d:\"sonar.host.url=$SONAR_HOST_URL\" "
- "dotnet build"
- "dotnet sonarscanner end /d:sonar.token=\"$SONAR_TOKEN\""
allow_failure: true
tags:
- group-ecommerce-shell-runner-build
only:
- tags
build:
stage: build
variables:
GIT_STRATEGY: clone
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker build -t $IMAGE_VERSION .
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner-build
when: manual
only:
- tags
dockerhub pushing:
stage: push registry
variables:
GIT_STRATEGY: none
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker push $IMAGE_VERSION
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner-build
needs:
- job: build
only:
- tags
deploy:
stage: deploy
variables:
GIT_STRATEGY: none
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker pull $IMAGE_VERSION
- sudo su ${USER_PROJECT} -c "
container_exists=\$(sudo docker ps -a -q -f name=${CI_PROJECT_NAME});
if [ ! -z \"\$container_exists\" ]; then
sudo docker rm -f ${CI_PROJECT_NAME};
fi;
sudo docker run --name ${CI_PROJECT_NAME} -dp ${BACKEND_PORT}:${BACKEND_PORT} ${IMAGE_VERSION}"
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner
needs:
- job: dockerhub pushing
only:
- tags
You can turn on the Visualize and Validate tabs to check if the content entered in gitlab-ci.yaml
is correct!
🡇 Content 🡇
You need to adjust the Project Key in this line to match yours to ensure correct results. If you prefer, you can add a new variable to avoid confusion.
- "dotnet sonarscanner begin /k:\"$PROJECT_BACKEND_ID\" /d:sonar.token=\"$SONAR_TOKEN\" /d:\"sonar.host.url=$SONAR_HOST_URL\""
You can get the Project Key from Project Information → Project Key → copy Project Key
🡅 Content 🡅
We create a new tag to trigger the pipeline.
The pipeline has successfully run. You can click on the job dependencies tab and turn on show dependencies to see the relationship between stages. Here you can see that the clone step runs first to fetch the project, followed by the SAST stage where both codelimate testing and sonarqube testing run in parallel. After that, the build step is manually triggered, followed by dockerhub pushing and finally deploy.
The SonarQube testing step has successfully completed.
If you cannot run the SonarQube testing step, run it manually using the script in the build instance with script permissions.
Reload the backend project, and you will see the overview interface.
Existing issues
Security issues listed in detail here
Project metrics
And code errors
We will also deploy similarly for frontend.
First, we need to install sonar-scanner in the build instance with gitlab-runner
permissions.
# Download and unzip sonar-scanner
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip
unzip sonar-scanner-cli-4.8.0.2856-linux.zip
# Move sonar-scanner to /usr/local for easier access
sudo mv sonar-scanner-4.8.0.2856-linux /usr/local/sonar-scanner
export PATH=$PATH:/usr/local/sonar-scanner/bin
# Ensure Java is installed on the system
sudo apt-get update
sudo apt-get install --yes openjdk-17-jre
# Check the version
sonar-scanner -v
Then, in the gitlab-ci.yaml
file of the frontend project, enter the following command:
variables:
USER_PROJECT: "ecommerce"
PATH_PROJECT: "/home/${USER_PROJECT}/${CI_PROJECT_NAME}"
IMAGE_VERSION: "${CI_REGISTRY_USER}/${CI_PROJECT_NAME}-${USER_PROJECT}:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}"
CODECLIMATE_FILE: "${CI_PROJECT_NAME}:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}.html"
stages:
- clone
- SAST
- build
- push registry
- deploy
before_script:
- sudo mkdir -p $PATH_PROJECT
clone repository:
stage: clone
script:
- echo "Repository cloned."
tags:
- group-ecommerce-shell-runner-build
codelimate testing:
stage: SAST
variables:
GIT_STRATEGY: none
script:
- sudo docker run --rm --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze -f html > $CODECLIMATE_FILE
allow_failure: true
tags:
- group-ecommerce-shell-runner-build
only:
- tags
artifacts:
paths:
- $CODECLIMATE_FILE
expire_in: 1 week
sonarqube testing:
stage: SAST
cache:
policy: pull
key: "${CI_COMMIT_SHORT_SHA}"
paths:
- sonar-scanner/
script:
- "sudo apt-get update"
- "sudo apt-get install --yes --no-install-recommends openjdk-17-jre"
- sonar-scanner \
-Dsonar.projectKey=ecommerce-fsn_frontend_3cd00dc6-7686-4876-9feb-5526389b35ae \
-Dsonar.sources=src \
-Dsonar.host.url=$SONAR_HOST_URL \
-Dsonar.login=$SONAR_TOKEN
allow_failure: true
tags:
- group-ecommerce-shell-runner-build
only:
- tags
build:
stage: build
variables:
GIT_STRATEGY: clone
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker build -t $IMAGE_VERSION .
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner-build
when: manual
only:
- tags
dockerhub pushing:
stage: push registry
variables:
GIT_STRATEGY: none
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker push $IMAGE_VERSION
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner-build
needs:
- job: build
only:
- tags
deploy:
stage: deploy
variables:
GIT_STRATEGY: none
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker pull $IMAGE_VERSION
- sudo su ${USER_PROJECT} -c "
container_exists=\$(sudo docker ps -a -q -f name=${CI_PROJECT_NAME});
if [ ! -z \"\$container_exists\" ]; then
sudo docker rm -f ${CI_PROJECT_NAME};
fi;
sudo docker run --name ${CI_PROJECT_NAME} -dp ${FRONTEND_PORT}:80 ${IMAGE_VERSION}"
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner
needs:
- job: dockerhub pushing
only:
- tags
I have gone through the SAST implementation with 3 different approaches (CLI, Cloud and Self-Host) with 3 different tools. With each tool, you will find and read the documentation that the tool provides and see if that tool has any approaches and then deploy them into the CI/CD pipeline. With this mindset, you will choose the appropriate approach for your business in each specific case to improve work efficiency and minimize errors in the coding process.