8편에서 HP iLO Redfish API를 구현했다면, 이번 편에서는 Dell iDRAC 8을 다룹니다. Redfish는 동일한 표준이지만 제조사마다 API 경로와 응답 구조가 다릅니다. iLO 코드를 그대로 쓰면 동작하지 않습니다.
iDRAC과 iLO의 차이
iDRAC(Integrated Dell Remote Access Controller)은 Dell 서버의 원격 관리 인터페이스로, HP의 iLO에 해당합니다. Redfish 표준을 따르지만 경로가 다릅니다:
| 항목 | HP iLO 4 | Dell iDRAC 8 |
|---|---|---|
| 시스템 | /redfish/v1/Systems/1 | /redfish/v1/Systems/System.Embedded.1 |
| 섀시 Thermal | /redfish/v1/Chassis/1/Thermal | /redfish/v1/Chassis/System.Embedded.1/Thermal |
| 섀시 Power | /redfish/v1/Chassis/1/Power | /redfish/v1/Chassis/System.Embedded.1/Power |
| 스토리지 | /redfish/v1/Systems/1/SmartStorage | /redfish/v1/Systems/System.Embedded.1/Storage |
가장 크게 다른 부분은 리소스 ID입니다. HP는 숫자(1)를, Dell은 System.Embedded.1 형태의 식별자를 사용합니다. 스토리지 경로명도 다릅니다(SmartStorage vs Storage).
SSL 처리
iDRAC 8도 구형 펌웨어에서 동일한 SSL 문제가 발생합니다. 8편에서 구현한 LegacySSLAdapter를 그대로 재사용합니다:
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)
def idrac_get(cfg, path):
s = requests.Session()
s.auth = (cfg["idrac_user"], cfg["idrac_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['idrac_host']}{path}", timeout=15)
if r.status_code == 200:
return r.json()
return None
호스트 설정
HOSTS = {
"pbs01": {
"idrac_host": "10.0.92.191",
"idrac_user": "관리자계정",
"idrac_pass": "비밀번호",
"zabbix_host": "pbs-idc-01-idrac",
"legacy_ssl": True,
},
}
시스템 헬스 수집
iDRAC의 시스템 엔드포인트는 iLO와 응답 구조가 유사합니다:
system = idrac_get(cfg, "/redfish/v1/Systems/System.Embedded.1")
if system:
sl("idrac.health.system", health_int(system.get("Status", {}).get("Health")))
sl("idrac.power.state", 1 if system.get("PowerState") == "On" else 0)
proc = system.get("ProcessorSummary", {})
sl("idrac.health.cpu", health_int(proc.get("Status")))
if proc.get("Count") is not None:
sl("idrac.cpu.count", proc["Count"])
mem = system.get("MemorySummary", {})
sl("idrac.health.memory", health_int(mem.get("Status")))
if mem.get("TotalSystemMemoryGiB") is not None:
sl("idrac.memory.total_gb", mem["TotalSystemMemoryGiB"])
iLO와 비교했을 때 키 이름만 ilo. → idrac.으로 바뀌고 로직은 동일합니다.
Thermal 수집
thermal = idrac_get(cfg, "/redfish/v1/Chassis/System.Embedded.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"idrac.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"idrac.fan.{k}.rpm", reading)
sl(f"idrac.fan.{k}.health", health_int(st))
Dell PowerEdge R730xd의 주요 온도 센서:
| 센서명 | 위치 |
|---|---|
| Inlet Temp | 서버 흡입구 |
| Exhaust Temp | 서버 배출구 |
| CPU1 Temp | CPU 1번 |
| CPU2 Temp | CPU 2번 |
팬은 FAN 1A, FAN 1B 형태로 이름이 붙으며, R730xd는 팬 모듈당 두 개의 팬(A/B)이 있습니다.
전력 수집
power = idrac_get(cfg, "/redfish/v1/Chassis/System.Embedded.1/Power")
if power:
for pc in power.get("PowerControl", []):
consumed = pc.get("PowerConsumedWatts")
if consumed is not None:
sl("idrac.power.consumed", consumed)
cap = pc.get("PowerCapacityWatts")
if cap is not None:
sl("idrac.power.capacity", cap)
break
for i, psu in enumerate(power.get("PowerSupplies", []), 1):
st = psu.get("Status", {})
if st.get("State") == "Absent":
continue
sl(f"idrac.power.ps{i}.health", health_int(st))
out = psu.get("PowerOutputWatts") or psu.get("LastPowerOutputWatts")
if out is not None:
sl(f"idrac.power.ps{i}.output", out)
volt = psu.get("LineInputVoltage")
if volt is not None:
sl(f"idrac.power.ps{i}.voltage", volt)
iLO와 동일한 구조입니다. Dell 서버도 PSU 이중화 구성이 일반적이며, PSU 하나가 Critical이면 즉시 교체가 필요합니다.
스토리지 수집 — iLO와 가장 다른 부분
iDRAC의 스토리지 수집은 iLO와 구조가 다릅니다. iDRAC은 컨트롤러 → 드라이브로 계층적으로 탐색합니다:
storage = idrac_get(cfg, "/redfish/v1/Systems/System.Embedded.1/Storage")
if storage:
for member in storage.get("Members", []):
mpath = member.get("@odata.id", "")
ctrl = idrac_get(cfg, mpath)
if not ctrl:
continue
ctrl_id = key_norm(ctrl.get("Id", mpath.rstrip("/").split("/")[-1]))
sl(f"idrac.storage.ctrl.{ctrl_id}.health", health_int(ctrl.get("Status")))
for drive_ref in ctrl.get("Drives", []):
dpath = drive_ref.get("@odata.id", "")
drive = idrac_get(cfg, dpath)
if not drive:
continue
slot = key_norm(drive.get("Id", dpath.rstrip("/").split("/")[-1]))
sl(f"idrac.storage.pd.{slot}.health", health_int(drive.get("Status")))
탐색 흐름:
/Storage
└── Members[]
└── @odata.id → 컨트롤러 경로
└── Drives[]
└── @odata.id → 드라이브 경로
컨트롤러 목록을 먼저 가져오고, 각 컨트롤러의 드라이브를 순서대로 탐색합니다. 컨트롤러와 드라이브 수만큼 API 호출이 발생하므로 서버당 수집 시간이 길어질 수 있습니다.
수집 키 예시:
idrac.storage.ctrl.boss_a1.health → 0 (OK)
idrac.storage.pd.disk_0_0_0.health → 0 (OK)
idrac.storage.pd.disk_0_1_0.health → 0 (OK)
컨트롤러 ID와 드라이브 슬롯 ID는 key_norm()으로 정규화해 Zabbix 키에 사용합니다.
iLO vs iDRAC 수집 항목 비교
| 수집 항목 | HP iLO 4 | Dell iDRAC 8 |
|---|---|---|
| 시스템 헬스 | ✅ | ✅ |
| 전원 상태 | ✅ | ✅ |
| CPU 요약 헬스 | ✅ | ✅ |
| 메모리 요약 헬스 | ✅ | ✅ |
| 온도 센서 | ✅ | ✅ |
| 팬 RPM/헬스 | ✅ | ✅ |
| 소비 전력 | ✅ | ✅ |
| PSU별 헬스/출력 | ✅ | ✅ |
| 스토리지 컨트롤러 헬스 | ✅ (SmartStorage) | ✅ (Storage) |
| 물리 드라이브 헬스 | ✅ (Smart Array만) | ✅ (RAID 컨트롤러 연결) |
수집 항목은 거의 동일하지만 API 경로와 탐색 방식이 다릅니다. 코드를 재사용하려면 경로만 교체 가능하도록 설계하는 것이 좋습니다.
crontab 등록
# Dell iDRAC 수집 (5분)
*/5 * * * * /usr/bin/python3 /data/zabbix/scripts/idrac_collector.py --site all >> /var/log/zabbix-collector.log 2>&1
마치며
이번 편에서 다룬 핵심 포인트입니다:
- Redfish 표준이지만 iLO와 iDRAC은 API 경로가 다름 — 혼용 불가
- 핵심 차이: 리소스 ID(
1vsSystem.Embedded.1), 스토리지 경로 - iDRAC 스토리지는 컨트롤러 → 드라이브 계층 탐색 구조
key_norm()으로 센서/드라이브 ID를 Zabbix 키로 정규화
다음 편에서는 iLO와 iDRAC 모니터링을 위한 Zabbix 호스트, 아이템, 트리거를 Zabbix API로 자동 생성하는 방법을 다룹니다. 수십 개의 아이템을 UI에서 수동으로 만드는 대신 스크립트 한 번으로 일괄 등록하는 구조를 설명합니다.