Tích hợp SonarQube vào CI/CD Pipeline: Phân tích mã nguồn tĩnh và Tự động hóa Quality Gate

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

Bối cảnh & Lý do: Phát hiện lỗi trước khi lên Production

Hầu hết các nhóm kỹ sư đều từng gặp phải tình huống quen thuộc: code trông hoàn hảo khi review pull request, nhưng chỉ hai ngày sau môi trường staging đã sập vì rò rỉ bộ nhớ (memory leak) hoặc cạn kiệt connection pool của cơ sở dữ liệu. Dưới áp lực tiến độ sprint, các kỹ sư thường tập trung cao độ vào logic nghiệp vụ. Kết quả là những bất cập về kiến trúc, các đoạn boilerplate copy-paste hay những nguy cơ bảo mật tiềm ẩn dễ dàng lọt qua mà không ai hay biết.

Review thủ công rõ ràng không thể mở rộng quy mô khi deadline cận kề. Mức độ kỹ lưỡng giữa các nhóm không đồng đều và sự mệt mỏi sẽ xuất hiện sau khi phải đọc một bản diff dài hơn 600 dòng. Nếu thiếu cơ chế kiểm soát chất lượng tự động ngay trong build chain, việc phân tích tĩnh (static analysis) sẽ chỉ dừng lại ở những lần chạy cục bộ tùy hứng mỗi quý một lần, hoặc biến thành nỗi hoảng loạn trước mỗi kỳ audit.

Tích hợp SonarQube trực tiếp vào CI pipeline sẽ dịch chuyển việc kiểm tra mã nguồn sang trái (shift-left) đúng như vị trí vốn có của nó. Mỗi commit hay pull request đều kích hoạt quy trình quét tức thì để phát hiện bug, rủi ro bảo mật (security hotspot), lỗi cú pháp và lỗ hổng kiểm thử.

Nếu phần code mới không đạt các ngưỡng chất lượng đề ra, pipeline sẽ dừng lại ngay lập tức. Khi áp dụng quy trình này cho một dịch vụ cổng thanh toán, tỷ lệ báo cáo lỗi sau phát hành của chúng tôi đã giảm gần 35% chỉ trong hai quý. Xử lý sự cố ngay khi ngữ cảnh còn rõ ràng giúp tiết kiệm hàng giờ chuyển đổi qua lại giữa các tác vụ sau này.

Cài đặt: Chạy SonarQube với Docker Compose

Trước khi kết nối các runner, bạn cần một máy chủ SonarQube ổn định. Với các nhóm tự host công cụ nội bộ, Docker Compose kết hợp với backend PostgreSQL riêng biệt sẽ giúp việc bảo trì trở nên đơn giản và dễ dàng tái lập.

1. Chuẩn bị thông số Kernel trên máy chủ Host

SonarQube tích hợp sẵn Elasticsearch để lập chỉ mục tìm kiếm dự án. Elasticsearch yêu cầu giới hạn bộ nhớ ảo và file descriptor cao hơn trên hệ điều hành host. Nếu không cấu hình, container sẽ bị crash khi khởi động do lỗi tràn bộ nhớ (out-of-memory). Hãy thiết lập trực tiếp các thông số này trên máy chủ Linux của bạn:

sudo sysctl -w vm.max_map_count=524288
sudo sysctl -w fs.file-max=131072
echo 'vm.max_map_count=524288' | sudo tee -a /etc/sysctl.conf
echo 'fs.file-max=131072' | sudo tee -a /etc/sysctl.conf

2. Cấu hình Compose

Tạo một thư mục riêng, thêm file docker-compose.yml này vào và liên kết cả PostgreSQL cùng container ứng dụng với nhau:

version: "3.8"

services:
  sonarqube-db:
    image: postgres:15-alpine
    container_name: sonarqube_postgres
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar_secret_password
      POSTGRES_DB: sonarqube
    volumes:
      - postgresql_data:/var/lib/postgresql/data
    networks:
      - sonarnet
    restart: unless-stopped

  sonarqube:
    image: sonarqube:10.4-community
    container_name: sonarqube_server
    depends_on:
      - sonarqube-db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://sonarqube-db:5432/sonarqube
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar_secret_password
    ports:
      - "9000:9000"
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
    networks:
      - sonarnet
    restart: unless-stopped

networks:
  sonarnet:
    driver: bridge

volumes:
  postgresql_data:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:

Khởi chạy các dịch vụ chạy nền và theo dõi log khởi động:

docker compose up -d
docker compose logs -f sonarqube

Mở http://<your-server-ip>:9000 khi log xác nhận SonarQube đã sẵn sàng. Đăng nhập bằng tài khoản mặc định (admin / admin), và đổi mật khẩu ngay lập tức.

Cấu hình: Tích hợp Pipeline và Quality Gate

