Node Exporter: Monitor Linux System Metrics (CPU, Memory, Disk)

Node Exporter เป็น exporter สำคัญของ Prometheus ที่ใช้เก็บ metric ระดับระบบปฏิบัติการ Linux/Unix เช่น CPU, Memory, Disk, Network, File system และ systemd service — เป็น exporter ตัวแรกที่ทุกคนต้องติดตั้งเมื่อเริ่มวาง monitoring stack เพราะครอบคลุม baseline metric ของ server ทั้งหมด

บทความนี้จะอธิบายวิธีติดตั้ง node_exporter แบบ binary และ Docker, เลือก collector ที่เหมาะสมกับ workload, ตั้ง systemd service, เชื่อมกับ Prometheus server และวิเคราะห์ metric สำคัญ ๆ ที่ควรใช้ในการ monitor ระบบ production

Node Exporter คืออะไร

Node Exporter เป็น official exporter ของ Prometheus ที่เขียนด้วย Go รันเป็น HTTP server ขนาดเล็ก (binary ประมาณ 20MB) เปิด endpoint /metrics บน port 9100 — เก็บ metric จาก /proc, /sys และ system call ของ Linux kernel โดยตรง ไม่พึ่ง external dependency

ข้อดีของ node_exporter คือเบาและประสิทธิภาพสูง — รัน 1000+ collector ได้ในเวลาไม่ถึงวินาที เหมาะกับทั้งเครื่อง production ที่ต้องการ overhead ต่ำ และเครื่องขนาดเล็กอย่าง Raspberry Pi หรือ IoT device

ติดตั้ง Node Exporter แบบ Binary

วิธีนี้เหมาะกับเครื่อง VM หรือ bare metal ที่ต้องการให้ node_exporter รันเป็น systemd service โดยตรง — ไม่ต้องมี Docker layer:

# Download binary ล่าสุดจาก GitHub releases
cd /tmp
VERSION=1.8.2
wget https://github.com/prometheus/node_exporter/releases/download/v${VERSION}/node_exporter-${VERSION}.linux-amd64.tar.gz
tar xzf node_exporter-${VERSION}.linux-amd64.tar.gz

# ติดตั้ง binary
sudo mv node_exporter-${VERSION}.linux-amd64/node_exporter /usr/local/bin/
sudo useradd --no-create-home --shell /usr/sbin/nologin node_exporter
sudo chown node_exporter:node_exporter /usr/local/bin/node_exporter

การสร้าง user เฉพาะที่ไม่มี shell และ home directory เป็น security best practice — ลดผลกระทบถ้ามีช่องโหว่ใน exporter

สร้าง Systemd Service

# /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target

[Service]
User=node_exporter
Group=node_exporter
Type=simple
Restart=on-failure
ExecStart=/usr/local/bin/node_exporter \
  --collector.systemd \
  --collector.processes \
  --collector.filesystem.mount-points-exclude='^/(sys|proc|dev|run)($|/)' \
  --web.listen-address=:9100

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
sudo systemctl status node_exporter

# ทดสอบ metric
curl http://localhost:9100/metrics | head -50

ถ้า curl return metric แบบ plain text พร้อม node_cpu_seconds_total, node_memory_MemAvailable_bytes แสดงว่า exporter ทำงานปกติ

ติดตั้ง Node Exporter ด้วย Docker

ถ้าใช้ Docker หรือ Kubernetes การรัน node_exporter ในคอนเทนเนอร์จะสะดวกกว่า แต่ต้อง mount path ของ host เข้าไปเพราะ exporter ต้องอ่าน /proc, /sys, /rootfs จากตัว host ไม่ใช่จากคอนเทนเนอร์:

docker run -d \
  --name node-exporter \
  --restart unless-stopped \
  --net=host \
  --pid=host \
  -v /:/host:ro,rslave \
  quay.io/prometheus/node-exporter:v1.8.2 \
  --path.rootfs=/host \
  --collector.filesystem.mount-points-exclude='^/(sys|proc|dev|host|etc)($|/)'

