네트워크 공부 & 실습/네트워크 실습

[Network] 모니터링 자동화 스크립트 파일 -> 라즈베리 파이로 이동하기

강_토발즈 2025. 5. 27. 23:50

1. 왜 라즈베리파이로 옮기나

실습한 모니터링 시스템은 데스크탑이 켜지면 15분 마다 정보를 수집한다. 물론 전력이 있을땐 수집 작업을 하지 않고, 전력이 들어올 때만 일하는게 좋다고 생각하지만, 모름지기 모니터링이란 쉬지 않고 일해야 가치가 있는 법! 그리고 집 안에는 쉬지 않고 일하는 NAS 와 라즈베리파이 자신이 있다. 이 기기들의 정보들도 계속해서 모니터링 해야 하기 때문에 24시간 작동하는 라즈베리 파이에 자동화 프로세스를 옮기도록 한다.

 

 

2. 라즈베리파이에 필요한 셋팅

 

2-1. SNMP 클라이언트 및 에이전트 구성

 

라즈베리파이는 다른 장비(예: 데스크탑)의 시스템 정보를 SNMP로 수집해야 하고, 자기 자신의 상태도 함께 모니터링하고자 한다. 이를 위해 SNMP 클라이언트SNMP 에이전트 모두가 필요하다.
일단 데스크탑 정보를 수집하는 현 상태를 먼저 구현하고, 성공적으로 마치면 라즈베리 파이의 정보도 수집하도록 확장하도록 한다. 일단 두 패키지는 모두 설치한다.

 

sudo apt update
sudo apt install snmp 
sudo apt install snmpd

 

패키지 설명

  • snmp: SNMP 클라이언트 도구 패키지로, 다른 장비에 SNMP 요청을 보내는 명령어(snmpget, snmpwalk)가 포함되어 있다.
  • snmpd: SNMP 에이전트 데몬으로, 라즈베리파이 자신이 SNMP 요청을 받을 수 있게 만들어준다. 이를 통해 라즈베리파이 내부의 CPU, 메모리, 디스크 사용량 등을 SNMP로도 수집할 수 있다.

즉, 이 두 패키지를 설치하면 라즈베리파이는 동시에 **수집자(client)**이자 피수집 대상(agent) 역할을 할 수 있게 된다.

 

수집 대상의 MIB 파일 패키지 설치

sudo apt install snmp-mibs-downloader

 

 

2-2. 파이썬 라이브러리 설치

 

파이썬 스크립트 파일을 실행하기 위해서 필요한 (가상화)라이브러리들을 설치한다.

 

sudo apt install python3 python3-pip python3-venv -y

python3 -m venv venv

source venv/bin/activate

pip install flask pymysql

pip list #설치 패키지 확인

 

 

3. 환경설정 파일 작성 – 여러 장비 수집을 위한 구조로 변경

 

#!/bin/bash

NOW=$(date '+%Y-%m-%d %H:%M:%S')
DATE_FILE=$(date '+%Y-%m-%d')
LOG_DIR="/home/kang/Documents/monitoring/csv_logs"
mkdir -p "$LOG_DIR"

# 수집 대상 장비 목록 (이름:IP:METHOD)
DEVICES=(
  "desktop:192.168.0.27:snmp"
  "raspberrypi:127.0.0.1:local"
)

