SonarQube: Tích hợp SonarQube vào hệ thống CI/CD Pipeline Jenkins
Cấu hình tích hợp SonarQube vào hệ thống CI/CD
Ở đây mình dùng Jenkins nhé.
Phần 1: Chuẩn bị "Chìa khóa" trên SonarQube
Để Jenkins có quyền gửi dữ liệu sang, nó cần một mã xác thực (Token) từ SonarQube.
-
Đăng nhập vào giao diện web của SonarQube bằng tài khoản
admin. -
Bấm vào Avatar ở góc phải trên cùng ➔ Chọn My Account.
-
Chuyển sang tab Security.
-
Tại mục Generate Tokens, điền thông tin:
-
Name:
jenkins-token(hoặc tên bất kỳ). -
Type:
Global Analysis Token(Quan trọng: Quyền này giúp Jenkins tự động tạo Project mới mà không cần bạn làm thủ công). -
Expires in:
No expirationhoặc thời hạn tùy ý.
-
-
Bấm Generate và Copy đoạn mã này lại.

Phần 2: Cấu hình trên Jenkins (Master)
Chúng ta cần dạy cho Jenkins biết SonarQube nằm ở đâu, dùng chìa khóa nào để vào, và dùng công cụ gì để quét.
Bước 2.1: Lưu trữ Token bảo mật (Credentials)
-
Vào Manage Jenkins ➔ Credentials ➔ System ➔ Global credentials.
-
Bấm Add Credentials.
-
Kind: Chọn
Secret text. -
Secret: Dán đoạn Token vừa copy từ SonarQube vào đây.
-
ID: Đặt là
sonar-token(ID này sẽ được gọi ngầm trong Jenkinsfile). -
Bấm Create.

