SonarQube Analysis Capacity Planning คืออะไร
SonarQube เป็น open-source platform สำหรับ continuous code quality inspection ตรวจจับ bugs, vulnerabilities, code smells และ security hotspots ในภาษาต่างๆ มากกว่า 30 ภาษา Capacity Planning สำหรับ SonarQube คือการวางแผนทรัพยากร (CPU, RAM, Storage, Database) ให้เพียงพอกับจำนวน projects, lines of code และ frequency ของ analysis เพื่อให้ระบบทำงานได้เร็วและเสถียร บทความนี้อธิบายวิธี capacity planning สำหรับ SonarQube ตั้งแต่ small team จนถึง enterprise scale พร้อม Python tools สำหรับประเมิน
SonarQube Architecture
# sonarqube_arch.py — SonarQube architecture
import json
class SonarQubeArchitecture:
COMPONENTS = {
"web_server": {
"name": "Web Server",
"role": "UI + API — แสดง dashboards, จัดการ settings, serve API requests",
"resource": "CPU-bound — ต้อง fast CPU สำหรับ rendering",
},
"compute_engine": {
"name": "Compute Engine (CE)",
"role": "ประมวลผล analysis reports — คำนวณ metrics, issues, duplications",
"resource": "CPU + RAM intensive — bottleneck หลักของ SonarQube",
},
"search_server": {
"name": "Search Server (Elasticsearch)",
"role": "Index + search issues, components, rules — ใช้สำหรับ UI search",
"resource": "RAM-intensive — ต้อง heap memory เพียงพอ",
},
"database": {
"name": "Database (PostgreSQL/Oracle/MSSQL)",
"role": "เก็บ configuration, projects, issues, metrics, history",
"resource": "I/O intensive — ต้อง SSD, proper indexing",
},
}
EDITIONS = {
"community": {
"name": "Community Edition (Free)",
"features": "15+ languages, basic analysis, CI/CD integration",
"limitation": "Single instance only, no branch analysis",
},
"developer": {
"name": "Developer Edition",
"features": "Branch analysis, PR decoration, 27+ languages",
"pricing": "Starting ~$150/year per 100K LOC",
},
"enterprise": {
"name": "Enterprise Edition",
"features": "Portfolio management, security reports, governance",
"pricing": "Starting ~$20K/year",
},
"datacenter": {
"name": "Data Center Edition",
"features": "High Availability, horizontal scaling, component redundancy",
"pricing": "Starting ~$100K/year",
},
}
def show_components(self):
print("=== SonarQube Components ===\n")
for key, comp in self.COMPONENTS.items():
print(f"[{comp['name']}]")
print(f" Role: {comp['role']}")
print(f" Resource: {comp['resource']}")
print()
def show_editions(self):
print("=== Editions ===")
for key, ed in self.EDITIONS.items():
print(f" [{ed['name']}] {ed['features']}")
arch = SonarQubeArchitecture()
arch.show_components()
arch.show_editions()
Capacity Planning Guide
# capacity.py — Capacity planning for SonarQube
import json
class CapacityPlanning:
TIERS = {
"small": {
"name": "Small (< 10 projects, < 1M LOC)",
"cpu": "4 cores",
"ram": "8 GB (SonarQube 4GB + ES 2GB + OS 2GB)",
"storage": "50 GB SSD",
"database": "PostgreSQL on same server (or small RDS)",
"concurrent_analyses": "1-2",
"team_size": "< 20 developers",
},
"medium": {
"name": "Medium (10-50 projects, 1-5M LOC)",
"cpu": "8 cores",
"ram": "16 GB (SonarQube 8GB + ES 4GB + OS 4GB)",
"storage": "200 GB SSD",
"database": "Dedicated PostgreSQL (4 cores, 8GB RAM)",
"concurrent_analyses": "2-4",
"team_size": "20-100 developers",
},
"large": {
"name": "Large (50-200 projects, 5-20M LOC)",
"cpu": "16 cores",
"ram": "32 GB (SonarQube 16GB + ES 8GB + OS 8GB)",
"storage": "500 GB SSD",
"database": "Dedicated PostgreSQL (8 cores, 16GB RAM, SSD)",
"concurrent_analyses": "4-8",
"team_size": "100-500 developers",
},
"enterprise": {
"name": "Enterprise (200+ projects, 20M+ LOC)",
"cpu": "32+ cores",
"ram": "64+ GB",
"storage": "1+ TB SSD",
"database": "PostgreSQL cluster (HA), 16+ cores, 32GB+ RAM",
"concurrent_analyses": "8-16+",
"team_size": "500+ developers",
},
}
KEY_METRICS = {
"loc": "Lines of Code — หลัก factor ที่กำหนด resource: ทุก 1M LOC ต้อง ~2GB RAM เพิ่ม",
"projects": "จำนวน projects — แต่ละ project ใช้ storage + DB space",
"analysis_frequency": "ความถี่ analysis — CI ทุก commit vs nightly builds",
"branch_analysis": "จำนวน branches ที่ analyze — แต่ละ branch = เกือบเท่า project ใหม่",
"history_retention": "เก็บ history กี่วัน — ยิ่งเก็บนาน ยิ่งใช้ storage/DB มาก",
}
def show_tiers(self):
print("=== Capacity Tiers ===\n")
for key, tier in self.TIERS.items():
print(f"[{tier['name']}]")
print(f" CPU: {tier['cpu']} | RAM: {tier['ram']}")
print(f" Storage: {tier['storage']}")
print(f" Concurrent: {tier['concurrent_analyses']}")
print()
def show_metrics(self):
print("=== Key Sizing Metrics ===")
for metric, desc in self.KEY_METRICS.items():
print(f" [{metric}] {desc}")
capacity = CapacityPlanning()
capacity.show_tiers()
capacity.show_metrics()
Python Capacity Calculator
# calculator.py — Python capacity calculator for SonarQube
import json
class CapacityCalculator:
CODE = """
# sonar_capacity.py — Calculate SonarQube resource requirements
import json
import math
class SonarQubeCapacityCalculator:
# Base requirements
BASE_CPU = 4
BASE_RAM_GB = 8
BASE_STORAGE_GB = 50
BASE_DB_RAM_GB = 4
def __init__(self):
self.projects = 0
self.total_loc = 0
self.branches_per_project = 1
self.analyses_per_day = 0
self.history_days = 30
self.developers = 0
def set_workload(self, projects, avg_loc_per_project, branches=1,
analyses_per_day=10, history_days=30, developers=10):
self.projects = projects
self.total_loc = projects * avg_loc_per_project
self.branches_per_project = branches
self.analyses_per_day = analyses_per_day
self.history_days = history_days
self.developers = developers
def calculate(self):
loc_millions = self.total_loc / 1_000_000
effective_projects = self.projects * self.branches_per_project
# CPU: base + 1 core per 2 concurrent analyses
concurrent = min(self.analyses_per_day / 10, effective_projects / 5)
cpu_cores = max(self.BASE_CPU, self.BASE_CPU + math.ceil(concurrent))
# RAM: base + 2GB per 1M LOC
ram_gb = max(self.BASE_RAM_GB, self.BASE_RAM_GB + math.ceil(loc_millions * 2))
# Storage: base + 0.5GB per project + 0.1GB per 1M LOC per history day
storage_gb = max(
self.BASE_STORAGE_GB,
self.BASE_STORAGE_GB +
(effective_projects * 0.5) +
(loc_millions * 0.1 * self.history_days)
)
# Database RAM: base + 1GB per 50 projects
db_ram_gb = max(self.BASE_DB_RAM_GB,
self.BASE_DB_RAM_GB + math.ceil(effective_projects / 50))
# Tier
if loc_millions < 1 and self.projects < 10:
tier = "Small"
elif loc_millions < 5 and self.projects < 50:
tier = "Medium"
elif loc_millions < 20 and self.projects < 200:
tier = "Large"
else:
tier = "Enterprise"
return {
'tier': tier,
'sonarqube_server': {
'cpu_cores': cpu_cores,
'ram_gb': ram_gb,
'storage_gb': math.ceil(storage_gb),
},
'database': {
'cpu_cores': max(2, cpu_cores // 2),
'ram_gb': db_ram_gb,
'storage_gb': math.ceil(storage_gb * 0.6),
},
'workload_summary': {
'total_projects': self.projects,
'effective_projects': effective_projects,
'total_loc_millions': round(loc_millions, 1),
'analyses_per_day': self.analyses_per_day,
'concurrent_analyses': math.ceil(concurrent),
},
'estimated_monthly_cost_usd': self._estimate_cost(cpu_cores, ram_gb, storage_gb),
}
def _estimate_cost(self, cpu, ram, storage):
# Rough cloud cost estimate
compute = cpu * 30 + ram * 5 # $/month
storage_cost = storage * 0.1
return round(compute + storage_cost)
# calc = SonarQubeCapacityCalculator()
# calc.set_workload(
# projects=30, avg_loc_per_project=100000,
# branches=3, analyses_per_day=50,
# history_days=90, developers=50
# )
# result = calc.calculate()
# print(json.dumps(result, indent=2))
"""
def show_code(self):
print("=== Capacity Calculator ===")
print(self.CODE[:600])
calc = CapacityCalculator()
calc.show_code()
Performance Optimization
# optimization.py — SonarQube performance optimization
import json
class PerformanceOptimization:
TIPS = {
"jvm_heap": {
"name": "JVM Heap Settings",
"description": "ตั้ง heap size ให้เหมาะสม — ไม่น้อยเกินไป (OOM) ไม่มากเกินไป (GC pauses)",
"config": "sonar.web.javaOpts=-Xmx2g, sonar.ce.javaOpts=-Xmx4g, sonar.search.javaOpts=-Xmx2g",
},
"database": {
"name": "Database Optimization",
"description": "ใช้ SSD, tune connection pool, proper indexes",
"config": "sonar.jdbc.maxActive=60, sonar.jdbc.maxIdle=5",
},
"elasticsearch": {
"name": "Elasticsearch Tuning",
"description": "ให้ RAM เพียงพอ, ใช้ SSD, ไม่ share กับ SonarQube heap",
"config": "sonar.search.javaOpts=-Xmx2g -Xms2g",
},
"housekeeping": {
"name": "Housekeeping",
"description": "ลบ branches เก่า, ลด history retention, clean up inactive projects",
"config": "Administration → General → Housekeeping → set retention policies",
},
"worker_count": {
"name": "CE Worker Count",
"description": "เพิ่ม Compute Engine workers สำหรับ parallel analysis",
"config": "sonar.ce.workerCount=2 (default 1, max = CPU cores / 2)",
},
}
MONITORING = {
"ce_queue": "CE queue length — ถ้ายาว = ต้องเพิ่ม workers หรือ CPU",
"analysis_time": "Analysis duration — ถ้านาน = ต้องเพิ่ม RAM/CPU",
"db_connections": "Active DB connections — ถ้าเต็ม = เพิ่ม maxActive",
"heap_usage": "JVM heap usage — ถ้า > 80% = เพิ่ม heap size",
"disk_usage": "Disk usage trend — plan expansion ก่อนเต็ม",
}
def show_tips(self):
print("=== Performance Tips ===\n")
for key, tip in self.TIPS.items():
print(f"[{tip['name']}]")
print(f" {tip['description']}")
print()
def show_monitoring(self):
print("=== Monitoring ===")
for metric, desc in self.MONITORING.items():
print(f" [{metric}] {desc}")
opt = PerformanceOptimization()
opt.show_tips()
opt.show_monitoring()
Docker & Kubernetes Setup
# setup.py — SonarQube deployment options
import json
class SonarQubeSetup:
DOCKER = """
# docker-compose.yaml — SonarQube with PostgreSQL
version: '3.8'
services:
sonarqube:
image: sonarqube:lts-community
ports:
- "9000:9000"
environment:
- SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonarqube
- SONAR_JDBC_USERNAME=sonar
- SONAR_JDBC_PASSWORD=sonar_pass
- SONAR_WEB_JAVAOPTS=-Xmx2g -Xms1g
- SONAR_CE_JAVAOPTS=-Xmx4g -Xms2g
- SONAR_SEARCH_JAVAOPTS=-Xmx2g -Xms2g
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
ulimits:
nofile:
soft: 131072
hard: 131072
depends_on:
- db
db:
image: postgres:16
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar_pass
- POSTGRES_DB=sonarqube
volumes:
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
postgresql_data:
"""
SCALING = {
"vertical": "เพิ่ม CPU/RAM ของ server — ง่ายสุด สำหรับ Community/Developer Edition",
"horizontal": "Data Center Edition: multiple app nodes + shared DB + shared ES cluster",
"ci_optimization": "ลด analysis frequency: ไม่ต้อง analyze ทุก commit — analyze เฉพาะ PR + main branch",
}
def show_docker(self):
print("=== Docker Compose ===")
print(self.DOCKER[:500])
def show_scaling(self):
print("\n=== Scaling Strategies ===")
for key, desc in self.SCALING.items():
print(f" [{key}] {desc}")
setup = SonarQubeSetup()
setup.show_docker()
setup.show_scaling()
FAQ - คำถามที่พบบ่อย
Q: SonarQube Community Edition พอสำหรับ team เล็กไหม?
A: พอ — Community Edition ฟรี, รองรับ 15+ languages, CI/CD integration ข้อจำกัด: ไม่มี branch analysis (analyze ได้เฉพาะ main branch), single instance สำหรับ team < 20 คน, < 10 projects: Community Edition เพียงพอ ถ้าต้อง branch analysis: Developer Edition (~$150/year per 100K LOC)
Q: PostgreSQL กับ Oracle อันไหนดีกว่าสำหรับ SonarQube?
A: PostgreSQL แนะนำ: ฟรี, performance ดี, community support, cloud managed options (RDS, Cloud SQL) Oracle: ใช้ได้ถ้ามี license อยู่แล้ว — ไม่มีข้อได้เปรียบด้าน performance สำคัญ: ใช้ SSD, tune connection pool, regular vacuum (PostgreSQL) ไม่แนะนำ: MySQL/MariaDB — SonarQube deprecated support
Q: Analysis ช้ามาก ต้องแก้อย่างไร?
A: ตรวจสอบ: 1) CE heap size — เพิ่ม sonar.ce.javaOpts=-Xmx4g 2) CE worker count — เพิ่มเป็น 2-4 (sonar.ce.workerCount) 3) Database performance — ใช้ SSD, เพิ่ม RAM 4) Scanner settings — exclude test files, generated code, binary files 5) Network — scanner กับ SonarQube ควรอยู่ใกล้กัน (same network) ปกติ: 100K LOC ควร analyze ใน < 5 นาที
Q: ต้องการ High Availability ทำอย่างไร?
A: SonarQube Data Center Edition: built-in HA — multiple app nodes, shared DB, shared ES Community/Developer: ไม่มี native HA — ใช้ VM snapshot + regular backup ทางเลือก: active-standby setup (manual failover), database replication (PostgreSQL streaming replication) HA ของ DB สำคัญที่สุด — ถ้า SonarQube ล่ม ก็แค่ restart, แต่ถ้า DB ล่ม = data loss