Khi máy chủ đã hoạt động ổn định, hãy kết nối hệ thống CI của bạn và thiết lập các quality gate rõ ràng.

1. Tạo Authentication Token và Project Key

  1. Truy cập My Account > Security và tạo một User Token với quyền phân tích (chẳng hạn như ci-token-backend).
  2. Chuyển sang Projects > Create Project > Manually. Nhập tên hiển thị dễ nhận biết và project key (ví dụ: backend-api-service).

2. Thêm file cấu hình Scanner vào dự án

Tạo file sonar-project.properties tại thư mục gốc của repository để điều hướng scanner quét qua các thư mục:

sonar.projectKey=backend-api-service
sonar.projectName=Backend API Service
sonar.projectVersion=1.0.0

# Thư mục mã nguồn
sonar.sources=src
sonar.tests=tests
sonar.sourceEncoding=UTF-8

# Loại trừ thư mục build và thư viện phụ thuộc
sonar.exclusions=**/node_modules/**,**/dist/**,**/build/**

# Đường dẫn báo cáo độ bao phủ kiểm thử (coverage)
sonar.javascript.lcov.reportPaths=coverage/lcov.info

3. Cấu hình CI/CD Pipeline (Ví dụ với GitHub Actions)

Thêm hai secret đã mã hóa trong phần cài đặt repository trên GitHub: SONAR_HOST_URL và SONAR_TOKEN. Tiếp theo, tạo file workflow tại .github/workflows/ci.yml:

name: Build & Code Quality Check

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  analysis:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Bắt buộc lấy toàn bộ lịch sử commit để chú thích git-blame chính xác

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Cài đặt Dependencies & Chạy Tests
        run: |
          npm ci
          npm test -- --coverage

      - name: SonarQube Scan
        uses: sonarsource/sonarqube-scan-action@v2
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

      - name: SonarQube Quality Gate Check
        uses: sonarsource/sonarqube-quality-gate-action@v1
        timeout-minutes: 5
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

Kiểm thử & Giám sát: Thực thi Quality Gate

Quét mã nguồn mà không chặn các commit lỗi sẽ biến phân tích tĩnh thành thứ vô nghĩa. SonarQube áp dụng Quality Gate nhằm chặn đứng các thay đổi lỗi trước khi chúng được hợp nhất vào các nhánh chính.

1. Điều chỉnh quy tắc Quality Gate

Mở mục Quality Gates trong SonarQube. Profile mặc định Sonar way là điểm khởi đầu rất tốt nhờ tập trung vào phương pháp “Clean as You Code”. Chúng tôi khuyến nghị nên đặt các ngưỡng tiêu chuẩn sau cho các pull request mới:

  • 0 Lỗ hổng mới (New Vulnerabilities): Bắt lỗi SQL injection, dữ liệu đầu ra chưa được escape và secret JWT bị hardcode.
  • 0 Bug mới (New Bugs): Cảnh báo promise rejection chưa được bắt, lỗi vòng lặp off-by-one và bẫy tham chiếu null.
  • Coverage >= 80%: Đảm bảo toàn bộ domain logic mới viết đều đi kèm bộ kiểm thử tự động.
  • Tỷ lệ trùng lặp dòng (Duplicated Lines) < 3%: Hạn chế thói quen copy-paste vá tạm thời và duy trì việc tái sử dụng utility chung.

2. Xử lý khi Pipeline thất bại

Bước sonarqube-quality-gate-action sẽ thăm dò API trạng thái của SonarQube. Nếu ai đó push một câu truy vấn cơ sở dữ liệu chưa được tham số hóa hoặc bỏ qua test cho controller mới, job sẽ bị hủy ngay lập tức:

TRẠNG THÁI QUALITY GATE: THẤT BẠI
- Lỗ hổng trên Code mới: 1 (Kỳ vọng: 0)
- Độ bao phủ trên Code mới: 42.5% (Kỳ vọng: >= 80.0%)
Lỗi: Quy trình hoàn tất với mã thoát 1.

GitHub sẽ vô hiệu hóa nút merge. Tác giả phải mở trực tiếp URL dashboard được đính kèm trong log của action, kiểm tra các dòng code bị gắn cờ và commit bản sửa lỗi trước khi pull request được phép tiếp tục.

3. Giám sát xu hướng liên tục

Kiểm tra tab Measures trong SonarQube để theo dõi sức khỏe dịch vụ qua từng sprint. Hãy quan sát kỹ chỉ số độ phức tạp cyclomatic (cyclomatic complexity) và tỷ lệ nợ kỹ thuật (technical debt) dài hạn. Khi nợ kỹ thuật bắt đầu tăng dần, hãy dành ra nửa ngày tái cấu trúc (refactoring) trong buổi lập kế hoạch sprint để xử lý các cảnh báo lặp lại trước khi chúng bám rễ quá sâu.

Share: