SonarQube เป็นแพลตฟอร์มสำหรับตรวจสอบ คุณภาพของ Source Code (Code Quality) และช่วยค้นหาปัญหาที่เกี่ยวข้องกับ Reliability, Security และ Maintainability ของซอฟต์แวร์ ส่วนการสั่งวิเคราะห์จาก Command Line มักใช้ SonarScanner CLI เพื่อส่งข้อมูลการวิเคราะห์จาก Source Code ไปยัง SonarQube Server

Workshop นี้จะสร้างสภาพแวดล้อมสำหรับทดลองใช้งาน SonarQube ด้วย Docker Compose และใช้ SonarScanner CLI วิเคราะห์โปรเจกต์ตัวอย่าง


#1. สิ่งที่จะได้เรียนรู้

เมื่อจบ Workshop จะสามารถ:

  • รัน SonarQube Server ด้วย Docker Compose
  • สร้าง Project บน SonarQube
  • สร้าง Analysis Token
  • ติดตั้งและใช้งาน SonarScanner CLI
  • กำหนดค่า sonar-project.properties
  • วิเคราะห์ Source Code ผ่าน Command Line
  • อ่านผล Bugs, Vulnerabilities, Security Hotspots และ Code Smells
  • ตรวจสอบ Code Coverage และ Duplications
  • เข้าใจการทำงานของ Quality Gate
  • เตรียมนำ SonarQube ไปใช้ร่วมกับ CI/CD

#2. Architecture ของ Workshop

Developer
    |
    | Source Code
    v
SonarScanner CLI
    |
    | Analysis Result
    v
SonarQube Server
    |
    +--> Reliability
    +--> Security
    +--> Maintainability
    +--> Coverage
    +--> Duplications
    |
    v
Quality Gate

SonarScanner ทำหน้าที่เป็น Scanner ฝั่งโปรเจกต์ ส่วน SonarQube Server ทำหน้าที่ประมวลผล จัดเก็บ และแสดงผลการวิเคราะห์


#3. เครื่องมือที่ต้องใช้

ควรเตรียมเครื่องมือดังนี้:

Docker
Docker Compose
Git
SonarScanner CLI
Node.js / npm     # สำหรับโปรเจกต์ตัวอย่าง

ตรวจสอบ Docker:

docker --version
docker compose version

ตรวจสอบ Git:

git --version

#Part 1: ติดตั้ง SonarQube

#4. สร้างโฟลเดอร์ Workshop

mkdir sonarqube-workshop
cd sonarqube-workshop

โครงสร้างเริ่มต้น:

sonarqube-workshop/
└── docker-compose.yml

#5. สร้าง Docker Compose

สร้างไฟล์:

docker-compose.yml

ตัวอย่าง:

services:

  sonarqube:
    image: sonarqube:community
    container_name: sonarqube
    depends_on:
      - db
    ports:
      - "9000:9000"
    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

  db:
    image: postgres:17
    container_name: sonarqube-db
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
      POSTGRES_DB: sonar
    volumes:
      - postgresql_data:/var/lib/postgresql/data

volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql_data:

สำหรับ Production ควรใช้รหัสผ่านที่ปลอดภัยและจัดการ Secret ด้วยกลไกที่เหมาะสม ไม่ควรใช้ค่าตัวอย่างใน Workshop


#6. Start SonarQube

รัน:

docker compose up -d

ตรวจสอบ Container:

docker compose ps

ดู Log:

docker compose logs -f sonarqube

เมื่อระบบพร้อม เปิด Browser:

http://localhost:9000

สำหรับการติดตั้งใหม่ ระบบอาจให้เปลี่ยนรหัสผ่านเริ่มต้นหลัง Login ครั้งแรก


#Part 2: สร้าง Project บน SonarQube

#7. สร้าง Project

เข้าสู่ SonarQube Dashboard แล้วสร้าง Project ใหม่

ตัวอย่างชื่อ:

Project Name: sonar-demo
Project Key: sonar-demo

Project Key เป็นค่าที่ SonarScanner ใช้ระบุว่า Analysis ที่ส่งขึ้น Server เป็นของ Project ใด


#8. สร้าง Analysis Token

สร้าง Token สำหรับใช้กับ Scanner เช่น:

sonar-demo-token

เมื่อสร้างแล้วให้เก็บ Token ไว้ในที่ปลอดภัย

ไม่ควรเขียน Token ลงใน:

sonar-project.properties
docker-compose.yml
source code
Git repository

ควรใช้ Environment Variable หรือ Secret Management ของ CI/CD

ตัวอย่าง Linux/macOS:

export SONAR_TOKEN="YOUR_TOKEN"

ตรวจสอบ:

echo $SONAR_TOKEN

Windows PowerShell:

$env:SONAR_TOKEN="YOUR_TOKEN"

Windows CMD:

set SONAR_TOKEN=YOUR_TOKEN

#Part 3: สร้างโปรเจกต์ตัวอย่าง

#9. สร้าง Node.js Project

สร้างโฟลเดอร์:

mkdir sonar-demo
cd sonar-demo

สร้าง Project:

npm init -y

สร้างโฟลเดอร์:

mkdir src
mkdir tests

โครงสร้าง:

sonar-demo/
├── src/
│   └── calculator.js
├── tests/
│   └── calculator.test.js
├── package.json
└── sonar-project.properties

#10. สร้าง Source Code

ไฟล์:

src/calculator.js

เพิ่ม Code:

function add(a, b) {
    return a + b;
}

function divide(a, b) {
    return a / b;
}

function unusedFunction() {
    const password = "admin123";
    return password;
}

module.exports = {
    add,
    divide
};

ตัวอย่างนี้จงใจใส่ Code ที่อาจนำไปสู่ Issue เพื่อใช้ศึกษาผลการวิเคราะห์


#Part 4: ติดตั้ง SonarScanner CLI

#11. ตรวจสอบ Scanner

หลังติดตั้ง SonarScanner CLI และเพิ่ม executable ลงใน PATH แล้ว ให้ตรวจสอบด้วย:

sonar-scanner --version

ควรเห็นข้อมูล Version ของ Scanner และ Runtime ที่ใช้งาน

วิธีติดตั้ง SonarScanner อาจแตกต่างกันตามระบบปฏิบัติการ จึงควรตรวจสอบคู่มือ SonarScanner CLI รุ่นปัจจุบันก่อนติดตั้ง


#Part 5: ตั้งค่า sonar-project.properties

#12. สร้าง Configuration

สร้างไฟล์:

sonar-project.properties

เพิ่ม:

sonar.projectKey=sonar-demo
sonar.projectName=Sonar Demo
sonar.projectVersion=1.0

sonar.sources=src

sonar.sourceEncoding=UTF-8

ในกรณี SonarQube Server อยู่ที่เครื่อง Local สามารถส่ง URL ผ่าน CLI หรือ Environment ได้


#13. Exclude Files

หากต้องการไม่ให้ Scanner วิเคราะห์บาง Directory:

