시리즈의 마지막 편입니다. 지금까지 스크립트 구현과 설정 방법을 다뤘다면, 이번 편에서는 실제 운영 중 마주친 문제들을 사례별로 정리합니다. 설치 가이드에는 나오지 않지만 실제로는 반드시 한 번씩 겪게 되는 문제들입니다.
이슈 1: 알림이 Slack으로 오지 않는다 — Media Type 기본값 Disabled
증상: 트리거가 발동하고 Zabbix 대시보드에는 알림이 보이지만, Slack 메시지가 도착하지 않습니다. Action log에는 다음 메시지만 반복됩니다:
ERROR: Media type disabled.
원인: Zabbix는 Media Type을 생성할 때 기본값이 Disabled입니다. 화면에 명시되지 않아 놓치기 쉽습니다.
실제로 이 시리즈의 PRD Slack Webhook은 생성 후 수주간 비활성 상태였습니다. Zabbix DB 감사 로그를 보면:
07-03 08:02:54 생성됨 (status 필드 없음 = 기본값 Disabled)
07-15 06:26:29 {"mediatype.status": ["update", "0", "1"]} → 활성화됨
생성과 활성화 사이의 모든 42건의 알림 기록이 ERROR: Media type disabled.였습니다.
해결: Media Type 생성 직후 Status를 Enabled로 변경합니다.
> [ Alerts > Media types > 생성한 항목 > Status 토글]

