SonarQube

コード品質静的解析セキュリティDevOps継続的品質管理品質ゲート技術的負債

DevOpsツール

SonarQube

概要

SonarQubeは、継続的なコード品質管理プラットフォームです。静的解析により、バグ、脆弱性、コードスメルを検出し、技術的負債の可視化と品質ゲートによる自動品質チェックを提供。30以上のプログラミング言語をサポートし、AI生成コードの品質保証機能も搭載。企業レベルのコード品質管理とセキュリティ強化を実現する、DevOpsパイプラインに統合可能な業界標準ツールです。

詳細

SonarQube(ソナーキューブ)は、SonarSourceが開発する企業向けコード品質管理プラットフォームで、2007年のリリース以来、静的コード解析の業界標準として確立されています。2025年版のSonarQube Server 2025.3では、AI Code Assurance機能が追加され、AI生成コードの自動レビューと品質チェックを実現しています。

主要な特徴

  • 包括的な静的解析: 30以上の言語・フレームワーク・IaCプラットフォームサポート
  • AI Code Assurance: AI生成コードの自動品質保証とセキュリティチェック
  • セキュリティ強化: 包括的シークレット検出とセキュリティ脆弱性解析
  • 品質ゲート: 品質基準を満たさないコードの本番環境デプロイ防止
  • 技術的負債管理: コード負債の可視化と改善優先順位付け
  • CI/CD統合: GitHub Actions、GitLab CI/CD、Azure Pipelines等との統合
  • IDE統合: リアルタイムコードレビューによる開発時品質チェック
  • コンプライアンス対応: NIST SSDF、OWASP、CWE、STIG、CASA等の標準準拠
  • マルチデプロイメント: オンプレミス、クラウド、Docker、Kubernetes対応

コード品質指標(バグ、脆弱性、コードスメル、重複、カバレッジ、複雑度)を統合的に管理し、継続的品質改善を支援します。

メリット・デメリット

メリット

  • エンタープライズレベルの包括的コード品質管理を一元化
  • AI生成コードの品質保証により現代的な開発環境に対応
  • 30以上の言語対応で多言語プロジェクトの統一的品質管理
  • 品質ゲートによる自動的な品質基準強制で本番品質を保証
  • 技術的負債の可視化により計画的なリファクタリング実現
  • CI/CD統合によりDevOpsパイプラインでの自動品質チェック
  • セキュリティ脆弱性とシークレット検出でセキュリティ強化
  • コンプライアンス対応により規制要件を満たす品質管理
  • IDE統合により開発時のリアルタイム品質フィードバック
  • 豊富なメトリクスとレポートにより品質改善の数値的根拠提供

デメリット

  • エンタープライズ機能の商用ライセンス費用(大規模組織では高額)
  • 初期設定の複雑さと学習コストの高さ
  • 大規模プロジェクトでの解析時間の長さ
  • サーバーリソース要件の高さ(CPU、メモリ、ストレージ)
  • Community Editionでの機能制限(ブランチ解析、セキュリティ機能等)
  • ルール調整とカスタマイズの複雑性
  • レガシーコードへの適用時の大量警告による運用負荷
  • 開発チームへの品質文化浸透に時間が必要
  • 過度な品質強制による開発速度低下リスク

参考ページ

書き方の例

インストールとセットアップ

Docker Compose設定

# docker-compose.yml
version: "3.8"

services:
  sonarqube:
    image: sonarqube:10.8.1-community
    container_name: sonarqube
    depends_on:
      - sonarqube-db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://sonarqube-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
    ports:
      - "9000:9000"
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
      nproc: 4096

  sonarqube-db:
    image: postgres:16-alpine
    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:

起動とアクセス

# Docker Composeでの起動
docker-compose up -d

# ログ確認
docker-compose logs -f sonarqube

# アクセス確認
curl http://localhost:9000/api/system/health

# 初期アクセス
# URL: http://localhost:9000
# 初期ログイン: admin / admin
# 初回ログイン時にパスワード変更が必要

