SNMP테스트서버: 두 판 사이의 차이

잉여 위키
둘러보기로 이동 검색으로 이동
새 문서: # Rocky Linux 9 · Zyxel 스위치 IPT 장애 모니터링 서버 구성 가이드 작성일: 2026-09-10 · 구성안 v1.0 ## 0. 목적·범위·검증 수준 **목적:** Zyxel L3/L2 → PoE 전화기 → PC 환경에서 전화 연결 실패 시 네트워크 혼잡·패킷 폐기·링크/PoE 이벤트가 동반되는지 기록한다. 콜서버의 TCP/UDP 5060 tcpdump는 별도로 수행하며 이 문서는 해당 설정을 변경하지 않는다. - [확정] KVM VM / Rocky Li...
 
편집 요약 없음
 
(같은 사용자의 중간 판 2개는 보이지 않습니다)
1번째 줄: 1번째 줄:
# Rocky Linux 9 · Zyxel 스위치 IPT 장애 모니터링 서버 구성 가이드
= Rocky Linux 9 통합 모니터링 서버 구축 가이드 - 초보자용 =
: Telegraf / InfluxDB / Grafana / Prometheus / Node Exporter / Windows Exporter / Loki / Alloy / rsyslog


작성일: 2026-09-10 · 구성안 v1.0
작성 목적: 처음 모니터링 서버를 설치하는 사용자가 "왜 이 프로그램이 필요한지", "어떤 순서로 설치하는지", "어디까지 정상이어야 다음 단계로 넘어가는지"를 이해하면서 구축할 수 있도록 작성한다.


## 0. 목적·범위·검증 수준
본 문서는 실제 운영 과정에서 구성하고 점검한 내용을 바탕으로 정리했으며, 실제 IP 주소와 인증 정보는 문서용 예시 값으로 변경하였다.


**목적:** Zyxel L3/L2 → PoE 전화기 → PC 환경에서 전화 연결 실패 시 네트워크 혼잡·패킷 폐기·링크/PoE 이벤트가 동반되는지 기록한다. 콜서버의 TCP/UDP 5060 tcpdump는 별도로 수행하며 이 문서는 해당 설정을 변경하지 않는다.
'''중요:''' 아래 명령을 한 번에 모두 실행하지 않는다. 각 절의 '''정상 확인''' 항목을 통과한 뒤 다음 단계로 진행한다.


- [확정] KVM VM / Rocky Linux 9, Zyxel L3/L2, 전화기 뒤 PC 연결, 콜서버 SIP 캡처 병행 계획.
__TOC__
- [미확인] 스위치 모델·펌웨어·대수·주소·Voice VLAN·콜서버 경로, SNMPv3 알고리즘, sFlow 지원, CPU/PoE 전용 OID.
- [제안] 먼저 **Telegraf + InfluxDB OSS 2.x + Grafana OSS + rsyslog**를 RPM/systemd로 직접 설치한다. 전체 포트 30초, 의심 포트/업링크 5초에서 시작하고 필요한 포트만 1초로 단축한다.
- **sFlow는 조건부 단계:** Akvorado가 후보지만 모델의 exporter 지원과 배포 릴리스 설정이 확정되지 않았으므로 §12는 준비·배포 검토·수신 검증 절차다. 아직 운영 기동을 보장하는 완성 설정이 아니다. SNMP/Syslog 기본 구성은 독립적으로 진행 가능하다.
- 실제 Rocky VM·Zyxel 장비에서 실행 시험한 문서는 아니다. 구성 예시를 정적 점검했으며 각 단계의 현장 검증을 통과한 후 진행한다.


### 목차
== 0. 이 문서에서 만들 시스템 ==


1. 구조와 준비값
=== 0.1 먼저 전체 구조부터 이해하기 ===
2. 변경 영향·백업
3. 기본 패키지·시간 동기화
4. 저장소와 모니터링 패키지
5. InfluxDB 초기화·토큰 분리
6. SNMP/MIB 사전 조회
7. Telegraf 수집 구성
8. Grafana 데이터소스·패널
9. Syslog 수신·회전
10. 방화벽·접속 검증
11. 시험 운영과 장애 분석
12. sFlow 조건부 추가
13. 백업·롤백·업그레이드
14. 장애 확인표·인수 체크리스트
15. 공식 자료


## 1. 구조와 준비값
모니터링을 처음 구성할 때 가장 혼동하기 쉬운 부분은 "Grafana가 모든 데이터를 직접 수집한다"고 생각하는 것이다.


| 서비스 | 설치 방식 | 연결/저장 |
Grafana는 주로 '''보여주는 역할'''을 한다. 실제 데이터 수집과 저장은 다른 프로그램이 담당한다.
|---|---|---|
| Telegraf 1.x | RPM / telegraf.service | 스위치 UDP/161 조회 → 로컬 InfluxDB |
| InfluxDB OSS 2.x | RPM / influxdb.service | 127.0.0.1:8086, 30일 raw bucket |
| Grafana OSS | RPM / grafana-server.service | 127.0.0.1:3000, SSH 터널 접속 |
| rsyslog 8.x | Rocky RPM / rsyslog.service | UDP/514 → /var/log/zyxel/<송신IP>/events.log |
| Akvorado 배포 스택 | 별도 검토 후 공식 Docker Compose | sFlow UDP/6343 → ClickHouse 등 |


SNMP Trap UDP/162는 이번 기본 구성에 포함하지 않는다. Rocky를 조회하는 SNMP agent인 `snmpd`도 불필요하다. 이 서버는 SNMP 조회자다.
이 문서에서는 데이터를 크게 두 종류로 나눈다.


### 준비표 — 모든 `<...>`를 실제 값으로 교체
# '''Metric''' : CPU 사용률, 메모리 사용률, 포트 트래픽, Ping 손실률처럼 숫자로 표현되는 값
# '''Log''' : Syslog, 서버 이벤트, NAS 접근 기록처럼 문자열로 기록되는 이벤트


| 자리표시자 | 의미 |
전체 데이터 흐름은 다음과 같다.
|---|---|
| `<COLLECTOR_IP>` | Rocky VM 고정 IPv4 |
| `<FW_ZONE>` | 관리 NIC에 실제 적용된 firewalld zone |
| `<SWITCH_SOURCE_CIDR>` | 실제 Syslog 송신 IP /32 또는 승인된 스위치 관리 대역 |
| `<SWITCH_IP>` / `<SWITCH_NAME>` | 수집 대상 관리 IP / 식별 이름 |
| `<IFINDEX>` / `<PORT_LABEL>` | SNMP 조회로 확인한 인덱스 / 포트명 |
| `<CALLSERVER_IP>` | 콜서버 IP |
| `<SNMP_USER>` 등 | 읽기 전용 SNMPv3 계정·인증값 |
| `<NTP_SERVER>` | 콜서버/스위치와 맞춘 승인된 시간 서버 |


소규모 시작 가정: 기본 구성 4 vCPU / RAM 8GB / SSD 100~200GB. Akvorado 동시 운영은 8 vCPU / RAM 16~32GB / SSD 300~500GB부터 부하 시험한다. 이는 보장 사양이 아니며 포트 수·샘플 PPS·저장량으로 조정한다. KVM CPU 과할당, 메모리 ballooning 및 호스트 디스크 포화를 피한다.
<syntaxhighlight lang="bash" line>
[Network Switch]
      |
      | SNMPv3
      v
  Telegraf
      |
      v
  InfluxDB
      |
      +-------------------+
                          |
[Linux / QNAP]            |
      |                  |
      | node_exporter    |
      v                  |
  Prometheus              |
      |                  |
      +-------------------+
                          |
[Network / Server / NAS]  |
      |                  |
      | Syslog            |
      v                  |
  rsyslog                |
      |                  |
      v                  |
  Log File              |
      |                  |
      v                  |
    Alloy                |
      |                  |
      v                  |
    Loki                |
      |                  |
      +-------------------+
                          |
                          v
                      Grafana
</syntaxhighlight>


VM은 VirtIO NIC를 KVM 관리 브리지에 연결하고, 스위치까지 라우팅 가능한 고정 IP를 사용한다. SNMP/sFlow에는 미러 포트나 promiscuous 설정이 필요 없다. 같은 VLAN이 필수도 아니다. NAT는 SNMP 접근제어와 sFlow 송신자 식별을 복잡하게 하므로 가능한 관리망 브리지/라우팅 연결을 사용한다.
즉 다음과 같이 기억하면 된다.


### 관측 범위 한계
{| class="wikitable"
! 프로그램
! 하는 일
! 쉽게 표현하면
|-
| Telegraf
| SNMP 장비의 값을 주기적으로 읽음
| 네트워크 장비용 수집기
|-
| InfluxDB
| Telegraf가 수집한 시계열 값을 저장
| SNMP 데이터 창고
|-
| Prometheus
| Exporter가 공개한 Metric을 주기적으로 가져와 저장
| 서버 Metric 수집기 + 데이터베이스
|-
| Node Exporter
| Linux/QNAP의 CPU, 메모리, 디스크, 네트워크 Metric 제공
| Linux 상태 측정기
|-
| Windows Exporter
| Windows 성능 카운터 Metric 제공
| Windows 상태 측정기
|-
| rsyslog
| 장비/서버가 보내는 Syslog를 수신하여 파일로 저장
| 로그 수신기
|-
| Alloy
| 로그 파일을 읽고 필요한 항목을 분리하여 Loki로 전달
| 로그 전달/가공기
|-
| Loki
| 로그를 저장하고 검색 가능하게 함
| 로그 데이터베이스
|-
| Grafana
| 위 데이터들을 Dashboard와 Alert로 표현
| 통합 화면
|}


- 전화기 연결 물리 포트의 카운터는 전화기와 PC 합산이다. IF-MIB만으로 Voice/Data를 분리할 수 없다.
=== 0.2 왜 하나의 프로그램으로 모두 처리하지 않는가 ===
- 같은 L2 내부에서 끝나는 트래픽은 L3만 관측해서는 보이지 않을 수 있다.
- 수집 경로가 폭주 구간과 겹치면 장애 때 데이터도 빠진다. OOB가 없으면 코어에 가까운 안정적 관리 경로를 확보한다.
- 1초 SNMP는 1초 구간 평균이며 장비 내부 카운터 갱신이 1초보다 느릴 수 있다. Microburst 부재를 증명하지 못한다.


## 2. 변경 영향·백업
SNMP, 서버 Metric, 로그는 데이터 성격이 서로 다르다.


전제는 **신규 전용 VM**이다. 기존 운영 서버에 적용한다면 같은 이름의 설정을 덮어쓰지 말고 병합한다. 기존 Telegraf 입력/출력, rsyslog 입력 포트, Grafana, InfluxDB 데이터가 있으면 본 초기화 절차를 중단한다.
예를 들어 스위치 포트 트래픽은 누적 Counter를 주기적으로 읽어 증가량을 계산해야 한다. 반면 Syslog는 특정 시점에 발생한 문자열 이벤트이다.


위험 및 롤백 원칙:
따라서 본 구성에서는 각 도구가 가장 잘하는 역할을 분리한다.


- 패키지 설치 자체로 전화 경로를 변경하지 않는다. 다만 고주기 SNMP/sFlow는 스위치 관리 CPU와 저장량을 증가시킬 수 있다.
* Switch SNMP → Telegraf + InfluxDB
- rsyslog 재시작 동안 로그 수신에 짧은 공백이 발생할 수 있다.
* Linux / Windows / QNAP OS Metric → Prometheus
- 보존기간 만료/로그 회전은 오래된 데이터를 자동 제거한다. 장애 증거는 만료 전에 별도 보관한다.
* Syslog → rsyslog + Alloy + Loki
- 방화벽은 기존 zone/SSH를 유지하고 특정 송신자 규칙만 추가한다. 실패하면 추가한 규칙만 제거한다.
* 최종 화면 → Grafana
- `firewall-cmd --complete-reload`는 설정 백업 복원이 아니다. 사용하지 않는다.
- VM 스냅샷은 단기 변경 복구용이며 별도 백업을 대체하지 않는다. 복원하면 스냅샷 이후 수집 데이터가 사라질 수 있다.


root 셸에서 실행한다. 아래 백업 변수는 이 작업용이며 동일 셸에서 유지한다.
이 구조를 이해하면 장애 발생 시 어느 부분을 확인해야 하는지도 쉽게 구분할 수 있다.


```bash
예를 들어 Grafana에서 Linux CPU가 보이지 않는다면 다음 순서로 생각한다.
umask 077
 
MON_BACKUP="/root/ipt-monitor-backup-$(date +%Y%m%d-%H%M%S)"
<syntaxhighlight lang="bash" line>
install -d -m 700 "$MON_BACKUP"
Grafana 문제인가?
cp -a /etc/rsyslog.conf "$MON_BACKUP/"
    ↓
cp -a /etc/rsyslog.d "$MON_BACKUP/"
Prometheus에 데이터가 있는가?
cp -a /etc/chrony.conf "$MON_BACKUP/"
    ↓
cp -a /etc/firewalld "$MON_BACKUP/"
Prometheus가 node_exporter를 수집하고 있는가?
firewall-cmd --list-all-zones > "$MON_BACKUP/firewall-runtime.txt"
    ↓
firewall-cmd --permanent --list-all-zones > "$MON_BACKUP/firewall-permanent.txt"
node_exporter가 정상 실행 중인가?
rpm -qa | sort > "$MON_BACKUP/packages-before.txt"
</syntaxhighlight>
```
 
== 1. 예제 환경과 주소 ==
 
실제 운영 IP를 문서에 노출하지 않기 위해 RFC 5737 문서용 주소를 사용한다.
 
{| class="wikitable"
! 대상
! 문서용 IP
! 역할
|-
| Monitoring Server
| 192.0.2.10
| Grafana / Prometheus / InfluxDB / Telegraf / Loki / Alloy / rsyslog
|-
| Linux Server
| 192.0.2.20
| node_exporter
|-
| Windows Server
| 192.0.2.30
| windows_exporter
|-
| QNAP NAS
| 198.51.100.10
| node_exporter / SNMP / Syslog
|-
| Switch-01
| 203.0.113.11
| SNMP / Syslog
|-
| Switch-02
| 203.0.113.12
| SNMP / Syslog
|-
| Switch-03
| 203.0.113.13
| SNMP / Syslog
|}
 
실제 설치 시 위 주소를 자신의 환경에 맞게 변경한다.
 
== 2. 설치 전에 알아둘 용어 ==
 
=== 2.1 Metric ===
 
Metric은 시간에 따라 변화하는 숫자 데이터이다.
 
예:
 
* CPU 32%
* Memory 71%
* Switch Port RX 120 Mbps
* Ping Packet Loss 0%
* HDD Temperature 43°C
 
이런 값은 시간 흐름에 따라 그래프로 보는 것이 중요하다.
 
=== 2.2 Counter와 Gauge ===
 
SNMP에서 자주 만나는 개념이다.
 
'''Gauge'''는 현재 값을 의미한다.
 
예:
 
<syntaxhighlight lang="bash" line>
CPU Usage = 35
Temperature = 44
operStatus = 1
</syntaxhighlight>
 
'''Counter'''는 계속 증가하는 누적값이다.
 
예:
 
<syntaxhighlight lang="bash" line>
ifHCInOctets = 1234567890123
</syntaxhighlight>
 
이 값을 그대로 그래프로 그리면 계속 증가하기만 한다.
 
그래서 실제 트래픽은 현재 값과 이전 값의 차이를 시간으로 나누어 계산한다.
 
<syntaxhighlight lang="bash" line>
현재 Counter - 이전 Counter
---------------------------
        경과 시간
</syntaxhighlight>
 
Octet은 8 bit이므로 bps를 구하려면 다시 8을 곱한다.
 
Grafana/Flux에서는 이를 derivative()로 처리한다.
 
=== 2.3 Label과 Tag ===
 
장비가 여러 대일 때 어떤 데이터가 어느 장비인지 구분하기 위한 값이다.
 
예:
 
<syntaxhighlight lang="bash" line>
source=203.0.113.11
if_name=port24
index=24
</syntaxhighlight>
 
Prometheus에서는 주로 '''label''', InfluxDB에서는 '''tag'''라고 부른다.
 
=== 2.4 Exporter ===
 
Prometheus가 직접 Linux 내부 명령을 실행하는 것은 아니다.
 
Exporter가 시스템 정보를 HTTP Metric 형식으로 공개하고 Prometheus가 이를 가져간다.
 
Node Exporter 예:
 
<syntaxhighlight lang="bash" line>
http://192.0.2.20:9100/metrics
</syntaxhighlight>
 
Windows Exporter:
 
<syntaxhighlight lang="bash" line>
http://192.0.2.30:9182/metrics
</syntaxhighlight>
 
== 3. Rocky Linux 9 기본 준비 ==


firewalld가 미설치/미실행이면 해당 명령 실패를 무시한 채 진행하지 말고, 현재 호스트 방화벽 구성과 SSH 허용부터 확인한다. KVM 콘솔을 확보한다. 스위치 설정도 GUI의 configuration backup으로 별도 내려받는다.
이 장에서는 모니터링 프로그램을 설치하기 전에 OS가 정상인지 확인한다.


## 3. 기본 패키지·시간 동기화
=== 3.1 시스템 상태 확인 ===


```bash
<syntaxhighlight lang="bash" line>
cat /etc/rocky-release
cat /etc/rocky-release
uname -m
uname -m
ip -br address
ip -br address
ip route
ip route
df -hT
df -hT
free -h
free -h
getenforce
getenforce
ss -lntup
ss -lntup
</syntaxhighlight>
각 명령의 의미:
{| class="wikitable"
! 명령
! 확인 내용
|-
| cat /etc/rocky-release
| Rocky Linux 버전
|-
| uname -m
| CPU Architecture, 일반적인 x86 서버는 x86_64
|-
| ip -br address
| 서버 IP 주소
|-
| ip route
| Gateway 및 Routing
|-
| df -hT
| 디스크 용량
|-
| free -h
| 메모리
|-
| getenforce
| SELinux 상태
|-
| ss -lntup
| 현재 사용 중인 TCP/UDP Port
|}
'''초보자 주의:''' SELinux가 Enforcing이라고 해서 바로 Disabled로 변경하지 않는다. 이후 permission 문제가 발생하면 원인을 확인하고 필요한 정책 또는 Context를 수정한다.
=== 3.2 기존 설정 백업 ===
기존 운영 서버에 구축하는 경우 설정을 변경하기 전에 백업한다.
<syntaxhighlight lang="bash" line>
umask 077
MON_BACKUP="/root/monitor-backup-$(date +%Y%m%d-%H%M%S)"
install -d -m 700 "$MON_BACKUP"
cp -a /etc/rsyslog.conf "$MON_BACKUP/" 2>/dev/null || true
cp -a /etc/rsyslog.d "$MON_BACKUP/" 2>/dev/null || true
cp -a /etc/chrony.conf "$MON_BACKUP/" 2>/dev/null || true
cp -a /etc/firewalld "$MON_BACKUP/" 2>/dev/null || true
rpm -qa | sort > "$MON_BACKUP/packages-before.txt"
</syntaxhighlight>
백업 경로 확인:
<syntaxhighlight lang="bash" line>
echo "$MON_BACKUP"
ls -al "$MON_BACKUP"
</syntaxhighlight>
=== 3.3 기본 관리 도구 설치 ===
<syntaxhighlight lang="bash" line>
dnf install -y \
    curl \
    ca-certificates \
    gnupg2 \
    vim-enhanced \
    net-snmp-utils \
    rsyslog \
    logrotate \
    chrony \
    tcpdump \
    iputils \
    sysstat \
    policycoreutils-python-utils \
    dnf-plugins-core \
    jq
</syntaxhighlight>
주요 패키지 용도:


dnf install -y curl ca-certificates gnupg2 vim-enhanced \
* net-snmp-utils : snmpget, snmpwalk 테스트
    net-snmp-utils rsyslog logrotate chrony tcpdump iputils \
* tcpdump : 실제 Packet 수신 확인
    sysstat policycoreutils-python-utils dnf-plugins-core
* jq : JSON 출력 가독성 개선
```
* sysstat : iostat 등 서버 I/O 점검
* chrony : 시간 동기화
* policycoreutils-python-utils : SELinux 진단/정책 작업


네트워크·SELinux는 일괄 비활성화하지 않는다. 신규 OS 업데이트/재부팅은 수집 시작 전 점검 시간에 수행한다.
=== 3.4 시간 동기화 ===


`/etc/chrony.conf`의 기존 시간 서버 정책을 확인하고, 승인된 서버로 맞춘다. 다음 한 줄은 설정 파일 예시이며 셸 명령이 아니다.
모니터링에서는 서버와 장비 시간이 맞지 않으면 장애 시각 비교가 어렵다.


```conf
Timezone 설정:
server <NTP_SERVER> iburst
```


```bash
<syntaxhighlight lang="bash" line>
timedatectl set-timezone Asia/Seoul
timedatectl set-timezone Asia/Seoul
</syntaxhighlight>
chronyd 시작:
<syntaxhighlight lang="bash" line>
systemctl enable --now chronyd
systemctl enable --now chronyd
systemctl restart chronyd
systemctl restart chronyd
</syntaxhighlight>
확인:
<syntaxhighlight lang="bash" line>
chronyc tracking
chronyc tracking
chronyc sources -v
chronyc sources -v
date -Ins
date -Ins
```
</syntaxhighlight>
 
'''정상 확인'''
 
* chronyd가 active
* chronyc sources에서 선택된 NTP Source 존재
* 현재 시간이 실제 시간과 크게 다르지 않음
 
== 4. InfluxDB와 Telegraf를 설치하는 이유 ==
 
스위치의 SNMP 값을 수집하려면 두 프로그램이 필요하다.
 
Telegraf는 장비에 SNMP Query를 보내는 역할을 한다.


정상 기준: 선택된 소스 `^*`, 동기화 정상, 콜서버 및 스위치와 시간 차이가 분석 해상도보다 충분히 작음. 1초 분석은 가능하면 100ms 미만 오차를 목표로 한다. 큰 시간 보정은 수집 시작 전에 한다.
InfluxDB는 Telegraf가 가져온 값을 시간 순서대로 저장한다.


## 4. 공식 저장소와 패키지 설치
<syntaxhighlight lang="bash" line>
Switch --SNMP--> Telegraf --> InfluxDB --> Grafana
</syntaxhighlight>


이 가이드는 **InfluxDB OSS 2.x / Flux**용이다. InfluxDB 3의 SQL 설정과 혼용하지 않는다. 최신 버전이라는 의미가 아니라 본 가이드의 호환 대상이다. 저장소에서 설치 가능한 정확한 RPM 버전을 먼저 확인·기록한다.
== 5. InfluxDB / Telegraf / Grafana 설치 ==