새 Media Type을 만들 때마다 이것을 체크리스트 첫 번째 항목으로 삼으세요.
이슈 2: Zabbix API 트리거 생성 실패 — 표현식 포맷 변경
증상: trigger.create API 호출 시 다음 오류가 반환됩니다:
{"code": -32602, "message": "Invalid params.",
"data": "Trigger expression ... is incorrect."}
원인: Zabbix 6.0부터 트리거 표현식 포맷이 변경됐습니다. 구버전 자료를 참고하면 잘못된 포맷을 사용하게 됩니다.
# 구버전 포맷 — 오류 발생
"{proxmox-cluster:proxmox.node.cpu[pve11].last()} > 90"
# 신버전 포맷 — 올바름
"last(/proxmox-cluster/proxmox.node.cpu[pve11]) > 90"
해결: 표현식을 함수(/호스트/키, 파라미터) 연산자 값 형식으로 변경합니다. Zabbix 7.0 공식 문서의 트리거 표현식 섹션을 기준으로 삼습니다.
이슈 3: trigger.create에 hostid 파라미터 오류
증상:
{"code": -32602, "message": "Invalid params.",
"data": "unexpected parameter "hostid""}
원인: 구버전 예제 코드에는 trigger.create 파라미터에 hostid가 포함된 경우가 있습니다. Zabbix 7.0에서는 이 파라미터가 없습니다. 호스트 정보는 expression 안에 포함됩니다.
# 잘못된 코드
{"hostid": "10234", "expression": "...", "description": "..."}
# 올바른 코드
{"expression": "last(/호스트명/키) > 값", "description": "..."}
해결: trigger.create 호출에서 hostid 파라미터를 제거합니다.
이슈 4: item.create trends 오류 — 문자열 타입 아이템
증상:
{"data": "Invalid parameter "/trends": value must be 0."}
원인: value_type이 1(Character) 또는 4(Text)인 아이템에는 trends 파라미터를 지정할 수 없습니다. 숫자 타입과 동일하게 처리하면 오류가 발생합니다.
# 잘못된 코드 — string 타입에도 trends 설정
params = {"value_type": 1, "trends": "365d", ...}
# 올바른 코드 — 조건부로 제외
params = {"value_type": vtype, ...}
if vtype not in (1, 4):
params["trends"] = "365d"
해결: 아이템 생성 시 value_type이 문자열(1, 4)이면 trends 파라미터를 아예 포함하지 않습니다.
이슈 5: HBA 교체 후 오탐 — Trapper stale value
증상: 서버의 RAID 컨트롤러를 HP P440ar에서 LSI HBA로 교체한 뒤에도 "Physical drive health degraded" 알림이 계속 발생합니다.
원인: P440ar에 연결되어 있던 드라이브 8개의 health 아이템이 Zabbix에 Trapper로 등록되어 있었습니다. 교체 후 스크립트가 해당 드라이브를 더 이상 수집하지 않았지만, Trapper 아이템은 마지막 값을 무기한 유지합니다. 이전에 수집된 degraded 값이 트리거를 계속 발동시켰습니다.
해결 방법 3가지:
- 즉각 해결: 해당 아이템을 Zabbix에서 수동 삭제
- 트리거 조건 추가: 데이터 미수신 시 비활성화
last(/host/proxmox.node.disk.health[pve13,sda]) = 1
and nodata(/host/proxmox.node.disk.health[pve13,sda], 30m) = 0
- 근본 해결: LLD Discovery +
Keep lost resources period설정으로 사라진 디스크 아이템 자동 삭제
이슈 6: netstat 수집 시 TypeError — 문자열 덧셈
증상: 노드 네트워크 수집 스크립트 실행 시 다음 오류가 발생합니다:
TypeError: can only concatenate str (not "int") to str
원인: Proxmox /nodes/{node}/netstat API의 in과 out 값이 문자열로 반환됩니다. 숫자처럼 보이지만 타입이 str입니다.
# 잘못된 코드
total_in += iface.get("in", 0) # "12345" + "67890" → 오류 또는 문자열 연결
# 올바른 코드
total_in += int(iface.get("in", 0) or 0)
or 0 처리는 값이 None이거나 빈 문자열일 때 int() 변환 오류를 방지합니다.
교훈: 외부 API 응답값의 타입을 절대 가정하지 마세요. 숫자처럼 보여도 문자열일 수 있습니다.
이슈 7: crontab 초기화 사고
증상: crontab 수정 후 확인했더니 내용이 전부 사라졌습니다.
원인: shell에서 awk 명령으로 crontab을 편집하는 과정에서 ! 문자가 shell history expansion으로 해석되어 의도하지 않은 명령이 실행됐습니다. 결과적으로 빈 내용이 crontab에 등록됐습니다.
해결: 백업 파일에서 복원했습니다.
crontab /app/zabbix/crontab.bak
예방 방법:
# crontab 수정 전 반드시 백업
crontab -l > /app/zabbix/crontab.bak
# crontab 직접 편집 (안전)
crontab -e
# shell 명령으로 crontab을 수정할 때는 heredoc 사용
crontab - << 'EOF'
* * * * * 명령어
EOF
중요: 'EOF'처럼 작은따옴표로 감싸면 heredoc 안에서 ! 같은 특수문자가 shell에 의해 해석되지 않습니다.
이슈 8: curl로 Zabbix API 호출 시 JSON 파싱 오류
증상: curl로 Zabbix API를 호출하면 응답이 잘려서 JSON 파싱 오류가 발생합니다.
원인: curl이 기본으로 Connection: close 헤더를 보내고, 서버가 응답 중간에 연결을 닫아 응답 body가 잘립니다.
해결: curl 대신 Python urllib을 사용합니다. 또는 curl에 -H "Connection: keep-alive" 옵션을 추가합니다.
이 시리즈 전체에서 API 호출을 Python urllib으로 구현한 이유 중 하나입니다.
이슈 9: Proxmox 디스크 SMART API 403 오류
증상: /nodes/{node}/disks/smart API 호출 시 403 Forbidden이 반환됩니다. 클러스터 API는 정상이지만 노드 로컬 API에서만 발생합니다.
원인: PVEAuditor 역할에는 Sys.Audit 권한이 포함되어 있지 않습니다. 물리 디스크 정보 조회에는 Sys.Audit이 추가로 필요합니다.
해결: /nodes 경로 또는 / 루트에 Sys.Audit을 포함한 Role을 추가 부여합니다.
> [Datacenter > Permissions > Add > API Token Permission > Path: /nodes, Role: Sys.Audit 포함 커스텀 Role]
이슈 10: iLO 구형 펌웨어 SSL 연결 오류
증상:
SSLError: [SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:1129)
원인: iLO 4 구형 펌웨어가 사용하는 Diffie-Hellman 키 크기가 현대 SSL 라이브러리의 최소 요구 기준을 충족하지 못합니다.
해결: LegacySSLAdapter로 SSL 보안 수준을 낮춥니다:
class LegacySSLAdapter(HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
ctx = ssl.create_default_context()
ctx.set_ciphers("DEFAULT:@SECLEVEL=1")
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
kwargs["ssl_context"] = ctx
super().init_poolmanager(*args, **kwargs)
이는 임시 해결책입니다. 근본 해결은 iLO 펌웨어 업그레이드입니다.
빠른 진단 가이드
문제가 발생했을 때 확인 순서입니다:
알림이 오지 않을 때:
- Reports > Action log — 오류 메시지 확인
- Media Type Status = Enabled 여부 확인
- User Media에 Media Type 등록 여부 확인
- Action의 Conditions와 Operations 확인
수집이 되지 않을 때:
- 스크립트 로그 확인 (
/data/zabbix/scripts/collector_*.log) - 스크립트를 직접 실행해 오류 확인
- Zabbix 아이템의 Last value 및 Last error 확인
- API 권한 확인 (curl로 직접 테스트)
오탐이 발생할 때:
- Trapper 아이템인 경우 — 마지막 수집 시간 확인 (stale value 여부)
- 트리거 조건 재검토 —
nodata()조건 추가 고려 - DEV 환경 — UNKNOWN 값 처리 방식 확인
시리즈를 마치며
15편에 걸쳐 다룬 내용을 정리하면:
| 편 | 주제 |
|---|---|
| 1~3 | Zabbix 기반 구축 (Docker, 알림, 핵심 개념) |
| 4~7 | Proxmox 전 계층 모니터링 (클러스터, 노드, 디스크) |
| 8~10 | 하드웨어 모니터링 (iLO, iDRAC, Zabbix API 자동화) |
| 11~12 | 네트워크·스토리지 (OPNsense, TrueNAS) |
| 13~14 | 운영 최적화 (스케줄, DEV/PRD 분리) |
| 15 | 실전 이슈 트러블슈팅 |
이 시리즈에서 구현한 모니터링이 수집하는 메트릭은 하루에도 수십만 건에 달합니다. 완벽한 모니터링 시스템은 없습니다. 운영하면서 오탐을 줄이고, 놓치는 항목을 추가하고, 임계값을 조정해가는 과정이 계속됩니다.
이 시리즈가 Zabbix를 처음 구축하거나 커스텀 모니터링을 추가하려는 분들에게 실질적인 도움이 되길 바랍니다.