SigNoz ไม่ได้เป็นแค่ระบบแสดงกราฟทั่วไป แต่มันคือ "ตาเทพ" (Full-stack Observability) ที่จะเข้ามาเติมเต็มส่วนที่เหลือของระบบให้สมบูรณ์ โดยประโยชน์หลักๆ สำหรับโปรเจกต์นี้มี 3 ด้าน ดังนี้
ในอนาคตโปรเจกต์นี้อาจจะมีข้อมูลวิ่งผ่านหลายด่านมาก (เช่น User -> Ingress -> APISIX -> FastAPI) หากวันหนึ่งหน้าเว็บโหลดช้า เราจะรู้ได้อย่างไรว่าช้าที่ไหน?
"SigNoz จะแสดงเป็นเส้น Timeline (Trace) ให้เห็นเลยว่า Request นี้เสียเวลาที่ APISIX กี่มิลลิวินาที และเสียเวลาที่ตัวแอป FastAPI กี่มิลลิวินาที ช่วยให้เรา "ชี้เป้า" ปัญหาได้ทันทีโดยไม่ต้องเดา"
ปกติเราต้องเปิดหลายหน้าจอ (เช่น Prometheus ดูตัวเลข, Grafana ดูกราฟ, หรือ Tail log ใน Terminal) SigNoz รวมทั้ง 3 อย่างไว้ในหน้าจอเดียว เมื่อเราเห็นกราฟ Error พุ่งขึ้น (Metrics) เราสามารถ "คลิกลงไป" ในกราฟนั้นเพื่อดู Log และ Trace ของ Request ที่ผิดปกติได้ทันทีในคลิกเดียว
เนื่องจากโปรเจกต์นี้เน้นความเป็น Cloud Native การใช้ SigNoz ซึ่งรองรับ OpenTelemetry (OTel) โดยตรงถือเป็นเรื่องสำคัญ เพราะเราไม่ต้องติดตั้ง Agent เฉพาะทางของบริษัทใดบริษัทหนึ่ง (เช่น Datadog หรือ New Relic) แต่ใช้มาตรฐานกลางของ OTel ซึ่งหมายความว่าในอนาคต หากเราอยากเปลี่ยนระบบ Monitoring ก็ไม่ต้องรื้อการตั้งค่าในแอปใหม่เลย
วัตถุประสงค์ คือ การใช้โปรโตคอล OpenTelemetry (OTel) เพื่อส่งข้อมูลจาก Jenkins (Build/Test/Scan) ไปปรากฏบนหน้าจอ SigNoz ในรูปแบบของ Distributed Tracing
ผลลัพธ์ที่จะได้รับหลังจากตั้งค่านี้
การทดสอบการ Build และ Push ไปยัง Nexus โดยมีการ Monitor ด้วย SigNoz
เพื่อแสดงให้เห็นถึงการใช้งาน SigNoz เพื่อ Monitor สายพานการผลิต GitLab -> Jenkins -> SonarQube -> Nexus
pipeline {
agent any
options {
timestamps()
disableConcurrentBuilds()
}
environment {
// ====== Credentials ======
SONAR_TOKEN = credentials('jenkins-token')
GITLAB_CREDS = credentials('gitlab-jenkins-creds')
NEXUS_CREDS = credentials('nexus-docker-auth')
// ====== OpenTelemetry → SigNoz ======
OTEL_EXPORTER_OTLP_ENDPOINT = 'https://collector.panmodel.com'
OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'
OTEL_SERVICE_NAME = 'Panmodel-Jenkins'
OTEL_RESOURCE_ATTRIBUTES = 'service.namespace=Production,deployment.environment=production,ci.tool=jenkins,ci.pipeline=my-factory-app'
OTEL_TRACES_SAMPLER = 'parentbased_always_on'
// ย้ำตัวส่งออก (กัน fallback)
OTEL_TRACES_EXPORTER = 'otlp'
OTEL_METRICS_EXPORTER = 'otlp'
// (ถ้าเคยเจอเงียบ ๆ บางเอเยนต์ ให้ uncomment 2 บรรทัดนี้)
// OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'https://collector.panmodel.com/v1/traces'
// OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/protobuf'
// ====== Flush เร็ว ลดโอกาส no-data ======
OTEL_BSP_SCHEDULE_DELAY = '200ms'
OTEL_BSP_MAX_EXPORT_BATCH_SIZE = '128'
OTEL_BSP_MAX_QUEUE_SIZE = '2048'
OTEL_BSP_EXPORT_TIMEOUT = '10s'
// ====== กัน Proxy ขวาง collector ======
NO_PROXY = 'collector.panmodel.com'
no_proxy = 'collector.panmodel.com'
}
stages {
// ---- Root trace & warm-up exporter ----
stage('0 - Initialize tracing (root span)') {
steps {
echo 'Initialize root trace & warm-up exporter'
sh 'sleep 1'
}
}
// ---- ตรวจ TLS/Reachability จาก agent จริง (ดีบักครั้งแรก ๆ) ----
stage('0.1 - OTEL Sanity (TLS from agent)') {
steps {
sh '''
set -euxo pipefail
echo "[1/3] TLS handshake"
(command -v openssl >/dev/null 2>&1 && \
echo | openssl s_client -connect collector.panmodel.com:443 -servername collector.panmodel.com -brief | sed -n '1,15p') || true
echo "[2/3] Reachability /v1/traces (404/405/415 is OK)"
curl -sS -o /dev/null -w "HTTP %{http_code}\\n" https://collector.panmodel.com/v1/traces || true
echo "[3/3] Effective OTEL env"
env | grep -E '^OTEL_|^NO_PROXY|^no_proxy' || true
'''
}
}
// ---- ยิง span ทดสอบผ่าน 443 จาก agent ----
stage('0.2 - Generate spans (telemetrygen)') {
steps {
sh '''
set -euxo pipefail
echo "[telemetrygen] send test spans over HTTPS:443 via OTLP/HTTP"
docker run --rm \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
traces \
--service "jenkins-agent-smoke" \
--duration 3s \
--rate 5 \
--otlp-endpoint collector.panmodel.com:443 \
--otlp-http
'''
}
}
stage('1. Checkout') {
steps { checkout scm }
}
stage('2. SonarQube Analysis') {
steps {
script {
def scannerHome = tool 'SonarScanner'
withSonarQubeEnv('SonarQube-Server-Name') {
sh """
${scannerHome}/bin/sonar-scanner \
-Dsonar.projectKey=my-factory-app \
-Dsonar.sources=. \
-Dsonar.host.url=https://sonar.panmodel.com \
-Dsonar.login=${SONAR_TOKEN}
"""
}
}
}
}
stage('3. Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
stage('4. Build & Push to Nexus') {
steps {
script {
docker.withRegistry('https://registry.panmodel.com', 'nexus-docker-auth') {
def image = docker.build("registry.panmodel.com/my-app:${env.BUILD_ID}")
image.push()
}
}
}
}
// ---- หน่วงสั้น ๆ ให้ batch สุดท้ายถูกส่งก่อนจบ ----
stage('5 - Ensure flush') {
steps {
sh 'sleep 2'
}
}
}
post {
always {
echo "SigNoz → https://monitor.panmodel.com (Last 15 min)"
echo "- Services ควรเห็น: Panmodel-Jenkins (trace เปิดได้), jenkins-agent-smoke (spans test)"
cleanWs()
}
}
}
ให้ลองกด Build ใน Jenkins เมื่อ Build ทำงานเสร็จ