ใช้ --net=host และ --pid=host เพื่อให้ exporter เห็น network และ process ของ host — ถ้ารันแบบ bridge network ค่าจะเป็นของคอนเทนเนอร์เท่านั้น

Docker Compose

services:
  node-exporter:
    image: quay.io/prometheus/node-exporter:v1.8.2
    container_name: node-exporter
    restart: unless-stopped
    network_mode: host
    pid: host
    volumes:
      - /:/host:ro,rslave
    command:
      - '--path.rootfs=/host'
      - '--collector.systemd'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'

Collector สำคัญ

Node Exporter มี collector กว่า 50 ตัว — แต่ละ collector เก็บ metric จากระบบคนละส่วน โดย default เปิดบาง collector และปิดบาง collector เพื่อลด overhead:

Collectorหน้าที่Default
cpuCPU utilization per core, modeเปิด
meminfoMemory usage, swap, cacheเปิด
diskstatsDisk I/O per deviceเปิด
filesystemDisk usage per mount pointเปิด
netdevNetwork interface trafficเปิด
loadavgLoad average 1/5/15 minuteเปิด
systemdSystemd service statusปิด (ต้องเปิดเอง)
processesProcess count, stateปิด
textfileCustom metric จากไฟล์ปิด
ntpNTP clock syncปิด

เปิด collector เพิ่มด้วย flag --collector.<name> และปิดด้วย --no-collector.<name> — เปิดเฉพาะที่จำเป็น เพราะบาง collector เช่น supervisord, wifi ไม่จำเป็นในเครื่อง server

เพิ่มใน Prometheus Config

scrape_configs:
  - job_name: 'node'
    scrape_interval: 15s
    static_configs:
      - targets:
          - 'server1:9100'
          - 'server2:9100'
          - 'server3:9100'
        labels:
          environment: 'production'
          role: 'web'

สำหรับ environment ที่ server เพิ่ม/ลดบ่อย เช่น autoscaling cluster ควรใช้ service discovery แทน static_configs — เช่น Consul, Kubernetes, EC2 SD

CPU Usage

# CPU usage ทุก mode รวมกัน (เป็น rate ต่อวินาที)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# CPU usage เฉพาะ user space
avg by (instance) (rate(node_cpu_seconds_total{mode="user"}[5m])) * 100

# CPU iowait สูง = disk bottleneck
avg by (instance) (rate(node_cpu_seconds_total{mode="iowait"}[5m])) * 100

Memory Usage

# Memory usage เป็น percentage (ใช้ MemAvailable ที่แม่นกว่า MemFree)
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100

# Swap usage
(node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes) / node_memory_SwapTotal_bytes * 100

ใช้ node_memory_MemAvailable_bytes แทน node_memory_MemFree_bytes เพราะ Linux kernel ใช้ RAM ว่างเป็น cache — MemAvailable บอกจริง ๆ ว่าเหลือเท่าไหร่ที่ application ใช้ได้

Disk Usage & I/O

# Disk ใช้งานเท่าไหร่ต่อ mount point
(node_filesystem_size_bytes{fstype!~"tmpfs|overlay"} - node_filesystem_avail_bytes)
  / node_filesystem_size_bytes * 100

# Disk read/write rate
rate(node_disk_read_bytes_total[5m])
rate(node_disk_written_bytes_total[5m])

# Disk latency เฉลี่ย
rate(node_disk_read_time_seconds_total[5m]) / rate(node_disk_reads_completed_total[5m])

Network

# Network throughput per interface
rate(node_network_receive_bytes_total{device!~"lo|docker.*"}[5m])
rate(node_network_transmit_bytes_total{device!~"lo|docker.*"}[5m])

# Network error rate
rate(node_network_receive_errs_total[5m])

Load Average

# Load average normalize ด้วยจำนวน CPU core
node_load5 / count by (instance) (node_cpu_seconds_total{mode="idle"})

Load average ค่ามากกว่าจำนวน core แปลว่า process ยืนต่อคิว — ถ้า load/core เกิน 1.0 เป็นระยะเวลานาน เครื่องมี bottleneck

Textfile Collector

textfile collector เป็นของพิเศษที่อ่าน metric จากไฟล์ .prom ใน directory ที่กำหนด — เหมาะกับ metric ที่ไม่ได้มาจาก HTTP server แต่มาจาก cron job หรือ backup script:

# เปิด collector แล้วกำหนด directory
node_exporter --collector.textfile.directory=/var/lib/node_exporter/textfile_collector

# สร้าง metric จาก script
cat > /var/lib/node_exporter/textfile_collector/backup.prom.$$ <<EOF
# HELP backup_last_success_timestamp Last successful backup
# TYPE backup_last_success_timestamp gauge
backup_last_success_timestamp $(date +%s)
EOF
mv /var/lib/node_exporter/textfile_collector/backup.prom.$$ \
   /var/lib/node_exporter/textfile_collector/backup.prom

เขียนแบบ atomic ด้วย temp file แล้ว mv เพื่อไม่ให้ exporter อ่านไฟล์กลางทาง — ป้องกัน corrupt metric

Best Practices

  • ติดตั้ง node_exporter ทุก server โดย default — baseline metric สำคัญกว่าที่คิด
  • เปิด systemd collector ถ้าเครื่องมี service สำคัญที่ต้อง alert เมื่อล่ม
  • ปิด collector ที่ไม่ใช้ เช่น mdadm, nfs, zfs ถ้าไม่ได้ใช้เทคโนโลยีเหล่านั้น
  • ใช้ flag --web.listen-address=127.0.0.1:9100 ถ้าใช้ reverse proxy กั้นหน้า — ไม่ให้ expose ต่อ public network ตรง ๆ
  • เปิด firewall เฉพาะ Prometheus server ให้เข้า port 9100 ได้
  • ใช้ Grafana dashboard 1860 (Node Exporter Full) เป็น starting point — ครอบคลุม metric ทั้งหมด
  • Alert บน up == 0 เพื่อจับ exporter ล่ม และ host unreachable
  • ใช้ label instance สม่ำเสมอระหว่าง production/staging — ช่วยทำ dashboard ที่ drill down ได้

Troubleshooting

อาการสาเหตุวิธีแก้
Port 9100 ไม่เปิดFirewall block หรือ service ล่มsystemctl status node_exporter, ss -tlnp | grep 9100
Metric filesystem ซ้ำDocker/Kubernetes mount pointเพิ่ม --collector.filesystem.mount-points-exclude
Metric ขาด (ไม่มี systemd)Collector ไม่ได้เปิดเพิ่ม --collector.systemd ใน ExecStart
Memory usage ผิดใช้ MemFree แทน MemAvailableเปลี่ยนมาใช้ node_memory_MemAvailable_bytes
Disk usage ผิด (tmpfs ถูกนับ)ไม่ได้กรอง fstypeเพิ่ม fstype!~"tmpfs|overlay" ใน query

สรุป

Node Exporter เป็น baseline exporter ที่ทุกระบบ monitoring ต้องมี — ครอบคลุม metric ของ OS, disk, network, process ให้ครบในตัวเดียว ติดตั้งง่าย ใช้ทรัพยากรน้อย และมี Grafana dashboard สำเร็จรูปให้ใช้ทันที การเปิด collector ที่เหมาะสมและตั้งค่า scrape interval อย่างถูกต้องจะช่วยให้ได้ visibility ของ infrastructure ที่สมบูรณ์โดยใช้ effort น้อยที่สุด

ขั้นตอนถัดไปคือเรียนรู้ PromQL เพื่อเขียน query ที่ดึง insight จาก metric เหล่านี้ได้ลึกกว่า dashboard มาตรฐาน และเขียน custom exporter เพื่อ monitor application ของตัวเอง