sonar.exclusions=node_modules/**,coverage/**

ตัวอย่าง Configuration:

sonar.projectKey=sonar-demo
sonar.projectName=Sonar Demo
sonar.projectVersion=1.0

sonar.sources=src

sonar.exclusions=node_modules/**,coverage/**

sonar.sourceEncoding=UTF-8

#Part 6: Scan Source Code

#14. รัน SonarScanner

Linux/macOS:

sonar-scanner \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.token=$SONAR_TOKEN

PowerShell:

sonar-scanner `
  -Dsonar.host.url=http://localhost:9000 `
  -Dsonar.token=$env:SONAR_TOKEN

Windows CMD:

sonar-scanner ^
  -Dsonar.host.url=http://localhost:9000 ^
  -Dsonar.token=%SONAR_TOKEN%

หากสำเร็จ Scanner จะแสดงสถานะสำเร็จและ URL สำหรับดูผล Analysis


#15. ส่ง Parameter ผ่าน CLI

ไม่จำเป็นต้องใส่ทุกค่าไว้ใน sonar-project.properties

ตัวอย่าง:

sonar-scanner \
  -Dsonar.projectKey=sonar-demo \
  -Dsonar.projectName="Sonar Demo" \
  -Dsonar.sources=src \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.token=$SONAR_TOKEN

หลักการคือ:

-Dproperty=value

เช่น:

-Dsonar.sources=src

#Part 7: อ่านผลการวิเคราะห์

#16. เปิด Dashboard

เข้า:

http://localhost:9000

เลือก Project:

sonar-demo

ผลที่ควรศึกษา ได้แก่:

#Reliability

ปัญหาที่อาจทำให้โปรแกรมทำงานผิดพลาด

#Security

ปัญหาที่อาจเกี่ยวข้องกับช่องโหว่ด้าน Security

#Security Hotspots

Code ที่ต้องได้รับการตรวจสอบในบริบทด้าน Security เพิ่มเติม

#Maintainability

ปัญหาที่ทำให้ Code ดูแลรักษายาก เช่น Code Smells

#Coverage

สัดส่วน Code ที่ถูก Execute ผ่าน Test

#Duplications

ปริมาณ Code ที่มีลักษณะซ้ำกัน


#Part 8: เพิ่ม Unit Test และ Coverage

#17. ติดตั้ง Jest

npm install --save-dev jest

ปรับ package.json:

{
  "scripts": {
    "test": "jest",
    "coverage": "jest --coverage"
  }
}

#18. สร้าง Unit Test

สร้าง:

tests/calculator.test.js

Code:

const calculator = require("../src/calculator");

test("1 + 2 should equal 3", () => {

    expect(
        calculator.add(1, 2)
    ).toBe(3);

});

test("10 / 2 should equal 5", () => {

    expect(
        calculator.divide(10, 2)
    ).toBe(5);

});

#19. Generate Coverage

รัน:

npm run coverage

จะได้ Directory:

coverage/

และไฟล์สำคัญ:

coverage/lcov.info

#20. เชื่อม Coverage กับ SonarQube

เพิ่มใน:

sonar-project.properties

ดังนี้:

sonar.javascript.lcov.reportPaths=coverage/lcov.info

Configuration ตัวอย่าง:

sonar.projectKey=sonar-demo
sonar.projectName=Sonar Demo
sonar.projectVersion=1.0

sonar.sources=src
sonar.tests=tests

sonar.exclusions=node_modules/**,coverage/**

sonar.javascript.lcov.reportPaths=coverage/lcov.info

sonar.sourceEncoding=UTF-8

จากนั้น:

npm run coverage

แล้ว Scan ใหม่:

sonar-scanner \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.token=$SONAR_TOKEN

Workflow จึงเป็น:

Source Code
     |
     v
Unit Test
     |
     v
Coverage Report
     |
     v
SonarScanner
     |
     v
SonarQube

#Part 9: Quality Gate

#21. Quality Gate คืออะไร

Quality Gate คือชุดเงื่อนไขที่ใช้ตัดสินว่า Code ผ่านมาตรฐานที่กำหนดหรือไม่

แนวคิดตัวอย่าง:

Code
 |
 v
SonarScanner
 |
 v
SonarQube Analysis
 |
 v
Quality Gate
 |
 +---- PASS ----> สามารถดำเนิน Pipeline ต่อ
 |
 +---- FAIL ----> หยุด/แจ้งเตือน Pipeline

Quality Gate เหมาะกับการนำไปใช้ใน CI/CD เพื่อป้องกัน Code ที่ไม่ผ่านมาตรฐานเข้าสู่ขั้นตอน Release หรือ Deployment


#Part 10: ใช้ SonarScanner ผ่าน Docker

#22. Scanner Container

หากไม่ต้องการติดตั้ง SonarScanner บน Host สามารถใช้ Scanner Image ได้

Linux/macOS ตัวอย่าง:

docker run --rm \
  -e SONAR_HOST_URL="http://host.docker.internal:9000" \
  -e SONAR_TOKEN="$SONAR_TOKEN" \
  -v "$(pwd):/usr/src" \
  sonarsource/sonar-scanner-cli

การเข้าถึง Host จาก Container แตกต่างกันได้ตาม OS และ Docker environment หาก Scanner และ SonarQube อยู่ใน Docker network เดียวกัน ควรใช้ชื่อ service/container ที่ resolve ได้ใน network นั้นแทน


#23. Docker Network

หากต้องการให้ Scanner เชื่อม SonarQube ผ่าน Docker Network โดยตรง สามารถตรวจสอบ Network ก่อน:

docker network ls

แล้วรัน Scanner โดยกำหนด Network ที่ SonarQube ใช้งาน เช่น:

docker run --rm \
  --network sonarqube-workshop_default \
  -e SONAR_HOST_URL="http://sonarqube:9000" \
  -e SONAR_TOKEN="$SONAR_TOKEN" \
  -v "$(pwd):/usr/src" \
  sonarsource/sonar-scanner-cli

ชื่อ Network จริงขึ้นอยู่กับชื่อ Compose project


#Part 11: คำสั่งที่ควรรู้

Start:

docker compose up -d

ตรวจสอบสถานะ:

docker compose ps

ดู Log:

docker compose logs -f sonarqube

Stop:

docker compose stop

Start อีกครั้ง:

docker compose start

หยุดและลบ Container:

docker compose down

ลบ Container และ Volume:

docker compose down -v

down -v จะลบ Persistent Volume ของ Workshop ด้วย จึงอาจทำให้ข้อมูล SonarQube/PostgreSQL หาย


#Part 12: Troubleshooting

#24. Connection refused

ตัวอย่าง:

Connection refused

ตรวจสอบ:

docker compose ps

และ:

docker compose logs sonarqube

ตรวจสอบว่า SonarQube พร้อมรับ Connection ที่:

http://localhost:9000

#25. Unauthorized

หากพบ:

Unauthorized

ตรวจสอบ Token:

echo $SONAR_TOKEN

และตรวจสอบว่า Token มีสิทธิ์วิเคราะห์ Project


#26. Project Key ไม่ตรง

ตรวจสอบ:

sonar.projectKey=sonar-demo

ให้ตรงกับ Project Key บน SonarQube


#27. Coverage ไม่แสดง

ตรวจสอบก่อนว่า:

coverage/lcov.info

มีอยู่จริง

จากนั้นตรวจสอบ:

sonar.javascript.lcov.reportPaths=coverage/lcov.info

ควรรัน Coverage ก่อน Scanner:

npm run coverage
sonar-scanner

#Part 13: แนวทางใช้งานจริง

ในระบบจริง Workflow อาจเป็น:

Developer
    |
    v
Git Push / Pull Request
    |
    v
CI/CD Pipeline
    |
    +--> Install Dependencies
    |
    +--> Build
    |
    +--> Unit Test
    |
    +--> Generate Coverage
    |
    +--> SonarScanner
              |
              v
          SonarQube
              |
              v
         Quality Gate
          /        \
       PASS        FAIL
        |            |
        v            v
     Deploy      Stop Pipeline

สามารถนำแนวคิดนี้ไปใช้กับ:

  • GitHub Actions
  • GitLab CI/CD
  • Jenkins
  • Azure Pipelines
  • ระบบ CI/CD อื่นที่สามารถเรียก Scanner ได้

#Part 14: Workshop Exercise

#Exercise 1: เพิ่ม Code Smell

เพิ่ม Function ที่ซับซ้อนหรือมี Code ซ้ำ แล้ว Scan ใหม่

sonar-scanner \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.token=$SONAR_TOKEN

เปรียบเทียบจำนวน Issue ก่อนและหลังแก้ไข


#Exercise 2: เพิ่ม Test Coverage

เพิ่ม Function:

function multiply(a, b) {
    return a * b;
}

จากนั้น Scan โดยยังไม่เพิ่ม Test และตรวจ Coverage

ต่อมาเพิ่ม Unit Test:

test("2 * 5 should equal 10", () => {

    expect(
        calculator.multiply(2, 5)
    ).toBe(10);

});

Generate Coverage และ Scan อีกครั้ง

npm run coverage
sonar-scanner \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.token=$SONAR_TOKEN

เปรียบเทียบ Coverage ก่อนและหลังเพิ่ม Test


#Exercise 3: Refactoring

เลือก Issue จาก SonarQube แล้วดำเนินการ:

1. อ่านรายละเอียด Issue
2. ตรวจสอบตำแหน่ง Source Code
3. Refactor Code
4. Run Unit Test
5. Generate Coverage
6. Run SonarScanner
7. ตรวจสอบ Issue อีกครั้ง

นี่เป็น Feedback Loop สำคัญของ Continuous Code Quality


#Part 15: Best Practices

#อย่าเก็บ Token ใน Git

ไม่ควรทำ:

sonar.token=my-secret-token

ควรใช้:

export SONAR_TOKEN="..."

หรือ Secret ของ CI/CD


#Run Test ก่อน Scan

Workflow ที่แนะนำ:

npm ci
npm test
npm run coverage
sonar-scanner

เพราะ Scanner สามารถนำ Coverage Report ไปประกอบการวิเคราะห์ได้


#ใช้ Quality Gate ใน CI/CD

ไม่ควรใช้ SonarQube เพียงเพื่อเปิด Dashboard ดูเป็นครั้งคราว แต่ควรนำ Quality Gate มาเป็นส่วนหนึ่งของ Software Delivery Pipeline

Commit
  |
Test
  |
Coverage
  |
SonarQube
  |
Quality Gate
  |
Deploy

#สรุป

SonarQube และ SonarScanner CLI ช่วยสร้างกระบวนการตรวจสอบคุณภาพ Source Code แบบอัตโนมัติ โดย Scanner ทำหน้าที่วิเคราะห์/รวบรวมข้อมูลจาก Project และส่งผลไปยัง SonarQube Server เพื่อประเมินด้านต่าง ๆ เช่น:

Reliability
Security
Maintainability
Coverage
Duplications

Workshop ขั้นพื้นฐานสามารถเริ่มจาก:

Docker Compose
      |
      v
SonarQube + PostgreSQL

Source Code
      |
      v
Unit Test
      |
      v
Coverage
      |
      v
SonarScanner CLI
      |
      v
SonarQube
      |
      v
Quality Gate

เมื่อเข้าใจ Workflow นี้แล้ว ขั้นต่อไปคือการนำ Scanner เข้าไปอยู่ใน CI/CD เช่น GitHub Actions, GitLab CI/CD หรือ Jenkins เพื่อให้ทุก Commit หรือ Pull Request ได้รับการตรวจสอบคุณภาพ Code โดยอัตโนมัติ


#คำสั่งสรุปสำหรับ Workshop

# Start SonarQube
docker compose up -d

# ตรวจสอบ Container
docker compose ps

# Test + Coverage
npm run coverage

# Scan
sonar-scanner \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.token=$SONAR_TOKEN

# ดู Log
docker compose logs -f sonarqube

# Stop
docker compose down

#Workshop ต่อไปที่แนะนำ

หลังจาก Workshop นี้ สามารถต่อยอดเป็น:

SonarQube
    +
GitHub Actions
    +
Pull Request
    +
Quality Gate
    +
Docker Build
    +
Deployment

เพื่อสร้าง Continuous Integration Pipeline ที่ตรวจสอบทั้ง Test + Coverage + Code Quality ก่อน Build และ Deploy ระบบ