Bước 2.2: Khai báo địa chỉ SonarQube Server
-
Vào Manage Jenkins ➔ System.
-
Cuộn xuống phần SonarQube servers, bấm Add SonarQube.
-
Name: Nhập
sonarqube-server(Tên này bắt buộc phải trùng với tên gọi trong fileJenkinsfile). -
Server URL: Nhập địa chỉ VPS chứa SonarQube của bạn (vd:
http://<IP-VPS-MỚI>:9000). -
Server authentication token: Sổ xuống và chọn cái
sonar-tokenvừa tạo ở Bước 2.1. -
Bấm Save.

Bước 2.3: Khai báo bộ cài đặt Sonar Scanner (Công cụ quét)
-
Vào Manage Jenkins ➔ Tools.
-
Cuộn xuống mục SonarQube Scanner installations, bấm Add SonarQube Scanner.
-
Name: Bắt buộc nhập chính xác là
sonar-scanner. -
Tích chọn Install automatically để Jenkins tự động tải bộ công cụ này về khi cần.
-
Bấm Save.

Phần 3: Cấu hình kịch bản Pipeline (Jenkinsfile)
Đây là file Jenkinsfile nguyên bản cho project nodejs của mình nhé :
pipeline {
agent any
stages {
stage('Checkout Code') {
steps {
// Kéo source code từ Git
git branch: 'main', url: 'https://github.com/tobi1008/profile.git'
}
}
stage('SonarQube Analysis') {
steps {
script {
def scannerHome = tool 'sonar-scanner'
withSonarQubeEnv('sonarqube-server') {
// Vẫn giữ lại lệnh bơm 1GB RAM để Java làm việc mượt mà (đây là Best Practice, không phải hack)
withEnv(["SONAR_SCANNER_OPTS=-Xmx1024m"]) {
sh """
${scannerHome}/bin/sonar-scanner \
-Dsonar.projectKey=demo-lab \
-Dsonar.projectName="Demo Lab Nodejs" \
-Dsonar.sources=. \
-Dsonar.exclusions=node_modules/**,test/** \
-Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
"""
}
}
}
}
}
}
}
Bây giờ, "đường ống" đã được lấp kín và trơn tru. Khi vào giao diện SonarQube, dự án Demo Lab Nodejs của bạn đã hiển thị xanh mướt.

Phân tích trạng thái SonarQube

1. Trạng thái tổng thể: Quality Gate (Cửa ải chất lượng)
Ngay góc phải, bạn thấy chữ Passed màu xanh lá.
-
Ý nghĩa: Quality Gate là một bộ tiêu chuẩn (rule) do SonarQube định nghĩa (hoặc do boss tự thiết lập). Nó giống như bác sĩ khám sức khỏe tổng quát. Chữ "Passed" nghĩa là dự án này dù còn vài khuyết điểm nhưng vẫn đạt chuẩn tối thiểu để "xuất viện" (được phép deploy lên môi trường tiếp theo).
-
Thực tế: Nếu chỗ này báo Failed (màu đỏ), một quy trình CI/CD chuẩn sẽ cấu hình để Jenkins lập tức đánh rớt bản build và cấm không cho deploy đoạn code đó.
2. Các chỉ số cốt lõi (Core Metrics)
Nhìn vào hàng ngang bên dưới, mã nguồn của boss (dài 519 dòng) đang có các "thông số sinh tồn" sau:
-
Security (Bảo mật) - Điểm A (0 Lỗi):
-
Đây là tin rất vui. SonarQube không tìm thấy bất kỳ Vulnerabilities (lỗ hổng bảo mật) rành rành nào có thể bị hacker khai thác trực tiếp (ví dụ: SQL Injection, Hardcode mật khẩu, v.v.).
-
-
Reliability (Độ tin cậy) - Điểm C (3 Lỗi):
-
Báo động vàng! Dự án đang dính 3 Bugs (Lỗi logic).
-
Đây là những đoạn code tuy vẫn chạy được qua mắt trình biên dịch, nhưng SonarQube dự đoán nó sẽ gây sập ứng dụng (Crash) trong một vài tình huống cụ thể (ví dụ: Lỗi Null Pointer, vòng lặp vô hạn, hoặc chia cho 0). Cần ưu tiên sửa ngay.
-
-
Maintainability (Khả năng bảo trì) - Điểm A (2 Lỗi):
-
SonarQube tìm thấy 2 Code Smells (Code "bốc mùi").
-
Nó không gây lỗi, nhưng là những đoạn mã viết quá rườm rà, đặt tên biến khó hiểu, hoặc một hàm quá dài. Sửa những chỗ này (gọi là trả nợ kỹ thuật - Tech Debt) giúp code sạch và dễ nâng cấp sau này.
-
-
Hotspots Reviewed - Điểm E (0.0%):
-
Điểm E (đỏ chót) duy nhất của dự án. Security Hotspots là những vùng mã nhạy cảm (ví dụ: mở port, phân quyền, cấu hình CORS). SonarQube không chắc nó có lỗi hay không, nên nó "khoanh đỏ" lại yêu cầu một con người (là boss) vào xem xét thủ công. 0.0% nghĩa là boss chưa review chỗ nào cả.
-
-
Coverage (Độ bao phủ kiểm thử) - 0.0%:
-
Chỉ số này cho biết có bao nhiêu % mã nguồn đã được test tự động (Unit Test). Hiện tại là 0%, có thể do dự án chưa viết Unit Test, hoặc Jenkins chưa thu thập được file báo cáo
lcov.infođể đẩy sang.
-
-
Duplications (Mã lặp lại) - 8.0%:
-
Có khoảng 8% (hơn 40 dòng) code bị copy-paste y hệt nhau ở nhiều file khác nhau. Mức này dưới 10% nên vẫn khá an toàn, nhưng nếu cao quá thì lúc sửa một logic sẽ phải đi sửa thủ công ở hàng chục nơi.
-
3. Kiểm tra các bug hiện có

Bạn hãy để ý kỹ nhé:
-
Lỗi thứ 1 (File
app/globals.css): Có icon hình con bọ 🐞 và dòng chữ Bug nhỏ xíu màu xám ở góc dưới cùng bên phải của ô đó. -
Lỗi thứ 3 (File
components/Terminal.tsx): Cũng có icon con bọ 🐞 và chữ Bug. (Còn cái lỗi thứ 2 ở giữa nó ghi là Code Smell).
Cách để đi sâu vào xem code: Bây giờ, bạn hãy click thẳng vào dòng chữ tiêu đề màu xanh nước biển của cái lỗi số 1 (chữ "Unexpected unknown at-rule '@theme'") hoặc lỗi số 3 ("Visible, non-interactive elements...").
Ngay lập tức, màn hình sẽ chẻ đôi ra, hiển thị chính xác dòng code nguồn của bạn đang dính cái lỗi này.

Hãy cùng mổ xẻ cái "Bug" này nhé:
1. Tại sao SonarQube lại la làng?
Nhìn vào dòng số 8, tôi đang dùng cú pháp @theme inline { ... }.
-
Về mặt tiêu chuẩn web gốc (Vanilla CSS), thế giới CSS chỉ có một bộ từ điển hữu hạn các quy tắc (at-rules) bắt đầu bằng dấu
@, ví dụ như:@media(dòng 16 của boss kìa),@import,@keyframes, hay@font-face. -
Cú pháp
@themekhông hề tồn tại trong tiêu chuẩn CSS chuẩn (W3C). Khả năng rất cao đây là dự án dùng Tailwind CSS v4 (phiên bản mới nhất vừa thay đổi cách config bằng CSS thay vì file tailwind.config.js). -
SonarQube đóng vai trò là một ông giám thị vô cùng nguyên tắc. Thấy một từ vựng lạ hoắc không nằm trong sách giáo khoa CSS, ông ấy lập tức gạch đỏ và kết tội: "Unexpected unknown at-rule" (Phát hiện một quy tắc @ lạ lẫm, không xác định).
2. Nó có làm sập ứng dụng không?
Hoàn toàn KHÔNG. Code của boss đang viết rất chuẩn xác theo syntax của Tailwind. Khi ứng dụng chạy (build), các công cụ đóng gói (như Vite, Next.js...) sẽ tự động dịch đoạn @theme này thành CSS chuẩn mà trình duyệt hiểu được.
Việc SonarQube báo lỗi chỉ đơn giản là vì bộ quy tắc (Rule) của nó chưa được cập nhật kịp với cú pháp "hot trend" mới nhất của Tailwind mà thôi.
3. Cách kỹ sư DevOps "bật" lại hệ thống
Khi gặp lỗi False Positive thế này, thay vì hì hục sửa code (làm phá vỡ framework), tôi có quyền dùng đặc quyền của mình để bỏ qua nó:
Nhìn ngay phía trên đoạn code bị lỗi, bạn sẽ thấy một nút có trạng thái là Open . Bạn hãy click vào đó và chọn:
-
Resolve as False Positive (Đánh dấu là hệ thống báo nhầm).
-
Hoặc Accept (Chấp nhận nợ kỹ thuật này và không sửa).
Ngay lập tức, "con bọ" này sẽ bị gạch bỏ khỏi báo cáo, không làm ảnh hưởng đến điểm Quality Gate của dự án nữa!

Mẹo nhỏ: Bạn có thể tự tay click sang tab Why is this an issue? ở ngay bên cạnh để xem SonarQube đưa ra tài liệu gốc giải thích nguyên lý hoạt động của các
at-rulechuẩn nhé.
Đi sang Bug tiếp theo thử nhé

SonarQube đã tóm gọn được một vi phạm về Accessibility (a11y - Khả năng truy cập). Bức ảnh cho thấy SonarQube đang "kẹp cổ" cái thẻ <div> ở dòng 282 và nã cho 2 tội danh liên tiếp.
Chúng ta cùng "giải phẫu" cái phốt này nhé:
1. Bản chất của vấn đề: <div> không sinh ra để được click!
Trong thế giới HTML gốc, mỗi thẻ có một ý nghĩa ngữ nghĩa (semantic) riêng:
-
Thẻ
<button>hoặc<a>sinh ra để tương tác. Trình duyệt biết chúng có thể được focus bằng phímTab, và có thể được kích hoạt bằng phímEnterhoặcSpacebar. -
Thẻ
<div>và<span>chỉ là những khối vô tri vô giác dùng để bao bọc layout.
Ở dòng 284,tôi đã gắn onClick={handleContainerClick} vào một cái <div>.
-
Với người dùng bình thường: Họ dùng chuột click vào, hàm vẫn chạy, mọi thứ trông có vẻ hoàn hảo.
-
Với người khiếm thị (dùng trình đọc màn hình - Screen Reader) hoặc người bị liệt (chỉ dùng được bàn phím): Họ sẽ bị "kẹt" lại hoàn toàn! Trình đọc màn hình sẽ đọc lướt qua cái
<div>này như một đoạn văn bản bình thường mà không biết nó có thể click được. Người dùng phímTabcũng không thể nào bôi đen cái khối này để nhấnEnterđược.
Đó là lý do SonarQube nổi giận: "Đừng dùng các thẻ vô tri để làm chức năng tương tác (Avoid non-native interactive elements). Và nếu đã gắn chuột (click handlers), thì phải gắn luôn cả phím cho người ta (must have at least one keyboard listener)."
2. Hai cách để "chuộc lỗi" với SonarQube
Nếu bạn click sang tab "How can I fix it?" trên giao diện, SonarQube cũng sẽ gợi ý cho bạn 2 con đường này:

Cách 1: Tiêu chuẩn vàng (Best Practice) - Đổi thẻ
Đây là cách dễ nhất và chuẩn nhất. Hãy trả lại tên cho em: Đổi <div> thành <button>. Thẻ button đã được trình duyệt lo liệu toàn bộ các sự kiện bàn phím và chuột.
// ❌ Đoạn code cũ dính lỗi
<div
className="w-full h-full bg-[#1e1e1e]/90..."
onClick={handleContainerClick}
>
...
</div>
// ✅ Đổi thành button
<button
type="button" // Bắt buộc thêm type="button" để tránh lỗi submit form bậy bạ
className="w-full h-full text-left bg-[#1e1e1e]/90..." // Có thể phải thêm text-left vì button mặc định canh giữa chữ
onClick={handleContainerClick}
>
...
</button>
Cách 2: Chữa cháy bằng ARIA (Nếu bắt buộc phải giữ thẻ div)
Nếu việc đổi thẻ div thành button làm vỡ nát toàn bộ CSS layout của boss và boss không muốn sửa CSS, boss phải tự tay cấu hình "giả làm button" cho cái div đó bằng cách thêm 3 thuộc tính:
-
role="button"(Báo cho máy đọc màn hình biết đây là nút). -
tabIndex={0}(Cho phép người dùng bấm phím Tab để nhảy vào div này). -
onKeyDown={...}(Lắng nghe sự kiện người dùng bấm phím Enter/Space để gọi hàm click).
// ✅ Chữa cháy giữ nguyên thẻ div
<div
className="w-full h-full bg-[#1e1e1e]/90..."
onClick={handleContainerClick}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleContainerClick(); // Gọi lại hàm click khi user bấm Enter/Space
}
}}
>
...
</div>
Tóm lại: Lỗi này không làm sập server hay văng app (crash), nhưng nó đánh giá trình độ chuyên nghiệp của dự án và sự quan tâm đến trải nghiệm người dùng (UX).
Bạn hãy mở VS Code lên (có thể dùng chung với Rojo luôn nếu đang cấu hình Roblox song song), sửa lại đoạn code đó theo 1 trong 2 cách trên. Sau đó commit lên GitHub, và ngồi nhìn Jenkins tự động đẩy code sang SonarQube để thấy 2 cái lỗi này biến mất không tì vết nhé!
Các chức năng nổi bật khác
Bạn đã nắm được linh hồn của SonarQube là quản lý lỗi (Issues) rồi! Nhưng công cụ này vẫn còn những "vũ khí hạng nặng" khác khiến nó trở thành tiêu chuẩn vàng trong mọi đường ống CI/CD.
Dưới đây là 4 tính năng nổi bật nhất mà một người làm hệ thống nhất định phải xem qua:
1. Security Hotspots (Rà soát Điểm nóng Bảo mật)
Nhớ cái điểm "E" màu đỏ chót ở tab Overview lúc nãy không? Đó chính là chức năng SAST (Static Application Security Testing) của SonarQube.
-
Nó làm gì: Thay vì chỉ bắt lỗi code chạy sai, nó đóng vai trò như một chuyên gia an ninh mạng. Nó rà quét toàn bộ code để tìm các rủi ro bảo mật như: Hardcode mật khẩu, mở port bừa bãi, cấu hình CORS lỏng lẻo, hoặc nguy cơ bị dính SQL Injection, XSS.
-
Điểm hay: Nó không tự tiện báo lỗi gắt, mà chỉ "khoanh vùng" (Hotspots) và yêu cầu tôi phải vào review thủ công. Nó sẽ giải thích cho tôi cách hacker có thể lợi dụng đoạn code đó như thế nào.

