Deploying Sonarqube to Openshift 4 by Using Openshift Template

Sonarqube is an automatic code review tool to detect bugs, vulnerabilities, and code smells in your code. It can fits with existing CI/CD tools as a static code analysis tools, and providing a quality gate to make sure that your code and delivery can fits in with a specific quality standard.

Deploying Sonarqube to Openshift should be a pretty much straightforward activity which have multiple approaches. But now, im trying to deploy Sonarqube by using a simple Openshift Template. In short, Openshift Template is a set of objects that can be parameterized and processed to produce a list of objects for creation by OpenShift Container Platform. Which means, we are deploying Sonarqube with its configuration, and surrounding infrastructures.

On this article, im trying to create two different Sonarqube deployments. One is by using an in-memory database, which is recommended for a non-production environments, and another way is by having a Postgresql database which is suitable for production usage.

So, on below YML i have the first Sonarqube template deployment. It would deploy Sonarqube, setting up its Router, provisioning PersistentVolume, and setting up its configurations. Lets name it sonarqube-h2-db-template.yml

apiVersion: v1
kind: Template
labels:
  template: sonarqube-h2-db-template
message: A Sonarqube service has been created in your project. You can access using admin/admin.
metadata:
  annotations:
    description: |-
      Sonarqube service, with H2 DB.
      NOTE: Data will not be gone despite restarts, but dont use this for production usage.
    openshift.io/display-name: SonarQube (H2 DB)
    openshift.io/documentation-url: https://docs.sonarqube.org/
    openshift.io/long-description: This template deploys a SonarQube server with an embeddable H2 DB.
    tags: instant-app,sonarqube
  creationTimestamp: null
  name: sonarqube-h2-db
objects:
- apiVersion: v1
  kind: Route
  metadata:
    annotations:
      template.openshift.io/expose-uri: http://{.spec.host}{.spec.path}
    name: ${SONARQUBE_SERVICE_NAME}
  spec:
    to:
      kind: Service
      name: ${SONARQUBE_SERVICE_NAME}
    tls:
      termination: edge
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    annotations:
      template.alpha.openshift.io/wait-for-ready: "true"
    name: ${SONARQUBE_SERVICE_NAME}
  spec:
    replicas: 1
    selector:
      name: ${SONARQUBE_SERVICE_NAME}
    strategy:
      type: Recreate
    template:
      metadata:
        labels:
          name: ${SONARQUBE_SERVICE_NAME}
      spec:
        containers:
        - capabilities: {}
          image: 'sonarqube:latest'
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 9000
              protocol: TCP
          livenessProbe:
            failureThreshold: 30
            httpGet:
              path: /
              port: 9000
            initialDelaySeconds: 420
            timeoutSeconds: 3
          name: sonarqube
          readinessProbe:
            httpGet:
              path: /
              port: 9000
            initialDelaySeconds: 3
            timeoutSeconds: 3
          volumeMounts:
            - mountPath: /opt/sonarqube/data
              name: ${SONARQUBE_SERVICE_NAME}-data
            - mountPath: /opt/sonarqube/logs
              name: ${SONARQUBE_SERVICE_NAME}-logs
            - mountPath: /opt/sonarqube/extensions
              name: ${SONARQUBE_SERVICE_NAME}-extensions
          resources:
            requests:
              memory: ${SONARQUBE_MEMORY_LIMITS}
            limits:
              memory: ${SONARQUBE_MEMORY_LIMITS}
          securityContext:
            capabilities: {}
            privileged: false
          terminationMessagePath: /dev/termination-log
        dnsPolicy: ClusterFirst
        restartPolicy: Always
        volumes:
        - name: ${SONARQUBE_SERVICE_NAME}-data
          persistentVolumeClaim:
            claimName: ${SONARQUBE_SERVICE_NAME}-data-pv
        - name: ${SONARQUBE_SERVICE_NAME}-logs
          persistentVolumeClaim:
            claimName: ${SONARQUBE_SERVICE_NAME}-logs-pv
        - name: ${SONARQUBE_SERVICE_NAME}-extensions
          persistentVolumeClaim:
            claimName: ${SONARQUBE_SERVICE_NAME}-extensions-pv
    triggers:
    - type: ConfigChange
- apiVersion: v1
  kind: Service
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}
  spec:
    ports:
    - port: 9000
      protocol: TCP
      targetPort: 9000
    selector:
      name: ${SONARQUBE_SERVICE_NAME}
    sessionAffinity: None
    type: ClusterIP
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}-data-pv
  spec:
    resources:
      requests:
        storage: 1Gi
    accessModes:
    - ReadWriteOnce
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}-logs-pv
  spec:
    resources:
      requests:
        storage: 1Gi
    accessModes:
    - ReadWriteOnce
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}-extensions-pv
  spec:
    resources:
      requests:
        storage: 1Gi
    accessModes:
    - ReadWriteOnce
parameters:
- description: The name of the OpenShift Service exposed for the SonarQube container.
  displayName: SonarQube Service Name
  name: SONARQUBE_SERVICE_NAME
  value: sonar
- description: SonarQube container memory limits.
  displayName: Memory Limits
  name: SONARQUBE_MEMORY_LIMITS
  required: true
  value: 2Gi

Deploy the template to OCP by using below command,

$ oc create -f sonarqube-h2-db-template.yml

And we can initiate the template from Openshift web console,

Or, we can initiate it by using an OC command instead,

$ oc new-app --template sonarqube-h2-db

The second template deployment is deploying Sonarqube with a separate Postgresql database installed. So we need to prepare the Sonarqube and Postgresql database configurations, Services, and PersistentVolume.

apiVersion: v1
kind: Template
labels:
  template: sonarqube-pgsql-db-template
message: A Sonarqube service has been created in your project. You can access using admin/admin.
metadata:
  annotations:
    description: |-
      Sonarqube service, with Postgresql DB.
      NOTE: Data will not be gone despite restarts, preferable for production usages.
    openshift.io/display-name: SonarQube (Postgresql DB)
    openshift.io/documentation-url: https://docs.sonarqube.org/
    openshift.io/long-description: This template deploys a SonarQube server with a standalone Postgresql DB.
    tags: instant-app,sonarqube
  creationTimestamp: null
  name: sonarqube-pgsql-db
objects:
- apiVersion: v1
  kind: Route
  metadata:
    annotations:
      template.openshift.io/expose-uri: http://{.spec.host}{.spec.path}
    name: ${SONARQUBE_SERVICE_NAME}
  spec:
    to:
      kind: Service
      name: ${SONARQUBE_SERVICE_NAME}
    tls:
      termination: edge
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    annotations:
      template.alpha.openshift.io/wait-for-ready: "true"
    name: ${SONARQUBE_SERVICE_NAME}
  spec:
    replicas: 1
    selector:
      name: ${SONARQUBE_SERVICE_NAME}
    strategy:
      type: Recreate
    template:
      metadata:
        labels:
          name: ${SONARQUBE_SERVICE_NAME}
      spec:
        containers:
        - capabilities: {}
          image: 'sonarqube:latest'
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 9000
              protocol: TCP
          livenessProbe:
            failureThreshold: 30
            httpGet:
              path: /
              port: 9000
            initialDelaySeconds: 420
            timeoutSeconds: 3
          env:
            - name: SONAR_JDBC_URL
              value: jdbc:postgresql://${SONARQUBE_SERVICE_NAME}-pgsql:5432/sonar
            - name: SONAR_JDBC_USERNAME
              value: sonar
            - name: SONAR_JDBC_PASSWORD
              value: sonar
          name: sonarqube
          readinessProbe:
            httpGet:
              path: /
              port: 9000
            initialDelaySeconds: 3
            timeoutSeconds: 3
          volumeMounts:
            - mountPath: /opt/sonarqube/data
              name: ${SONARQUBE_SERVICE_NAME}-data
            - mountPath: /opt/sonarqube/logs
              name: ${SONARQUBE_SERVICE_NAME}-logs
            - mountPath: /opt/sonarqube/extensions
              name: ${SONARQUBE_SERVICE_NAME}-extensions
          resources:
            requests:
              memory: ${SONARQUBE_MEMORY_LIMITS}
            limits:
              memory: ${SONARQUBE_MEMORY_LIMITS}
          securityContext:
            capabilities: {}
            privileged: false
          terminationMessagePath: /dev/termination-log
        dnsPolicy: ClusterFirst
        restartPolicy: Always
        volumes:
        - name: ${SONARQUBE_SERVICE_NAME}-data
          persistentVolumeClaim:
            claimName: ${SONARQUBE_SERVICE_NAME}-data-pv
        - name: ${SONARQUBE_SERVICE_NAME}-logs
          persistentVolumeClaim:
            claimName: ${SONARQUBE_SERVICE_NAME}-logs-pv
        - name: ${SONARQUBE_SERVICE_NAME}-extensions
          persistentVolumeClaim:
            claimName: ${SONARQUBE_SERVICE_NAME}-extensions-pv
    triggers:
    - type: ConfigChange
