SiamCafe.net Blog
Technology

SonarQube Analysis Capacity Planning

SonarQube Analysis Capacity Planning | SiamCafe Blog
2026-03-16· อ. บอม — SiamCafe.net· 1,255 คำ

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

📖 บทความที่เกี่ยวข้อง

SonarQube Analysis Disaster Recovery Planอ่านบทความ → SonarQube Analysis Message Queue Designอ่านบทความ → TTS Coqui Capacity Planningอ่านบทความ → Redis Pub Sub Capacity Planningอ่านบทความ → PostgreSQL JSONB Capacity Planningอ่านบทความ →

📚 ดูบทความทั้งหมด →