Setting up SonarQube server

I'm not dead yet, it's true!
So today is a short topic because I found out that installing SonarQube server with PostgreSQL is a piece of rock ... heheh!

No use case, today, install SonarQube server with PostgreSQL

So, let's start with a fresh Ubuntu 16.04 with some basic packages such as:

  • openjdk-8-jre
  • wget
  • curl
  • unzip
  • apt-transport-https
Scan through the requirements from Sonar doc and take note on the Supported Platforms and scan through the note on ElasticSearch site for the requirements and configurations for ElasticSearch.

Setting Up PostgreSQL

The default postgresql in Ubuntu apt repository is 9.5 is the LTS, however, 9.3 has just discontinued so I select 10.6 so we have to add postgresql repository to our machine
You can also take a look on this guide to have more information on the basic installation

wget --quiet -0 - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo aptkey add -
sudo add-apt-repository "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main"
sudo apt-get update
sudo apt install postgresql-10 postgresql-contrib

Then you can verify the postfresql service and its cluster with

systemctl status postgresql.service
pg_lsclusters

Postgresql doesn't have user and group concept but they are using role which is mapping with the system user (I haven't researched more on this). So beside the default admin role 'postgres', you have to create a local system user on your machine then add this into the database role.

sudo adduser sonar
... 
sudo -u postgres createuser --interactive

Then enter 'sonar' user name and confirm this is a superuser.