공식 설치 근거: [InfluxDB 2 RPM 설치](https://docs.influxdata.com/influxdb/v2/install/?t=Linux), [Telegraf RPM 설치](https://docs.influxdata.com/telegraf/v1/install/), [Grafana OSS RPM 설치](https://grafana.com/docs/grafana/latest/setup-grafana/installation/redhat-rhel-fedora/). 지속 갱신 문서, 확인일 2026-09-10.
=== 5.1 InfluxData Repository 추가 ===


### 4.1 InfluxData 저장소
공식 Repository Key를 내려받는다.


```bash
<syntaxhighlight lang="bash" line>
curl -fL https://repos.influxdata.com/influxdata-archive.key \
curl -fL https://repos.influxdata.com/influxdata-archive.key \
     -o /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata
     -o /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata
gpg --show-keys --with-fingerprint /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata
```


확인한 공식 문서의 기본키 fingerprint: `24C975CBA61A024EE1B631787C3D57159FC2F927`. 일치하지 않으면 키 교체 공지를 공식 문서에서 확인하기 전에는 import하지 않는다. `gpgcheck=0` 또는 `--nogpgcheck`로 우회하지 않는다.
gpg --show-keys --with-fingerprint \
    /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata


```bash
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata
</syntaxhighlight>
Repository 파일 생성:
<syntaxhighlight lang="bash" line>
vi /etc/yum.repos.d/influxdata.repo
vi /etc/yum.repos.d/influxdata.repo
```
</syntaxhighlight>
 
파일 내용 (`$basearch`는 DNF가 해석하므로 그대로 둔다):


```ini
<syntaxhighlight lang="bash" line>
[influxdata]
[influxdata]
name=InfluxData Repository - Stable
name=InfluxData Repository - Stable
164번째 줄: 442번째 줄:
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata
sslverify=1
sslverify=1
```
</syntaxhighlight>


### 4.2 Grafana 저장소
=== 5.2 Grafana Repository 추가 ===


```bash
<syntaxhighlight lang="bash" line>
vi /etc/yum.repos.d/grafana.repo
vi /etc/yum.repos.d/grafana.repo
```
</syntaxhighlight>


```ini
<syntaxhighlight lang="bash" line>
[grafana]
[grafana]
name=Grafana OSS repository
name=Grafana OSS repository
181번째 줄: 459번째 줄:
gpgkey=https://rpm.grafana.com/gpg.key
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1
sslverify=1
```
</syntaxhighlight>


Grafana 키 import 요청 시 공식 키/문서와 대조한다. 패키지는 `grafana`이며 `grafana-enterprise`를 설치하지 않는다.
=== 5.3 Repository 확인 후 설치 ===


```bash
<syntaxhighlight lang="bash" line>
dnf makecache
dnf makecache
dnf list --showduplicates telegraf influxdb2 influxdb2-cli grafana
 
dnf install telegraf influxdb2 influxdb2-cli grafana
dnf list --showduplicates \
rpm -q telegraf influxdb2 influxdb2-cli grafana rsyslog
    telegraf \
    influxdb2 \
    influxdb2-cli \
    grafana
</syntaxhighlight>
 
설치:
 
<syntaxhighlight lang="bash" line>
dnf install -y \
    telegraf \
    influxdb2 \
    influxdb2-cli \
    grafana
</syntaxhighlight>
 
버전 확인:
 
<syntaxhighlight lang="bash" line>
rpm -q telegraf influxdb2 influxdb2-cli grafana
 
telegraf --version
telegraf --version
influxd version
influxd version
influx version
influx version
```
</syntaxhighlight>
 
'''왜 버전을 기록하는가?'''
 
나중에 설정 문법 또는 Plugin 동작이 달라졌을 때 원인을 판단하기 쉽기 때문이다.
 
실제 구성 과정에서는 Telegraf 1.40.0 환경에서 동작을 확인하였다.
 
== 6. InfluxDB 초기 설정 ==


`influxdb2-cli`가 저장소에 없다면 임의의 다른 CLI를 설치하지 말고 공식 InfluxDB 2 CLI 설치 방법으로 해당 아키텍처의 서명/체크섬 검증된 패키지를 설치한다. 서버/CLI의 버전 번호는 서로 같지 않을 수 있다.
=== 6.1 InfluxDB를 localhost에만 Bind ===


이후 원격 운영 전 위 버전 출력을 작업 기록에 보관한다. Telegraf/Grafana는 검증된 버전을 변경 관리 대상으로 관리하고 장애 조사 중 자동 업그레이드는 하지 않는다.
현재 구성에서는 InfluxDB를 외부 장비가 직접 접근할 필요가 없다.


## 5. InfluxDB 초기 설정 및 권한 분리
Telegraf와 Grafana가 같은 서버에 있으므로 127.0.0.1에만 Bind하면 공격 표면을 줄일 수 있다.


### 5.1 로컬에서만 API 수신
<syntaxhighlight lang="bash" line>
install -d -m 755 \
  /etc/systemd/system/influxdb.service.d


```bash
vi /etc/systemd/system/influxdb.service.d/10-listen.conf
install -d -m 755 /etc/systemd/system/influxdb.service.d
</syntaxhighlight>
vi /etc/systemd/system/influxdb.service.d/10-ipt-listen.conf
```


```ini
<syntaxhighlight lang="bash" line>
[Service]
[Service]
Environment="INFLUXD_HTTP_BIND_ADDRESS=127.0.0.1:8086"
Environment="INFLUXD_HTTP_BIND_ADDRESS=127.0.0.1:8086"
```
</syntaxhighlight>
 
적용:


```bash
<syntaxhighlight lang="bash" line>
systemctl daemon-reload
systemctl daemon-reload
systemctl enable --now influxdb
systemctl enable --now influxdb
systemctl restart influxdb
systemctl restart influxdb
systemctl status influxdb --no-pager
systemctl status influxdb --no-pager
</syntaxhighlight>
Listen 확인:
<syntaxhighlight lang="bash" line>
ss -lntp | grep ':8086'
ss -lntp | grep ':8086'
</syntaxhighlight>
Health Check:
<syntaxhighlight lang="bash" line>
curl -fsS http://127.0.0.1:8086/health
curl -fsS http://127.0.0.1:8086/health
```
</syntaxhighlight>
 
'''정상 확인'''


정상: `127.0.0.1:8086`에만 수신, health 정상. 전체 주소로 열리면 서비스/설정의 기존 바인딩 override를 확인한다. VM 방화벽에 8086을 열지 않는다.
127.0.0.1:8086에서 LISTEN하고 health 응답이 정상이어야 한다.


### 5.2 초기화
=== 6.2 InfluxDB 최초 Setup ===


```bash
<syntaxhighlight lang="bash" line>
influx setup
influx setup
```
</syntaxhighlight>


대화형 입력값:
초기 입력 예:


| 항목 | 입력 |
{| class="wikitable"
|---|---|
! 항목
| Username | 전용 관리자 계정 |
! 예시
| Password | 고유한 강한 암호 |
! 설명
| Organization | `network` |
|-
| Bucket | `snmp_raw` |
| Username
| Retention | `720h` (30일) |
| monitor-admin
| InfluxDB 관리 계정
|-
| Organization
| network
| 관련 데이터의 논리적 그룹
|-
| Bucket
| snmp_raw
| SNMP 데이터 저장 위치
|-
| Retention
| 720h
| 30일 보존
|}


이미 setup 완료로 표시되면 재초기화하지 말고 기존 조직·bucket을 확인한다. `influx bucket list`로 `snmp_raw` retention이 무제한이 아닌 720h인지 검증한다. 별도 테스트 환경이 아닌 기존 DB를 삭제해서 다시 시작하지 않는다.
확인:


```bash
<syntaxhighlight lang="bash" line>
influx bucket list --org network
influx bucket list --org network
```
</syntaxhighlight>
 
'''Bucket이란?'''
 
일반 Database의 Database 또는 Table과 완전히 같지는 않지만, 처음에는 "Metric 저장 공간" 정도로 이해하면 된다.
 
=== 6.3 Token을 서비스별로 나누는 이유 ===
 
Telegraf는 데이터를 쓰기만 하면 되고 Grafana는 읽기만 하면 된다.
 
따라서 하나의 관리자 Token을 모든 프로그램에 넣지 않는다.


### 5.3 관리자 접속과 서비스 토큰
권장:


관리자 PC에서 SSH 터널을 유지한다. `127.0.0.1`은 PC 자신의 주소이며 SSH가 VM으로 전달한다.
{| class="wikitable"
! Token
! 권한
|-
| telegraf-write
| snmp_raw Write
|-
| grafana-read
| snmp_raw Read
|-
| Operator Token
| 관리자 전용
|}


```bash
실제 Token 값은 Wiki에 기록하지 않는다.
ssh -N -L 8086:127.0.0.1:8086 -L 3000:127.0.0.1:3000 <SSH_USER>@<COLLECTOR_IP>
```


브라우저에서 `http://127.0.0.1:8086` 접속 → API Tokens → Custom Token:
== 7. Telegraf 기본 설정 ==


| 토큰 이름 | 범위 |
=== 7.1 Telegraf의 역할 ===
|---|---|
| `telegraf-write` | `snmp_raw` Write만 |
| `grafana-read` | `snmp_raw` Read만 |
| 초기 Operator 토큰 | 관리자 초기화·백업용, 서비스에 배포 금지 |


발급된 토큰은 승인된 비밀 관리 위치에 보관한다. 화면/문서/채팅에 붙여넣지 않는다. 초기 CLI credential도 비밀이며 root만 읽게 유지한다. Grafana 연결에 필요한 읽기 권한과 Telegraf 쓰기 권한을 분리한다.
Telegraf는 일정 시간마다 스위치에 SNMP Query를 보내고 결과를 InfluxDB에 저장한다.


## 6. SNMP 및 MIB 사전 확인
<syntaxhighlight lang="bash" line>
Telegraf
  |
  | UDP/161 SNMP GET
  v
Switch


### 6.1 Zyxel 측 선행 작업
Telegraf
  |
  | HTTP 8086
  v
InfluxDB
</syntaxhighlight>


모델별 UI/CLI가 다르므로 임의 명령을 넣지 않는다. 관리 화면에서 다음 항목을 구성한다.
=== 7.2 인증 정보를 설정 파일과 분리 ===


- SNMP 활성화, SNMPv3 읽기 전용 사용자.
SNMP Password와 InfluxDB Token을 여러 설정 파일에 직접 적으면 관리가 어렵다.
- 인증/암호화 알고리즘은 장비와 Rocky가 함께 지원하는 조합. 아래 예시는 SHA/AES이며 다른 알고리즘이면 양쪽을 같이 수정.
- 조회 허용 원본은 Rocky 관리 IP. NAT가 있다면 장비에 보이는 실제 원본 IP 확인.
- IF-MIB/system 조회 권한. 빈 테이블을 기능 미지원으로 단정하기 전 SNMP view 확인.
- 포트 description에는 전화기 내선/위치나 업링크 상대를 운영 규칙에 따라 기록하되 개인정보 최소화.


### 6.2 CLI credential을 명령행에 노출하지 않기
환경변수 파일로 분리한다.


아래는 root 조회용 Net-SNMP 사용자 설정이다. 기존 파일이 있으면 백업 후 병합한다. Telegraf는 이 파일을 사용하지 않는다.
<syntaxhighlight lang="bash" line>
vi /etc/telegraf/monitor.env
</syntaxhighlight>


```bash
<syntaxhighlight lang="bash" line>
install -d -m 700 /root/.snmp
INFLUX_WRITE_TOKEN='<WRITE_TOKEN>'
vi /root/.snmp/snmp.conf
```


```conf
ZYXEL_SNMP_USER='<SNMP_USER>'
defVersion 3
ZYXEL_SNMP_AUTH='<AUTH_PASSWORD>'
defSecurityName <SNMP_USER>
ZYXEL_SNMP_PRIV='<PRIV_PASSWORD>'
defSecurityLevel authPriv
defAuthType SHA
defAuthPassphrase <SNMP_AUTH_PASSWORD>
defPrivType AES
defPrivPassphrase <SNMP_PRIV_PASSWORD>
```


```bash
QNAP_SNMP_USER='<QNAP_SNMP_USER>'
chmod 600 /root/.snmp/snmp.conf
QNAP_SNMP_AUTH='<QNAP_AUTH_PASSWORD>'
snmpget -v3 -t 2 -r 0 -On <SWITCH_IP> .1.3.6.1.2.1.1.3.0
QNAP_SNMP_PRIV='<QNAP_PRIV_PASSWORD>'
snmpwalk -v3 -t 2 -r 0 -On <SWITCH_IP> .1.3.6.1.2.1.31.1.1.1.1
</syntaxhighlight>
snmpwalk -v3 -t 2 -r 0 -On <SWITCH_IP> .1.3.6.1.2.1.31.1.1.1.18
```


`ifName` OID의 마지막 숫자가 ifIndex다. 실제 포트 번호와 같다고 가정하지 않는다. 스위치 IP·포트명·ifIndex·전화기 IP·내선을 기록하고 재부팅/스택 변경 후 재확인한다.
권한 제한:


```bash
<syntaxhighlight lang="bash" line>
snmpget -v3 -t 2 -r 0 -On <SWITCH_IP> \
chown root:root /etc/telegraf/monitor.env
    .1.3.6.1.2.1.31.1.1.1.6.<IFINDEX> \
chmod 600 /etc/telegraf/monitor.env
    .1.3.6.1.2.1.31.1.1.1.10.<IFINDEX> \
</syntaxhighlight>
    .1.3.6.1.2.1.31.1.1.1.15.<IFINDEX> \
    .1.3.6.1.2.1.2.2.1.8.<IFINDEX>
```


정상: Counter64 / Counter64 / Mbps 단위 속도 / operStatus 값. Timeout이면 경로·ACL·SNMP credential, No Such Instance면 인덱스, No Such Object면 view/지원 확인. 지원하지 않는 필드는 수집 설정에서 제외하고 미수집으로 기록한다. 0으로 대체하지 않는다.
systemd가 환경 파일을 읽도록 설정:


### 6.3 MIB 파일 정책
<syntaxhighlight lang="bash" line>
install -d -m 755 \
  /etc/systemd/system/telegraf.service.d


기본 수집은 숫자 OID + 명시적 field name을 사용한다. IF-MIB 파일 로딩 실패와 원격 SNMP 조회 실패는 별개다. `net-snmp-config`는 기본 utils 패키지에 없을 수 있어 필수 명령으로 사용하지 않는다.
vi /etc/systemd/system/telegraf.service.d/10-monitor-env.conf
</syntaxhighlight>


```bash
<syntaxhighlight lang="bash" line>
rpm -ql net-snmp-libs | grep '/mibs/'
[Service]
snmptranslate -On IF-MIB::ifHCInOctets
EnvironmentFile=/etc/telegraf/monitor.env
```
</syntaxhighlight>


제조사 MIB는 Zyxel 공식 지원 페이지에서 **정확한 모델/펌웨어용 묶음**을 받는다. CPU·PoE·큐 드롭 OID는 확인 전 추가하지 않는다. SNMPv3 쓰기 권한 또는 PoE 전원 제어는 수집기에 부여하지 않는다.
=== 7.3 Telegraf Main 설정 ===


## 7. Telegraf 구성
원본 백업:


### 7.1 메인 파일과 환경 파일
<syntaxhighlight lang="bash" line>
cp -a /etc/telegraf/telegraf.conf \
  /etc/telegraf/telegraf.conf.orig
</syntaxhighlight>


기존 설정 교체 시 수집 공백 발생 가능. 신규 전용 VM에서 기본 예제 설정을 백업한 후 아래 내용으로 교체한다. `/etc/telegraf/telegraf.d/`에 기존 활성 conf가 있으면 먼저 내용을 확인한다.
편집:


```bash
<syntaxhighlight lang="bash" line>
cp -a /etc/telegraf/telegraf.conf "$MON_BACKUP/telegraf.conf.package"
ls -l /etc/telegraf/telegraf.d/
vi /etc/telegraf/telegraf.conf
vi /etc/telegraf/telegraf.conf
```
</syntaxhighlight>
 
기본 예:


```toml
<syntaxhighlight lang="bash" line>
[agent]
[agent]
   interval = "30s"
   interval = "30s"
355번째 줄: 708번째 줄:


[[inputs.internal]]
[[inputs.internal]]
[[inputs.cpu]]
[[inputs.cpu]]
   percpu = false
   percpu = false
   totalcpu = true
   totalcpu = true
[[inputs.mem]]
[[inputs.mem]]
[[inputs.disk]]
[[inputs.disk]]
   mount_points = ["/"]
   mount_points = ["/"]
[[inputs.net]]
[[inputs.net]]
```
</syntaxhighlight>
 
각 값의 의미:
 
* interval = 30s : 기본 수집 주기
* flush_interval = 5s : 모아둔 Metric을 DB에 보내는 주기
* outputs.influxdb_v2 : 수집 결과를 어느 InfluxDB에 저장할지 지정
* inputs.internal : Telegraf 자신의 상태
* inputs.cpu/mem/disk/net : 모니터링 서버 자체 상태
 
== 8. SNMP가 정상인지 먼저 수동으로 확인 ==
 
Telegraf 설정 전에 SNMP 자체가 되는지 확인하는 것이 중요하다.
 
Telegraf가 안 된다고 바로 Telegraf 설정만 수정하면 실제 원인이 SNMP ACL인지 Password인지 구분하기 어렵다.
 
=== 8.1 SNMPv3 계정 파일 ===
 
<syntaxhighlight lang="bash" line>
install -d -m 700 /root/.snmp
 
vi /root/.snmp/snmp.conf
</syntaxhighlight>
 
예:
 
<syntaxhighlight lang="bash" line>
defVersion 3
defSecurityName <SNMP_USER>
defSecurityLevel authPriv
defAuthType SHA
defAuthPassphrase <SNMP_AUTH_PASSWORD>
defPrivType AES
defPrivPassphrase <SNMP_PRIV_PASSWORD>
</syntaxhighlight>
 
권한:
 
<syntaxhighlight lang="bash" line>
chmod 600 /root/.snmp/snmp.conf
</syntaxhighlight>
 
=== 8.2 Uptime 조회 ===
 
<syntaxhighlight lang="bash" line>
snmpget -v3 \
  -t 2 \
  -r 0 \
  -On \
  203.0.113.11 \
  .1.3.6.1.2.1.1.3.0
</syntaxhighlight>
 
응답이 나오면 최소한 다음이 정상이다.
 
* IP Routing
* UDP/161
* SNMP User
* Authentication
* Privacy Password
* SNMP View
 
=== 8.3 Interface 이름 확인 ===
 
<syntaxhighlight lang="bash" line>
snmpwalk -v3 \
  -t 2 \
  -r 0 \
  -On \
  203.0.113.11 \
  .1.3.6.1.2.1.31.1.1.1.1
</syntaxhighlight>
 
여기서 마지막 숫자가 ifIndex이다.
 
예를 들어 결과가 다음과 같다고 가정한다.
 
<syntaxhighlight lang="bash" line>
.1.3.6.1.2.1.31.1.1.1.1.24 = STRING: port24
</syntaxhighlight>
 
이 경우 ifIndex는 24이다.
 
'''중요:''' 물리 Port 24번이 항상 ifIndex 24라는 의미는 아니다. 실제 SNMP 결과를 기준으로 한다.
 
== 9. Telegraf SNMP 설정 ==
 
=== 9.1 모델별 파일로 나누는 이유 ===
 
제조사가 같아도 모델마다 CPU/Memory OID와 SNMP 암호화 방식이 다를 수 있다.
 
따라서 하나의 거대한 파일보다는 모델별 파일로 나눈다.
 
<syntaxhighlight lang="bash" line>
/etc/telegraf/telegraf.d/
├── 10-zyxel-gs1900.conf
├── 11-zyxel-gs1920.conf
├── 20-zyxel-es3128.conf
├── 30-icmp_check.conf
└── 40-qnap_nas.conf
</syntaxhighlight>
 
=== 9.2 GS1920 계열 ===
 
실제 확인된 특성:
 
* SNMPv3 SHA + DES
* CPU/Memory Private OID 사용
 
CPU:
 
<syntaxhighlight lang="bash" line>
.1.3.6.1.4.1.890.1.15.3.49.1.7.0
</syntaxhighlight>
 
Memory:
 
<syntaxhighlight lang="bash" line>
Total  .1.3.6.1.4.1.890.1.15.3.50.1.1.1.3.1
Used    .1.3.6.1.4.1.890.1.15.3.50.1.1.1.4.1
Percent .1.3.6.1.4.1.890.1.15.3.50.1.1.1.5.1
</syntaxhighlight>
 
=== 9.3 GS1900 계열 ===
 
CPU:
 
<syntaxhighlight lang="bash" line>
.1.3.6.1.4.1.890.1.15.3.2.4.0
</syntaxhighlight>


별도 데이터 디스크를 쓰면 실제 마운트 경로를 `mount_points`에 추가한다. `inputs.cpu`는 Rocky VM의 CPU이며 스위치 CPU가 아니다.
Memory:


```bash
<syntaxhighlight lang="bash" line>
vi /etc/telegraf/ipt-monitor.env
.1.3.6.1.4.1.890.1.15.3.2.5.0
```
</syntaxhighlight>


```conf
실제 구축 과정에서는 SNMP 응답이 느려 다음 값이 안정적이었다.
INFLUX_WRITE_TOKEN='<TELEGRAF_WRITE_TOKEN>'
ZYXEL_SNMP_USER='<SNMP_USER>'
ZYXEL_SNMP_AUTH='<SNMP_AUTH_PASSWORD>'
ZYXEL_SNMP_PRIV='<SNMP_PRIV_PASSWORD>'
```


위 파일은 systemd EnvironmentFile 형식이다. 셸에서 source로 읽지 않는다. 비밀값에 큰따옴표/역슬래시가 있으면 TOML 확장 시 구문에 영향을 줄 수 있으므로 공식 secret store를 사용하거나 이스케이프를 검증한다.
<syntaxhighlight lang="bash" line>
timeout = "5s"
retries = 1
</syntaxhighlight>


```bash
'''왜 Timeout을 늘렸는가?'''
chown root:root /etc/telegraf/ipt-monitor.env
chmod 600 /etc/telegraf/ipt-monitor.env
install -d -m 755 /etc/systemd/system/telegraf.service.d
vi /etc/systemd/system/telegraf.service.d/10-ipt-monitor.conf
```


```ini
수동 snmpget은 되는데 Telegraf에서만 간헐적으로 누락된다면 Telegraf timeout이 장비 응답시간보다 짧을 수 있다.
[Service]
EnvironmentFile=/etc/telegraf/ipt-monitor.env
```


systemd가 환경 파일을 읽어 서비스에 전달한다. 일반 사용자의 읽기는 막지만 root나 동일 서비스 권한으로부터 완전히 숨기는 방식은 아니다.
Timeout을 무조건 크게 설정하기보다는 실제 응답을 보고 조정한다.


### 7.2 전체 포트 30초 수집
=== 9.4 ES-3128GP ===


`/etc/telegraf/telegraf.d/10-zyxel-ports.conf`를 작성한다. 한 대의 예시이며 같은 credential을 쓰는 장비는 `agents`에 추가한다. 인증값이 다르면 입력 블록과 환경변수 이름을 분리한다.
실제 확인된 SNMPv3 방식:


**table 본체에는 oid를 지정하지 않는다.** 필요한 열만 아래 field로 선택해 전체 테이블의 불필요한 열 조회를 피한다.
* SHA
* AES


```toml
sysObjectID:
[[inputs.snmp]]
  agents = ["udp://<SWITCH_IP>:161"]
  version = 3
  sec_name = "${ZYXEL_SNMP_USER}"
  sec_level = "authPriv"
  auth_protocol = "SHA"
  auth_password = "${ZYXEL_SNMP_AUTH}"
  priv_protocol = "AES"
  priv_password = "${ZYXEL_SNMP_PRIV}"
  timeout = "2s"
  retries = 0
  interval = "30s"
  agent_host_tag = "source"
  name = "switch_system"


  [[inputs.snmp.field]]
<syntaxhighlight lang="bash" line>
    name = "uptime"
.1.3.6.1.4.1.7800.1.190
    oid = ".1.3.6.1.2.1.1.3.0"
</syntaxhighlight>


  [[inputs.snmp.table]]
Port Metric은 수집 가능하지만 CPU/Memory Private OID는 정상 확인되지 않아 Dashboard에서 억지로 0으로 표시하지 않는다.
    name = "switch_port"
    index_as_tag = true
    inherit_tags = ["source"]


    [[inputs.snmp.table.field]]
'''수집되지 않는 값과 0은 의미가 다르다.'''
      name = "if_name"
      oid = ".1.3.6.1.2.1.31.1.1.1.1"
      is_tag = true
    [[inputs.snmp.table.field]]
      name = "if_alias"
      oid = ".1.3.6.1.2.1.31.1.1.1.18"
    [[inputs.snmp.table.field]]
      name = "in_octets"
      oid = ".1.3.6.1.2.1.31.1.1.1.6"
    [[inputs.snmp.table.field]]
      name = "out_octets"
      oid = ".1.3.6.1.2.1.31.1.1.1.10"
    [[inputs.snmp.table.field]]
      name = "in_ucast"
      oid = ".1.3.6.1.2.1.31.1.1.1.7"
    [[inputs.snmp.table.field]]
      name = "in_mcast"
      oid = ".1.3.6.1.2.1.31.1.1.1.8"
    [[inputs.snmp.table.field]]
      name = "in_bcast"
      oid = ".1.3.6.1.2.1.31.1.1.1.9"
    [[inputs.snmp.table.field]]
      name = "out_ucast"
      oid = ".1.3.6.1.2.1.31.1.1.1.11"
    [[inputs.snmp.table.field]]
      name = "out_mcast"
      oid = ".1.3.6.1.2.1.31.1.1.1.12"
    [[inputs.snmp.table.field]]
      name = "out_bcast"
      oid = ".1.3.6.1.2.1.31.1.1.1.13"
    [[inputs.snmp.table.field]]
      name = "speed_mbps"
      oid = ".1.3.6.1.2.1.31.1.1.1.15"
    [[inputs.snmp.table.field]]
      name = "discontinuity"
      oid = ".1.3.6.1.2.1.31.1.1.1.19"
    [[inputs.snmp.table.field]]
      name = "oper_status"
      oid = ".1.3.6.1.2.1.2.2.1.8"
    [[inputs.snmp.table.field]]
      name = "in_discards"
      oid = ".1.3.6.1.2.1.2.2.1.13"
    [[inputs.snmp.table.field]]
      name = "out_discards"
      oid = ".1.3.6.1.2.1.2.2.1.19"
    [[inputs.snmp.table.field]]
      name = "in_errors"
      oid = ".1.3.6.1.2.1.2.2.1.14"
    [[inputs.snmp.table.field]]
      name = "out_errors"
      oid = ".1.3.6.1.2.1.2.2.1.20"
```


`if_alias`는 설명 변경에 따른 series 증가를 줄이기 위해 field로 저장한다. 실제 출력에 `source`, `index`, `if_name` 태그가 있는지 확인한다.
CPU를 조회할 수 없는 장비에 CPU 0%라고 표시하면 운영자가 정상 상태라고 오해할 수 있다.


### 7.3 중요 포트 5초 수집
=== 9.5 기본 Port Metric ===


`/etc/telegraf/telegraf.d/20-zyxel-fast.conf`를 작성한다. 필요한 포트 수만큼 블록을 복제하고 IP·ifIndex·태그를 모두 바꾼다. 같은 포트의 중복 설정은 금지하며 일반 수집과 measurement를 분리한다.
가능하면 제조사 Private MIB보다 표준 IF-MIB를 먼저 사용한다.


```toml
주요 항목:
[[inputs.snmp]]
  agents = ["udp://<SWITCH_IP>:161"]
  version = 3
  sec_name = "${ZYXEL_SNMP_USER}"
  sec_level = "authPriv"
  auth_protocol = "SHA"
  auth_password = "${ZYXEL_SNMP_AUTH}"
  priv_protocol = "AES"
  priv_password = "${ZYXEL_SNMP_PRIV}"
  timeout = "800ms"
  retries = 0
  interval = "5s"
  agent_host_tag = "source"
  name = "switch_port_fast"


  [inputs.snmp.tags]
* ifName
    if_name = "<PORT_LABEL>"
* ifAlias
    index = "<IFINDEX>"
* ifSpeed
* ifHCInOctets
* ifHCOutOctets
* ifOperStatus
* ifInErrors
* ifOutErrors
* ifInDiscards
* ifOutDiscards
* Broadcast
* Multicast


  [[inputs.snmp.field]]
== 10. ICMP Ping 수집 ==
    name = "in_octets"
    oid = ".1.3.6.1.2.1.31.1.1.1.6.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "out_octets"
    oid = ".1.3.6.1.2.1.31.1.1.1.10.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "in_ucast"
    oid = ".1.3.6.1.2.1.31.1.1.1.7.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "in_mcast"
    oid = ".1.3.6.1.2.1.31.1.1.1.8.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "in_bcast"
    oid = ".1.3.6.1.2.1.31.1.1.1.9.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "out_ucast"
    oid = ".1.3.6.1.2.1.31.1.1.1.11.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "out_mcast"
    oid = ".1.3.6.1.2.1.31.1.1.1.12.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "out_bcast"
    oid = ".1.3.6.1.2.1.31.1.1.1.13.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "in_discards"
    oid = ".1.3.6.1.2.1.2.2.1.13.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "out_discards"
    oid = ".1.3.6.1.2.1.2.2.1.19.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "oper_status"
    oid = ".1.3.6.1.2.1.2.2.1.8.<IFINDEX>"
  [[inputs.snmp.field]]
    name = "discontinuity"
    oid = ".1.3.6.1.2.1.31.1.1.1.19.<IFINDEX>"
```


표준 정의: [RFC 2863](https://www.rfc-editor.org/rfc/rfc2863.html). 각 열의 지원 여부는 실장비에서 확인한다.
SNMP가 정상이어도 장비 자체가 네트워크에서 사라질 수 있다.


1초 수집은 해당 블록의 `interval = "1s"`로 변경한다. 먼저 카운터 갱신과 수집 소요시간을 확인한다. 800ms는 개별 요청 timeout이지 전체 수집 완료 보장이 아니다. 실패를 반복 재시도하지 않고 결측으로 남긴다. DB에 5초마다 묶어 써도 원래 수집 timestamp는 유지된다.
따라서 Ping 결과도 별도로 저장한다.


### 7.4 응답시간·손실 보조 수집
<syntaxhighlight lang="bash" line>
vi /etc/telegraf/telegraf.d/30-icmp_check.conf
</syntaxhighlight>


`/etc/telegraf/telegraf.d/30-ipt-ping.conf`:
예:


```toml
<syntaxhighlight lang="bash" line>
[[inputs.ping]]
[[inputs.ping]]
   urls = ["<SWITCH_IP>", "<CALLSERVER_IP>"]
   urls = [
    "203.0.113.11",
    "203.0.113.12",
    "203.0.113.13"
  ]
 
   method = "native"
   method = "native"
  privileged = true
   count = 3
   count = 1
   deadline = 2.0
   deadline = "1s"
   interval = 10.0
   interval = "5s"
</syntaxhighlight>
```
 
=== 10.1 CAP_NET_RAW가 필요한 이유 ===
 
native Ping은 Raw Socket을 사용한다.
 
root로 telegraf --test를 실행하면 정상인데 systemd 서비스에서는 Ping이 실패할 수 있다.


승인된 시험 전화기 IP를 필요 시 추가한다. ICMP 비응답 장비는 제외한다. 콜서버 Ping은 SIP 서비스 감시가 아니며 VM에서의 경로도 전화기에서의 경로와 같다고 보장할 수 없다.
이 경우 서비스에 CAP_NET_RAW를 부여한다.


`/etc/systemd/system/telegraf.service.d/10-ipt-monitor.conf`의 같은 `[Service]`에 추가한다:
<syntaxhighlight lang="bash" line>
systemctl edit telegraf
</syntaxhighlight>


```ini
<syntaxhighlight lang="bash" line>
[Service]
CapabilityBoundingSet=CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_RAW
AmbientCapabilities=CAP_NET_RAW
AmbientCapabilities=CAP_NET_RAW
```
</syntaxhighlight>
 
반영:
 
<syntaxhighlight lang="bash" line>
systemctl daemon-reload
systemctl restart telegraf
</syntaxhighlight>
 
== 11. Telegraf 설정 검사 ==
 
서비스를 재시작하기 전에 설정 오류를 확인한다.
 
<syntaxhighlight lang="bash" line>
telegraf \
  --config /etc/telegraf/telegraf.conf \
  --config-directory /etc/telegraf/telegraf.d \
  --test
</syntaxhighlight>


Telegraf를 root로 실행하는 설정은 아니다. [공식 Ping 권한 설명](https://docs.influxdata.com/telegraf/v1/input-plugins/ping/). 기존 입력이 있으면 필요한 capability를 제거하지 않도록 확인한다.
'''주의:''' --test는 Metric을 화면에 출력하지만 일반적으로 Output 저장까지 검증하는 용도가 아니다.


### 7.5 검사·기동
서비스 계정 조건까지 확인하려면 다음 방식이 더 정확하다.


```bash
<syntaxhighlight lang="bash" line>
chown root:telegraf /etc/telegraf/telegraf.conf /etc/telegraf/telegraf.d/*-*.conf
systemd-run \
chmod 640 /etc/telegraf/telegraf.conf /etc/telegraf/telegraf.d/*-*.conf
  --unit=telegraf-config-check \
systemctl daemon-reload
  --wait \
systemd-run --unit=ipt-telegraf-check --wait --pipe --collect \
  --pipe \
    -p User=telegraf -p Group=telegraf \
  --collect \
    -p EnvironmentFile=/etc/telegraf/ipt-monitor.env \
  -p User=telegraf \
    -p CapabilityBoundingSet=CAP_NET_RAW \
  -p Group=telegraf \
    -p AmbientCapabilities=CAP_NET_RAW \
  -p EnvironmentFile=/etc/telegraf/monitor.env \
    /usr/bin/telegraf --config /etc/telegraf/telegraf.conf \
  -p CapabilityBoundingSet=CAP_NET_RAW \
    --config-directory /etc/telegraf/telegraf.d --test
  -p AmbientCapabilities=CAP_NET_RAW \
```
  /usr/bin/telegraf \
  --config /etc/telegraf/telegraf.conf \
  --config-directory /etc/telegraf/telegraf.d \
  --test
</syntaxhighlight>


실제로 SNMP/Ping을 한 번 수집하는 검사다. 출력의 IP 등은 사내 정보로 취급한다. `--test`는 InfluxDB 쓰기를 검증하지 않는다. 수집 검사 성공 후:
정상 후 서비스 시작:


```bash
<syntaxhighlight lang="bash" line>
systemctl enable --now telegraf
systemctl enable --now telegraf
systemctl restart telegraf
systemctl restart telegraf
systemctl status telegraf --no-pager
systemctl status telegraf --no-pager
journalctl -u telegraf -n 100 --no-pager
```


설정 근거: [SNMP Input Plugin](https://docs.influxdata.com/telegraf/v1/input-plugins/snmp/), [Telegraf 설정](https://docs.influxdata.com/telegraf/v1/configuration/). 입력과 DB 출력이 모두 성공하는지 확인하고 unauthorized, timeout, collection interval 초과, buffer/drop 메시지를 조사한다.
journalctl -u telegraf \
  -n 100 \
  --no-pager
</syntaxhighlight>


## 8. Grafana 구성과 계산
로그에서 주의할 문자열:


### 8.1 서비스
<syntaxhighlight lang="bash" line>
timeout
unauthorized
permission denied
buffer
drop
address already in use
</syntaxhighlight>


먼저 백업한 뒤 `/etc/grafana/grafana.ini`의 기존 섹션을 수정한다. 중복 섹션을 추가하지 않는다.
== 12. Grafana 설치 후 처음 해야 할 작업 ==


```bash
Grafana는 데이터를 직접 수집하지 않는다.
cp -a /etc/grafana/grafana.ini "$MON_BACKUP/grafana.ini.initial"
vi /etc/grafana/grafana.ini
```


```ini
먼저 InfluxDB와 Prometheus 같은 데이터소스를 등록해야 한다.
[server]
http_addr = 127.0.0.1
http_port = 3000


[users]
=== 12.1 Grafana 서비스 ===
allow_sign_up = false


[auth.anonymous]
<syntaxhighlight lang="bash" line>
enabled = false
systemctl enable --now grafana-server
```


```bash
systemctl enable --now grafana-server
systemctl restart grafana-server
systemctl status grafana-server --no-pager
systemctl status grafana-server --no-pager
curl -fsS http://127.0.0.1:3000/api/health
ss -lntp | grep ':3000'
```


초기 관리자로 로그인 후 즉시 강한 비밀번호로 변경한다. SSH 터널의 `http://127.0.0.1:3000`을 사용한다. 공유 접속이 필요하면 인증·TLS reverse proxy와 접근제어를 별도 구성하며 평문 HTTP를 전사망에 개방하지 않는다.
curl -fsS \
  http://127.0.0.1:3000/api/health
</syntaxhighlight>
 
웹 접속:
 
<syntaxhighlight lang="bash" line>
http://192.0.2.10:3000
</syntaxhighlight>
 
내부망에서만 사용할 경우에도 방화벽 접근 범위를 관리망으로 제한하는 것이 좋다.
 
=== 12.2 InfluxDB Datasource 등록 ===
 
Grafana 메뉴:
 
<syntaxhighlight lang="bash" line>
Connections
→ Data sources
→ Add data source
→ InfluxDB
</syntaxhighlight>
 
설정:
 
{| class="wikitable"
! 항목
! 값
|-
| Query Language
| Flux
|-
| URL
| http://127.0.0.1:8086
|-
| Organization
| network
|-
| Token
| grafana-read Token
|-
| Default Bucket
| snmp_raw
|}
 
Save & Test 성공 여부를 확인한다.


### 8.2 InfluxDB 데이터소스
== 13. 첫 번째 SNMP Dashboard 만들기 ==


Connections → Data sources → Add data source → InfluxDB:
처음부터 모든 장비를 한 화면에 넣지 않는다.


| 설정 | 값 |
'''한 대의 스위치 + 한 포트'''로 정상 그래프를 만든 뒤 확대하는 것이 가장 쉽다.
|---|---|
| Name | `IPT-SNMP` |
| Query language | Flux |
| URL | `http://127.0.0.1:8086` |
| Organization | `network` |
| Token | grafana-read 토큰 |
| Default Bucket | `snmp_raw` |
| Min time interval | `1s` (실제 수집 간격은 패널별로 반영) |


Save & test 성공 후 Explore에서 measurement를 조회한다. [Grafana 공식 연결 설정](https://grafana.com/docs/grafana/latest/datasources/influxdb/configure/).
=== 13.1 원시 Counter 확인 ===


### 8.3 필드와 태그 확인
Explore에서 다음과 같이 최근 데이터를 확인한다.


```flux
<syntaxhighlight lang="bash" line>
from(bucket: "snmp_raw")
from(bucket: "snmp_raw")
   |> range(start: -10m)
   |> range(start: -10m)
   |> filter(fn: (r) => r._measurement == "switch_port_fast")
  |> limit(n: 20)
   |> limit(n: 5)
</syntaxhighlight>
```
 
여기서 실제 Measurement, source, index, if_name 값을 확인한다.
 
=== 13.2 Port RX/TX 계산 ===
 
누적 Octet Counter를 bps로 변환한다.
 
<syntaxhighlight lang="bash" line>
from(bucket: "snmp_raw")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
   |> filter(fn: (r) =>
      r._measurement == "switch_port"
  )
  |> filter(fn: (r) =>
      r.source == "203.0.113.11"
  )
  |> filter(fn: (r) =>
      r._field == "in_octets" or
      r._field == "out_octets"
  )
   |> derivative(
      unit: 1s,
      nonNegative: true
  )
  |> map(fn: (r) => ({
      r with
      _value: r._value * 8.0
  }))
</syntaxhighlight>
 
Grafana Unit:


실제 `source` 태그를 확인해 아래 `<SOURCE_TAG_VALUE>`에 넣는다. 일반적으로 장비 IP지만 출력으로 확인한다. 먼저 변수 없이 한 대·한 포트의 표시를 완성한다.
<syntaxhighlight lang="bash" line>
bits/sec
</syntaxhighlight>


### 8.4 In/Out bps
=== 13.3 Error / Discard ===


패널: Time series / Unit: bits/sec / 결측 구간 연결 안 함 / 시간대 Asia/Seoul / 범위 최근 15분 / refresh 5s.
Error나 Discard 역시 Counter이므로 증가율을 표시한다.


```flux
<syntaxhighlight lang="bash" line>
from(bucket: "snmp_raw")
from(bucket: "snmp_raw")
   |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
   |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
   |> filter(fn: (r) => r._measurement == "switch_port_fast")
   |> filter(fn: (r) =>
   |> filter(fn: (r) => r.source == "<SOURCE_TAG_VALUE>" and r.index == "<IFINDEX>")
      r._measurement == "switch_port"
  |> filter(fn: (r) => r._field == "in_octets" or r._field == "out_octets")
  )
   |> derivative(unit: 1s, nonNegative: true)
   |> filter(fn: (r) =>
   |> map(fn: (r) => ({r with _value: r._value * 8.0}))
      r._field == "in_errors" or
```
      r._field == "out_errors" or
      r._field == "in_discards" or
      r._field == "out_discards"
  )
   |> derivative(
      unit: 1s,
      nonNegative: true
  )
</syntaxhighlight>
 
'''해석'''
 
* Error 증가 → 물리계층, Frame, Interface 문제 가능
* Discard 증가 → Queue, Buffer, QoS, 혼잡 등에 의해 폐기될 가능성
* 값이 0인 상태가 일반적이지만 장비 특성과 Traffic에 따라 해석해야 함
 
=== 13.4 operStatus ===
 
operStatus는 Counter가 아니다.
 
따라서 derivative를 사용하지 않는다.
 
일반적인 값:
 
<syntaxhighlight lang="bash" line>
1 = up
2 = down
</syntaxhighlight>
 
Grafana Value Mapping으로 사람이 읽기 쉬운 문자열로 변환한다.
 
== 14. Prometheus를 추가하는 이유 ==
 
SNMP는 네트워크 장비 상태에 좋지만 Linux/Windows 서버 자원 모니터링에는 Exporter + Prometheus가 더 편하다.
 
Prometheus 구조:
 
<syntaxhighlight lang="bash" line>
Node Exporter ----\
                  \
Windows Exporter ---> Prometheus ---> Grafana
                  /
QNAP Exporter -----/
</syntaxhighlight>
 
Prometheus는 일정 주기마다 Exporter HTTP Endpoint에 접속하여 Metric을 가져간다.
 
이를 '''Pull 방식'''이라고 한다.
 
== 15. Prometheus 설치 ==
 
=== 15.1 사용자와 디렉터리 ===
 
<syntaxhighlight lang="bash" line>
useradd \
  --system \
  --no-create-home \
  --shell /sbin/nologin \
  prometheus
 
install -d \
  -o prometheus \
  -g prometheus \
  /etc/prometheus \
  /var/lib/prometheus
</syntaxhighlight>
 
Prometheus 공식 바이너리를 준비한 뒤:
 
<syntaxhighlight lang="bash" line>
install -m 0755 \
  prometheus \
  /usr/local/bin/prometheus
 
install -m 0755 \
  promtool \
  /usr/local/bin/promtool
</syntaxhighlight>
 
버전 확인:
 
<syntaxhighlight lang="bash" line>
prometheus --version
promtool --version
</syntaxhighlight>
 
=== 15.2 prometheus.yml ===
 
<syntaxhighlight lang="bash" line>
vi /etc/prometheus/prometheus.yml
</syntaxhighlight>
 
<syntaxhighlight lang="bash" line>
global:
  scrape_interval: 30s
 
scrape_configs:
 
  - job_name: prometheus
    static_configs:
      - targets:
          - "127.0.0.1:9090"
        labels:
          server_name: monitoring-server
 
  - job_name: node
    static_configs:
      - targets:
          - "127.0.0.1:9100"
        labels:
          server_name: monitoring-server
 
      - targets:
          - "192.0.2.20:9100"
        labels:
          server_name: linux-server
 
  - job_name: windows
    static_configs:
      - targets:
          - "192.0.2.30:9182"
        labels:
          server_name: windows-server
 
  - job_name: qnap
    static_configs:
      - targets:
          - "198.51.100.10:9100"
        labels:
          server_name: nas-01
</syntaxhighlight>
 
'''scrape_interval = 30s'''는 Prometheus가 30초마다 각 Target을 조회한다는 의미이다.
 
=== 15.3 설정 검사 ===
 
YAML은 들여쓰기에 매우 민감하다.
 
실제 구축 중에도 job을 scrape_configs 바깥에 잘못 넣으면 Prometheus가 시작하지 못했다.
 
반드시 검사한다.
 
<syntaxhighlight lang="bash" line>
promtool check config \
  /etc/prometheus/prometheus.yml
</syntaxhighlight>
 
=== 15.4 systemd 등록 ===
 
<syntaxhighlight lang="bash" line>
vi /etc/systemd/system/prometheus.service
</syntaxhighlight>
 
<syntaxhighlight lang="bash" line>
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target
 
[Service]
User=prometheus
Group=prometheus
 
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --storage.tsdb.retention.time=30d \
  --storage.tsdb.retention.size=20GB \
  --web.listen-address=127.0.0.1:9090
 
Restart=always
 
[Install]
WantedBy=multi-user.target
</syntaxhighlight>
 
권한:
 
<syntaxhighlight lang="bash" line>
chown -R prometheus:prometheus \
  /etc/prometheus \
  /var/lib/prometheus
</syntaxhighlight>
 
시작:
 
<syntaxhighlight lang="bash" line>
systemctl daemon-reload
systemctl enable --now prometheus
 
systemctl status prometheus --no-pager
</syntaxhighlight>
 
Health:
 
<syntaxhighlight lang="bash" line>
curl -s \
  http://127.0.0.1:9090/-/healthy
</syntaxhighlight>
 
== 16. Linux Node Exporter 설치 ==
 
=== 16.1 Node Exporter가 하는 일 ===
 
Node Exporter는 Linux Kernel과 /proc, /sys 등의 정보를 읽어 Prometheus 형식으로 공개한다.
 
대표 Metric:
 
* CPU
* Memory
* Load
* Filesystem
* Disk I/O
* Network Traffic
* Network Error/Drop
* Uptime
* File Descriptor
* 일부 Hardware Metric
 
=== 16.2 서비스 계정 ===
 
<syntaxhighlight lang="bash" line>
useradd \
  --system \
  --no-create-home \
  --shell /sbin/nologin \
  node_exporter
</syntaxhighlight>
 
바이너리 설치:
 
<syntaxhighlight lang="bash" line>
install -m 0755 \
  node_exporter \
  /usr/local/bin/node_exporter
</syntaxhighlight>
 
=== 16.3 systemd ===
 
<syntaxhighlight lang="bash" line>
vi /etc/systemd/system/node_exporter.service
</syntaxhighlight>
 
<syntaxhighlight lang="bash" line>
[Unit]
Description=Node Exporter
After=network-online.target
Wants=network-online.target
 
[Service]
User=node_exporter
Group=node_exporter
 
ExecStart=/usr/local/bin/node_exporter
 
Restart=always
 
[Install]
WantedBy=multi-user.target
</syntaxhighlight>
 
<syntaxhighlight lang="bash" line>
systemctl daemon-reload
systemctl enable --now node_exporter
 
systemctl status node_exporter --no-pager
</syntaxhighlight>
 
Metric 확인:
 
<syntaxhighlight lang="bash" line>
curl -s \
  http://127.0.0.1:9100/metrics \
  | head
</syntaxhighlight>
 
다른 서버에 설치한 경우 Prometheus 서버에서 확인:
 
<syntaxhighlight lang="bash" line>
curl -s \
  http://192.0.2.20:9100/metrics \
   | head
</syntaxhighlight>
 
'''정상 확인'''
 
Metric 문자열이 여러 줄 출력되면 Exporter 자체는 정상이다.
 
Prometheus에서 다음 Query도 확인한다.
 
<syntaxhighlight lang="bash" line>
up
</syntaxhighlight>
 
값:
 
<syntaxhighlight lang="bash" line>
1 = 정상 Scrape
0 = Scrape 실패
</syntaxhighlight>
 
== 17. Windows Exporter ==
 
Windows에서는 windows_exporter를 설치한다.
 
기본 Port:
 
<syntaxhighlight lang="bash" line>
9182/tcp
</syntaxhighlight>
 
Prometheus에서 다음 주소를 조회할 수 있어야 한다.
 
<syntaxhighlight lang="bash" line>
http://192.0.2.30:9182/metrics
</syntaxhighlight>
 
Windows PowerShell 테스트:
 
<syntaxhighlight lang="bash" line>
Invoke-WebRequest \
  -UseBasicParsing \
  http://127.0.0.1:9182/metrics
</syntaxhighlight>
 
성능 카운터가 비정상인 경우 실제 구축 과정에서 다음 복구 절차를 사용하였다.
 
<syntaxhighlight lang="bash" line>
lodctr /R
winmgmt /resyncperf
</syntaxhighlight>
 
그 후:
 
<syntaxhighlight lang="bash" line>
Restart-Service windows_exporter
</syntaxhighlight>
 
== 18. Grafana에 Prometheus 등록 ==
 
Grafana 메뉴:
 
<syntaxhighlight lang="bash" line>
Connections
→ Data sources
→ Prometheus
</syntaxhighlight>
 
URL:
 
<syntaxhighlight lang="bash" line>
http://127.0.0.1:9090
</syntaxhighlight>
 
Save & Test 후 Explore에서:
 
<syntaxhighlight lang="bash" line>
up
</syntaxhighlight>
 
Target별 1이 표시되면 정상이다.
 
== 19. Linux Server Dashboard 이해하기 ==
 
처음에는 다음 항목만 만든다.
 
* Uptime
* CPU
* Memory
* Load Average
* Filesystem
* Network RX/TX
 
이후 익숙해지면 다음을 추가한다.
 
* Disk I/O
* Network Error/Drop
* inode
* Swap
* Process
* Application Log
 
=== 19.1 CPU Usage ===
 
Node Exporter는 CPU 시간을 Mode별 누적으로 제공한다.
 
idle을 제외한 비율로 CPU 사용률을 계산할 수 있다.
 
<syntaxhighlight lang="bash" line>
100 - (
  avg by(instance) (
    rate(
      node_cpu_seconds_total{
        mode="idle"
      }[5m]
    )
  ) * 100
)
</syntaxhighlight>
 
=== 19.2 Memory Usage ===
 
<syntaxhighlight lang="bash" line>
100 * (
  1 -
  node_memory_MemAvailable_bytes
  /
  node_memory_MemTotal_bytes
)
</syntaxhighlight>
 
=== 19.3 Filesystem Usage ===
 
Filesystem은 tmpfs, overlay, snapshot 등 운영자가 원하지 않는 Mount가 함께 보일 수 있다.
 
따라서 실제 데이터 Mount Point를 확인한 뒤 필터링한다.
 
== 20. QNAP TS-264 모니터링 ==
 
QNAP은 한 가지 수집 방식만 사용하면 정보가 부족하다.
 
따라서 세 경로를 함께 사용한다.
 
<syntaxhighlight lang="bash" line>
QNAP
|
+-- node_exporter --> Prometheus
|
+-- SNMP ----------> Telegraf --> InfluxDB
|
+-- Syslog --------> rsyslog --> Alloy --> Loki
</syntaxhighlight>
 
역할:
 
{| class="wikitable"
! 항목
! 데이터 소스
|-
| CPU / Memory / Network
| Node Exporter
|-
| Filesystem
| Node Exporter
|-
| Disk I/O
| Node Exporter
|-
| HDD Model / 온도 / 상태
| SNMP
|-
| RAID / Volume
| SNMP
|-
| Event Log
| Syslog
|-
| Access Log
| Syslog
|}
 
=== 20.1 QNAP SNMPv3 ===
 
실제 TS-264 환경에서는 다음 조합을 사용하였다.
 
* Authentication: SHA
* Privacy: DES
 
QNAP은 해당 구성에서 AES가 동작하지 않았다.
 
따라서 다른 장비에서 AES가 된다는 이유로 QNAP도 AES라고 가정하지 않는다.
 
=== 20.2 주요 Measurement ===
 
<syntaxhighlight lang="bash" line>
QNAP_TS264
qnap_disk
qnap_raid
qnap_storage_pool
qnap_volume
</syntaxhighlight>
 
=== 20.3 Volume 값이 11 GiB로 잘못 보이는 문제 ===
 
실제 QTS에서 DataVol1은 약 11.33 TiB인데 SNMP 원시값을 Grafana에서 byte로 바로 처리하면 약 11.3 GiB로 표시되는 문제가 있었다.
 
실제 장비의 qnap_volume capacity/free 값이 KiB 성격으로 반환되어 1024 배 보정이 필요하였다.
 
<syntaxhighlight lang="bash" line>
|> map(fn: (r) => ({
    r with
 
    capacity_bytes:
      uint(v: r.capacity_bytes)
      * uint(v: 1024),
 
    free_bytes:
      uint(v: r.free_bytes)
      * uint(v: 1024),
 
    used_bytes:
      (
        uint(v: r.capacity_bytes)
        - uint(v: r.free_bytes)
      )
      * uint(v: 1024),
 
    used_percent:
      if float(v: r.capacity_bytes) > 0.0 then
        (
          float(v: r.capacity_bytes)
          - float(v: r.free_bytes)
        )
        /
        float(v: r.capacity_bytes)
        * 100.0
      else
        0.0
}))
</syntaxhighlight>
 
'''왜 Percent에는 1024가 필요 없는가?'''
 
분자와 분모가 같은 단위이므로 비율 계산에서는 단위가 상쇄된다.
 
=== 20.4 실제 Data Volume 찾기 ===
 
Node Exporter가 QNAP 내부의 Snapshot Mount까지 모두 보여주므로 처음에는 여러 개의 11.33 TiB Filesystem이 보일 수 있다.
 
실제 사용자 Data Volume은 다음이었다.
 
<syntaxhighlight lang="bash" line>
mountpoint="/share/CACHEDEV1_DATA"
device="/dev/mapper/cachedev1"
fstype="ext4"
</syntaxhighlight>
 
QNAP Snapshot:
 
<syntaxhighlight lang="bash" line>
/mnt/snapshot/1/10001
/mnt/snapshot/1/10002
...
</syntaxhighlight>
 
Dashboard에서는 Snapshot을 제외하고 실제 Volume만 표시한다.
 
사용률:
 
<syntaxhighlight lang="bash" line>
100 * (
  1 -
  node_filesystem_avail_bytes{
    instance="198.51.100.10:9100",
    mountpoint="/share/CACHEDEV1_DATA"
  }
  /
  node_filesystem_size_bytes{
    instance="198.51.100.10:9100",
    mountpoint="/share/CACHEDEV1_DATA"
  }
)
</syntaxhighlight>
 
실제 QTS 화면과 약 2.86%로 일치하는 것을 확인하였다.
 
=== 20.5 QNAP Network는 bond0만 표시 ===
 
QNAP에는 내부 Interface와 Virtual Interface가 여러 개 보일 수 있다.
 
실제 외부 Traffic을 담당하는 bond0만 필터링한다.
 
RX:
 
<syntaxhighlight lang="bash" line>
rate(
  node_network_receive_bytes_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
) * 8
</syntaxhighlight>
 
TX:
 
<syntaxhighlight lang="bash" line>
rate(
  node_network_transmit_bytes_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
) * 8
</syntaxhighlight>
 
=== 20.6 Network Error / Drop ===
 
RX Error:
 
<syntaxhighlight lang="bash" line>
rate(
  node_network_receive_errs_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
)
</syntaxhighlight>
 
TX Error:
 
<syntaxhighlight lang="bash" line>
rate(
  node_network_transmit_errs_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
)
</syntaxhighlight>
 
RX Drop:
 
<syntaxhighlight lang="bash" line>
rate(
  node_network_receive_drop_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
)
</syntaxhighlight>
 
TX Drop:
 
<syntaxhighlight lang="bash" line>
rate(
  node_network_transmit_drop_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
)
</syntaxhighlight>
 
=== 20.7 Disk I/O ===
 
QNAP에는 md, dm, loop 등 내부 Device가 많으므로 물리 Disk sd*만 표시한다.
 
Read Bytes/sec:
 
<syntaxhighlight lang="bash" line>
rate(
  node_disk_read_bytes_total{
    instance="198.51.100.10:9100",
    device=~"sd[a-z]+"
  }[$__rate_interval]
)
</syntaxhighlight>
 
Write Bytes/sec:
 
<syntaxhighlight lang="bash" line>
rate(
  node_disk_written_bytes_total{
    instance="198.51.100.10:9100",
    device=~"sd[a-z]+"
  }[$__rate_interval]
)
</syntaxhighlight>
 
Read IOPS:
 
<syntaxhighlight lang="bash" line>
rate(
  node_disk_reads_completed_total{
    instance="198.51.100.10:9100",
    device=~"sd[a-z]+"
  }[$__rate_interval]
)
</syntaxhighlight>
 
Write IOPS:
 
<syntaxhighlight lang="bash" line>
rate(
  node_disk_writes_completed_total{
    instance="198.51.100.10:9100",
    device=~"sd[a-z]+"
  }[$__rate_interval]
)
</syntaxhighlight>
 
== 21. Syslog를 왜 별도로 구성하는가 ==
 
Metric만으로는 다음과 같은 내용을 알기 어렵다.
 
* Port가 왜 Down 되었는가
* 사용자가 NAS에 어떤 파일을 접근했는가
* 서비스가 언제 재시작되었는가
* 인증 실패가 발생했는가
 
이런 이벤트는 Log가 필요하다.
 
본 구성의 로그 흐름:
 
<syntaxhighlight lang="bash" line>
Device
  |
  | Syslog
  v
rsyslog
  |
  v
Log File
  |
  v
Alloy
  |
  v
Loki
  |
  v
Grafana
</syntaxhighlight>
 
== 22. rsyslog 구성 ==
 
=== 22.1 Network Syslog ===
 
네트워크 장비 로그:
 
<syntaxhighlight lang="bash" line>
/var/log/network-syslog/events.log
</syntaxhighlight>
 
기본 수신:
 
<syntaxhighlight lang="bash" line>
TCP/UDP 514
</syntaxhighlight>
 
=== 22.2 Server Syslog ===
 
서버용 로그를 네트워크 장비와 분리하면 Grafana에서 Query하기 쉽다.
 
예:
 
<syntaxhighlight lang="bash" line>
/var/log/server-syslog/events.log
</syntaxhighlight>
 
수신 Port:
 
<syntaxhighlight lang="bash" line>
TCP 5514
</syntaxhighlight>
 
=== 22.3 QNAP Event와 Access Log 분리 ===
 
QNAP QuLog의 두 성격이 다르므로 포트부터 분리한다.


`derivative`로 실제 timestamp 차이를 사용해 초당 값으로 바꾼 뒤 8배한다. 고정 간격 5로 나누지 않는다. 장기간 표시에서 집계한다면 **rate 계산 후** `aggregateWindow(every: v.windowPeriod, fn: max, createEmpty: false)`를 추가하고 ‘구간 내 최대 수집간격 평균’으로 표시한다. 누적 카운터를 먼저 평균내지 않는다.
{| class="wikitable"
! 종류
! 포트
! 파일
! 의미
|-
| Event
| TCP 5515
| /var/log/qnap/event.log
| 시스템/서비스 이벤트
|-
| Access
| TCP 5516
| /var/log/qnap/access.log
| 사용자/파일 접근
|}


`nonNegative`만으로 모든 리셋을 판별할 수 없다. discontinuity 변경·sysUpTime 감소·링크 변경 구간은 무효 처리한다. 첫 점은 이전 값이 없어 표시되지 않는다. 결측은 0으로 채우지 않으며 긴 결측을 가로지르는 rate는 장애 증거에 사용하지 않는다. [Flux derivative](https://docs.influxdata.com/flux/v0/stdlib/universe/derivative/).
Template:


### 8.5 PPS、Broadcast、Discard
<syntaxhighlight lang="bash" line>
template(name="QnapSyslogLine" type="string"
  string="%timegenerated:::date-rfc3339% src=%fromhost-ip% severity=%syslogseverity-text% host=%hostname% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%\n")
</syntaxhighlight>


위 쿼리의 `_field` 조건을 다음으로 바꾸고 8배하는 `map` 행은 제거한다. 각 계열을 별도로 표시한다.
이렇게 저장하면 한 줄이 대략 다음 구조가 된다.


```flux
<syntaxhighlight lang="bash" line>
  |> filter(fn: (r) => r._field == "in_ucast" or r._field == "in_mcast" or r._field == "in_bcast")
2026-09-13T23:40:03+09:00 \
```
src=198.51.100.10 \
severity=info \
host=nas-01 \
qulogd: ...
</syntaxhighlight>


합계 In PPS는 세 계열의 합이다. Out PPS는 대응하는 `out_` 세 계열로 만든다. Broadcast/Multicast 전용 패널도 구성해 정상 시 기준과 비교한다.
Event ruleset:


```flux
<syntaxhighlight lang="bash" line>
  |> filter(fn: (r) => r._field == "in_discards" or r._field == "out_discards")
ruleset(name="QnapEventLog") {
```


위 값은 폐기 packets/sec다. 누적값이 아니라 증가율을 표시한다. 일반 수집 measurement의 `in_errors/out_errors`도 같은 방식으로 별도 패널을 만든다.
    action(
        type="omfile"
        file="/var/log/qnap/event.log"
        template="QnapSyslogLine"


### 8.6 상태·손실·대시보드
        fileOwner="root"
        fileGroup="alloy"
        fileCreateMode="0640"


- `oper_status`는 rate로 바꾸지 않는다. 1=up, 2=down 등으로 state timeline에 표시한다.
        dirOwner="root"
- `speed_mbps`는 Mbps 단위다. In 사용률=In bps÷(speed_mbps×1,000,000)×100. Out은 따로 계산하며 전이중의 In/Out을 합쳐 100%로 판정하지 않는다.
        dirGroup="alloy"
- `ping`의 `average_response_ms`, `percent_packet_loss`는 이미 계산된 값이므로 derivative 불필요. 1패킷/5초 설정에서는 각 관측의 손실이 0% 또는 100%다.
        dirCreateMode="0750"
- LAG 논리 포트와 물리 멤버를 별도로 표시하고 합산 중복 집계를 피한다.
- 초기 패널: 중요 포트 bps, Broadcast/Multicast PPS, Discard/Error 증가율, operStatus, Ping 지연·손실, VM CPU·메모리·디스크, Telegraf 오류·결측.
- 스위치 CPU·PoE는 모델별 OID 확인 후 추가한다. 미설정을 0/정상으로 표시하지 않는다.


대시보드를 저장하고 JSON으로 내보낸다. 이번 Syslog 구성은 파일 조회용으로 Grafana에서 로그를 검색할 수는 없다. 별도 로그 검색 UI를 추가하기 전에는 §11의 CLI로 대조한다.
        createDirs="on"
    )


## 9. Zyxel Syslog 수신
    stop
}


### 9.1 포트 중복 확인
input(
    type="imtcp"
    port="5515"
    ruleset="QnapEventLog"
)
</syntaxhighlight>


```bash
Access ruleset:
grep -RniE 'imudp|imtcp|port="514"|UDPServerRun' /etc/rsyslog.conf /etc/rsyslog.d
ss -lunp | grep ':514'
```


기존 입력이 있으면 module/input을 중복 생성하지 말고 기존 설정에 통합한다. 아래는 미구성 신규 VM용이다.
<syntaxhighlight lang="bash" line>
ruleset(name="QnapAccessLog") {


```bash
    action(
install -d -m 750 /var/log/zyxel
        type="omfile"
restorecon -RF /var/log/zyxel
        file="/var/log/qnap/access.log"
vi /etc/rsyslog.d/30-zyxel-remote.conf
        template="QnapSyslogLine"
```


```conf
        fileOwner="root"
module(load="imudp")
        fileGroup="alloy"
        fileCreateMode="0640"


template(name="ZyxelRemotePath" type="string"
        dirOwner="root"
  string="/var/log/zyxel/%fromhost-ip:::secpath-replace%/events.log")
        dirGroup="alloy"
        dirCreateMode="0750"


template(name="ZyxelRemoteLine" type="string"
        createDirs="on"
  string="received=%timegenerated:::date-rfc3339% reported=%timereported:::date-rfc3339% src=%fromhost-ip% host=%hostname% facility=%syslogfacility-text% severity=%syslogseverity-text% tag=%syslogtag% msg=%msg%\n")
    )


ruleset(name="ZyxelRemoteRules") {
     stop
  action(type="omfile"
     dynaFile="ZyxelRemotePath"
    template="ZyxelRemoteLine"
    createDirs="on"
    dirCreateMode="0750"
    fileCreateMode="0640")
  stop
}
}


input(type="imudp" address="<COLLECTOR_IP>" port="514"
input(
  ruleset="ZyxelRemoteRules")
    type="imtcp"
```
    port="5516"
    ruleset="QnapAccessLog"
)
</syntaxhighlight>
 
=== 22.4 rsyslog 설정 검사 ===


송신 IP별 파일에 수신 시각과 장비 보고 시각을 함께 기록한다. 장비 Syslog에 시간대 정보가 없으면 완전한 보정은 불가능하므로 실장비 테스트로 대조한다.
재시작 전에 반드시 검사한다.


```bash
<syntaxhighlight lang="bash" line>
rsyslogd -N1
rsyslogd -N1
systemctl enable --now rsyslog
</syntaxhighlight>
 
오류가 없으면:
 
<syntaxhighlight lang="bash" line>
systemctl restart rsyslog
systemctl restart rsyslog
ss -lunp | grep ':514'
journalctl -u rsyslog -n 100 --no-pager
```


재시작 중 짧은 수신 공백에 주의한다. 실패하면 변경 전 파일을 복원하고 `rsyslogd -N1` 검사 후 재시작한다. SELinux 거부는 다음으로 확인하며 비활성화하지 않는다.
ss -lntp \
  | grep -E ':5514|:5515|:5516'
</syntaxhighlight>
 
실제 로그 확인:
 
<syntaxhighlight lang="bash" line>
tail -f \
  /var/log/qnap/access.log
</syntaxhighlight>
 
'''문제 분리 방법'''
 
<syntaxhighlight lang="bash" line>
tcpdump에는 Packet이 안 보임
→ 장비 송신/방화벽/라우팅 확인
 
tcpdump에는 보이지만 파일이 안 생김
→ rsyslog 설정 확인
 
파일은 생기지만 Grafana에 안 보임
→ Alloy/Loki 확인
</syntaxhighlight>
 
== 23. Loki와 Alloy의 역할 ==
 
rsyslog가 파일을 만드는 것만으로 Grafana가 그 파일을 검색할 수 있는 것은 아니다.
 
Loki가 로그 저장소 역할을 하고 Alloy가 파일을 읽어 Loki에 넣는다.
 
<syntaxhighlight lang="bash" line>
/var/log/...
    |
    v
  Alloy
    |
    v
  Loki
    |
    v
Grafana
</syntaxhighlight>
 
현재 구성에서는 Loki가 localhost 3100에서 동작한다.
 
<syntaxhighlight lang="bash" line>
127.0.0.1:3100
</syntaxhighlight>
 
Alloy 로컬 관리 Endpoint:
 
<syntaxhighlight lang="bash" line>
127.0.0.1:12345
</syntaxhighlight>
 
== 24. Alloy 설정 이해하기 ==
 
=== 24.1 Network Syslog ===
 
<syntaxhighlight lang="bash" line>
loki.source.file "network_syslog" {
 
  targets = [
    {
      __path__ = "/var/log/network-syslog/events.log",
      job      = "network-syslog",
    },
  ]
 
  forward_to = [
    loki.process.network_syslog.receiver
  ]
}
</syntaxhighlight>
 
'''source.file'''은 어떤 파일을 읽을지 지정한다.
 
'''job'''은 Loki에서 로그 종류를 구분하기 위한 Label이다.
 
그 다음 regex로 한 줄을 분해한다.
 
<syntaxhighlight lang="bash" line>
loki.process "network_syslog" {
 
  stage.regex {
    expression = `^(?P<received_at>\S+) src=(?P<device_ip>\S+) severity=(?P<severity>\S+) host=(?P<device_host>\S+) (?P<message>.*)$`
  }
 
  stage.timestamp {
    source            = "received_at"
    format            = "RFC3339Nano"
    action_on_failure = "skip"
  }
 
  stage.labels {
    values = {
      device_ip = ""
      severity  = ""
    }
  }
 
  forward_to = [
    loki.write.local.receiver
  ]
}
</syntaxhighlight>
 
여기서 다음 Label이 생긴다.
 
<syntaxhighlight lang="bash" line>
job
device_ip
severity
</syntaxhighlight>
 
'''device_host를 Label로 추가하려면'''
 
<syntaxhighlight lang="bash" line>
stage.labels {
  values = {
    device_ip  = ""
    device_host = ""
    severity    = ""
  }
}
</syntaxhighlight>
 
이는 이후 Alert Mail에서 Hostname까지 표시하고 싶을 때 유용하다.
 
=== 24.2 QNAP Access ===
 
<syntaxhighlight lang="bash" line>
loki.source.file "qnap_access" {
 
  targets = [
    {
      __path__ = "/var/log/qnap/access.log",
      job      = "qnap-access",
    },
  ]
 
  forward_to = [
    loki.process.qnap_access.receiver
  ]
}
 
loki.process "qnap_access" {
 
  stage.regex {
    expression = `^(?P<received_at>\S+) src=(?P<nas_ip>\S+) severity=(?P<severity>\S+) host=(?P<nas_host>\S+) (?P<message>.*)$`
  }
 
  stage.timestamp {
    source            = "received_at"
    format            = "RFC3339Nano"
    action_on_failure = "skip"
  }


```bash
  stage.labels {
ausearch -m AVC -ts recent
    values = {
ls -Zd /var/log/zyxel
      nas_ip  = ""
```
      nas_host = ""
      severity = ""
      log_type = "access"
    }
  }


근거: [imudp](https://docs.rsyslog.com/doc/configuration/modules/imudp.html), [omfile](https://docs.rsyslog.com/doc/configuration/modules/omfile.html). 참조 문서는 최신판을 포함하므로 설치된 rsyslog의 구문 검사를 반드시 수행한다.
  forward_to = [
    loki.write.local.receiver
  ]
}
</syntaxhighlight>


### 9.2 로그 회전과 보존
=== 24.3 Loki Write ===


`/etc/logrotate.d/zyxel-remote`:
<syntaxhighlight lang="bash" line>
loki.write "local" {


```conf
  endpoint {
/var/log/zyxel/*/events.log {
    url = "http://127.0.0.1:3100/loki/api/v1/push"
    daily
  }
    rotate 30
    missingok
    notifempty
    compress
    delaycompress
    create 0640 root root
    sharedscripts
    postrotate
        /usr/bin/systemctl kill -s HUP --kill-who=main rsyslog.service >/dev/null 2>&1 || true
    endscript
}
}
```
</syntaxhighlight>
 
즉 Alloy에서 처리한 로그를 로컬 Loki로 전송한다.
 
=== 24.4 설정 검사 ===
 
<syntaxhighlight lang="bash" line>
alloy validate \
  /etc/alloy/config.alloy
</syntaxhighlight>
 
정상이라면 아무 오류 없이 종료된다.
 
반영:
 
<syntaxhighlight lang="bash" line>
systemctl restart alloy
 
systemctl status alloy \
  --no-pager
</syntaxhighlight>
 
Loki에 실제 Job이 만들어졌는지 확인:
 
<syntaxhighlight lang="bash" line>
curl -s \
  'http://127.0.0.1:3100/loki/api/v1/label/job/values' \
  | jq
</syntaxhighlight>
 
중요한 점은 Alloy 설정에 job을 적었다고 바로 Loki에 Label이 생기는 것이 아니라 '''실제 로그가 최소 한 번 Loki에 저장되어야''' 조회 결과에 나타난다는 것이다.
 
== 25. Alloy Permission 문제 해결 사례 ==
 
실제 구축 과정에서 QNAP 로그 파일은 존재하지만 Alloy가 다음 오류를 출력하였다.
 
<syntaxhighlight lang="bash" line>
failed to tail file
stat failed
permission denied
</syntaxhighlight>
 
먼저 경로 전체 권한 확인:
 
<syntaxhighlight lang="bash" line>
namei -l \
  /var/log/qnap/access.log
</syntaxhighlight>
 
정상 예:
 
<syntaxhighlight lang="bash" line>
drwxr-x--- root alloy qnap
-rw-r----- root alloy access.log
</syntaxhighlight>
 
Alloy 계정으로 직접 읽기:
 
<syntaxhighlight lang="bash" line>
sudo -u alloy \
  head /var/log/qnap/access.log
</syntaxhighlight>
 
그래도 실패하면 SELinux 확인:
 
<syntaxhighlight lang="bash" line>
getenforce
 
ausearch \
  -m AVC \
  -ts recent \
  | grep -Ei 'alloy|qnap'
 
ls -Zd /var/log/qnap
ls -Z /var/log/qnap/access.log
</syntaxhighlight>
 
'''문제 해결 원칙'''
 
권한 문제가 있다고 SELinux를 바로 끄지 않는다.
 
다음 순서로 본다.
 
# 파일 Owner/Group
# 파일 Mode
# 상위 Directory execute 권한
# Alloy Service User
# SELinux Context / AVC
 
== 26. Grafana에 Loki 추가 ==
 
Grafana:
 
<syntaxhighlight lang="bash" line>
Connections
→ Data sources
→ Add data source
→ Loki
</syntaxhighlight>
 
URL:
 
<syntaxhighlight lang="bash" line>
http://127.0.0.1:3100
</syntaxhighlight>
 
Explore에서 Network 로그 확인:
 
<syntaxhighlight lang="bash" line>
{job="network-syslog"}
</syntaxhighlight>
 
QNAP Access:
 
<syntaxhighlight lang="bash" line>
{job="qnap-access"}
</syntaxhighlight>
 
QNAP Event:
 
<syntaxhighlight lang="bash" line>
{job="qnap-event"}
</syntaxhighlight>
 
== 27. Grafana Alert를 처음 구성할 때 알아둘 점 ==
 
Grafana Alert는 Dashboard의 그래프와 달리 최종적으로 '''숫자 하나 또는 Alert Instance별 숫자'''를 평가해야 한다.
 
Range Query를 그대로 Alert Condition에 사용하면 다음 오류가 발생할 수 있다.
 
<syntaxhighlight lang="bash" line>
looks like time series data,
only reduced data can be alerted on
</syntaxhighlight>
 
따라서 다음 중 하나를 사용한다.
 
# Query를 Instant로 구성
# Range Query → Reduce → Threshold
 
== 28. Syslog Critical Alert ==
 
Level 3(Error) 이상:
 
<syntaxhighlight lang="bash" line>
sum by (device_ip, severity) (
  count_over_time(
    {
      job="network-syslog",
      severity=~"emerg|alert|crit|err"
    }[1m]
  )
)
</syntaxhighlight>
 
권장:
 
<syntaxhighlight lang="bash" line>
A = Loki Instant Query
 
B = Threshold
A IS ABOVE 0
</syntaxhighlight>
 
Summary:
 
<syntaxhighlight lang="bash" line>
[Syslog 경고]
{{ $labels.device_ip }}
{{ $labels.severity }}
</syntaxhighlight>
 
Description:
 
<syntaxhighlight lang="bash" line>
장비 {{ $labels.device_ip }} 에서
Syslog Level 3 이상 로그가 발생했습니다.
 
Severity:
{{ $labels.severity }}
 
최근 1분 발생 건수:
{{ $values.A.Value }}
</syntaxhighlight>
 
=== 28.1 Alert Mail에 실제 로그 내용이 없는 이유 ===
 
count_over_time()은 문자열 로그를 숫자로 집계한다.
 
따라서 Query 결과에는 주로 다음만 남는다.
 
* Label
* 발생 건수
 
로그 message 전체를 Loki Label로 만들면 종류가 지나치게 많아져 Cardinality 문제가 발생할 수 있으므로 권장하지 않는다.
 
메일에는 다음 정도를 넣고 실제 내용은 Grafana Explore에서 확인하는 구조가 안정적이다.
 
* Device IP
* Hostname
* Severity
* 발생 건수
* Grafana Link
 
== 29. ICMP Down Alert ==
 
Flux:


보존은 ‘30회전분’이며 매일 회전하면 약 30일이다. 정확히 30일이 지난 순간 삭제되는 방식은 아니다. 오래된 파일 삭제를 수반하므로 장애 로그는 별도 보관한다. 신규 VM의 rsyslog root 실행 기준이며 privilege drop을 사용한다면 소유자를 맞춘다.
<syntaxhighlight lang="bash" line>
from(bucket: "snmp_raw")
  |> range(start: -5m)


```bash
  |> filter(fn: (r) =>
logrotate -d /etc/logrotate.d/zyxel-remote
    r._measurement == "ping" and
systemctl status logrotate.timer --no-pager
    r._field == "percent_packet_loss"
systemctl list-timers --all | grep logrotate
  )
```


일일 실행이 비활성이라면 패키지 timer/cron을 확인해 활성화한다. 일일 회전만으로 하루 동안의 폭증에 따른 디스크 고갈은 막을 수 없다. 디스크 80% 예고 감시와 증가량 점검을 수행한다. 근거 없이 중요 로그를 제한해 버리지 않는다.
  |> group(
    columns: ["url"]
  )


### 9.3 Zyxel 측 송신
  |> last()


Syslog 목적지 `<COLLECTOR_IP>` / UDP514. Informational을 포함하는 범위로 시작하고 상시 Debug는 피한다. Link Up/Down, STP 변경, 루프 탐지, PoE 이상·전력 부족, 재시작 등을 모델 지원 범위에서 활성화한다. 포트별 이벤트 설정이 필요하면 함께 적용한다.
  |> keep(
    columns: [
      "_time",
      "_value",
      "url"
    ]
  )
</syntaxhighlight>


## 10. firewalld와 접속 확인
Alert 구조:


기본 웹 접속은 SSH 터널만 사용한다. 3000/8086과 서버 수신 UDP161/162를 열지 않는다. SNMP는 서버에서 스위치 UDP161로 조회하고 응답을 받는다. 외향 제한이 있는 경우에만 기존 정책에 필요 허용을 추가한다.
<syntaxhighlight lang="bash" line>
A = Flux Query


### 10.1 Syslog만 허용
B = Reduce
    Last
    Strict


영향: 지정 송신원에서 UDP514 수신을 추가한다. zone 변경·SSH 규칙 삭제·광범위 허용은 하지 않는다. 먼저 확인:
C = Threshold
    B > 99


```bash
Pending = 2m
firewall-cmd --get-active-zones
</syntaxhighlight>
firewall-cmd --zone=<FW_ZONE> --list-all
```


실제 값으로 설정한다. 송신자를 엄격히 제한하려면 장비별 /32 규칙을 사용한다.
No Data 정책:


```bash
<syntaxhighlight lang="bash" line>
MON_ZONE='<FW_ZONE>'
Keep Last State
MON_SYSLOG_RULE='rule family="ipv4" source address="<SWITCH_SOURCE_CIDR>" port port="514" protocol="udp" accept'
</syntaxhighlight>
firewall-cmd --zone="$MON_ZONE" --add-rich-rule="$MON_SYSLOG_RULE" --timeout=600
```


10분 안에 장비 송신과 SSH 접속 유지를 확인한다. 임시 규칙은 자동 만료된다. 승인 후 동일 규칙만 영구 및 runtime에 적용:
'''왜 Keep Last State인가?'''


```bash
Metric이나 Log Source가 순간적으로 No Data가 되었을 때 별도의 DatasourceNoData 메일이 발생하면서 실제 장비 Label이 없는 알림이 발송될 수 있다.
firewall-cmd --permanent --zone="$MON_ZONE" --add-rich-rule="$MON_SYSLOG_RULE"
firewall-cmd --zone="$MON_ZONE" --remove-rich-rule="$MON_SYSLOG_RULE"
firewall-cmd --zone="$MON_ZONE" --add-rich-rule="$MON_SYSLOG_RULE"
```


이미 만료됐다면 remove의 not enabled를 확인하고 다음으로 진행한다. 전체 runtime 저장이나 reload로 다른 작업 변경을 포함하지 않는다.
No Data 자체를 별도 장애로 관리할 필요가 있다면 별도의 수집 상태 Alert를 구성하는 것이 더 명확하다.


롤백 — 이번 작업에서 새로 추가한 규칙만 제거:
== 30. 최종 Dashboard 구성 방법 ==


```bash
처음 설치한 사용자는 Dashboard를 한 번에 완성하려 하지 말고 다음 단계로 만든다.
firewall-cmd --zone="$MON_ZONE" --remove-rich-rule="$MON_SYSLOG_RULE"
firewall-cmd --permanent --zone="$MON_ZONE" --remove-rich-rule="$MON_SYSLOG_RULE"
```


### 10.2 실장비 도달 확인
# 데이터소스 Save & Test
# Explore에서 실제 데이터 확인
# Stat 패널 하나 생성
# Time Series 하나 생성
# 장비 한 대 정상 확인
# 변수 추가
# 여러 장비로 확대
# Alert 추가


```bash
=== 30.1 Network Switch Dashboard ===
timeout 20 tcpdump -ni any -nn 'udp dst port 514'
tail -n 50 /var/log/zyxel/<SWITCH_SYSLOG_SOURCE_IP>/events.log
```


VM 자신의 logger는 파일 저장 검사용이며 스위치에서의 경로·ACL 검증을 대신하지 못한다.
권장 Row:


```bash
{| class="wikitable"
logger --udp --server <COLLECTOR_IP> --port 514 --tag IPT_TEST 'collector receiver test'
! Row
```
! 표시 내용
! 목적
|-
| Device 상태
| Device Name / Uptime / CPU / Memory / Ping
| 장비 자체 상태
|-
| Port 상태
| ifName / ifAlias / Speed / operStatus
| Link 상태
|-
| Traffic
| RX / TX bps
| 대역폭 사용량
|-
| Packet
| Unicast / Broadcast / Multicast PPS
| Broadcast 폭주 및 Packet 패턴
|-
| Error
| Error / Discard
| 품질 및 혼잡 징후
|-
| Syslog
| warning / err / crit
| 이벤트 원인 확인
|}


## 11. 시험 운영·장애 확인
=== 30.2 Linux Dashboard ===


### 11.1 초기 24시간
권장:


1. 한 대·중요 포트 1~2개로 시작한다.
* Uptime
2. 실제 SNMP 카운터와 InfluxDB 값 증가를 대조한다.
* CPU
3. bps/PPS 변동과 operStatus 표시를 확인한다.
* Memory
4. 정상 발신 테스트의 시각·단말 IP·Call-ID를 기록한다. 본망에서 부하용 iperf나 루프를 만들지 않는다.
* Load
5. 별도 수집 중인 콜서버 SIP와 전후 2분의 네트워크 기록을 비교한다.
* Disk Capacity
6. 5060 외 SIP/TLS와 RTP는 해당 캡처 범위 밖임을 기록한다.
* Disk I/O
7. 수집시간·스위치 CPU·VM CPU/IO·결측·로그 증가량이 허용 범위면 대상 장비를 늘린다.
* Network RX/TX
8. 중요 포트 1초화는 카운터 갱신을 실측한 후 진행한다.
* Network Error/Drop
* Application/Security Log


### 11.2 운영 확인 명령
=== 30.3 Windows Dashboard ===


```bash
권장:
systemctl is-active influxdb telegraf grafana-server rsyslog chronyd
journalctl -u telegraf --since '-10 min' --no-pager
chronyc tracking
df -h
du -sh /var/log/zyxel
vmstat 1 5
iostat -xz 1 5
nstat -az | grep -E 'Udp(InErrors|RcvbufErrors|InDatagrams)'
```


UDP 통계는 누적값이므로 시간 차분으로 본다. VM/호스트의 수신 드롭을 스위치 송신 중단으로 오해하지 않는다. `inputs.internal`의 수집시간·오류와 DB 출력 drop도 대시보드에 추가한다.
* Exporter UP
* Uptime
* CPU
* Memory
* Logical Disk
* Disk I/O
* Network
* Windows Event 연동 여부


### 11.3 장애 기록 양식
=== 30.4 QNAP Dashboard ===


| 항목 | 기록 |
현재 실제 운영 구성을 기준으로 다음과 같이 구성한다.
|---|---|
| 장애 일시·시간대 | 초 단위, KST |
| 발신·착신 | 내선, 전화기 IP, 등록 상태 |
| 연결 위치 | L2 이름, 포트, ifIndex, Voice VLAN |
| PC 상태 | 동시 정상·불통·미확인 |
| SIP | INVITE 도착, 응답 코드, Call-ID, 재전송 |
| SNMP | In/Out bps、PPS、Broadcast、Discard、operStatus |
| 로그 | Link, STP, PoE, 재부팅 등 |
| 수집 상태 | NTP, 결측, VM 수신 drop, tcpdump drop |


로그 검색:
상단:


```bash
* Node Exporter UP
grep -Ei 'link|stp|loop|poe|power|reboot|restart|topology' \
* Uptime
    /var/log/zyxel/<SWITCH_SYSLOG_SOURCE_IP>/events.log
* CPU Usage
```
* Memory Usage
* HDD 최고 온도


메시지의 수신·보고 시각을 장애 전후와 비교한다. 키워드가 없다는 이유만으로 이벤트가 없다고 판단하지 않는다. 장비 문구는 모델에 따라 다르다.
중단:


### 11.4 초기 알림 제안
* CPU / Memory / Load
* bond0 RX / TX
* Disk 상태
* DataVol1 Volume
* DataVol1 Filesystem 사용률
* bond0 Error / Drop
* Physical Disk I/O / IOPS


아래는 운영 후 Grafana 관리형 알림으로 구성할 제안값이며 자동 생성된 설정은 아니다.
하단:


- 중요 포트 한 방향 사용률 80% 이상이 30초 지속: 혼잡 예고이며 원인 확정은 아니다.
* QNAP Event Log
- 중요 포트 Discard 증가: 조사 이벤트. QoS·버퍼·정책 확인.
* QNAP Access Log
- Ping 3회 연속 손실: VM에서의 도달성 이상이며 SIP 장애 확정은 아니다.
- 고주기 데이터 15초 이상 결측: No Data를 정상으로 처리하지 않는다.
- 디스크 사용량 80% 이상: 보존 공간 경고.
- Broadcast PPS는 24시간 정상값을 확보한 뒤 임계값을 결정한다. 모든 현장에 공통 고정값을 적용하지 않는다.


전화 연결 실패와 높은 사용률·Discard 증가가 동반되면 혼잡이 유력하다. INVITE 불착만으로 단말 미송신과 경로 손실은 구분할 수 없다. 등록·PoE·Voice VLAN 상태와 함께 판단한다.
제거한 항목:


## 12. sFlow 추가 — 조건부 절차
* 상단 RAID 상태
* Storage Pool 상태
* Storage Pool 사용률
* RAID 상세 Table
* Storage Pool 상세 Table


### 12.1 착수 조건
삭제 이유는 Dashboard를 운영자가 빠르게 읽을 수 있도록 단순화하기 위해서이다.


**Zyxel이라는 제조사명만으로 sFlow 지원을 확정할 수 없다.** 정확한 모델·펌웨어의 공식 매뉴얼에서 exporter, 대상 인터페이스, sampling 범위, source/agent IP 설정을 확인한다. 확인 전 컨테이너 전체를 기동할 필요는 없다.
RAID/Storage Pool 상세는 필요 시 별도의 상세 Dashboard에서 확인할 수 있다.


| 장비 설정 | 계획 |
== 31. 초보자가 자주 만나는 문제 ==
|---|---|
| Collector | `<COLLECTOR_IP>` |
| UDP port | 6343, 수집기와 일치 |
| Agent / source | SNMP로 도달 가능한 고정 관리 IP |
| Sampling | 지원 범위에서 1:1024 전후를 시험 시작값으로 검토 |
| Counter polling | 10~30초 중 장비 지원값 |
| 관측 대상 | 중요 L2/L3 포트, 가능하면 ingress 중심 |


패킷 샘플링과 counter polling은 별개다. sFlow는 SIP 전체 패킷 캡처를 대체하지 않는다. IP별 사용률은 추정이고 L3만으로 같은 L2 내부 트래픽을 놓칠 수 있다. 여러 스위치·입출력의 동일 패킷을 중복 합산하지 않는다.
=== 31.1 Telegraf --test는 되는데 서비스는 안 됨 ===


### 12.2 Akvorado 도입 방안
root 권한에서는 Ping이 되지만 telegraf 서비스 계정에서는 Raw Socket 권한이 없을 수 있다.


[Akvorado 공식 프로젝트](https://github.com/akvorado/akvorado)는 NetFlow/IPFIX/sFlow, SNMP 메타데이터 보강, ClickHouse 저장과 웹 분석을 제공한다. 공식 Docker Compose는 본문의 RPM 운영과 별개다. **이전 설명의 Podman Compose 호환을 전제하지 않는다.** Docker와 Podman 호환 패키지도 혼용하지 않는다.
확인:


공식 `.env`는 여러 `docker/docker-compose-*.yml`을 조합한다. main은 개발 구성을 포함하므로 그대로 운영 배포하지 않는다. [공식 구성 디렉터리](https://github.com/akvorado/akvorado/tree/main/docker).
<syntaxhighlight lang="bash" line>
journalctl -u telegraf \
  -n 100 \
  --no-pager
</syntaxhighlight>


지원 확인 후 KVM 스냅샷과 방화벽 백업을 확보하고 Docker Engine의 RHEL/CentOS계 설치 방법을 검토한다. Rocky가 Docker의 공식 지원 대상과 동일하다고 가정하지 않는다. [Docker CentOS 절차](https://docs.docker.com/engine/install/centos/).
native Ping이면 CAP_NET_RAW 설정을 확인한다.


신규 전용 VM이며 패키지 충돌이 없다고 확인된 경우의 준비 명령:
=== 31.2 snmpget은 되는데 Telegraf에서 일부 장비만 누락 ===


```bash
가능한 원인:
rpm -q podman-docker docker-ce docker-ce-cli containerd.io
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
dnf list --showduplicates docker-ce docker-compose-plugin
dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```


충돌 패키지를 자동 삭제하지 않는다. Docker는 전송·패킷 필터 규칙을 변경할 수 있으므로 KVM 게스트에서만 적용하고 관리 경로를 검증한다. KVM 호스트에서는 실행하지 않는다.
* timeout이 너무 짧음
* retries가 0
* OID가 모델과 다름
* DES/AES 조합이 장비와 다름
* SNMP View 제한


```bash
먼저 동일 OID를 snmpget으로 확인한다.
systemctl enable --now docker
docker version
docker compose version
dnf install -y git
git clone https://github.com/akvorado/akvorado.git /opt/akvorado
cd /opt/akvorado
git tag --sort=-version:refname | head -20
```


기존 파일이 있는 `/opt/akvorado`에는 clone하지 않는다. 공식 안정 릴리스의 태그를 선택하고 해당 Changelog·문서를 읽는다. 본문에서 태그는 미확정이다.
=== 31.3 Prometheus가 재시작 반복 ===


```bash
가장 먼저:
git switch --detach <VERIFIED_STABLE_TAG>
git rev-parse HEAD
docker compose config --services
docker compose config --images
docker compose config --quiet
```


`docker compose config --quiet`는 Compose 구문만 검사한다. 앱 설정 검증과 다르다. 아래 환경값을 **선택한 릴리스의 schema로 구성한 후** 기동한다. 추정한 키 이름으로 작성한 YAML은 제공하지 않는다.
<syntaxhighlight lang="bash" line>
promtool check config \
  /etc/prometheus/prometheus.yml
</syntaxhighlight>


| 필수 설정 | 확인 사항 |
YAML 들여쓰기를 확인한다.
|---|---|
| sFlow 입력 | UDP6343 수신·바인딩 |
| SNMP metadata | agent/source 주소, 장비별 인증값, 포트명 보강 |
| 내부 네트워크 | Voice/Data CIDR과 사설 IP 분류 |
| 불필요 서비스 | demo exporter 비활성, Grafana 중복 배치 금지, GeoIP 의존성 확인 |
| 인증·공개 | 웹은 loopback+SSH 또는 인증 TLS, DB/Kafka는 외부 공개 금지 |
| 보존 | 원시·집계 데이터 기간, ClickHouse TTL, 용량 계획 |
| 영속화·SELinux | named volume 또는 전용 bind mount 라벨, 공유 OS 경로에 `:Z` 사용 금지 |
| 재시작 | 서비스 restart policy, Docker 자동 기동 |


**Docker 공개 포트를 firewalld INPUT 규칙만으로 제한할 수 있다고 가정하지 않는다.** 실제 백엔드의 전달 규칙/DOCKER-USER 상당 경로, 상위 ACL과 관리 IP 바인딩을 확인하고 비허가 단말에서 차단되는지 시험한다. 관리 IP 바인딩은 송신원 제한이 아니다. §10 rich rule만으로 Docker의 UDP6343 제한을 완료 처리하지 않는다.
그 다음:


위 조건을 확정한 뒤 실행할 기동·검증 명령:
<syntaxhighlight lang="bash" line>
journalctl -u prometheus \
  -n 100 \
  --no-pager
</syntaxhighlight>


```bash
=== 31.4 Grafana에서 Filesystem이 너무 많이 보임 ===
cd /opt/akvorado
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100
```


태그·설정이 미정인 상태에서 일괄 실행하는 명령이 아니다. 같은 릴리스의 앱 설정 검사 방법을 따르고 healthy 표시뿐 아니라 실제 데이터 표시도 확인한다.
Linux/QNAP에는 tmpfs, loop, snapshot, container mount 등이 존재할 수 있다.


### 12.3 수신·분석 합격 기준
전체를 보여주기보다 운영자가 실제 사용하는 Mount Point만 필터링한다.


```bash
QNAP 예:
timeout 20 tcpdump -ni any -nn 'udp dst port 6343'
ss -lunp | grep ':6343'
nstat -az | grep -E 'Udp(InErrors|RcvbufErrors|InDatagrams)'
```


Docker의 포트 구현에 따라 호스트 `ss`에 예상 소켓이 안 보일 수 있다. 실제 Compose 포트 매핑과 수신 메트릭을 함께 확인한다.
<syntaxhighlight lang="bash" line>
mountpoint="/share/CACHEDEV1_DATA"
</syntaxhighlight>


합격: 스위치 source에서 패킷 도착, 수집·디코드 카운터 증가, 오류 급증 없음, 웹에서 exporter·입력 포트·단말 IP·시간대 표시, 포트 번호의 실제 이름 매핑. tcpdump 도착만으로 성공 판정하지 않는다.
=== 31.5 Loki에 Job이 안 보임 ===


미지원 장비는 SNMP/Syslog를 유지하고 필요한 사건만 승인된 포트 미러링으로 분석한다. 미러링은 별도 vNIC·물리 연결·대역폭 검토가 필요하며 현재 관리 NIC를 임의로 미러 수신 포트로 바꾸지 않는다.
설정에 job이 있다고 바로 나타나는 것이 아니다.


## 13. 백업·롤백
실제 Log가 Loki에 한 번 이상 저장되어야 한다.


### 13.1 일반 백업
확인 순서:


Influx CLI 관리자 인증으로 여유 공간이 있는 새 디렉터리에 저장한다. Grafana 읽기 전용·Telegraf 쓰기 전용 토큰으로는 백업할 수 없다. [InfluxDB backup](https://docs.influxdata.com/influxdb/v2/reference/cli/influx/backup/).
<syntaxhighlight lang="bash" line>
tail -n 10 \
  /var/log/qnap/access.log


```bash
journalctl -u alloy \
umask 077
  -n 100 \
MON_ARCHIVE="/root/ipt-monitor-save-$(date +%Y%m%d-%H%M%S)"
  --no-pager
install -d -m 700 "$MON_ARCHIVE"
influx backup "$MON_ARCHIVE/influxdb"
tar -czf "$MON_ARCHIVE/configs.tar.gz" \
    /etc/telegraf /etc/grafana /etc/rsyslog.conf /etc/rsyslog.d \
    /etc/logrotate.d/zyxel-remote /etc/chrony.conf /etc/firewalld \
    /etc/systemd/system/telegraf.service.d \
    /etc/systemd/system/influxdb.service.d
```


백업에는 SNMP 비밀과 토큰이 포함된다. root 전용 권한을 유지하고 접근제어된 다른 매체에 암호화 복제한다. root 디스크에만 둔 것은 백업 완료로 간주하지 않는다.
curl -s \
  'http://127.0.0.1:3100/loki/api/v1/label/job/values' \
  | jq
</syntaxhighlight>


Grafana 기본 SQLite의 실행 중 파일만 단순 복사하지 않는다. 화면 중단이 허용되는 시간에 다음을 수행한다:
== 32. 구축 완료 후 점검 Checklist ==


```bash
{| class="wikitable"
systemctl stop grafana-server
! 확인
tar -czf "$MON_ARCHIVE/grafana-data.tar.gz" /var/lib/grafana
! 항목
systemctl start grafana-server
|-
curl -fsS http://127.0.0.1:3000/api/health
| □
```
| Rocky Linux 시간 동기화 정상
|-
| □
| InfluxDB health 정상
|-
| □
| Telegraf 서비스 정상
|-
| □
| SNMP 장비별 응답 확인
|-
| □
| InfluxDB에 SNMP 데이터 저장
|-
| □
| Prometheus health 정상
|-
| □
| Node Exporter Target UP
|-
| □
| Windows Exporter Target UP
|-
| □
| QNAP Node Exporter Target UP
|-
| □
| rsyslog Network 로그 수신
|-
| □
| QNAP Event / Access Log 분리 수신
|-
| □
| Alloy Permission 정상
|-
| □
| Loki job 확인
|-
| □
| Grafana InfluxDB Save & Test
|-
| □
| Grafana Prometheus Save & Test
|-
| □
| Grafana Loki Save & Test
|-
| □
| Switch Traffic 그래프 정상
|-
| □
| Error/Discard 그래프 정상
|-
| □
| Linux/Windows Dashboard 정상
|-
| □
| QNAP DataVol1 용량 QTS와 일치
|-
| □
| QNAP Snapshot 제외
|-
| □
| QNAP bond0만 Network 표시
|-
| □
| Syslog Alert Mail에 장비 IP / Severity 표시
|-
| □
| ICMP Down Alert 정상
|}


이 동안 Telegraf 수집은 계속된다. 외부 DB를 사용하는 Grafana는 별도 DB 정합 백업이 필요하다. 장애 구간 Syslog·콜서버 pcap도 보존한다. 기록 중인 파일은 변경될 수 있으므로 닫힌 파일이나 정합 스냅샷으로 증거를 확보한다.
== 33. 전체 장애 점검 순서 ==


### 13.2 설정 변경 롤백
모니터링이 안 될 때 Grafana부터 무작정 수정하지 않는다.


- 고주기 SNMP 부하: `20-zyxel-fast.conf`의 interval을 5/10초로 복귀 후 검사·재시작. 긴급 시 `systemctl stop telegraf`를 수행하고 수집 중단을 기록한다.
=== 33.1 SNMP ===
- Telegraf 오류: 해당 설정의 변경 전 버전 복원 → 검사 → 재시작. DB는 삭제하지 않는다.
- 신규 Syslog 설정만 취소하려면 아래처럼 비활성화한다. 수신 중단을 알리고 기존 파일을 수정했던 경우에는 변경 전 버전으로 복원한다.


```bash
<syntaxhighlight lang="bash" line>
mv /etc/rsyslog.d/30-zyxel-remote.conf /etc/rsyslog.d/30-zyxel-remote.conf.disabled
Switch
rsyslogd -N1
  ↓
systemctl restart rsyslog
snmpget
```
  ↓
Telegraf --test
  ↓
Telegraf Service
  ↓
InfluxDB
  ↓
Grafana Explore
  ↓
Dashboard
</syntaxhighlight>


- firewalld는 §10에서 추가한 정확히 일치하는 규칙만 제거한다.
=== 33.2 Prometheus ===
- Akvorado 중지는 `/opt/akvorado`에서 `docker compose stop`. `down -v`, volume 삭제, ClickHouse DROP은 하지 않는다.
- 장비의 sFlow는 추가한 export 설정만 되돌린다. Voice VLAN·PoE·STP·QoS는 일괄 초기화하지 않는다.


### 13.3 데이터 복원·업그레이드
<syntaxhighlight lang="bash" line>
Exporter /metrics
  ↓
Prometheus Target
  ↓
Prometheus Query
  ↓
Grafana Explore
  ↓
Dashboard
</syntaxhighlight>


운영 InfluxDB에 full restore를 바로 수행하면 덮어쓰기 위험이 있다. 격리된 새 VM에 같은 버전으로 공식 restore 절차를 시험한다. Grafana는 중지 후 동일 구성의 데이터·설정을 복원하고 소유자/SELinux 라벨을 확인해 기동한다. 새 운영 데이터와의 통합은 별도 계획한다.
=== 33.3 Syslog ===


업그레이드: RPM/이미지 버전 기록 → 백업 → 시험 VM 검증 → 운영 적용. 메이저 변경·DB schema 변경 후 바이너리만 내려서 롤백하지 않는다.
<syntaxhighlight lang="bash" line>
Device 송신
  ↓
tcpdump
  ↓
rsyslog
  ↓
Log File
  ↓
Alloy
  ↓
Loki
  ↓
Grafana Explore
  ↓
Dashboard / Alert
</syntaxhighlight>


## 14. 장애 확인표·인수 체크리스트
이 순서대로 확인하면 문제 지점을 빠르게 좁힐 수 있다.


| 이상 | 첫 확인 |
== 34. 운영 원칙 요약 ==
|---|---|
| SNMP timeout | 같은 조건 CLI, 관리 경로·ACL·CPU·인증·암호화 |
| No Such Object/Instance | view·모델 지원·ifIndex·인터페이스 선택 |
| Telegraf 값은 있는데 Grafana가 비어 있음 | DB 쓰기 토큰·bucket·measurement/tag·UTC/KST·시간 범위 |
| 계단형 그래프·이상 피크 | 카운터 갱신 주기·리셋·결측·중복 수집 |
| 재부팅 후 다른 포트 표시 | ifIndex 재조회, 고정 OID·태그 수정 |
| Syslog는 도착하지만 파일 없음 | ruleset·구문·권한·SELinux·실제 송신 IP |
| sFlow 패킷만 도착 | 포트 매핑·디코딩·metadata·DB·시간·필터 |
| PoE/CPU 없음 | 본 구성 미포함. 모델 전용 MIB 필요 |
| 전화 장애 때 전체 데이터 결측 | VM/KVM 부하·관리 경로·NTP·수신 drop |


### 초기 구성 완료 조건
* 처음부터 모든 장비를 추가하지 않는다.
* 한 장비, 한 Port를 먼저 완성한다.
* 설정 변경 후 항상 해당 서비스의 검사 명령을 실행한다.
* Counter와 현재 상태값을 구분한다.
* No Data와 0을 같은 의미로 처리하지 않는다.
* SNMP Password와 Token을 Wiki에 저장하지 않는다.
* Grafana는 수집기가 아니라 시각화 계층임을 기억한다.
* QNAP처럼 제조사별 특이사항은 실제 값과 Vendor 화면을 대조한다.
* Alert는 처음부터 너무 많이 만들지 않는다.
* 정상 상태의 기준 데이터를 먼저 쌓은 후 임계값을 결정한다.
* 장애 시에는 수집 경로를 앞단부터 순서대로 확인한다.


- [ ] OS/RPM 버전과 IP·포트 대응표를 저장했다.
== 35. 최종 구성 요약 ==
- [ ] SNMPv3 읽기 전용으로 대상 조회 및 ifIndex를 확인했다.
- [ ] 실데이터의 bytes·PPS·Broadcast·Discard가 확인된다.
- [ ] Grafana가 누적값 대신 rate를 표시하고 상태는 원래 값으로 표시한다.
- [ ] DB 저장·화면 표시와 재부팅 후 수집 지속을 확인했다.
- [ ] 실장비 Syslog 저장과 logrotate dry-run을 확인했다.
- [ ] 콜서버·스위치와 NTP가 일치한다.
- [ ] DB/API 비공개, SSH·송신원 허용을 확인했다.
- [ ] 결측을 0/정상으로 표시하지 않는다.
- [ ] sFlow·PoE·CPU 미확인 항목을 완료 처리하지 않는다.
- [ ] 보존 만료 전 장애 증거 보관과 복원 방침을 정했다.


## 15. 공식 자료·대상 버전
<syntaxhighlight lang="bash" line>
                  +----------------------+
                  |      Grafana        |
                  +----------+-----------+
                            |
          +------------------+------------------+
          |                  |                  |
          v                  v                  v
    Prometheus          InfluxDB              Loki
          ^                  ^                  ^
          |                  |                  |
    Exporters          Telegraf            Alloy
          ^                  ^                  ^
          |                  |                  |
Linux / Windows / QNAP  SNMP Device      Syslog Files
                                                ^
                                                |
                                            rsyslog
                                                ^
                                                |
                                      Network / Server / NAS
</syntaxhighlight>


공식 자료 확인일: 2026-09-10. 지속 갱신 문서는 고정 게시일이 없으며 실제 RPM 버전을 §4 출력으로 기록한다.
이 구조가 완성되면 다음 세 종류의 정보를 한 Grafana에서 확인할 수 있다.


| 대상 | 공식 자료 |
# 장비와 서버의 현재 상태
|---|---|
# 시간에 따른 성능 변화
| Telegraf 1.x | [설치](https://docs.influxdata.com/telegraf/v1/install/) / [설정](https://docs.influxdata.com/telegraf/v1/configuration/) / [SNMP](https://docs.influxdata.com/telegraf/v1/input-plugins/snmp/) / [Ping](https://docs.influxdata.com/telegraf/v1/input-plugins/ping/) |
# 장애 시점의 이벤트 로그
| InfluxDB OSS 2.x | [RPM 설치](https://docs.influxdata.com/influxdb/v2/install/?t=Linux) / [backup](https://docs.influxdata.com/influxdb/v2/reference/cli/influx/backup/) |
| Flux | [derivative](https://docs.influxdata.com/flux/v0/stdlib/universe/derivative/) |
| Grafana OSS | [RPM 설치](https://grafana.com/docs/grafana/latest/setup-grafana/installation/redhat-rhel-fedora/) / [InfluxDB 연결](https://grafana.com/docs/grafana/latest/datasources/influxdb/configure/) |
| Net-SNMP | [snmp.conf](https://www.net-snmp.org/docs/man/snmp.conf.html) |
| IF-MIB | [RFC 2863, 2000-06](https://www.rfc-editor.org/rfc/rfc2863.html) |
| rsyslog 8.x | [imudp](https://docs.rsyslog.com/doc/configuration/modules/imudp.html) / [omfile](https://docs.rsyslog.com/doc/configuration/modules/omfile.html) |
| Akvorado | [공식 프로젝트](https://github.com/akvorado/akvorado) / [Compose](https://github.com/akvorado/akvorado/tree/main/docker) |
| Docker Engine | [CentOS 설치 참고](https://docs.docker.com/engine/install/centos/) |


추가로 필요한 정보: Zyxel L3/L2 모델·펌웨어·대수·관리 IP/Voice VLAN 구조. 확인 후 장비별 SNMPv3/Syslog/sFlow 명령과 CPU·PoE·큐 OID를 확정할 수 있다.
초보자는 먼저 "데이터가 어디서 생성되어 어디를 거쳐 Grafana까지 도착하는지"를 이해한 뒤 설정값을 수정하는 것이 가장 중요하다.

2026년 9월 23일 (수) 22:28 기준 최신판

Rocky Linux 9 통합 모니터링 서버 구축 가이드 - 초보자용

Telegraf / InfluxDB / Grafana / Prometheus / Node Exporter / Windows Exporter / Loki / Alloy / rsyslog

작성 목적: 처음 모니터링 서버를 설치하는 사용자가 "왜 이 프로그램이 필요한지", "어떤 순서로 설치하는지", "어디까지 정상이어야 다음 단계로 넘어가는지"를 이해하면서 구축할 수 있도록 작성한다.

본 문서는 실제 운영 과정에서 구성하고 점검한 내용을 바탕으로 정리했으며, 실제 IP 주소와 인증 정보는 문서용 예시 값으로 변경하였다.

중요: 아래 명령을 한 번에 모두 실행하지 않는다. 각 절의 정상 확인 항목을 통과한 뒤 다음 단계로 진행한다.

0. 이 문서에서 만들 시스템

0.1 먼저 전체 구조부터 이해하기

모니터링을 처음 구성할 때 가장 혼동하기 쉬운 부분은 "Grafana가 모든 데이터를 직접 수집한다"고 생각하는 것이다.

Grafana는 주로 보여주는 역할을 한다. 실제 데이터 수집과 저장은 다른 프로그램이 담당한다.

이 문서에서는 데이터를 크게 두 종류로 나눈다.

  1. Metric : CPU 사용률, 메모리 사용률, 포트 트래픽, Ping 손실률처럼 숫자로 표현되는 값
  2. Log : Syslog, 서버 이벤트, NAS 접근 기록처럼 문자열로 기록되는 이벤트

전체 데이터 흐름은 다음과 같다.

[Network Switch]
      |
      | SNMPv3
      v
  Telegraf
      |
      v
  InfluxDB
      |
      +-------------------+
                          |
[Linux / QNAP]            |
      |                   |
      | node_exporter     |
      v                   |
  Prometheus              |
      |                   |
      +-------------------+
                          |
[Network / Server / NAS]  |
      |                   |
      | Syslog            |
      v                   |
   rsyslog                |
      |                   |
      v                   |
   Log File               |
      |                   |
      v                   |
    Alloy                 |
      |                   |
      v                   |
     Loki                 |
      |                   |
      +-------------------+
                          |
                          v
                       Grafana

즉 다음과 같이 기억하면 된다.

프로그램 하는 일 쉽게 표현하면
Telegraf SNMP 장비의 값을 주기적으로 읽음 네트워크 장비용 수집기
InfluxDB Telegraf가 수집한 시계열 값을 저장 SNMP 데이터 창고
Prometheus Exporter가 공개한 Metric을 주기적으로 가져와 저장 서버 Metric 수집기 + 데이터베이스
Node Exporter Linux/QNAP의 CPU, 메모리, 디스크, 네트워크 Metric 제공 Linux 상태 측정기
Windows Exporter Windows 성능 카운터 Metric 제공 Windows 상태 측정기
rsyslog 장비/서버가 보내는 Syslog를 수신하여 파일로 저장 로그 수신기
Alloy 로그 파일을 읽고 필요한 항목을 분리하여 Loki로 전달 로그 전달/가공기
Loki 로그를 저장하고 검색 가능하게 함 로그 데이터베이스
Grafana 위 데이터들을 Dashboard와 Alert로 표현 통합 화면

0.2 왜 하나의 프로그램으로 모두 처리하지 않는가

SNMP, 서버 Metric, 로그는 데이터 성격이 서로 다르다.

예를 들어 스위치 포트 트래픽은 누적 Counter를 주기적으로 읽어 증가량을 계산해야 한다. 반면 Syslog는 특정 시점에 발생한 문자열 이벤트이다.

따라서 본 구성에서는 각 도구가 가장 잘하는 역할을 분리한다.

  • Switch SNMP → Telegraf + InfluxDB
  • Linux / Windows / QNAP OS Metric → Prometheus
  • Syslog → rsyslog + Alloy + Loki
  • 최종 화면 → Grafana

이 구조를 이해하면 장애 발생 시 어느 부분을 확인해야 하는지도 쉽게 구분할 수 있다.

예를 들어 Grafana에서 Linux CPU가 보이지 않는다면 다음 순서로 생각한다.

Grafana 문제인가?
    ↓
Prometheus에 데이터가 있는가?
    ↓
Prometheus가 node_exporter를 수집하고 있는가?
    ↓
node_exporter가 정상 실행 중인가?

1. 예제 환경과 주소

실제 운영 IP를 문서에 노출하지 않기 위해 RFC 5737 문서용 주소를 사용한다.

대상 문서용 IP 역할
Monitoring Server 192.0.2.10 Grafana / Prometheus / InfluxDB / Telegraf / Loki / Alloy / rsyslog
Linux Server 192.0.2.20 node_exporter
Windows Server 192.0.2.30 windows_exporter
QNAP NAS 198.51.100.10 node_exporter / SNMP / Syslog
Switch-01 203.0.113.11 SNMP / Syslog
Switch-02 203.0.113.12 SNMP / Syslog
Switch-03 203.0.113.13 SNMP / Syslog

실제 설치 시 위 주소를 자신의 환경에 맞게 변경한다.

2. 설치 전에 알아둘 용어

2.1 Metric

Metric은 시간에 따라 변화하는 숫자 데이터이다.

예:

  • CPU 32%
  • Memory 71%
  • Switch Port RX 120 Mbps
  • Ping Packet Loss 0%
  • HDD Temperature 43°C

이런 값은 시간 흐름에 따라 그래프로 보는 것이 중요하다.

2.2 Counter와 Gauge

SNMP에서 자주 만나는 개념이다.

Gauge는 현재 값을 의미한다.

예:

CPU Usage = 35
Temperature = 44
operStatus = 1

Counter는 계속 증가하는 누적값이다.

예:

ifHCInOctets = 1234567890123

이 값을 그대로 그래프로 그리면 계속 증가하기만 한다.

그래서 실제 트래픽은 현재 값과 이전 값의 차이를 시간으로 나누어 계산한다.

현재 Counter - 이전 Counter
---------------------------
        경과 시간

Octet은 8 bit이므로 bps를 구하려면 다시 8을 곱한다.

Grafana/Flux에서는 이를 derivative()로 처리한다.

2.3 Label과 Tag

장비가 여러 대일 때 어떤 데이터가 어느 장비인지 구분하기 위한 값이다.

예:

source=203.0.113.11
if_name=port24
index=24

Prometheus에서는 주로 label, InfluxDB에서는 tag라고 부른다.

2.4 Exporter

Prometheus가 직접 Linux 내부 명령을 실행하는 것은 아니다.

Exporter가 시스템 정보를 HTTP Metric 형식으로 공개하고 Prometheus가 이를 가져간다.

Node Exporter 예:

http://192.0.2.20:9100/metrics

Windows Exporter:

http://192.0.2.30:9182/metrics

3. Rocky Linux 9 기본 준비

이 장에서는 모니터링 프로그램을 설치하기 전에 OS가 정상인지 확인한다.

3.1 시스템 상태 확인

cat /etc/rocky-release
uname -m

ip -br address
ip route

df -hT
free -h

getenforce
ss -lntup

각 명령의 의미:

명령 확인 내용
cat /etc/rocky-release Rocky Linux 버전
uname -m CPU Architecture, 일반적인 x86 서버는 x86_64
ip -br address 서버 IP 주소
ip route Gateway 및 Routing
df -hT 디스크 용량
free -h 메모리
getenforce SELinux 상태
ss -lntup 현재 사용 중인 TCP/UDP Port

초보자 주의: SELinux가 Enforcing이라고 해서 바로 Disabled로 변경하지 않는다. 이후 permission 문제가 발생하면 원인을 확인하고 필요한 정책 또는 Context를 수정한다.

3.2 기존 설정 백업

기존 운영 서버에 구축하는 경우 설정을 변경하기 전에 백업한다.

umask 077

MON_BACKUP="/root/monitor-backup-$(date +%Y%m%d-%H%M%S)"

install -d -m 700 "$MON_BACKUP"

cp -a /etc/rsyslog.conf "$MON_BACKUP/" 2>/dev/null || true
cp -a /etc/rsyslog.d "$MON_BACKUP/" 2>/dev/null || true
cp -a /etc/chrony.conf "$MON_BACKUP/" 2>/dev/null || true
cp -a /etc/firewalld "$MON_BACKUP/" 2>/dev/null || true

rpm -qa | sort > "$MON_BACKUP/packages-before.txt"

백업 경로 확인:

echo "$MON_BACKUP"
ls -al "$MON_BACKUP"

3.3 기본 관리 도구 설치

dnf install -y \
    curl \
    ca-certificates \
    gnupg2 \
    vim-enhanced \
    net-snmp-utils \
    rsyslog \
    logrotate \
    chrony \
    tcpdump \
    iputils \
    sysstat \
    policycoreutils-python-utils \
    dnf-plugins-core \
    jq

주요 패키지 용도:

  • net-snmp-utils : snmpget, snmpwalk 테스트
  • tcpdump : 실제 Packet 수신 확인
  • jq : JSON 출력 가독성 개선
  • sysstat : iostat 등 서버 I/O 점검
  • chrony : 시간 동기화
  • policycoreutils-python-utils : SELinux 진단/정책 작업

3.4 시간 동기화

모니터링에서는 서버와 장비 시간이 맞지 않으면 장애 시각 비교가 어렵다.

Timezone 설정:

timedatectl set-timezone Asia/Seoul

chronyd 시작:

systemctl enable --now chronyd
systemctl restart chronyd

확인:

chronyc tracking
chronyc sources -v
date -Ins

정상 확인

  • chronyd가 active
  • chronyc sources에서 선택된 NTP Source 존재
  • 현재 시간이 실제 시간과 크게 다르지 않음

4. InfluxDB와 Telegraf를 설치하는 이유

스위치의 SNMP 값을 수집하려면 두 프로그램이 필요하다.

Telegraf는 장비에 SNMP Query를 보내는 역할을 한다.

InfluxDB는 Telegraf가 가져온 값을 시간 순서대로 저장한다.

Switch --SNMP--> Telegraf --> InfluxDB --> Grafana

5. InfluxDB / Telegraf / Grafana 설치

5.1 InfluxData Repository 추가

공식 Repository Key를 내려받는다.

curl -fL https://repos.influxdata.com/influxdata-archive.key \
    -o /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata

gpg --show-keys --with-fingerprint \
    /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata

rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata

Repository 파일 생성:

vi /etc/yum.repos.d/influxdata.repo
[influxdata]
name=InfluxData Repository - Stable
baseurl=https://repos.influxdata.com/stable/$basearch/main
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-influxdata
sslverify=1

5.2 Grafana Repository 추가

vi /etc/yum.repos.d/grafana.repo
[grafana]
name=Grafana OSS repository
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1

5.3 Repository 확인 후 설치

dnf makecache

dnf list --showduplicates \
    telegraf \
    influxdb2 \
    influxdb2-cli \
    grafana

설치:

dnf install -y \
    telegraf \
    influxdb2 \
    influxdb2-cli \
    grafana

버전 확인:

rpm -q telegraf influxdb2 influxdb2-cli grafana

telegraf --version
influxd version
influx version

왜 버전을 기록하는가?

나중에 설정 문법 또는 Plugin 동작이 달라졌을 때 원인을 판단하기 쉽기 때문이다.

실제 구성 과정에서는 Telegraf 1.40.0 환경에서 동작을 확인하였다.

6. InfluxDB 초기 설정

6.1 InfluxDB를 localhost에만 Bind

현재 구성에서는 InfluxDB를 외부 장비가 직접 접근할 필요가 없다.

Telegraf와 Grafana가 같은 서버에 있으므로 127.0.0.1에만 Bind하면 공격 표면을 줄일 수 있다.

install -d -m 755 \
  /etc/systemd/system/influxdb.service.d

vi /etc/systemd/system/influxdb.service.d/10-listen.conf
[Service]
Environment="INFLUXD_HTTP_BIND_ADDRESS=127.0.0.1:8086"

적용:

systemctl daemon-reload

systemctl enable --now influxdb
systemctl restart influxdb

systemctl status influxdb --no-pager

Listen 확인:

ss -lntp | grep ':8086'

Health Check:

curl -fsS http://127.0.0.1:8086/health

정상 확인

127.0.0.1:8086에서 LISTEN하고 health 응답이 정상이어야 한다.

6.2 InfluxDB 최초 Setup

influx setup

초기 입력 예:

항목 예시 설명
Username monitor-admin InfluxDB 관리 계정
Organization network 관련 데이터의 논리적 그룹
Bucket snmp_raw SNMP 데이터 저장 위치
Retention 720h 30일 보존

확인:

influx bucket list --org network

Bucket이란?

일반 Database의 Database 또는 Table과 완전히 같지는 않지만, 처음에는 "Metric 저장 공간" 정도로 이해하면 된다.

6.3 Token을 서비스별로 나누는 이유

Telegraf는 데이터를 쓰기만 하면 되고 Grafana는 읽기만 하면 된다.

따라서 하나의 관리자 Token을 모든 프로그램에 넣지 않는다.

권장:

Token 권한
telegraf-write snmp_raw Write
grafana-read snmp_raw Read
Operator Token 관리자 전용

실제 Token 값은 Wiki에 기록하지 않는다.

7. Telegraf 기본 설정

7.1 Telegraf의 역할

Telegraf는 일정 시간마다 스위치에 SNMP Query를 보내고 결과를 InfluxDB에 저장한다.

Telegraf
   |
   | UDP/161 SNMP GET
   v
Switch

Telegraf
   |
   | HTTP 8086
   v
InfluxDB

7.2 인증 정보를 설정 파일과 분리

SNMP Password와 InfluxDB Token을 여러 설정 파일에 직접 적으면 관리가 어렵다.

환경변수 파일로 분리한다.

vi /etc/telegraf/monitor.env
INFLUX_WRITE_TOKEN='<WRITE_TOKEN>'

ZYXEL_SNMP_USER='<SNMP_USER>'
ZYXEL_SNMP_AUTH='<AUTH_PASSWORD>'
ZYXEL_SNMP_PRIV='<PRIV_PASSWORD>'

QNAP_SNMP_USER='<QNAP_SNMP_USER>'
QNAP_SNMP_AUTH='<QNAP_AUTH_PASSWORD>'
QNAP_SNMP_PRIV='<QNAP_PRIV_PASSWORD>'

권한 제한:

chown root:root /etc/telegraf/monitor.env
chmod 600 /etc/telegraf/monitor.env

systemd가 환경 파일을 읽도록 설정:

install -d -m 755 \
  /etc/systemd/system/telegraf.service.d

vi /etc/systemd/system/telegraf.service.d/10-monitor-env.conf
[Service]
EnvironmentFile=/etc/telegraf/monitor.env

7.3 Telegraf Main 설정

원본 백업:

cp -a /etc/telegraf/telegraf.conf \
  /etc/telegraf/telegraf.conf.orig

편집:

vi /etc/telegraf/telegraf.conf

기본 예:

[agent]
  interval = "30s"
  round_interval = true
  flush_interval = "5s"
  precision = "1ms"
  metric_batch_size = 1000
  metric_buffer_limit = 20000
  omit_hostname = false
  snmp_translator = "gosmi"

[[outputs.influxdb_v2]]
  urls = ["http://127.0.0.1:8086"]
  token = "${INFLUX_WRITE_TOKEN}"
  organization = "network"
  bucket = "snmp_raw"

[[inputs.internal]]

[[inputs.cpu]]
  percpu = false
  totalcpu = true

[[inputs.mem]]

[[inputs.disk]]
  mount_points = ["/"]

[[inputs.net]]

각 값의 의미:

  • interval = 30s : 기본 수집 주기
  • flush_interval = 5s : 모아둔 Metric을 DB에 보내는 주기
  • outputs.influxdb_v2 : 수집 결과를 어느 InfluxDB에 저장할지 지정
  • inputs.internal : Telegraf 자신의 상태
  • inputs.cpu/mem/disk/net : 모니터링 서버 자체 상태

8. SNMP가 정상인지 먼저 수동으로 확인

Telegraf 설정 전에 SNMP 자체가 되는지 확인하는 것이 중요하다.

Telegraf가 안 된다고 바로 Telegraf 설정만 수정하면 실제 원인이 SNMP ACL인지 Password인지 구분하기 어렵다.

8.1 SNMPv3 계정 파일

install -d -m 700 /root/.snmp

vi /root/.snmp/snmp.conf

예:

defVersion 3
defSecurityName <SNMP_USER>
defSecurityLevel authPriv
defAuthType SHA
defAuthPassphrase <SNMP_AUTH_PASSWORD>
defPrivType AES
defPrivPassphrase <SNMP_PRIV_PASSWORD>

권한:

chmod 600 /root/.snmp/snmp.conf

8.2 Uptime 조회

snmpget -v3 \
  -t 2 \
  -r 0 \
  -On \
  203.0.113.11 \
  .1.3.6.1.2.1.1.3.0

응답이 나오면 최소한 다음이 정상이다.

  • IP Routing
  • UDP/161
  • SNMP User
  • Authentication
  • Privacy Password
  • SNMP View

8.3 Interface 이름 확인

snmpwalk -v3 \
  -t 2 \
  -r 0 \
  -On \
  203.0.113.11 \
  .1.3.6.1.2.1.31.1.1.1.1

여기서 마지막 숫자가 ifIndex이다.

예를 들어 결과가 다음과 같다고 가정한다.

.1.3.6.1.2.1.31.1.1.1.1.24 = STRING: port24

이 경우 ifIndex는 24이다.

중요: 물리 Port 24번이 항상 ifIndex 24라는 의미는 아니다. 실제 SNMP 결과를 기준으로 한다.

9. Telegraf SNMP 설정

9.1 모델별 파일로 나누는 이유

제조사가 같아도 모델마다 CPU/Memory OID와 SNMP 암호화 방식이 다를 수 있다.

따라서 하나의 거대한 파일보다는 모델별 파일로 나눈다.

/etc/telegraf/telegraf.d/
├── 10-zyxel-gs1900.conf
├── 11-zyxel-gs1920.conf
├── 20-zyxel-es3128.conf
├── 30-icmp_check.conf
└── 40-qnap_nas.conf

9.2 GS1920 계열

실제 확인된 특성:

  • SNMPv3 SHA + DES
  • CPU/Memory Private OID 사용

CPU:

.1.3.6.1.4.1.890.1.15.3.49.1.7.0

Memory:

Total   .1.3.6.1.4.1.890.1.15.3.50.1.1.1.3.1
Used    .1.3.6.1.4.1.890.1.15.3.50.1.1.1.4.1
Percent .1.3.6.1.4.1.890.1.15.3.50.1.1.1.5.1

9.3 GS1900 계열

CPU:

.1.3.6.1.4.1.890.1.15.3.2.4.0

Memory:

.1.3.6.1.4.1.890.1.15.3.2.5.0

실제 구축 과정에서는 SNMP 응답이 느려 다음 값이 안정적이었다.

timeout = "5s"
retries = 1

왜 Timeout을 늘렸는가?

수동 snmpget은 되는데 Telegraf에서만 간헐적으로 누락된다면 Telegraf timeout이 장비 응답시간보다 짧을 수 있다.

Timeout을 무조건 크게 설정하기보다는 실제 응답을 보고 조정한다.

9.4 ES-3128GP

실제 확인된 SNMPv3 방식:

  • SHA
  • AES

sysObjectID:

.1.3.6.1.4.1.7800.1.190

Port Metric은 수집 가능하지만 CPU/Memory Private OID는 정상 확인되지 않아 Dashboard에서 억지로 0으로 표시하지 않는다.

수집되지 않는 값과 0은 의미가 다르다.

CPU를 조회할 수 없는 장비에 CPU 0%라고 표시하면 운영자가 정상 상태라고 오해할 수 있다.

9.5 기본 Port Metric

가능하면 제조사 Private MIB보다 표준 IF-MIB를 먼저 사용한다.

주요 항목:

  • ifName
  • ifAlias
  • ifSpeed
  • ifHCInOctets
  • ifHCOutOctets
  • ifOperStatus
  • ifInErrors
  • ifOutErrors
  • ifInDiscards
  • ifOutDiscards
  • Broadcast
  • Multicast

10. ICMP Ping 수집

SNMP가 정상이어도 장비 자체가 네트워크에서 사라질 수 있다.

따라서 Ping 결과도 별도로 저장한다.

vi /etc/telegraf/telegraf.d/30-icmp_check.conf

예:

[[inputs.ping]]
  urls = [
    "203.0.113.11",
    "203.0.113.12",
    "203.0.113.13"
  ]

  method = "native"
  count = 3
  deadline = 2.0
  interval = 10.0

10.1 CAP_NET_RAW가 필요한 이유

native Ping은 Raw Socket을 사용한다.

root로 telegraf --test를 실행하면 정상인데 systemd 서비스에서는 Ping이 실패할 수 있다.

이 경우 서비스에 CAP_NET_RAW를 부여한다.

systemctl edit telegraf
[Service]
CapabilityBoundingSet=CAP_NET_RAW
AmbientCapabilities=CAP_NET_RAW

반영:

systemctl daemon-reload
systemctl restart telegraf

11. Telegraf 설정 검사

서비스를 재시작하기 전에 설정 오류를 확인한다.

telegraf \
  --config /etc/telegraf/telegraf.conf \
  --config-directory /etc/telegraf/telegraf.d \
  --test

주의: --test는 Metric을 화면에 출력하지만 일반적으로 Output 저장까지 검증하는 용도가 아니다.

서비스 계정 조건까지 확인하려면 다음 방식이 더 정확하다.

systemd-run \
  --unit=telegraf-config-check \
  --wait \
  --pipe \
  --collect \
  -p User=telegraf \
  -p Group=telegraf \
  -p EnvironmentFile=/etc/telegraf/monitor.env \
  -p CapabilityBoundingSet=CAP_NET_RAW \
  -p AmbientCapabilities=CAP_NET_RAW \
  /usr/bin/telegraf \
  --config /etc/telegraf/telegraf.conf \
  --config-directory /etc/telegraf/telegraf.d \
  --test

정상 후 서비스 시작:

systemctl enable --now telegraf
systemctl restart telegraf

systemctl status telegraf --no-pager

journalctl -u telegraf \
  -n 100 \
  --no-pager

로그에서 주의할 문자열:

timeout
unauthorized
permission denied
buffer
drop
address already in use

12. Grafana 설치 후 처음 해야 할 작업

Grafana는 데이터를 직접 수집하지 않는다.

먼저 InfluxDB와 Prometheus 같은 데이터소스를 등록해야 한다.

12.1 Grafana 서비스

systemctl enable --now grafana-server

systemctl status grafana-server --no-pager

curl -fsS \
  http://127.0.0.1:3000/api/health

웹 접속:

http://192.0.2.10:3000

내부망에서만 사용할 경우에도 방화벽 접근 범위를 관리망으로 제한하는 것이 좋다.

12.2 InfluxDB Datasource 등록

Grafana 메뉴:

Connections
→ Data sources
→ Add data source
→ InfluxDB

설정:

항목 값
Query Language Flux
URL http://127.0.0.1:8086
Organization network
Token grafana-read Token
Default Bucket snmp_raw

Save & Test 성공 여부를 확인한다.

13. 첫 번째 SNMP Dashboard 만들기

처음부터 모든 장비를 한 화면에 넣지 않는다.

한 대의 스위치 + 한 포트로 정상 그래프를 만든 뒤 확대하는 것이 가장 쉽다.

13.1 원시 Counter 확인

Explore에서 다음과 같이 최근 데이터를 확인한다.

from(bucket: "snmp_raw")
  |> range(start: -10m)
  |> limit(n: 20)

여기서 실제 Measurement, source, index, if_name 값을 확인한다.

13.2 Port RX/TX 계산

누적 Octet Counter를 bps로 변환한다.

from(bucket: "snmp_raw")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) =>
      r._measurement == "switch_port"
  )
  |> filter(fn: (r) =>
      r.source == "203.0.113.11"
  )
  |> filter(fn: (r) =>
      r._field == "in_octets" or
      r._field == "out_octets"
  )
  |> derivative(
      unit: 1s,
      nonNegative: true
  )
  |> map(fn: (r) => ({
      r with
      _value: r._value * 8.0
  }))

Grafana Unit:

bits/sec

13.3 Error / Discard

Error나 Discard 역시 Counter이므로 증가율을 표시한다.

from(bucket: "snmp_raw")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) =>
      r._measurement == "switch_port"
  )
  |> filter(fn: (r) =>
      r._field == "in_errors" or
      r._field == "out_errors" or
      r._field == "in_discards" or
      r._field == "out_discards"
  )
  |> derivative(
      unit: 1s,
      nonNegative: true
  )

해석

  • Error 증가 → 물리계층, Frame, Interface 문제 가능
  • Discard 증가 → Queue, Buffer, QoS, 혼잡 등에 의해 폐기될 가능성
  • 값이 0인 상태가 일반적이지만 장비 특성과 Traffic에 따라 해석해야 함

13.4 operStatus

operStatus는 Counter가 아니다.

따라서 derivative를 사용하지 않는다.

일반적인 값:

1 = up
2 = down

Grafana Value Mapping으로 사람이 읽기 쉬운 문자열로 변환한다.

14. Prometheus를 추가하는 이유

SNMP는 네트워크 장비 상태에 좋지만 Linux/Windows 서버 자원 모니터링에는 Exporter + Prometheus가 더 편하다.

Prometheus 구조:

Node Exporter ----\
                   \
Windows Exporter ---> Prometheus ---> Grafana
                   /
QNAP Exporter -----/

Prometheus는 일정 주기마다 Exporter HTTP Endpoint에 접속하여 Metric을 가져간다.

이를 Pull 방식이라고 한다.

15. Prometheus 설치

15.1 사용자와 디렉터리

useradd \
  --system \
  --no-create-home \
  --shell /sbin/nologin \
  prometheus

install -d \
  -o prometheus \
  -g prometheus \
  /etc/prometheus \
  /var/lib/prometheus

Prometheus 공식 바이너리를 준비한 뒤:

install -m 0755 \
  prometheus \
  /usr/local/bin/prometheus

install -m 0755 \
  promtool \
  /usr/local/bin/promtool

버전 확인:

prometheus --version
promtool --version

15.2 prometheus.yml

vi /etc/prometheus/prometheus.yml
global:
  scrape_interval: 30s

scrape_configs:

  - job_name: prometheus
    static_configs:
      - targets:
          - "127.0.0.1:9090"
        labels:
          server_name: monitoring-server

  - job_name: node
    static_configs:
      - targets:
          - "127.0.0.1:9100"
        labels:
          server_name: monitoring-server

      - targets:
          - "192.0.2.20:9100"
        labels:
          server_name: linux-server

  - job_name: windows
    static_configs:
      - targets:
          - "192.0.2.30:9182"
        labels:
          server_name: windows-server

  - job_name: qnap
    static_configs:
      - targets:
          - "198.51.100.10:9100"
        labels:
          server_name: nas-01

scrape_interval = 30s는 Prometheus가 30초마다 각 Target을 조회한다는 의미이다.

15.3 설정 검사

YAML은 들여쓰기에 매우 민감하다.

실제 구축 중에도 job을 scrape_configs 바깥에 잘못 넣으면 Prometheus가 시작하지 못했다.

반드시 검사한다.

promtool check config \
  /etc/prometheus/prometheus.yml

15.4 systemd 등록

vi /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus

ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --storage.tsdb.retention.time=30d \
  --storage.tsdb.retention.size=20GB \
  --web.listen-address=127.0.0.1:9090

Restart=always

[Install]
WantedBy=multi-user.target

권한:

chown -R prometheus:prometheus \
  /etc/prometheus \
  /var/lib/prometheus

시작:

systemctl daemon-reload
systemctl enable --now prometheus

systemctl status prometheus --no-pager

Health:

curl -s \
  http://127.0.0.1:9090/-/healthy

16. Linux Node Exporter 설치

16.1 Node Exporter가 하는 일

Node Exporter는 Linux Kernel과 /proc, /sys 등의 정보를 읽어 Prometheus 형식으로 공개한다.

대표 Metric:

  • CPU
  • Memory
  • Load
  • Filesystem
  • Disk I/O
  • Network Traffic
  • Network Error/Drop
  • Uptime
  • File Descriptor
  • 일부 Hardware Metric

16.2 서비스 계정

useradd \
  --system \
  --no-create-home \
  --shell /sbin/nologin \
  node_exporter

바이너리 설치:

install -m 0755 \
  node_exporter \
  /usr/local/bin/node_exporter

16.3 systemd

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

[Service]
User=node_exporter
Group=node_exporter

ExecStart=/usr/local/bin/node_exporter

Restart=always

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

systemctl status node_exporter --no-pager

Metric 확인:

curl -s \
  http://127.0.0.1:9100/metrics \
  | head

다른 서버에 설치한 경우 Prometheus 서버에서 확인:

curl -s \
  http://192.0.2.20:9100/metrics \
  | head

정상 확인

Metric 문자열이 여러 줄 출력되면 Exporter 자체는 정상이다.

Prometheus에서 다음 Query도 확인한다.

up

값:

1 = 정상 Scrape
0 = Scrape 실패

17. Windows Exporter

Windows에서는 windows_exporter를 설치한다.

기본 Port:

9182/tcp

Prometheus에서 다음 주소를 조회할 수 있어야 한다.

http://192.0.2.30:9182/metrics

Windows PowerShell 테스트:

Invoke-WebRequest \
  -UseBasicParsing \
  http://127.0.0.1:9182/metrics

성능 카운터가 비정상인 경우 실제 구축 과정에서 다음 복구 절차를 사용하였다.

lodctr /R
winmgmt /resyncperf

그 후:

Restart-Service windows_exporter

18. Grafana에 Prometheus 등록

Grafana 메뉴:

Connections
→ Data sources
→ Prometheus

URL:

http://127.0.0.1:9090

Save & Test 후 Explore에서:

up

Target별 1이 표시되면 정상이다.

19. Linux Server Dashboard 이해하기

처음에는 다음 항목만 만든다.

  • Uptime
  • CPU
  • Memory
  • Load Average
  • Filesystem
  • Network RX/TX

이후 익숙해지면 다음을 추가한다.

  • Disk I/O
  • Network Error/Drop
  • inode
  • Swap
  • Process
  • Application Log

19.1 CPU Usage

Node Exporter는 CPU 시간을 Mode별 누적으로 제공한다.

idle을 제외한 비율로 CPU 사용률을 계산할 수 있다.

100 - (
  avg by(instance) (
    rate(
      node_cpu_seconds_total{
        mode="idle"
      }[5m]
    )
  ) * 100
)

19.2 Memory Usage

100 * (
  1 -
  node_memory_MemAvailable_bytes
  /
  node_memory_MemTotal_bytes
)

19.3 Filesystem Usage

Filesystem은 tmpfs, overlay, snapshot 등 운영자가 원하지 않는 Mount가 함께 보일 수 있다.

따라서 실제 데이터 Mount Point를 확인한 뒤 필터링한다.

20. QNAP TS-264 모니터링

QNAP은 한 가지 수집 방식만 사용하면 정보가 부족하다.

따라서 세 경로를 함께 사용한다.

QNAP
 |
 +-- node_exporter --> Prometheus
 |
 +-- SNMP ----------> Telegraf --> InfluxDB
 |
 +-- Syslog --------> rsyslog --> Alloy --> Loki

역할:

항목 데이터 소스
CPU / Memory / Network Node Exporter
Filesystem Node Exporter
Disk I/O Node Exporter
HDD Model / 온도 / 상태 SNMP
RAID / Volume SNMP
Event Log Syslog
Access Log Syslog

20.1 QNAP SNMPv3

실제 TS-264 환경에서는 다음 조합을 사용하였다.

  • Authentication: SHA
  • Privacy: DES

QNAP은 해당 구성에서 AES가 동작하지 않았다.

따라서 다른 장비에서 AES가 된다는 이유로 QNAP도 AES라고 가정하지 않는다.

20.2 주요 Measurement

QNAP_TS264
qnap_disk
qnap_raid
qnap_storage_pool
qnap_volume

20.3 Volume 값이 11 GiB로 잘못 보이는 문제

실제 QTS에서 DataVol1은 약 11.33 TiB인데 SNMP 원시값을 Grafana에서 byte로 바로 처리하면 약 11.3 GiB로 표시되는 문제가 있었다.

실제 장비의 qnap_volume capacity/free 값이 KiB 성격으로 반환되어 1024 배 보정이 필요하였다.

|> map(fn: (r) => ({
    r with

    capacity_bytes:
      uint(v: r.capacity_bytes)
      * uint(v: 1024),

    free_bytes:
      uint(v: r.free_bytes)
      * uint(v: 1024),

    used_bytes:
      (
        uint(v: r.capacity_bytes)
        - uint(v: r.free_bytes)
      )
      * uint(v: 1024),

    used_percent:
      if float(v: r.capacity_bytes) > 0.0 then
        (
          float(v: r.capacity_bytes)
          - float(v: r.free_bytes)
        )
        /
        float(v: r.capacity_bytes)
        * 100.0
      else
        0.0
}))

왜 Percent에는 1024가 필요 없는가?

분자와 분모가 같은 단위이므로 비율 계산에서는 단위가 상쇄된다.

20.4 실제 Data Volume 찾기

Node Exporter가 QNAP 내부의 Snapshot Mount까지 모두 보여주므로 처음에는 여러 개의 11.33 TiB Filesystem이 보일 수 있다.

실제 사용자 Data Volume은 다음이었다.

mountpoint="/share/CACHEDEV1_DATA"
device="/dev/mapper/cachedev1"
fstype="ext4"

QNAP Snapshot:

/mnt/snapshot/1/10001
/mnt/snapshot/1/10002
...

Dashboard에서는 Snapshot을 제외하고 실제 Volume만 표시한다.

사용률:

100 * (
  1 -
  node_filesystem_avail_bytes{
    instance="198.51.100.10:9100",
    mountpoint="/share/CACHEDEV1_DATA"
  }
  /
  node_filesystem_size_bytes{
    instance="198.51.100.10:9100",
    mountpoint="/share/CACHEDEV1_DATA"
  }
)

실제 QTS 화면과 약 2.86%로 일치하는 것을 확인하였다.

20.5 QNAP Network는 bond0만 표시

QNAP에는 내부 Interface와 Virtual Interface가 여러 개 보일 수 있다.

실제 외부 Traffic을 담당하는 bond0만 필터링한다.

RX:

rate(
  node_network_receive_bytes_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
) * 8

TX:

rate(
  node_network_transmit_bytes_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
) * 8

20.6 Network Error / Drop

RX Error:

rate(
  node_network_receive_errs_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
)

TX Error:

rate(
  node_network_transmit_errs_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
)

RX Drop:

rate(
  node_network_receive_drop_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
)

TX Drop:

rate(
  node_network_transmit_drop_total{
    instance="198.51.100.10:9100",
    device="bond0"
  }[$__rate_interval]
)

20.7 Disk I/O

QNAP에는 md, dm, loop 등 내부 Device가 많으므로 물리 Disk sd*만 표시한다.

Read Bytes/sec:

rate(
  node_disk_read_bytes_total{
    instance="198.51.100.10:9100",
    device=~"sd[a-z]+"
  }[$__rate_interval]
)

Write Bytes/sec:

rate(
  node_disk_written_bytes_total{
    instance="198.51.100.10:9100",
    device=~"sd[a-z]+"
  }[$__rate_interval]
)

Read IOPS:

rate(
  node_disk_reads_completed_total{
    instance="198.51.100.10:9100",
    device=~"sd[a-z]+"
  }[$__rate_interval]
)

Write IOPS:

rate(
  node_disk_writes_completed_total{
    instance="198.51.100.10:9100",
    device=~"sd[a-z]+"
  }[$__rate_interval]
)

21. Syslog를 왜 별도로 구성하는가

Metric만으로는 다음과 같은 내용을 알기 어렵다.

  • Port가 왜 Down 되었는가
  • 사용자가 NAS에 어떤 파일을 접근했는가
  • 서비스가 언제 재시작되었는가
  • 인증 실패가 발생했는가

이런 이벤트는 Log가 필요하다.

본 구성의 로그 흐름:

Device
  |
  | Syslog
  v
rsyslog
  |
  v
Log File
  |
  v
Alloy
  |
  v
Loki
  |
  v
Grafana

22. rsyslog 구성

22.1 Network Syslog

네트워크 장비 로그:

/var/log/network-syslog/events.log

기본 수신:

TCP/UDP 514

22.2 Server Syslog

서버용 로그를 네트워크 장비와 분리하면 Grafana에서 Query하기 쉽다.

예:

/var/log/server-syslog/events.log

수신 Port:

TCP 5514

22.3 QNAP Event와 Access Log 분리

QNAP QuLog의 두 성격이 다르므로 포트부터 분리한다.

종류 포트 파일 의미
Event TCP 5515 /var/log/qnap/event.log 시스템/서비스 이벤트
Access TCP 5516 /var/log/qnap/access.log 사용자/파일 접근

Template:

template(name="QnapSyslogLine" type="string"
  string="%timegenerated:::date-rfc3339% src=%fromhost-ip% severity=%syslogseverity-text% host=%hostname% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%\n")

이렇게 저장하면 한 줄이 대략 다음 구조가 된다.

2026-09-13T23:40:03+09:00 \
src=198.51.100.10 \
severity=info \
host=nas-01 \
qulogd: ...

Event ruleset:

ruleset(name="QnapEventLog") {

    action(
        type="omfile"
        file="/var/log/qnap/event.log"
        template="QnapSyslogLine"

        fileOwner="root"
        fileGroup="alloy"
        fileCreateMode="0640"

        dirOwner="root"
        dirGroup="alloy"
        dirCreateMode="0750"

        createDirs="on"
    )

    stop
}

input(
    type="imtcp"
    port="5515"
    ruleset="QnapEventLog"
)

Access ruleset:

ruleset(name="QnapAccessLog") {

    action(
        type="omfile"
        file="/var/log/qnap/access.log"
        template="QnapSyslogLine"

        fileOwner="root"
        fileGroup="alloy"
        fileCreateMode="0640"

        dirOwner="root"
        dirGroup="alloy"
        dirCreateMode="0750"

        createDirs="on"
    )

    stop
}

input(
    type="imtcp"
    port="5516"
    ruleset="QnapAccessLog"
)

22.4 rsyslog 설정 검사

재시작 전에 반드시 검사한다.

rsyslogd -N1

오류가 없으면:

systemctl restart rsyslog

ss -lntp \
  | grep -E ':5514|:5515|:5516'

실제 로그 확인:

tail -f \
  /var/log/qnap/access.log

문제 분리 방법

tcpdump에는 Packet이 안 보임
→ 장비 송신/방화벽/라우팅 확인

tcpdump에는 보이지만 파일이 안 생김
→ rsyslog 설정 확인

파일은 생기지만 Grafana에 안 보임
→ Alloy/Loki 확인

23. Loki와 Alloy의 역할

rsyslog가 파일을 만드는 것만으로 Grafana가 그 파일을 검색할 수 있는 것은 아니다.

Loki가 로그 저장소 역할을 하고 Alloy가 파일을 읽어 Loki에 넣는다.

/var/log/...
    |
    v
  Alloy
    |
    v
  Loki
    |
    v
 Grafana

현재 구성에서는 Loki가 localhost 3100에서 동작한다.

127.0.0.1:3100

Alloy 로컬 관리 Endpoint:

127.0.0.1:12345

24. Alloy 설정 이해하기

24.1 Network Syslog

loki.source.file "network_syslog" {

  targets = [
    {
      __path__ = "/var/log/network-syslog/events.log",
      job      = "network-syslog",
    },
  ]

  forward_to = [
    loki.process.network_syslog.receiver
  ]
}

source.file은 어떤 파일을 읽을지 지정한다.

job은 Loki에서 로그 종류를 구분하기 위한 Label이다.

그 다음 regex로 한 줄을 분해한다.

loki.process "network_syslog" {

  stage.regex {
    expression = `^(?P<received_at>\S+) src=(?P<device_ip>\S+) severity=(?P<severity>\S+) host=(?P<device_host>\S+) (?P<message>.*)$`
  }

  stage.timestamp {
    source            = "received_at"
    format            = "RFC3339Nano"
    action_on_failure = "skip"
  }

  stage.labels {
    values = {
      device_ip = ""
      severity  = ""
    }
  }

  forward_to = [
    loki.write.local.receiver
  ]
}

여기서 다음 Label이 생긴다.

job
device_ip
severity

device_host를 Label로 추가하려면

stage.labels {
  values = {
    device_ip   = ""
    device_host = ""
    severity    = ""
  }
}

이는 이후 Alert Mail에서 Hostname까지 표시하고 싶을 때 유용하다.

24.2 QNAP Access

loki.source.file "qnap_access" {

  targets = [
    {
      __path__ = "/var/log/qnap/access.log",
      job      = "qnap-access",
    },
  ]

  forward_to = [
    loki.process.qnap_access.receiver
  ]
}

loki.process "qnap_access" {

  stage.regex {
    expression = `^(?P<received_at>\S+) src=(?P<nas_ip>\S+) severity=(?P<severity>\S+) host=(?P<nas_host>\S+) (?P<message>.*)$`
  }

  stage.timestamp {
    source            = "received_at"
    format            = "RFC3339Nano"
    action_on_failure = "skip"
  }

  stage.labels {
    values = {
      nas_ip   = ""
      nas_host = ""
      severity = ""
      log_type = "access"
    }
  }

  forward_to = [
    loki.write.local.receiver
  ]
}

24.3 Loki Write

loki.write "local" {

  endpoint {
    url = "http://127.0.0.1:3100/loki/api/v1/push"
  }
}

즉 Alloy에서 처리한 로그를 로컬 Loki로 전송한다.

24.4 설정 검사

alloy validate \
  /etc/alloy/config.alloy

정상이라면 아무 오류 없이 종료된다.

반영:

systemctl restart alloy

systemctl status alloy \
  --no-pager

Loki에 실제 Job이 만들어졌는지 확인:

curl -s \
  'http://127.0.0.1:3100/loki/api/v1/label/job/values' \
  | jq

중요한 점은 Alloy 설정에 job을 적었다고 바로 Loki에 Label이 생기는 것이 아니라 실제 로그가 최소 한 번 Loki에 저장되어야 조회 결과에 나타난다는 것이다.

25. Alloy Permission 문제 해결 사례

실제 구축 과정에서 QNAP 로그 파일은 존재하지만 Alloy가 다음 오류를 출력하였다.

failed to tail file
stat failed
permission denied

먼저 경로 전체 권한 확인:

namei -l \
  /var/log/qnap/access.log

정상 예:

drwxr-x--- root alloy qnap
-rw-r----- root alloy access.log

Alloy 계정으로 직접 읽기:

sudo -u alloy \
  head /var/log/qnap/access.log

그래도 실패하면 SELinux 확인:

getenforce

ausearch \
  -m AVC \
  -ts recent \
  | grep -Ei 'alloy|qnap'

ls -Zd /var/log/qnap
ls -Z /var/log/qnap/access.log

문제 해결 원칙

권한 문제가 있다고 SELinux를 바로 끄지 않는다.

다음 순서로 본다.

  1. 파일 Owner/Group
  2. 파일 Mode
  3. 상위 Directory execute 권한
  4. Alloy Service User
  5. SELinux Context / AVC

26. Grafana에 Loki 추가

Grafana:

Connections
→ Data sources
→ Add data source
→ Loki

URL:

http://127.0.0.1:3100

Explore에서 Network 로그 확인:

{job="network-syslog"}

QNAP Access:

{job="qnap-access"}

QNAP Event:

{job="qnap-event"}

27. Grafana Alert를 처음 구성할 때 알아둘 점

Grafana Alert는 Dashboard의 그래프와 달리 최종적으로 숫자 하나 또는 Alert Instance별 숫자를 평가해야 한다.

Range Query를 그대로 Alert Condition에 사용하면 다음 오류가 발생할 수 있다.

looks like time series data,
only reduced data can be alerted on

따라서 다음 중 하나를 사용한다.

  1. Query를 Instant로 구성
  2. Range Query → Reduce → Threshold

28. Syslog Critical Alert

Level 3(Error) 이상:

sum by (device_ip, severity) (
  count_over_time(
    {
      job="network-syslog",
      severity=~"emerg|alert|crit|err"
    }[1m]
  )
)

권장:

A = Loki Instant Query

B = Threshold
A IS ABOVE 0

Summary:

[Syslog 경고]
{{ $labels.device_ip }}
{{ $labels.severity }}

Description:

장비 {{ $labels.device_ip }} 에서
Syslog Level 3 이상 로그가 발생했습니다.

Severity:
{{ $labels.severity }}

최근 1분 발생 건수:
{{ $values.A.Value }}

28.1 Alert Mail에 실제 로그 내용이 없는 이유

count_over_time()은 문자열 로그를 숫자로 집계한다.

따라서 Query 결과에는 주로 다음만 남는다.

  • Label
  • 발생 건수

로그 message 전체를 Loki Label로 만들면 종류가 지나치게 많아져 Cardinality 문제가 발생할 수 있으므로 권장하지 않는다.

메일에는 다음 정도를 넣고 실제 내용은 Grafana Explore에서 확인하는 구조가 안정적이다.

  • Device IP
  • Hostname
  • Severity
  • 발생 건수
  • Grafana Link

29. ICMP Down Alert

Flux:

from(bucket: "snmp_raw")
  |> range(start: -5m)

  |> filter(fn: (r) =>
    r._measurement == "ping" and
    r._field == "percent_packet_loss"
  )

  |> group(
    columns: ["url"]
  )

  |> last()

  |> keep(
    columns: [
      "_time",
      "_value",
      "url"
    ]
  )

Alert 구조:

A = Flux Query

B = Reduce
    Last
    Strict

C = Threshold
    B > 99

Pending = 2m

No Data 정책:

Keep Last State

왜 Keep Last State인가?

Metric이나 Log Source가 순간적으로 No Data가 되었을 때 별도의 DatasourceNoData 메일이 발생하면서 실제 장비 Label이 없는 알림이 발송될 수 있다.

No Data 자체를 별도 장애로 관리할 필요가 있다면 별도의 수집 상태 Alert를 구성하는 것이 더 명확하다.

30. 최종 Dashboard 구성 방법

처음 설치한 사용자는 Dashboard를 한 번에 완성하려 하지 말고 다음 단계로 만든다.

  1. 데이터소스 Save & Test
  2. Explore에서 실제 데이터 확인
  3. Stat 패널 하나 생성
  4. Time Series 하나 생성
  5. 장비 한 대 정상 확인
  6. 변수 추가
  7. 여러 장비로 확대
  8. Alert 추가

30.1 Network Switch Dashboard

권장 Row:

Row 표시 내용 목적
Device 상태 Device Name / Uptime / CPU / Memory / Ping 장비 자체 상태
Port 상태 ifName / ifAlias / Speed / operStatus Link 상태
Traffic RX / TX bps 대역폭 사용량
Packet Unicast / Broadcast / Multicast PPS Broadcast 폭주 및 Packet 패턴
Error Error / Discard 품질 및 혼잡 징후
Syslog warning / err / crit 이벤트 원인 확인

30.2 Linux Dashboard

권장:

  • Uptime
  • CPU
  • Memory
  • Load
  • Disk Capacity
  • Disk I/O
  • Network RX/TX
  • Network Error/Drop
  • Application/Security Log

30.3 Windows Dashboard

권장:

  • Exporter UP
  • Uptime
  • CPU
  • Memory
  • Logical Disk
  • Disk I/O
  • Network
  • Windows Event 연동 여부

30.4 QNAP Dashboard

현재 실제 운영 구성을 기준으로 다음과 같이 구성한다.

상단:

  • Node Exporter UP
  • Uptime
  • CPU Usage
  • Memory Usage
  • HDD 최고 온도

중단:

  • CPU / Memory / Load
  • bond0 RX / TX
  • Disk 상태
  • DataVol1 Volume
  • DataVol1 Filesystem 사용률
  • bond0 Error / Drop
  • Physical Disk I/O / IOPS

하단:

  • QNAP Event Log
  • QNAP Access Log

제거한 항목:

  • 상단 RAID 상태
  • Storage Pool 상태
  • Storage Pool 사용률
  • RAID 상세 Table
  • Storage Pool 상세 Table

삭제 이유는 Dashboard를 운영자가 빠르게 읽을 수 있도록 단순화하기 위해서이다.

RAID/Storage Pool 상세는 필요 시 별도의 상세 Dashboard에서 확인할 수 있다.

31. 초보자가 자주 만나는 문제

31.1 Telegraf --test는 되는데 서비스는 안 됨

root 권한에서는 Ping이 되지만 telegraf 서비스 계정에서는 Raw Socket 권한이 없을 수 있다.

확인:

journalctl -u telegraf \
  -n 100 \
  --no-pager

native Ping이면 CAP_NET_RAW 설정을 확인한다.

31.2 snmpget은 되는데 Telegraf에서 일부 장비만 누락

가능한 원인:

  • timeout이 너무 짧음
  • retries가 0
  • OID가 모델과 다름
  • DES/AES 조합이 장비와 다름
  • SNMP View 제한

먼저 동일 OID를 snmpget으로 확인한다.

31.3 Prometheus가 재시작 반복

가장 먼저:

promtool check config \
  /etc/prometheus/prometheus.yml

YAML 들여쓰기를 확인한다.

그 다음:

journalctl -u prometheus \
  -n 100 \
  --no-pager

31.4 Grafana에서 Filesystem이 너무 많이 보임

Linux/QNAP에는 tmpfs, loop, snapshot, container mount 등이 존재할 수 있다.

전체를 보여주기보다 운영자가 실제 사용하는 Mount Point만 필터링한다.

QNAP 예:

mountpoint="/share/CACHEDEV1_DATA"

31.5 Loki에 Job이 안 보임

설정에 job이 있다고 바로 나타나는 것이 아니다.

실제 Log가 Loki에 한 번 이상 저장되어야 한다.

확인 순서:

tail -n 10 \
  /var/log/qnap/access.log

journalctl -u alloy \
  -n 100 \
  --no-pager

curl -s \
  'http://127.0.0.1:3100/loki/api/v1/label/job/values' \
  | jq

32. 구축 완료 후 점검 Checklist

확인 항목
□ Rocky Linux 시간 동기화 정상
□ InfluxDB health 정상
□ Telegraf 서비스 정상
□ SNMP 장비별 응답 확인
□ InfluxDB에 SNMP 데이터 저장
□ Prometheus health 정상
□ Node Exporter Target UP
□ Windows Exporter Target UP
□ QNAP Node Exporter Target UP
□ rsyslog Network 로그 수신
□ QNAP Event / Access Log 분리 수신
□ Alloy Permission 정상
□ Loki job 확인
□ Grafana InfluxDB Save & Test
□ Grafana Prometheus Save & Test
□ Grafana Loki Save & Test
□ Switch Traffic 그래프 정상
□ Error/Discard 그래프 정상
□ Linux/Windows Dashboard 정상
□ QNAP DataVol1 용량 QTS와 일치
□ QNAP Snapshot 제외
□ QNAP bond0만 Network 표시
□ Syslog Alert Mail에 장비 IP / Severity 표시
□ ICMP Down Alert 정상

33. 전체 장애 점검 순서

모니터링이 안 될 때 Grafana부터 무작정 수정하지 않는다.

33.1 SNMP

Switch
  ↓
snmpget
  ↓
Telegraf --test
  ↓
Telegraf Service
  ↓
InfluxDB
  ↓
Grafana Explore
  ↓
Dashboard

33.2 Prometheus

Exporter /metrics
  ↓
Prometheus Target
  ↓
Prometheus Query
  ↓
Grafana Explore
  ↓
Dashboard

33.3 Syslog

Device 송신
  ↓
tcpdump
  ↓
rsyslog
  ↓
Log File
  ↓
Alloy
  ↓
Loki
  ↓
Grafana Explore
  ↓
Dashboard / Alert

이 순서대로 확인하면 문제 지점을 빠르게 좁힐 수 있다.

34. 운영 원칙 요약

  • 처음부터 모든 장비를 추가하지 않는다.
  • 한 장비, 한 Port를 먼저 완성한다.
  • 설정 변경 후 항상 해당 서비스의 검사 명령을 실행한다.
  • Counter와 현재 상태값을 구분한다.
  • No Data와 0을 같은 의미로 처리하지 않는다.
  • SNMP Password와 Token을 Wiki에 저장하지 않는다.
  • Grafana는 수집기가 아니라 시각화 계층임을 기억한다.
  • QNAP처럼 제조사별 특이사항은 실제 값과 Vendor 화면을 대조한다.
  • Alert는 처음부터 너무 많이 만들지 않는다.
  • 정상 상태의 기준 데이터를 먼저 쌓은 후 임계값을 결정한다.
  • 장애 시에는 수집 경로를 앞단부터 순서대로 확인한다.

35. 최종 구성 요약

                  +----------------------+
                  |       Grafana        |
                  +----------+-----------+
                             |
          +------------------+------------------+
          |                  |                  |
          v                  v                  v
     Prometheus          InfluxDB              Loki
          ^                  ^                  ^
          |                  |                  |
     Exporters           Telegraf             Alloy
          ^                  ^                  ^
          |                  |                  |
 Linux / Windows / QNAP   SNMP Device      Syslog Files
                                                ^
                                                |
                                             rsyslog
                                                ^
                                                |
                                      Network / Server / NAS

이 구조가 완성되면 다음 세 종류의 정보를 한 Grafana에서 확인할 수 있다.

  1. 장비와 서버의 현재 상태
  2. 시간에 따른 성능 변화
  3. 장애 시점의 이벤트 로그

초보자는 먼저 "데이터가 어디서 생성되어 어디를 거쳐 Grafana까지 도착하는지"를 이해한 뒤 설정값을 수정하는 것이 가장 중요하다.