Proxmox 모니터링이 OS·VM 수준의 지표를 다룬다면, iLO 모니터링은 서버 하드웨어 자체의 상태를 다룹니다. OS가 정상이어도 팬이 멈추거나 PSU가 고장나면 서버는 언제든 다운될 수 있습니다. 하드웨어 헬스 모니터링이 별도로 필요한 이유입니다.
HP iLO와 Redfish API
iLO(Integrated Lights-Out)는 HP 서버의 원격 관리 인터페이스입니다. 서버 본체와 독립된 전용 네트워크 포트와 프로세서를 가지고 있어, OS가 다운된 상태에서도 서버 상태를 모니터링하고 제어할 수 있습니다.
Redfish는 DMTF 표준 REST API로, iLO 4(펌웨어 2.x 이상)부터 지원합니다. JSON 형식으로 하드웨어 상태를 조회할 수 있어 스크립트 통합이 쉽습니다.
주요 Redfish 엔드포인트:
| 경로 | 수집 내용 |
|---|---|
/redfish/v1/Systems/1 | 전체 시스템 헬스, 전원 상태, CPU/메모리 요약 |
/redfish/v1/Chassis/1/Thermal | 온도 센서, 팬 RPM 및 상태 |
/redfish/v1/Chassis/1/Power | 소비 전력, 전력 용량, PSU별 상태 |
/redfish/v1/Systems/1/SmartStorage | 스마트 스토리지 컨트롤러 및 드라이브 |
DH_KEY_TOO_SMALL 오류와 LegacySSLAdapter
iLO 4 구형 펌웨어에서 Python requests로 HTTPS 요청을 보내면 다음 오류가 발생합니다:
SSLError: [SSL: DH_KEY_TOO_SMALL] dh key too small
이는 iLO가 사용하는 Diffie-Hellman 키 크기가 현대 SSL 라이브러리의 최소 기준(1024비트)에 미치지 못하기 때문입니다. 펌웨어 업그레이드가 불가능한 환경에서는 SSL 컨텍스트의 보안 수준을 낮춰 우회합니다:
import ssl
from requests.adapters import HTTPAdapter
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)
요청 시 이 어댑터를 https://에 마운트합니다:
def idrac_get(cfg, path):
s = requests.Session()
s.auth = (cfg["ilo_user"], cfg["ilo_pass"])
s.verify = False
s.headers.update({"Accept": "application/json"})
if cfg.get("legacy_ssl"):
s.mount("https://", LegacySSLAdapter())
r = s.get(f"https://{cfg['ilo_host']}{path}", timeout=15)
if r.status_code == 200:
return r.json()
return None
legacy_ssl 플래그를 호스트 설정에 넣어두면 펌웨어가 최신인 서버와 구형 서버를 같은 코드로 처리할 수 있습니다.
다중 호스트 설정
여러 서버를 하나의 스크립트로 관리합니다:
HOSTS = {
"idc02": {
"ilo_host": "10.0.92.112",
"ilo_user": "관리자계정",
"ilo_pass": "비밀번호",
"zabbix_host": "proxmox-idc-02-ilo",
"legacy_ssl": True,
},
"idc03": {
"ilo_host": "10.0.92.113",
"ilo_user": "관리자계정",
"ilo_pass": "비밀번호",
"zabbix_host": "proxmox-idc-03-ilo",
},
"nas01": {
"ilo_host": "10.0.92.161",
"ilo_user": "관리자계정",
"ilo_pass": "비밀번호",
"zabbix_host": "truenas-prd-01-ilo",
"legacy_ssl": True,
},
}
실행 시 --site idc02 또는 --site all로 대상을 선택합니다.
Health 값 정규화
Redfish는 헬스 상태를 문자열("OK", "Warning", "Critical")로 반환합니다. Zabbix 트리거에서 비교하기 쉽도록 정수로 변환합니다:
HEALTH_MAP = {"OK": 0, "Warning": 1, "Critical": 2, "Unknown": 3}
def health_int(obj):
if obj is None:
return -1
if isinstance(obj, dict):
h = obj.get("Health") or obj.get("HealthRollup")
if h is None:
return -1
obj = h
return HEALTH_MAP.get(str(obj), 3)
| 문자열 | 정수 | 트리거 기준 |
|---|---|---|
| OK | 0 | – |
| Warning | 1 | Average 알림 |
| Critical | 2 | High 알림 |
| Unknown | 3 | Warning 알림 |
| N/A (없음) | -1 | 트리거 제외 |
시스템 헬스 수집
system = ilo_get(cfg, "/redfish/v1/Systems/1")
if system:
sl("ilo.health.system", health_int(system.get("Status", {}).get("Health")))
sl("ilo.power.state", 1 if system.get("PowerState") == "On" else 0)
proc = system.get("ProcessorSummary", {})
sl("ilo.health.cpu", health_int(proc.get("Status")))
sl("ilo.cpu.count", proc.get("Count", 0))
mem = system.get("MemorySummary", {})
sl("ilo.health.memory", health_int(mem.get("Status")))
sl("ilo.memory.total_gb", mem.get("TotalSystemMemoryGiB", 0))
HealthRollup은 하위 컴포넌트 중 가장 나쁜 상태를 요약합니다. Health가 OK여도 HealthRollup이 Warning이라면 어딘가 이상이 있다는 신호입니다.
온도 및 팬 수집
thermal = ilo_get(cfg, "/redfish/v1/Chassis/1/Thermal")
if thermal:
for temp in thermal.get("Temperatures", []):
st = temp.get("Status", {})
if st.get("State") not in ("Enabled", "Absent"):
continue
reading = temp.get("ReadingCelsius")
if reading is None:
continue
k = key_norm(temp.get("Name", "unknown"))
sl(f"ilo.temp.{k}", reading)
for fan in thermal.get("Fans", []):
st = fan.get("Status", {})
if st.get("State") != "Enabled":
continue
name = fan.get("FanName") or fan.get("Name", "")
k = key_norm(name)
reading = fan.get("ReadingRPM") or fan.get("Reading")
if reading is not None:
sl(f"ilo.fan.{k}.rpm", reading)
sl(f"ilo.fan.{k}.health", health_int(st))
센서 이름을 Zabbix 키에 사용할 수 있도록 정규화합니다:
import re
def key_norm(name):
# "Inlet Ambient" → "inlet_ambient"
return re.sub(r"[^a-z0-9]+", "_", name.lower()).strip("_")
HP ProLiant 서버의 주요 온도 센서:
| 센서명 | 의미 |
|---|---|
| Inlet Ambient | 서버 흡입구 온도 |
| CPU 1 | CPU 1번 온도 |
| P/S 1 Zone | 전원부 온도 |
| System Board | 메인보드 온도 |
전력 및 PSU 수집
power = ilo_get(cfg, "/redfish/v1/Chassis/1/Power")
if power:
for pc in power.get("PowerControl", []):
consumed = pc.get("PowerConsumedWatts")
if consumed is not None:
sl("ilo.power.consumed", consumed)
cap = pc.get("PowerCapacityWatts")
if cap is not None:
sl("ilo.power.capacity", cap)
break # 첫 번째 PowerControl만 사용
for i, psu in enumerate(power.get("PowerSupplies", []), 1):
st = psu.get("Status", {})
if st.get("State") == "Absent":
continue
sl(f"ilo.power.ps{i}.health", health_int(st))
out = psu.get("PowerOutputWatts") or psu.get("LastPowerOutputWatts")
if out is not None:
sl(f"ilo.power.ps{i}.output", out)
volt = psu.get("LineInputVoltage")
if volt is not None:
sl(f"ilo.power.ps{i}.voltage", volt)
PSU가 Absent 상태인 경우 슬롯이 비어 있는 것이므로 건너뜁니다. PSU가 있지만 Critical이라면 즉시 교체가 필요합니다.
iLO가 볼 수 없는 디스크
iLO에서 물리 드라이브 상태를 확인할 수 있는 것은 HP Smart Array(P-Series) 컨트롤러에 연결된 드라이브에 한합니다. LSI HBA(IT 모드)나 다른 제조사의 컨트롤러에 연결된 드라이브는 iLO가 인식하지 못합니다.
7편에서 다룬 HBA 교체 오탐 사례가 바로 이 한계에서 비롯된 것입니다:
P440ar 연결 드라이브 → iLO/Smart Storage API로 조회 가능
LSI 9217 연결 드라이브 → iLO 조회 불가 → Proxmox API로 수집
서버에 HBA 카드가 설치되어 있다면 Proxmox API의 disks/list + disks/smart를 통해 수집하고, iLO의 Smart Storage 수집에서는 해당 드라이브를 기대하지 않아야 합니다.
crontab 등록
# HP iLO 수집 (5분)
*/5 * * * * /usr/bin/python3 /data/zabbix/scripts/ilo_collector.py --site all >> /var/log/zabbix-collector.log 2>&1
하드웨어 헬스는 5분 주기로 수집합니다. 1분 주기는 iLO에 불필요한 부하를 줄 수 있고, 온도·팬·전력 이상은 보통 수분에 걸쳐 나타나기 때문에 5분으로 충분합니다.
마치며
이번 편에서 다룬 핵심 포인트입니다:
- iLO Redfish API는 OS 독립적으로 하드웨어 상태 수집 가능
- 구형 펌웨어의 DH_KEY_TOO_SMALL →
LegacySSLAdapter로 해결 - Health 문자열을 정수로 변환해 Zabbix 트리거에서 쉽게 비교
- 센서명은
key_norm()으로 정규화해 Zabbix 키로 사용 - iLO는 HP Smart Array 컨트롤러 드라이브만 인식 — HBA 드라이브는 Proxmox API로 별도 수집
다음 편에서는 Dell 서버의 iDRAC 8 Redfish API를 다룹니다. HP iLO와 동일한 Redfish 표준이지만 API 경로와 응답 구조가 다른 부분을 비교하며 설명합니다.