for DEVICE in "${DEVICES[@]}"; do
  IFS=':' read -r NAME IP METHOD <<< "$DEVICE"

  if [ "$METHOD" == "snmp" ]; then
    COMMUNITY="public"
    CPU_LOAD=$(snmpget -v2c -c $COMMUNITY $IP UCD-SNMP-MIB::laLoad.1 | awk '{print $NF}')
    MEM_TOTAL=$(snmpget -v2c -c $COMMUNITY $IP UCD-SNMP-MIB::memTotalReal.0 | awk '{print $(NF-1)}')
    MEM_AVAIL=$(snmpget -v2c -c $COMMUNITY $IP UCD-SNMP-MIB::memAvailReal.0 | awk '{print $(NF-1)}')
    MEM_USED=$((MEM_TOTAL - MEM_AVAIL))
    DISK_USED=$(snmpget -v2c -c $COMMUNITY $IP UCD-SNMP-MIB::dskPercent.1 | awk '{print $NF}')
    NET_IN=$(snmpget -v2c -c $COMMUNITY $IP IF-MIB::ifInOctets.2 | awk '{print $NF}')
    NET_OUT=$(snmpget -v2c -c $COMMUNITY $IP IF-MIB::ifOutOctets.2 | awk '{print $NF}')
    GPU_UTIL=""; GPU_MEM=""; GPU_TEMP=""

  elif [ "$METHOD" == "local" ]; then
    CPU_LOAD=$(uptime | awk -F'load average: ' '{print $2}' | cut -d',' -f1 | xargs)
    MEM_TOTAL=$(free | awk '/Mem:/ {print $2}')
    MEM_USED=$(free | awk '/Mem:/ {print $3}')
    DISK_USED=$(df / | awk 'END{print $(NF-1)}' | tr -d '%')
    NET_IN=$(cat /proc/net/dev | awk '/eth0|wlan0/ {gsub(/:/,""); print $2}' | head -n1)
    NET_OUT=$(cat /proc/net/dev | awk '/eth0|wlan0/ {gsub(/:/,""); print $10}' | head -n1)
    GPU_UTIL=""; GPU_MEM=""; GPU_TEMP=""
  fi

  CSV_LINE="$NOW,$NAME,$CPU_LOAD,$MEM_USED,$MEM_TOTAL,$DISK_USED,$NET_IN,$NET_OUT,$GPU_UTIL,$GPU_MEM,$GPU_TEMP"
  echo "$CSV_LINE" >> "$LOG_DIR/$DATE_FILE.csv"
done

 

 

 

수집 대상 장비를 Desktop 으로 고정하고 localhost 로 수집하던 방식에서, 배열 안에 두 개의 장비를 넣고 순회하여 정보를 수집하도록 한다.

 

 

4. 라즈베리 파이 정보 저장을 위한 테이블 생성

 

NAS 에 접속하여, 사용하는 데이터베이스에 접속한 후 데스크탑 수집 정보를 저장할 테이블, 라즈베리 파이 수집 정보를 저장 할 테이블을 작성하고 실행한다.

CREATE TABLE sys_metrics_desktop (
    timestamp    DATETIME PRIMARY KEY,
    hostname     VARCHAR(32),
    cpu_load     FLOAT,
    mem_used     INT,
    mem_total    INT,
    disk_used    INT,
    net_in       BIGINT,
    net_out      BIGINT,
    gpu_util     INT,
    gpu_mem      INT,
    gpu_temp     INT
);

 

CREATE TABLE sys_metrics_raspberrypi (
    timestamp    DATETIME,
    hostname     VARCHAR(32),
    cpu_load     FLOAT,
    mem_used     INT,
    mem_total    INT,
    disk_used    INT,
    net_in       BIGINT,
    net_out      BIGINT,
    PRIMARY KEY (timestamp, hostname)
);

 

 

5. DB insert 파이썬 파일 수정 (2개의 테이블로 Insert 실행)

 

import csv
import os
import pymysql
from datetime import datetime

# 공통 설정
DATE = datetime.now().strftime('%Y-%m-%d')
CSV_FILES = {
    'desktop': f'/home/kang/Documents/monitoringProject/csv_logs/{DATE}.csv',
    'raspberrypi': f'/home/kang/Documents/monitoringProject/csv_logs/{DATE}_rs.csv'
}

TABLES = {
    'desktop': 'sys_metrics_desktop',
    'raspberrypi': 'sys_metrics_raspberrypi'
}

DB_CONFIG = {
    'host': '192.168.0.20',
    'port': 3306,
    'user': 'root',
    'password': '',
    'database': 'monitoring',
    'charset': 'utf8'
}

conn = pymysql.connect(**DB_CONFIG)