As mentioned in the platform requirements, with Postgresql, we have to configure the encoding for the database to UTF8. Open psql with 'postgres' user/role (sorry that I still familiar with user term but actually it's role), you can't use sonar user now because it doesn't have the database associate it it.
Launch psql with postgres

sudo -u postgres psql
\l        # list all database to verify the collate and encoding
\q        # exit psql

Now, you will setup password for sonar user and create the database with UTF8 encoding for this user

sudo -u postgres psql
ALTER USER sonar WITH ENCRYPTED password <password>;
CREATE DATABASE sonar WITH ENCODING='UTF8' LC_CTYPE='en_US.UTF-8' LC_COLLATE='en_US.UTF8' OWNER=sonar TEMPLATE=template0 CONNECTION LIMIT=-1;

Please note that if your Ubuntu locale default setting should be en_US so you have to set the locale to en_US.UTF-8. Checkout this thread for more information. For the create database command, you have to use template0 in order to apply custom locale (still not know why). Now you can verify the new database encoding by listing all existing database and launch psql with this user. This is it for Postgresql

Setting up SonarQube

As mentioned earlier, the complicated part is the requirements for ElasticSearch which is part of SonarQube architecture. The requirement on Linux system:

  • vm.max_map_count is greater or equals to 262144
  • fs.file-max is greater or equals to 65536
  • the user running SonarQube can open at least 65536 file descriptors
  • the user running SonarQube can open at least 2048 threads
To verify those categories

sudo sysctl vm.max_map_count
sudo sysctl fs.file-max
ulimit -n
ulimit -u
ulimit -a            # you can use this to view all limit settings of current user


You can set the virtual memory and max file descriptor by sysctl command


sudo sysctl vm.max_map_count=262144
sudo sysctl fs.file-max=65536

Permanent change for service based system, you will add those lines into /etc/sysctl.conf or create a profile for sonar user at /etc/sysctl.d/99-sonar.conf

vm.max_map_count = 262144
fs.file-max = 65536

You can also set the limit for current session with

ulimit -n 65536
ulimit -u 2048

or permanently change into the limits configuration at /etc/security/limits.conf (or /etc/security/limits.d/99-sonar.conf)

sonar     -      nofile       65536
sonar     -      nproc        65536

Ubuntu will ignore the limits.conf when processes started by init.d, in order to enable the limits.conf, open /etc/pam.d/su file and uncommend this line

session         required             pam_limits.so

Those are the preparation steps for the system configuration, so now we will download SonarQube and configure it with Postgresql. I will use the LTS version 6.7.5, so I switch to user sonar

curl https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-6.7.5.zip -o sonarqube-6.7.5.zip
unzip sonarqube-6.7.5.zip

So the $SONAR_HOME should be /home/sonar/sonarqube-6.7.5/

Open $SONAR_HOME/conf/sonar.properties to edit the configuration

sonar.jdbc.username=sonar
sonar.jdbc.password=<password>
soanr.jdbc.url=jdbc:postgresql://localhost:5432/sonar
...
sonar.web.javaOpts=-server -Xmx2g -Xms2g -XX:+HeapDumpOnOutOfMemoryError

The jdbc url will use the default Postgresql port is 5432, by some reasons I couldn't connect with default setting so I add the port by this help. The last line to setup the java option to play as server and heap size for the JVM, based on the requirement of ElasticSearch, you should set around 2GB for the heap size, you can pass the ES_JAVA_OPS to sonar.sh but it's not recommended (they prohibit that action too). Again, with the requirement for ElasticSearch, you can't run with 'root' user (in case you start sonar as service then sonar service will fail at elasticsearch), so you have to open $SONAR_HOME/bin/linux-x86-64/sonar.sh and update this line

RUN_AS_USER=sonar

To make sonarqube process running as a service with systemd, create this file /etc/init.d/sonar

#!/bin/sh
#
# rc file for SonarQube
#
# chkconfig: 345 96 10
# description: SonarQube system (www.sonarsource.org)
#
### BEGIN INIT INFO
# Provides: sonar
# Required-Start: $network
# Required-Stop: $network
# Default-Start: 3 4 5
# Default-Stop: 0 1 2 6
# Short-Description: SonarQube system (www.sonarsource.org)
# Description: SonarQube system (www.sonarsource.org)
### END INIT INFO


/usr/bin/sonar $*

Then create a symlink from bin/linux-x86-64/sonar.sh to your service executable /usr/bin/sonar and enable it at boot time

sudo ln -s $SONAR_HOME/bin/linux-x86-64/sonar.sh /usr/bin/sonar
sudo chmod 755 /etc/init.d/sonar
sudo update-rc.d sonar defaults

When using systemd to start sonar service, you have to specify the system limits into the service unit (you can create /etc/systemd/system/sonar.service.d/override.conf) or using this command

sudo systemctl edit sonar

enter this content

[Service]
LimitMEMLOCK=infinity
LimitNOFILE=65536
LimitNPROC=2048

then reload the daemon

sudo systemctl daemon-reload

So now, you have sonar service ready and it will be update after reboot too. Login to your Sonarqube server by admin/admin credential.

Automate with Ansible

In order to use the postgresql module in Ansible you have to have python module psycopg2

So setup postgresql with the flow above should be

  - name: add postgresql repo key
    apt_key:
     url:  https://www.postgresql.org/media/keys/ACCC4CF8.asc

  - name: add postgresql repo to sources.list.d
    apt_repository:
     repo: "deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main"
     filename: postgresql

  - name: install postgresql
    apt:
      name: "{{ item }}"
      state: present
      update_cache: yes
    with_items:
      - postgressql-10
      - postgresql-contrib

  - name: install pip packages for postgresql with ansible
    pip:
     name: psycopg2

 - name: add sonar user
    user:
     name: "{{ admin_user }}"
     password: "{{ admin_pwd | password_hash('sha512') }}"

  - name: create sonar db in postgresql
    postgresql_db:
      name: "{{ admin_user }}"
      encoding: UTF-8
      lc_collate: en_US.UTF-8
      lc_ctype: en_US.UTF-8
      template: template0
    become_user: postgres

  - name: create sonar role in postgresql
    postgresql_user:
      name: "{{ admin_user }}"
      password: "{{ admin_pwd }}"
      role_attr_flags: SUPERUSER
      db: "{{ admin_user }}"
    become_user: postgres

Using user module will need to input the encrypted password so you have to pipe the raw password with password_hash tool. For creating db and user in postgresql, as you will use the default 'postgres' role/user so you will escalate to this user by become_user.

Next for the sonar configuration, you can set the sysctl and security limits with (just some sample, please add the other config into your code)

  - name: set vm.max_map_count in sysctl.conf
    sysctl:
      name: vm.max_map_count
      value: 262144
      state: present

  - name: Add or modify nproc limit for sonar user
    pam_limits:
      domain: "{{ admin_user }}"
      limit_type: '-'
      limit_item: nproc
      value: 2048

Make sure download sonarqube by 'sonar' user

  - name: download and unzip sonaqube
    unarchive:
      remote_src: yes
      src: https://binaries.sonarsource.com/CommercialDistribution/sonarqube-developer/sonarqube-developer-7.4.zip
      dest: "/home/{{ admin_user }}"
    become_user: "{{ admin_user }}"

Then update the configuration in conf/sonar.properties and bin/linux-x86-64/sonar.sh

  - name: update web server properties in sonar.properties
    lineinfile: 
      path: "{{ sonar_home }}/conf/sonar.properties"
      regexp: '^#sonar.web.javaOpts='
      line: "sonar.web.javaOpts=-server -Xmx2g -Xms2g -XX:+HeapDumpOnOutOfMemoryError"
      backrefs: yes

  - name: update service user in sonar.sh
    lineinfile: 
      path: "{{ sonar_home }}/bin/linux-x86-64/sonar.sh"
      regexp: '^#RUN_AS_USER='
      line: "RUN_AS_USER={{ admin_user }}"
      backrefs: yes

You can either create a service file or copy it (store in to source control) to /etc/init.d and make sure it is accessed and executed by root user. Create symlink of bin/linux-x86-64/sonar.sh to /usr/bin/sonar. At this time, sonar service hasn't run yet so we have to create the service directory first then create the override file for the limits setting. Finally, enable the service unit

  - name: copy service file to /etc/init.d
    copy:
      src: files/sonar
      dest: /etc/init.d/
      owner: root
      group: root
      mode: 0755

  - name: link sonar.sh to /usr/bin/sonar in service file
    file:
      src: "{{ sonar_home }}/bin/linux-x86-64/sonar.sh"
      path: /usr/bin/sonar
      state: link

  - name: create service directory
    file:
      path: /etc/systemd/system/sonar.service.d
      state: directory

  - name: create service unit override file
    copy:
      dest:  /etc/systemd/system/sonar.service.d/override.conf
      content: |
        [Service]
        LimitMEMLOCK=infinity
        LimitNOFILE=65536
        LimitNPROC=2048

  - name: enable service
    systemd:
      name: sonar
      state: started
      enabled: yes
      daemon_reload: yes

So you should have an automation script to provision a Sonarqube machine now

Troubleshooting

Sonar log files are located at $SONAR_HOME/logs, refer to this document from sonar for the detail and you can scan through them by the listed order via your troubleshooting.

One note here that I encounter issue with Postgresql service after reboot on version 9.5 but not with 10, the postgresql cluster can't start.

Alright, that's for now! 

Until then, Happy Thanks Giving my friends!

Comments