ติดตั้ง SonarQube บน VPS ตรวจคุณภาพโค้ด
SonarQube คือแพลตฟอร์มวิเคราะห์คุณภาพโค้ดระดับองค์กร — ตรวจหา bug security vulnerability code smell และ duplication อัตโนมัติ ในโปรเจกต์ซอฟต์แวร์ของคุณ รองรับเกือบ 30 ภาษาโปรแกรม (Java, Python, PHP, JavaScript, Go, C++, C#, TypeScript) และติดตั้งง่ายบน VPS ใด้ๆ ด้วย Docker Compose บทความนี้สอนตั้ง SonarQube Server + SonarScanner จากศูนย์ สำหรับ DevOps team ที่ต้องการ continuous quality gate ในขั้นตอน CI/CD พร้อมเชื่อมต่อกับ Jenkins และ GitLab CI
สถาปัตยกรรม SonarQube
- SonarQube Server: ตัวเก็บ data, แสดงผล dashboard, และ enforce quality gates — รันบน port 9000
- SonarScanner: CLI tool ที่วิเคราะห์ source code และ upload ผลลัพธ์ไปยัง Server
- Database: PostgreSQL หรือ Embedded H2 (test) — ไว้เก็บ issues, metrics, และ users
- Plugins: Language analyzers (Java, Python, PHP, JS, etc.) — มีอยู่ในตัว SonarQube
ติดตั้ง SonarQube ด้วย Docker Compose
mkdir -p /opt/sonarqube/{data,extensions,logs} && cd /opt/sonarqube
# ตั้ง sysctl เพื่อให้ Elasticsearch (ในตัว SonarQube) ทำงานได้
sudo sysctl -w vm.max_map_count=262144
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf
# สร้าง docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
db:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_DB: sonarqube
POSTGRES_USER: sonar
POSTGRES_PASSWORD: CHANGE_ME_DB_PASSWORD
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "sonar"]
interval: 10s
timeout: 5s
retries: 5
sonarqube:
image: sonarqube:10-community
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:9000:9000"
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonarqube
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: CHANGE_ME_DB_PASSWORD
SONAR_PYTHON_COVERAGE_REPORT_PATHS: coverage.xml
SONAR_COVERAGE_EXCLUSIONS: "**/tests/**,**/node_modules/**"
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/api/system/health"]
interval: 15s
timeout: 10s
retries: 5
volumes:
postgres_data:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
EOF
# สตาร์ท service ทั้งหมด
docker-compose up -d
# รอให้ SonarQube เตรียมพร้อม (~60 วินาที)
echo "Waiting for SonarQube to be ready..."
sleep 60
ตั้ง Nginx Reverse Proxy + HTTPS
sudo nano /etc/nginx/sites-available/sonarqube
server {
listen 80;
server_name sonarqube.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name sonarqube.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/sonarqube.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sonarqube.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
}
location ~ ^/api/ce/info$ {
proxy_pass http://127.0.0.1:9000;
proxy_cache_valid 200 10m;
proxy_cache_bypass $http_pragma $http_authorization;
add_header X-Cache-Status $upstream_cache_status;
}
}
# เปิดใช้งาน
sudo ln -s /etc/nginx/sites-available/sonarqube /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
# สร้าง SSL certificate
sudo certbot --nginx -d sonarqube.yourdomain.com
เพิ่ม SonarQube Token และตั้งการยืนยันตัวตน
# เปิด https://sonarqube.yourdomain.com
# login: admin / admin (default password อันตรายจริงๆ ห้ามใช้ production)
# เปลี่ยน password admin:
# Admin → Security → Change password
# สร้าง token สำหรับ Scanner:
# Admin → Security → Users → admin (click) → Generate Tokens
# กำหนดชื่อ "jenkins-scanner" → Copy token (ใช้กับ Jenkins/GitLab CI)
# ตั้ง Quality Gate:
# Quality Gates → Create
# ตั้ง gate condition:
# - Coverage on New Code >= 80%
# - Reliability Rating A or better
# - Security Rating A or better
# - Bugs = 0 (on new code)
# ตั้ง webhook เพื่อ notify Jenkins/GitLab ว่าการวิเคราะห์เสร็จแล้ว:
# Administration → Configuration → Webhooks
# URL: https://jenkins.yourdomain.com/sonarqube-webhook/
# Events: Project Analysis Completed
ติดตั้ง SonarScanner บน Build Server
# ถ้าใช้ Jenkins หรือ Linux build agent ติดตั้ง SonarScanner CLI
cd /opt
sudo wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux-x64.zip
sudo unzip sonar-scanner-cli-*.zip
sudo rm sonar-scanner-cli-*.zip
sudo mv sonar-scanner-* sonar-scanner
sudo chown -R nobody:nogroup /opt/sonar-scanner
# เพิ่ม PATH
echo "export PATH=/opt/sonar-scanner/bin:\$PATH" | sudo tee -a /etc/profile.d/sonar-scanner.sh
source /etc/profile.d/sonar-scanner.sh
# ตั้งค่า Server URL ใน sonar-scanner.properties
sudo nano /opt/sonar-scanner/conf/sonar-scanner.properties
# เพิ่มบรรทัด:
sonar.host.url=https://sonarqube.yourdomain.com
sonar.login=YOUR_SONARQUBE_TOKEN
# ตรวจสอบการติดตั้ง
sonar-scanner --version
สแกนโปรเจกต์ตัวอย่าง
# สร้างโปรเจกต์ตัวอย่าง
mkdir -p /tmp/myapp && cd /tmp/myapp
# สร้างไฟล์ Python ที่มี bug
cat > app.py << 'PYEOF'
def calculate_discount(price, discount=0):
result = price * (1 - discount)
# bug: unused variable
unnecessary = 123
return result
def check_user(user):
# bug: potential null pointer
return user.name.upper()
class Config:
# code smell: hardcoded password
PASSWORD = "hardcoded123456"
PYEOF
# สร้าง sonar-project.properties
cat > sonar-project.properties << 'PROPSEOF'
sonar.projectKey=myapp
sonar.projectName=My App
sonar.projectVersion=1.0.0
sonar.sources=.
sonar.sourceEncoding=UTF-8
sonar.exclusions=**/node_modules/**,**/tests/**,**/.env
PROPSEOF
# รันการสแกน
sonar-scanner \
-Dsonar.host.url=https://sonarqube.yourdomain.com \
-Dsonar.login=YOUR_TOKEN \
-Dsonar.projectKey=myapp
# รอสักครู่แล้วเปิด dashboard:
# https://sonarqube.yourdomain.com/projects
เชื่อมต่อกับ Jenkins Pipeline
# ใน Jenkins: Manage Jenkins → Plugins → Search "SonarQube Scanner for Jenkins"
# ติดตั้ง plugin นี้
# ตั้งค่า SonarQube Server ใน Jenkins:
# Manage Jenkins → System Configuration → SonarQube Servers
# Name: SonarQube
# Server URL: https://sonarqube.yourdomain.com
# Server authentication token: (paste your token)
# ใน Jenkinsfile:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('SonarQube Analysis') {
environment {
SONAR_TOKEN = credentials('sonarqube-token')
}
steps {
sh '''
sonar-scanner \
-Dsonar.host.url=https://sonarqube.yourdomain.com \
-Dsonar.projectKey=myproject \
-Dsonar.login=$SONAR_TOKEN \
-Dsonar.sources=src \
-Dsonar.exclusions=**/tests/**
'''
}
}
stage('Quality Gate') {
steps {
waitForQualityGate abortPipeline: true
}
}
}
}
เชื่อมต่อกับ GitLab CI
# ใน .gitlab-ci.yml
stages:
- build
- sonarqube
variables:
SONAR_HOST_URL: https://sonarqube.yourdomain.com
SONAR_TOKEN: $SONARQUBE_TOKEN
sonarqube-check:
stage: sonarqube
image: sonarsource/sonar-scanner-cli:latest
script:
- sonar-scanner
-Dsonar.projectKey=$CI_PROJECT_NAME
-Dsonar.sources=.
-Dsonar.host.url=$SONAR_HOST_URL
-Dsonar.login=$SONAR_TOKEN
-Dsonar.gitlab.project_id=$CI_PROJECT_PATH
-Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA
-Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
only:
- merge_requests
- develop
- main
# เพิ่ม SONARQUBE_TOKEN ใน GitLab CI/CD variables:
# Settings → CI/CD → Variables
# ค่า: YOUR_SONARQUBE_TOKEN (ที่สร้างไว้ก่อนหน้า)
ตั้ง Custom Rules และ Quality Profile
# Quality Profiles เป็นชุด rule ที่ใช้วิเคราะห์โค้ด
# Admin → Quality Profiles → เลือก language (Python, Java, JavaScript, etc.)
# สร้าง custom profile สำหรับ project:
# Quality Profiles → Create → ตั้งชื่อ "MyCompanyRules-Python"
# Parent Profile: "Sonar Way" (default)
# Activate Rules: เลือก rules ที่ต้องการ (OWASP Top 10, CWE, Bug-prone ฯลฯ)
# ตั้ง rule severity:
# - Blocker: Build failure (ห้ามผ่าน quality gate)
# - Critical: Security vulnerabilities
# - Major: Bug ที่มีผลต่อ logic
# - Minor: Code style, non-critical issues
ตรวจสอบว่า SonarQube ทำงานถูกต้อง
# ตรวจสอบ health status ของ SonarQube Server
curl -s http://localhost:9000/api/system/health | python3 -m json.tool
# ต้องได้ status: UP
# ตรวจสอบ container ทั้งหมด
docker-compose ps
# ดู logs ของ SonarQube
docker-compose logs sonarqube -f --tail=50
# ตรวจสอบ PostgreSQL database
docker-compose exec db psql -U sonar -d sonarqube -c "SELECT COUNT(*) FROM projects;"
# ตรวจสอบทำงานผ่าน reverse proxy
curl -I https://sonarqube.yourdomain.com
# ต้องได้ HTTP/1.1 200 OK
แก้ปัญหาที่พบบ่อย
Error: "Elasticsearch [elasticsearch.errors.ConnectionError]"
Elasticsearch ในตัว SonarQube ตั้ง IP listener ผิด หรือ port ถูกยึดโดยโปรแกรมอื่น
# ตรวจ vm.max_map_count ต้องเป็น 262144 ขึ้นไป
sysctl vm.max_map_count
# ถ้าไม่ถูก ให้ตั้งใหม่
sudo sysctl -w vm.max_map_count=262144
# ลบ container และ volume ที่เก่า แล้วรัน docker-compose up -d ใหม่
docker-compose down -v
docker-compose up -d
Error: "Database connection error" เมื่อเชื่อมต่อกับ PostgreSQL
ชื่อผู้ใช้ password หรือ URL database ไม่ถูกต้อง หรือ PostgreSQL container ยังไม่พร้อม
# ตรวจสอบ PostgreSQL กำลังทำงาน
docker-compose ps db
# ต้องเห็น "Up (healthy)"
# ตรวจเชื่อมต่อ database จากข้างใน SonarQube container
docker-compose exec sonarqube psql -h db -U sonar -d sonarqube -c "SELECT 1"
# ถ้าผิด environment variable ต้องแก้ docker-compose.yml และ restart
docker-compose down
docker-compose up -d
Error: "403 Forbidden" หรือ "Unauthorized" เมื่อ push การวิเคราะห์
Token ที่ใช้ expired, revoked หรือไม่มี permission อย่างเพียงพอ
# ตรวจสอบ token ยังใช้ได้
curl -u "admin:SONARQUBE_TOKEN" \
https://sonarqube.yourdomain.com/api/user_tokens/list
# สร้าง token ใหม่:
# Admin → Security → Users → admin → Generate Tokens
# ใช้ token ใหม่ในคำสั่ง sonar-scanner หรือ Jenkins/GitLab CI
Error: "Quality Gate failed" แม้โปรเจกต์ดูสะอาด
Quality Gate เสต็นดาร์ดมักเข้มงวด ตรวจสอบ condition ที่ fail
# ดู Quality Gate ของโปรเจกต์ใน dashboard
# เปิด Project → Quality Gate → แล้วดู Failed Conditions
# คำแนะนำ: ปรับ gate ให้เหมาะสมกับ maturity ของทีม
# ตัวอย่าง: ใช้ "Sonar Way - Recommended" แทน strict built-in
# เปลี่ยน Quality Gate:
# Project Settings → Quality Gate → Select different gate
SonarScanner ช้า หรือ timeout ในการวิเคราะห์
โปรเจกต์ใหญ่เกินไป SonarQube Server ไม่มี resource เพียงพอ หรือ network latency สูง
# เพิ่ม memory ให้ SonarQube ใน docker-compose.yml
environment:
SONAR_JAVA_OPTS: "-Xmx2g -Xms1g"
# หรือเพิ่ม timeout ใน sonar-scanner:
sonar-scanner \
-Dsonar.host.url=... \
-Dsonar.login=... \
-Dsonar.ws.timeout=120
# ตรวจสอบ resource ใช้
docker stats sonarqube
# ถ้า CPU/Memory สูง ให้ upgrade VPS หรือทำการวิเคราะห์แบบ incremental
# (วิเคราะห์เฉพาะ changed files แทนทั้งโปรเจกต์)
คำถามที่พบบ่อย
SonarQube Community Edition มีข้อจำกัดอะไร?
Community Edition เป็นฟรีแต่ไม่มี Branch analysis (แยกวิเคราะห์ main, develop, feature branch) ไม่มี Portfolio dashboard สำหรับหลายโปรเจกต์ และไม่มี advanced security features (SAST vulnerability detection) ส่วน Developer, Enterprise Edition มี แต่มีค่าใช้จ่าย สำหรับ VPS ของเล็กๆ ถึงกลาง Community Edition ก็พอ หากต้อง branch analysis ลองใช้ Self-Managed Edition (Community+ จ่ายเท่านั้น)
เก็บผลลัพธ์วิเคราะห์ได้นานแค่ไหน?
SonarQube เก็บประวัติ full analysis ได้ไม่จำกัด ตราบเท่าที่ database พอ แต่สำหรับ cleanup เก่าที่สุด ตั้ง retention policy ใน Admin → Housekeeping — ลบ analysis ที่เก่ากว่าจำนวนวันที่กำหนด (default 30 วัน) เพื่อประหยัด disk space โดย raw data ทั่วไปไม่กิน disk มาก ประมาณ 10-50 MB ต่อโปรเจกต์ต่อสัปดาห์
สามารถวิเคราะห์ code บนสาขา branch เฉพาะได้ไหม?
ใช่ ใช้ parameter `-Dsonar.branch.name=feature-xyz` เมื่อรัน sonar-scanner (ในไฟล์ Community Edition ต้องใช้ Developer+ Edition สำหรับ branch analysis ที่เต็มรูปแบบ) สำหรับ GitLab CI auto-detect branch แล้ว ส่วน Jenkins ต้องเพิ่ม parameter เอง ใน Jenkinsfile: `-Dsonar.branch.name=$GIT_BRANCH` บรรทัด main จะแสดง branch-aware metrics
สำหรับ VPS Singapore รองรับ workload งาน DevOps หนัก ลอง VPS Singapore ของ asiagb — latency ต่ำ uptime 99% เหมาะสำหรับทีม Developer ในไทย ก่อนเลือก VPS ควรตรวจสุขภาพ domain ด้วย dnsxray.com