with conn:
    with conn.cursor() as cur:
        for device, path in CSV_FILES.items():
            if not os.path.exists(path):
                continue

            inserted = 0
            with open(path, newline='') as csvfile:
                reader = csv.reader(csvfile)
                for row in reader:
                    timestamp = row[0]
                    hostname = row[1]

                    # 중복 확인
                    cur.execute(
                        f"SELECT COUNT(*) FROM {TABLES[device]} WHERE timestamp=%s AND hostname=%s",
                        (timestamp, hostname)
                    )
                    if cur.fetchone()[0] > 0:
                        continue

                    if device == 'desktop':
                        sql = f"""INSERT INTO {TABLES[device]} (
                            timestamp, hostname, cpu_load, mem_used, mem_total, disk_used,
                            net_in, net_out, gpu_util, gpu_mem, gpu_temp
                        ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""
                        cur.execute(sql, tuple(row))
                    else:
                        sql = f"""INSERT INTO {TABLES[device]} (
                            timestamp, hostname, cpu_load, mem_used, mem_total, disk_used,
                            net_in, net_out
                        ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)"""
                        cur.execute(sql, tuple(row[:8]))  # GPU 정보 제외

                    inserted += 1

            print(f"{inserted} new rows inserted into {TABLES[device]}")
        conn.commit()

 

 

6. 시스템 정보 수집 후 Desktop , Raspberry PI 데이터를 분리해서 저장하도록 스크립트 파일 수정

 

import csv
import os
import pymysql
from datetime import datetime

DATE = datetime.now().strftime('%Y-%m-%d')
CSV_FILES = {
    'desktop': f'/home/kang/Documents/monitoringProject/csv_logs/{DATE}.csv',
    'raspberrypi': f'/home/kang/Documents/monitoringProject/csv_logs/{DATE}_rs.csv'
}

TABLES = {
    'desktop': 'sys_metrics_desktop',
    'raspberrypi': 'sys_metrics_raspberrypi'
}

DB_CONFIG = {
    'host': '192.168.0.20',
    'port': 3306,
    'user': 'root',
    'password': '',
    'database': 'monitoring',
    'charset': 'utf8'
}

conn = pymysql.connect(**DB_CONFIG)

with conn:
    with conn.cursor() as cur:
        for device, path in CSV_FILES.items():
            if not os.path.exists(path):
                continue

            inserted = 0
            with open(path, newline='') as csvfile:
                reader = csv.reader(csvfile)
                for row in reader:
                    timestamp = row[0]
                    hostname = row[1]
                     # 중복 확인
                    cur.execute(
                        f"SELECT COUNT(*) FROM {TABLES[device]} WHERE timestamp=%s AND hostname=%s",
                        (timestamp, hostname)
                    )
                    if cur.fetchone()[0] > 0:
                        continue

                    if device == 'desktop':
                        # GPU 정보 포함한 정확히 11개 필드 사용
                        if len(row) >= 11:
                            sql = f"""INSERT INTO {TABLES[device]} (
                                timestamp, hostname, cpu_load, mem_used, mem_total, disk_used,
                                net_in, net_out, gpu_util, gpu_mem, gpu_temp
                            ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""
                            cleaned = [val if val.strip() != '' else None for val in row[:11]]
                            cur.execute(sql, tuple(cleaned))
                    else:
                        # GPU 없는 8개 필드만 사용
                        if len(row) >= 8:
                            sql = f"""INSERT INTO {TABLES[device]} (
                                timestamp, hostname, cpu_load, mem_used, mem_total, disk_used,
                                net_in, net_out
                            ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)"""
                            cur.execute(sql, tuple(row[:8]))

                    inserted += 1

            print(f"{inserted} new rows inserted into {TABLES[device]}")
        conn.commit()

 

 

7. 결과 확인

 

데이터들이 분리되어 잘 저장되고 있다!!

 

 

8. 마무리

 

생각보다 이사를 하는데 시간이 오래 걸렸다. 디렉터리 경로에 대한 오타가 있어서 시간이 걸렸고, 라즈베리 파이 장치를 추가하면서 두 장비에 대한 정보 분리, 분리된 데이터 삽입까지 설계하였다. 마지막으로 라즈베리 파이는 24시간 동작하지만, 데스크탑은 사용하지 않으면 전원을 끌 것이기 때문에 에러를 방지하기 위해 스크립트를 조금 수정하였다. 

 

이제 이 테이블에 있는 정보들을 라즈베리 파이에서 웹 을 띄워 동작하도록 하고 모니터링 시스템 이주 계획은 마무리 해야겠다!

 

#!/bin/bash
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin

NOW=$(date '+%Y-%m-%d %H:%M:%S')
DATE_FILE=$(date '+%Y-%m-%d')
LOG_DIR="/home/kang/Documents/monitoringProject/csv_logs"
LOG_FILE="/home/kang/Documents/monitoringProject/log_scrape.txt"
mkdir -p "$LOG_DIR"

# 수집 대상 장비 목록 (이름:IP:METHOD)
DEVICES=(
  "desktop:192.168.0.27:snmp"
  "raspberrypi:127.0.0.1:local"
)

for DEVICE in "${DEVICES[@]}"; do
  IFS=':' read -r NAME IP METHOD <<< "$DEVICE"

  if [ "$METHOD" == "snmp" ]; then
    COMMUNITY="public"

    {
      CPU_LOAD=$(snmpget -v2c -c $COMMUNITY $IP UCD-SNMP-MIB::laLoad.1 | awk '{print $NF}')
      MEM_TOTAL=$(snmpget -v2c -c $COMMUNITY $IP UCD-SNMP-MIB::memTotalReal.0 | awk '{print $(NF-1)}')
      MEM_AVAIL=$(snmpget -v2c -c $COMMUNITY $IP UCD-SNMP-MIB::memAvailReal.0 | awk '{print $(NF-1)}')
      MEM_USED=$((MEM_TOTAL - MEM_AVAIL))
      DISK_USED=$(snmpget -v2c -c $COMMUNITY $IP UCD-SNMP-MIB::dskPercent.1 | awk '{print $NF}')
      NET_IN=$(snmpget -v2c -c $COMMUNITY $IP IF-MIB::ifInOctets.2 | awk '{print $NF}')
      NET_OUT=$(snmpget -v2c -c $COMMUNITY $IP IF-MIB::ifOutOctets.2 | awk '{print $NF}')
      GPU_UTIL=""; GPU_MEM=""; GPU_TEMP=""
    } || {
      echo "[$NOW] SNMP 요청 실패: $NAME ($IP)" >> "$LOG_FILE"
      continue
    }

  elif [ "$METHOD" == "local" ]; then
    CPU_LOAD=$(uptime | awk -F'load average: ' '{print $2}' | cut -d',' -f1 | xargs)
    MEM_TOTAL=$(free | awk '/Mem:/ {print $2}')
    MEM_USED=$(free | awk '/Mem:/ {print $3}')
    DISK_USED=$(df / | awk 'END{print $(NF-1)}' | tr -d '%')
    NET_IN=$(cat /proc/net/dev | awk '/eth0|wlan0/ {gsub(/:/,""); print $2}' | head -n1)
    NET_OUT=$(cat /proc/net/dev | awk '/eth0|wlan0/ {gsub(/:/,""); print $10}' | head -n1)
    GPU_UTIL=""; GPU_MEM=""; GPU_TEMP=""
  fi

  # 파일명 분기: 라즈베리파이는 _rs.csv 로 저장
  if [ "$NAME" == "raspberrypi" ]; then
    OUTFILE="$LOG_DIR/${DATE_FILE}_rs.csv"
  else
    OUTFILE="$LOG_DIR/${DATE_FILE}.csv"
  fi

  CSV_LINE="$NOW,$NAME,$CPU_LOAD,$MEM_USED,$MEM_TOTAL,$DISK_USED,$NET_IN,$NET_OUT,$GPU_UTIL,$GPU_MEM,$GPU_TEMP"
  echo "$CSV_LINE" >> "$OUTFILE"
done