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
'네트워크 공부 & 실습 > 네트워크 실습' 카테고리의 다른 글
| [Network] DNS 구축으로 IP 주소 대신 문자열 사용하기 (2) | 2025.06.02 |
|---|---|
| [Network] AWS 탈퇴 및 Blog 스크랩 자동화 스크립트 셋팅하기 (to 라즈베리파이) (2) | 2025.05.28 |
| [VPN] Raspberry Pi 를 VPN 의 EndPoint 로 만들기 (1) | 2025.05.26 |
| [Network]포트가 열렸는지 확인하는 가장 간단한 방법 – Netcat(nc) 개념과 실습 (0) | 2025.05.07 |
| [Network] SNMP 실습편 – snmpget과 snmpwalk로 장비 상태 확인하기 (2부) (1) | 2025.04.22 |