2. Tùy chỉnh Quality Gates (Người Gác Cổng Tàn Nhẫn)
Hiện tại dự án của tôi đang dùng cổng kiểm duyệt mặc định (gọi là Sonar way). Cổng này khá hiền, chỉ bắt lỗi trên những dòng code mới viết.
-
Quyền năng của bạn: bạn có thể tự tạo ra một "Cửa ải" cực gắt mang tên riêng của mình. Ví dụ, thiết lập luật: Độ bao phủ kiểm thử (Coverage) phải > 80% và Số lượng Bug phải = 0.
-
Tích hợp: Nếu dự án không đạt tiêu chuẩn này, cổng Quality Gate sẽ chuyển sang màu đỏ (Failed). Nếu boss tích hợp thêm Webhook, SonarQube sẽ lập tức "bắn" tín hiệu về cho Jenkins để hủy ngay lệnh Deploy, cấm không cho đoạn code kém chất lượng lên máy chủ.

3. Quality Profiles (Quốc có quốc pháp, Gia có gia quy)
Nhớ pha SonarQube "báo oan" cú pháp @theme của Tailwind CSS không? Nếu mỗi file CSS nó đều báo lỗi thì boss click Resolve as False Positive gãy tay mất.
-
Cách giải quyết: Quality Profiles chính là kho chứa "luật lệ". Bạn có thể nhân bản bộ luật mặc định ra, sau đó tìm đúng cái luật kiểm tra
@(at-rule) của CSS và Tắt (Deactivate) nó đi trên toàn hệ thống. Từ nay về sau, SonarQube sẽ nhắm mắt làm ngơ với mọi cú pháp Tailwind.

4. Technical Debt & Activity (Đo lường "Nợ Kỹ Thuật")
SonarQube không chỉ xem xét hiện tại mà còn lưu trữ lịch sử.
-
Chuyển sang tab Activity, bạn sẽ thấy một biểu đồ tiến trình. Qua hàng chục lần Jenkins chạy build, biểu đồ này sẽ cho biết code của bạn đang ngày càng "sạch" hơn, hay team đang đẻ ra ngày càng nhiều lỗi.
-
Nó quy đổi code rác thành thời gian thực tế gọi là Technical Debt (Nợ Kỹ Thuật). Ví dụ: "Dự án này đang có 4 ngày nợ kỹ thuật". Nghĩa là bạn sẽ phải bỏ ra 4 ngày làm việc liên tục chỉ để ngồi dọn dẹp lại code cho sạch, trước khi tính đến chuyện làm thêm tính năng mới.

Cả 4 tính năng này đều nằm trên thanh Menu ngang trên cùng của SonarQube. Theo định hướng vận hành tự động hóa.