プロジェクト解析設定

sonar-project.properties設定

# sonar-project.properties - 基本設定
sonar.projectKey=my-project
sonar.projectName=My Project
sonar.projectVersion=1.0
sonar.sourceEncoding=UTF-8

# ソースコード設定
sonar.sources=src
sonar.tests=tests
sonar.exclusions=**/node_modules/**,**/dist/**,**/*.min.js
sonar.test.exclusions=**/node_modules/**

# JavaScript/TypeScript設定
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.typescript.lcov.reportPaths=coverage/lcov.info

# Java設定
sonar.java.source=17
sonar.java.target=17
sonar.java.binaries=target/classes
sonar.java.test.binaries=target/test-classes
sonar.junit.reportPaths=target/surefire-reports

# Python設定
sonar.python.coverage.reportPaths=coverage.xml
sonar.python.xunit.reportPaths=test-results.xml

# C#設定
sonar.cs.nunit.reportsPaths=TestResults/*.xml
sonar.cs.opencover.reportsPaths=TestResults/coverage.opencover.xml

# カバレッジしきい値
sonar.coverage.exclusions=**/*test*/**,**/migrations/**

SonarScannerを使った解析実行

JavaScript/TypeScript プロジェクト

# SonarScannerインストール
npm install -g sonar-scanner

# package.json設定
npm pkg set scripts.sonar="sonar-scanner"

# 解析実行
sonar-scanner \
  -Dsonar.projectKey=my-js-project \
  -Dsonar.sources=src \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.token=your_token_here

# カバレッジ付き解析
npm run test -- --coverage
sonar-scanner \
  -Dsonar.projectKey=my-js-project \
  -Dsonar.sources=src \
  -Dsonar.tests=src \
  -Dsonar.test.inclusions=**/*.test.ts,**/*.spec.ts \
  -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.token=your_token_here

Maven(Java)プロジェクト

<!-- pom.xml -->
<properties>
    <sonar.host.url>http://localhost:9000</sonar.host.url>
    <sonar.token>your_token_here</sonar.token>
    <sonar.coverage.exclusions>
        **/test/**,
        **/target/**
    </sonar.coverage.exclusions>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.sonarsource.scanner.maven</groupId>
            <artifactId>sonar-maven-plugin</artifactId>
            <version>4.0.0.4121</version>
        </plugin>
        
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.12</version>
            <executions>
                <execution>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                </execution>
                <execution>
                    <id>report</id>
                    <phase>test</phase>
                    <goals>
                        <goal>report</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
# Maven解析実行
mvn clean compile test jacoco:report sonar:sonar

# 特定品質ゲート確認
mvn sonar:sonar -Dsonar.qualitygate.wait=true

.NET プロジェクト

# .NET SonarScannerインストール
dotnet tool install --global dotnet-sonarscanner

# 解析開始
dotnet sonarscanner begin \
  /k:"my-dotnet-project" \
  /d:sonar.host.url="http://localhost:9000" \
  /d:sonar.token="your_token_here" \
  /d:sonar.cs.opencover.reportsPaths="TestResults/coverage.opencover.xml"

# ビルドとテスト
dotnet build
dotnet test --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover

# 解析終了
dotnet sonarscanner end /d:sonar.token="your_token_here"

CI/CD統合例

GitHub Actions

# .github/workflows/sonarqube.yml
name: SonarQube Analysis

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  sonarqube:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0  # Shallow clones should be disabled
    
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '20'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests with coverage
      run: npm run test -- --coverage --watchAll=false
    
    - name: SonarQube Scan
      uses: sonarqube-quality-gate-action@master
      env:
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
      with:
        projectBaseDir: .
        args: >
          -Dsonar.projectKey=${{ github.repository }}
          -Dsonar.sources=src
          -Dsonar.tests=src
          -Dsonar.test.inclusions=**/*.test.ts,**/*.spec.ts
          -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
    
    - name: Quality Gate Check
      uses: sonarqube-quality-gate-action@master
      timeout-minutes: 5
      env:
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

GitLab CI/CD

# .gitlab-ci.yml
variables:
  SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
  GIT_DEPTH: "0"

cache:
  key: "${CI_JOB_NAME}"
  paths:
    - .sonar/cache

stages:
  - test
  - quality

test:
  stage: test
  image: node:20-alpine
  before_script:
    - npm ci
  script:
    - npm run test -- --coverage --watchAll=false
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    paths:
      - coverage/
    expire_in: 1 day

sonarqube-check:
  stage: quality
  image: 
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  dependencies:
    - test
  script:
    - sonar-scanner
      -Dsonar.projectKey=${CI_PROJECT_NAME}
      -Dsonar.sources=src
      -Dsonar.tests=src
      -Dsonar.test.inclusions=**/*.test.ts,**/*.spec.ts
      -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
      -Dsonar.host.url=${SONAR_HOST_URL}
      -Dsonar.token=${SONAR_TOKEN}
      -Dsonar.qualitygate.wait=true
  only:
    - main
    - develop
    - merge_requests

品質ゲート設定

カスタム品質ゲート設定例

{
  "name": "Strict Quality Gate",
  "conditions": [
    {
      "metric": "new_coverage",
      "operation": "LT",
      "threshold": "80",
      "periodIndex": "1"
    },
    {
      "metric": "new_duplicated_lines_density",
      "operation": "GT",
      "threshold": "3",
      "periodIndex": "1"
    },
    {
      "metric": "new_maintainability_rating",
      "operation": "GT",
      "threshold": "1",
      "periodIndex": "1"
    },
    {
      "metric": "new_reliability_rating",
      "operation": "GT",
      "threshold": "1",
      "periodIndex": "1"
    },
    {
      "metric": "new_security_rating",
      "operation": "GT",
      "threshold": "1",
      "periodIndex": "1"
    },
    {
      "metric": "new_security_hotspots_reviewed",
      "operation": "LT",
      "threshold": "100",
      "periodIndex": "1"
    }
  ]
}

プロジェクト固有設定とルールカスタマイズ

Quality Profile作成

# SonarQube Web APIを使用したQuality Profile操作

# 新しいQuality Profile作成
curl -X POST \
  "${SONAR_HOST_URL}/api/qualityprofiles/create" \
  -H "Authorization: Bearer ${SONAR_TOKEN}" \
  -d "language=js" \
  -d "name=MyCompany TypeScript Rules"

# ルール追加
curl -X POST \
  "${SONAR_HOST_URL}/api/qualityprofiles/activate_rule" \
  -H "Authorization: Bearer ${SONAR_TOKEN}" \
  -d "key=MyCompany_TypeScript_Rules" \
  -d "rule=typescript:S1066" \
  -d "severity=MAJOR"

# ルール設定変更
curl -X POST \
  "${SONAR_HOST_URL}/api/qualityprofiles/activate_rule" \
  -H "Authorization: Bearer ${SONAR_TOKEN}" \
  -d "key=MyCompany_TypeScript_Rules" \
  -d "rule=typescript:S3776" \
  -d "severity=CRITICAL" \
  -d "params=threshold=15"

レポートとメトリクス活用

SonarQube Web API活用

# プロジェクト品質メトリクス取得
curl -H "Authorization: Bearer ${SONAR_TOKEN}" \
  "${SONAR_HOST_URL}/api/measures/component?component=${PROJECT_KEY}&metricKeys=coverage,duplicated_lines_density,bugs,vulnerabilities,code_smells,sqale_rating,reliability_rating,security_rating"

# 品質ゲート状態確認
curl -H "Authorization: Bearer ${SONAR_TOKEN}" \
  "${SONAR_HOST_URL}/api/qualitygates/project_status?projectKey=${PROJECT_KEY}"

# 問題一覧取得
curl -H "Authorization: Bearer ${SONAR_TOKEN}" \
  "${SONAR_HOST_URL}/api/issues/search?componentKeys=${PROJECT_KEY}&types=BUG,VULNERABILITY,CODE_SMELL"

カスタムダッシュボード用データ取得

# Python例:SonarQube metrics collector
import requests
import json

class SonarQubeCollector:
    def __init__(self, host_url, token):
        self.host_url = host_url
        self.headers = {"Authorization": f"Bearer {token}"}
    
    def get_project_metrics(self, project_key):
        """プロジェクトメトリクス取得"""
        metrics = [
            'coverage', 'duplicated_lines_density', 'bugs', 
            'vulnerabilities', 'code_smells', 'sqale_rating',
            'reliability_rating', 'security_rating', 'sqale_index'
        ]
        
        url = f"{self.host_url}/api/measures/component"
        params = {
            'component': project_key,
            'metricKeys': ','.join(metrics)
        }
        
        response = requests.get(url, headers=self.headers, params=params)
        return response.json()
    
    def get_quality_gate_status(self, project_key):
        """品質ゲート状態取得"""
        url = f"{self.host_url}/api/qualitygates/project_status"
        params = {'projectKey': project_key}
        
        response = requests.get(url, headers=self.headers, params=params)
        return response.json()
    
    def get_issues_summary(self, project_key):
        """問題集計取得"""
        url = f"{self.host_url}/api/issues/search"
        params = {
            'componentKeys': project_key,
            'facets': 'types,severities,rules',
            'ps': 1  # 件数は不要
        }
        
        response = requests.get(url, headers=self.headers, params=params)
        return response.json()

# 使用例
collector = SonarQubeCollector('http://localhost:9000', 'your_token')
metrics = collector.get_project_metrics('my-project')
quality_gate = collector.get_quality_gate_status('my-project')

セキュリティとコンプライアンス設定

セキュリティホットスポット管理

# セキュリティホットスポット一覧取得
curl -H "Authorization: Bearer ${SONAR_TOKEN}" \
  "${SONAR_HOST_URL}/api/hotspots/search?projectKey=${PROJECT_KEY}"

# ホットスポット詳細取得
curl -H "Authorization: Bearer ${SONAR_TOKEN}" \
  "${SONAR_HOST_URL}/api/hotspots/show?hotspot=${HOTSPOT_KEY}"

# ホットスポット状態更新(確認済み)
curl -X POST \
  -H "Authorization: Bearer ${SONAR_TOKEN}" \
  "${SONAR_HOST_URL}/api/hotspots/change_status" \
  -d "hotspot=${HOTSPOT_KEY}" \
  -d "status=REVIEWED" \
  -d "resolution=SAFE"

コンプライアンスレポート設定

# compliance-check.yml - コンプライアンスチェック自動化
name: Compliance Check

on:
  schedule:
    - cron: '0 2 * * 1'  # 毎週月曜日午前2時
  workflow_dispatch:

jobs:
  compliance:
    runs-on: ubuntu-latest
    steps:
    - name: Generate Compliance Report
      run: |
        # OWASP Top 10チェック
        curl -H "Authorization: Bearer ${{ secrets.SONAR_TOKEN }}" \
          "${{ secrets.SONAR_HOST_URL }}/api/measures/component?component=${{ github.repository }}&metricKeys=security_rating,security_hotspots,vulnerabilities" \
          > security-report.json
        
        # CWEカテゴリ別脆弱性集計
        curl -H "Authorization: Bearer ${{ secrets.SONAR_TOKEN }}" \
          "${{ secrets.SONAR_HOST_URL }}/api/issues/search?componentKeys=${{ github.repository }}&types=VULNERABILITY&facets=cwe" \
          > cwe-report.json
        
        # レポート生成(Python scriptなど)
        python generate-compliance-report.py
    
    - name: Upload Compliance Report
      uses: actions/upload-artifact@v4
      with:
        name: compliance-report
        path: compliance-report.pdf