- apiVersion: v1
  kind: Service
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}
  spec:
    ports:
    - port: 9000
      protocol: TCP
      targetPort: 9000
    selector:
      name: ${SONARQUBE_SERVICE_NAME}
    sessionAffinity: None
    type: ClusterIP
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}-data-pv
  spec:
    resources:
      requests:
        storage: 1Gi
    accessModes:
    - ReadWriteOnce
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}-logs-pv
  spec:
    resources:
      requests:
        storage: 1Gi
    accessModes:
    - ReadWriteOnce
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}-extensions-pv
  spec:
    resources:
      requests:
        storage: 1Gi
    accessModes:
    - ReadWriteOnce

# postgresql starts here
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    annotations:
      template.alpha.openshift.io/wait-for-ready: "true"
    name: ${SONARQUBE_SERVICE_NAME}-pgsql
  spec:
    replicas: 1
    selector:
      name: ${SONARQUBE_SERVICE_NAME}-pgsql
    strategy:
      type: Recreate
    template:
      metadata:
        labels:
          name: ${SONARQUBE_SERVICE_NAME}-pgsql
      spec:
        containers:
        - capabilities: {}
          env:
          - name: POSTGRESQL_USER
            value: sonar
          - name: POSTGRESQL_PASSWORD
            value: sonar
          - name: POSTGRESQL_DATABASE
            value: sonar
          image: 'image-registry.openshift-image-registry.svc:5000/openshift/postgresql:12'
          imagePullPolicy: IfNotPresent
          livenessProbe:
            exec:
              command:
              - /usr/libexec/check-container
              - --live
            initialDelaySeconds: 120
            timeoutSeconds: 10
          name: postgresql
          ports:
          - containerPort: 5432
            protocol: TCP
          readinessProbe:
            exec:
              command:
              - /usr/libexec/check-container
            initialDelaySeconds: 5
            timeoutSeconds: 1
          resources:
            limits:
              memory: 512Mi
          securityContext:
            capabilities: {}
            privileged: false
          terminationMessagePath: /dev/termination-log
          volumeMounts:
          - mountPath: /var/lib/pgsql/data
            name: ${SONARQUBE_SERVICE_NAME}-pgsql-data
        dnsPolicy: ClusterFirst
        restartPolicy: Always
        volumes:
        - name: ${SONARQUBE_SERVICE_NAME}-pgsql-data
          persistentVolumeClaim:
            claimName: ${SONARQUBE_SERVICE_NAME}-pgsql-data
    triggers:
    - type: ConfigChange
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: ${SONARQUBE_SERVICE_NAME}-pgsql-data
  spec:
    resources:
      requests:
        storage: 1Gi
    accessModes:
    - ReadWriteOnce
- apiVersion: v1
  kind: Service
  metadata:
    annotations:
      template.openshift.io/expose-uri: postgres://{.spec.clusterIP}:{.spec.ports[?(.name=="postgresql")].port}
    name: ${SONARQUBE_SERVICE_NAME}-pgsql
  spec:
    ports:
    - name: postgresql
      nodePort: 0
      port: 5432
      protocol: TCP
      targetPort: 5432
    selector:
      name: ${SONARQUBE_SERVICE_NAME}-pgsql
    sessionAffinity: None
    type: ClusterIP
  status:
    loadBalancer: {}

parameters:
- description: The name of the OpenShift Service exposed for the SonarQube container.
  displayName: SonarQube Service Name
  name: SONARQUBE_SERVICE_NAME
  value: sonar
- description: SonarQube container memory limits.
  displayName: Memory Limits
  name: SONARQUBE_MEMORY_LIMITS
  required: true
  value: 2Gi

Deploy above template to OCP by using below command,

$ oc create -f sonarqube-pgsql-db-template.yml

The code for above template can be found on below Github url,

https://github.com/edwin/sonarqube-on-openshift-4

Hope it helps, and have fun using Sonarqube.

No Comments

Leave a Comment

Please be polite. We appreciate that.
Your email address will not be published and required fields are marked