
자빅스와 그라파나, 앤서블을 사용해 PSI 대시보드를 구성하여 알림 룰까지 설정하는 문서입니다.
낭만인프라는 온프레미스 서버와 물리 SBC(라즈베리파이, LattePanda 등)가 섞인 이기종 fleet입니다.
여기에 PSI(Pressure Stall Information) 기반 메모리 관측 파이프라인을 붙인 과정을 정리했습니다.
기존 스택은 Zabbix(수집·저장) + Grafana(시각화·알림) 이고, 여기에 다음을 더했습니다.
/proc/pressure/memory 파싱전통적인 메모리 사용률 지표는 "얼마나 찼는가"만 알려줍니다.
하지만 리눅스에서 메모리 사용률이 높은 것은 대부분 page cache 때문이라 정상이고, 오히려 오탐의 원인이 됩니다.
PSI는 "리소스를 기다리느라 태스크가 정지한 시간의 비율(%)" 을 직접 측정합니다. 즉 사용률(utilization)이 아니라 압박(pressure) 을 봅니다.
핵심은 "사용률 90%"가 아니라 "지금 실제로 아픈가"를 보는 것입니다.
이 관점이 이 프로젝트 전체를 관통합니다.

PSI는 Zabbix 기본 아이템이 없어서, UserParameter로 직접 수집하도록 만들었습니다. /proc/pressure/memory를 awk로 파싱하는 스크립트와 UserParameter 정의입니다.
# /usr/local/bin/psi_mem.sh
#!/bin/sh
# $1 = some|full, $2 = avg10|avg60|avg300|total
awk -v line="$1" -v field="$2" \
'$0 ~ "^"line {for(i=1;i<=NF;i++){split($i,a,"=");if(a[1]==field)print a[2]}}' \
/proc/pressure/memory
# /etc/zabbix/zabbix_agent2.d/psi.conf
UserParameter=psi.memory[*], /usr/local/bin/psi_mem.sh "$1" "$2"
파라미터화([*]) 덕분에 some/full × avg10/avg60/avg300 6개 지표를 UserParameter 하나로 뽑을 수 있습니다.
먼저 한 호스트에서 검증했습니다.

한 호스트에서 검증한 뒤, 전 fleet에 Ansible로 배포했습니다.
이기종 fleet를 안전하게 다루기 위해 세 가지를 신경 썼습니다.
stat 가드: /proc/pressure/memory(PSI)와 /etc/zabbix/zabbix_agent2.d(agent 2)가 모두 있는 호스트에만 배포합니다.copy로 배포하고, 설정이 바뀔 때만 notify로 agent를 재시작합니다.not ansible_check_mode 조건으로, dry-run 시 검증 태스크는 건너뜁니다.- name: PSI 메모리 수집 배포 (Zabbix Agent 2 UserParameter)
hosts: all
become: yes
gather_facts: yes
tasks:
- name: PSI 지원 여부 확인
ansible.builtin.stat: { path: /proc/pressure/memory }
register: psi_stat
- name: Zabbix agent 2 설치 여부 확인
ansible.builtin.stat: { path: /etc/zabbix/zabbix_agent2.d }
register: agent2_stat
- name: 배포 대상 아님 알림 (건너뜀)
ansible.builtin.debug:
msg: "건너뜀: {{ inventory_hostname }} (PSI={{ psi_stat.stat.exists }}, agent2={{ agent2_stat.stat.exists }})"
when: not (psi_stat.stat.exists and agent2_stat.stat.exists)
# psi_mem.sh / psi.conf copy — when: psi_stat and agent2_stat
# 검증 태스크 — when: ... and not ansible_check_mode
handlers:
- name: Zabbix Agent 2 재시작
ansible.builtin.service: { name: zabbix-agent2, state: restarted }
무작정 전체에 밀지 않았습니다. 검증된 그룹 → Check(dry-run) → Run → 다음 그룹 순으로 확장했습니다.

Check 모드로 대상/건너뛸 호스트와 변경 예정 사항을 먼저 확인한 뒤 실제 배포했습니다.

멱등성이 눈으로 확인되더라고요.
이미 수동으로 설정해 둔 호스트는
changed=1, 신규 호스트는changed=3으로 나왔습니다.
이미 적용된 것은 다시 건드리지 않는다는 뜻입니다.
호스트마다 아이템을 따로 만들지 않고, 4개 아이템(full/some × avg60/avg300)을 담은 Template를 만들어 호스트 그룹에 링크했습니다.

**Numeric (float)** — PSI 값은 0.00 같은 소수라, 정수형으로 두면 잘립니다.%, 태그 component: memory
Memory Utilization 패널 바로 옆에 PSI 패널을 배치했습니다.
의도가 있었습니다.

사용률 vs 압박. 사용률이 높아도 PSI가 0이면 page cache로 찬 정상 상태이고, PSI가 오르는 순간이 실제로 "아픈" 순간입니다. 두 지표를 나란히 두면 이 차이가 한눈에 보입니다.
full avg60 기준으로, 평상시 0(초록)이고 압박 시 주황·빨강으로 변하도록 임계값을 구성했습니다.
full avg300 > 5% 조건으로 알림을 만들었습니다.
여기서도 기준은 사용률이 아니라 압박입니다.
severity: warning, Annotations에 summary/description
알림 메시지에는 호스트·값·확인 항목을 넣었습니다.
stress-ng로 실제 메모리 압박을 만들어 파이프라인 전 구간을 검증했습니다.
stress-ng --vm 1 --vm-bytes 3G --vm-keep --timeout 240s
RAM을 넘겨 swap까지 밀어붙이자 PSI가 상승했고, 대시보드 셀이 빨강으로 바뀌었으며, 임계값 초과로 Mattermost 알림이 발동했습니다.
압박이 풀린 뒤 복구 알림까지 확인했습니다.


구축하면서 마주친 함정들이, 오히려 이 시스템이 어떻게 동작하는지를 알려줬습니다.
| 문제 | 원인 | 대응 |
|---|---|---|
| SBC 배포 건너뜀 | PSI는 있으나 agent 미설치 | Zabbix agent 2 선행 설치 |
| Grafana 패널 NaN·깜빡임 | 수집 초기 데이터 희소 + Last reduce | Last *(non-null)로 변경 |
| 라벨이 아이템명으로 표시 | 단일 호스트라 시리즈에 host 접두어 없음 | fleet 확장 시 자동 해결 |
| Zabbix 아이템 NaN | 템플릿을 미배포 호스트에 링크 | 배포 범위 = 모니터링 범위 원칙 |
알림 값이 [no value] | classic condition은 값을 노출하지 않음 | Reduce+Threshold로 전환 → {{ $values.B.Value }} |
관측 대상 범위와 수집기 배포 범위는 반드시 일치시켜야 합니다.
그리고 아키텍처·OS·에이전트 버전이 섞인 fleet에서는, 가드로 "준비되지 않은 호스트"를 걸러내는 것이 안전한 자동화의 핵심이라고 느꼈습니다.
감사합니다.
정택준
Team: https://nangman.cloud/ko
E-mail: taekjunnnn@gmail.com