답변문의 답변드리겠습니다.
안녕하세요~ 고객님, ipTIME 기술팀 입니다.
문의하신 내용에 답변 드리겠습니다.
개발부서에 전달하겠습니다.
감사합니다.
일단 제쪽에서 원인은 찾아서 해결 했습니다.
------------------
[버그 리포트 및 개선 건의]
ipTIME ICC 도커(icc:1_018) 구동 시 일 1.1TB+ 디스크 쓰기 폭주 원인 및 패치 건의
1. 현상 요약
ipTIME ICC(통합 관제 컨트롤러) 도커 컨테이너(icc:1_018) 운영 중, 관리 사이트(Site)가 단 2곳뿐임에도 불구하고 약 9.7일 동안 물리 디스크에 11.4TB(하루 평균 1.17TB, 상시 15~40MB/s)의 비정상적인 디스크 쓰기가 발생하여 심각한 I/O 병목이 발생하는 문제를 확인했습니다.
정밀 분석 결과, 단순 DB 설정 문제를 넘어 plugin_icc.so 내부의 Logs 테이블 처리 쿼리 결함과 이벤트 큐 적체 현상이 결합되어 발생하는 구조적 버그임을 확인하여 개선을 건의드립니다.
2. 정밀 원인 분석 (Root Cause)
[원인 1] 사이트 연결/끊김 시 실행되는 UPDATE 쿼리의 WHERE 조건 누락 및 인덱스 미비 (핵심 원인) 사이트가 재연결될 때마다 plugin_icc.so에서 아래 쿼리를 호출합니다.
-------------------------------------------------- UPDATE `Logs` SET checked = 'System' WHERE type = 'event' AND s_id = '6' AND device_mac IS NULL AND `key` = 'event.site.disconnected'
--------------------------------------------------
- 문제점 A (WHERE 조건 누락): 조건절에 `AND checked = 'Active'` (또는 `checked != 'System'`) 조건이 빠져 있어, 이미 과거에 'System'으로 처리된 수십만 건의 과거 로그까지 매번 업데이트 대상으로 스캔합니다.
- 문제점 B (복합 인덱스 부재): `Logs` 테이블에 단일 컬럼 인덱스(idx_s_id, idx_type, idx_mac, idx_key)만 존재하고 복합 인덱스가 없다 보니, MariaDB 옵티마이저가 매 쿼리마다 수십만 건의 행을 Index Merge 및 풀스캔하며 쿼리 1회당 수십 MB의 디스크 I/O를 발생시킵니다. (1초에 125,000건 이상의 Handler_update 및 11MB/s 이상의 Redo 로그 기록 발생 실측)
[원인 2] `event.site.*` 이벤트 로그의 자동 정리(Retention) 누락 plugin_icc.so 내 로그 정리 쿼리를 확인해 보면 다음과 같습니다.
-------------------------------------------------- DELETE FROM Logs WHERE `key` LIKE 'event.station%%' && date < DATE_SUB(NOW(), INTERVAL 15 DAY)
--------------------------------------------------
- 단말 이벤트(`event.station%`)만 15일 경과 시 삭제하고, 사이트 연결/끊김 이벤트(`event.site.connected`, `event.site.disconnected`)는 삭제 대상에서 누락되어 있습니다.
- 그 결과 네트워크 순단이나 재연결이 반복되면 `Logs` 테이블에 수십만 건(실측 53만 건 이상)의 `event.site.*` 레코드가 무한정 누적되며, [원인 1]의 UPDATE 쿼리 부하를 기하급수적으로 증폭시킵니다.
[원인 3] servd 이벤트 큐(Event Runner)의 무제한 적체 및 지연 처리 현상
- 특정 시점(예: 네트워크 불안정 등)에 다수의 사이트 연결/끊김 이벤트가 발생하면, DB(Logs 테이블)의 UPDATE 처리 속도가 느려지면서 `servd` 프로세스 메모리에 미처리 이벤트 큐가 수십만 건(메모리 120MB 이상) 적체됩니다.
- 이후 사이트 연결이 아예 해제된 상태에서도, `servd`가 일주일 전에 메모리에 쌓인 과거 `connected / disconnected` 이벤트를 초당 수십 건씩 꺼내어 며칠 내내 DB에 계속 INSERT 및 UPDATE를 반복(실측: 10월 19일 시점에 10월 12일 자 이벤트를 계속 DB에 기록)하며 디스크에 하루 1TB 이상의 쓰기를 유발합니다.
- 동일한 사이트(s_id)의 연결/끊김 상태 변경 이벤트는 큐에 중복 적재하지 않고 최신 상태만 반영(Debounce / Coalesce)하도록 개선이 필요합니다.
[원인 4] 컨테이너 내 MariaDB 5.5 기본 설정 파일(/etc/my.cnf) 부재
- 컨테이너 내부에 `/etc/my.cnf` 파일이 없어 MariaDB 5.5 초기 기본값으로 구동됩니다.
* `innodb_log_file_size = 5M` (기본값): Redo 로그가 5MBx2=10MB에 불과해 수 초마다 강제 체크포인트(Checkpoint)가 발생하며 `ibdata1`에 과도한 플러시 발생
* `innodb_buffer_pool_size = 128M` (기본값): `ibdata1` 크기가 커지면 메모리 부족으로 디스크 스래싱 발생
* `innodb_flush_log_at_trx_commit = 1` (기본값): 매 이벤트 쿼리마다 물리 디스크 강제 fsync() 호출
3. 패치 및 개선 제안 사항
(1) plugin_icc.so 내 SQL 쿼리 및 테이블 스키마 개선
- `Logs` 업데이트 쿼리에 미확인 건만 갱신하도록 조건 추가:
UPDATE `Logs` SET checked = 'System' WHERE type = 'event' AND s_id = '%d' AND device_mac IS NULL AND `key` = 'event.site.disconnected' AND checked = 'Active';
- `Logs` 테이블에 복합 인덱스 기본 추가:
ALTER TABLE `Logs` ADD INDEX `idx_site_event` (`s_id`, `type`, `key`, `checked`, `device_mac`);
- 15일 경과 로그 삭제 쿼리에 `event.site.%`도 포함되도록 수정:
DELETE FROM `Logs` WHERE (`key` LIKE 'event.station%%' OR `key` LIKE 'event.site%%') AND date < DATE_SUB(NOW(), INTERVAL 15 DAY);
(2) servd 사이트 연결/끊김 이벤트 디바운싱(Debounce) 또는 큐 중복 제거
- 짧은 시간 내 반복되는 `event.site.connected` / `event.site.disconnected` 이벤트가 큐에 수만 건씩 쌓여 뒷북 처리되지 않도록 큐 크기 제한 또는 상태 병합 처리
(3) 도커 이미지 내 `/etc/my.cnf` 기본 튜닝값 탑재
--------------------------------------------------
[mysqld]
innodb_buffer_pool_size = 512M innodb_log_file_size = 128M innodb_flush_log_at_trx_commit = 2 innodb_doublewrite = 0
--------------------------------------------------
------
```
문의하신 내용에 답변 드리겠습니다.
개발부서에 전달하겠습니다.
감사합니다.
일단 제쪽에서 원인은 찾아서 해결 했습니다.
------------------
[버그 리포트 및 개선 건의]
ipTIME ICC 도커(icc:1_018) 구동 시 일 1.1TB+ 디스크 쓰기 폭주 원인 및 패치 건의
1. 현상 요약
ipTIME ICC(통합 관제 컨트롤러) 도커 컨테이너(icc:1_018) 운영 중, 관리 사이트(Site)가 단 2곳뿐임에도 불구하고 약 9.7일 동안 물리 디스크에 11.4TB(하루 평균 1.17TB, 상시 15~40MB/s)의 비정상적인 디스크 쓰기가 발생하여 심각한 I/O 병목이 발생하는 문제를 확인했습니다.
정밀 분석 결과, 단순 DB 설정 문제를 넘어 plugin_icc.so 내부의 Logs 테이블 처리 쿼리 결함과 이벤트 큐 적체 현상이 결합되어 발생하는 구조적 버그임을 확인하여 개선을 건의드립니다.
2. 정밀 원인 분석 (Root Cause)
[원인 1] 사이트 연결/끊김 시 실행되는 UPDATE 쿼리의 WHERE 조건 누락 및 인덱스 미비 (핵심 원인) 사이트가 재연결될 때마다 plugin_icc.so에서 아래 쿼리를 호출합니다.
-------------------------------------------------- UPDATE `Logs` SET checked = 'System' WHERE type = 'event' AND s_id = '6' AND device_mac IS NULL AND `key` = 'event.site.disconnected'
--------------------------------------------------
- 문제점 A (WHERE 조건 누락): 조건절에 `AND checked = 'Active'` (또는 `checked != 'System'`) 조건이 빠져 있어, 이미 과거에 'System'으로 처리된 수십만 건의 과거 로그까지 매번 업데이트 대상으로 스캔합니다.
- 문제점 B (복합 인덱스 부재): `Logs` 테이블에 단일 컬럼 인덱스(idx_s_id, idx_type, idx_mac, idx_key)만 존재하고 복합 인덱스가 없다 보니, MariaDB 옵티마이저가 매 쿼리마다 수십만 건의 행을 Index Merge 및 풀스캔하며 쿼리 1회당 수십 MB의 디스크 I/O를 발생시킵니다. (1초에 125,000건 이상의 Handler_update 및 11MB/s 이상의 Redo 로그 기록 발생 실측)
[원인 2] `event.site.*` 이벤트 로그의 자동 정리(Retention) 누락 plugin_icc.so 내 로그 정리 쿼리를 확인해 보면 다음과 같습니다.
-------------------------------------------------- DELETE FROM Logs WHERE `key` LIKE 'event.station%%' && date < DATE_SUB(NOW(), INTERVAL 15 DAY)
--------------------------------------------------
- 단말 이벤트(`event.station%`)만 15일 경과 시 삭제하고, 사이트 연결/끊김 이벤트(`event.site.connected`, `event.site.disconnected`)는 삭제 대상에서 누락되어 있습니다.
- 그 결과 네트워크 순단이나 재연결이 반복되면 `Logs` 테이블에 수십만 건(실측 53만 건 이상)의 `event.site.*` 레코드가 무한정 누적되며, [원인 1]의 UPDATE 쿼리 부하를 기하급수적으로 증폭시킵니다.
[원인 3] servd 이벤트 큐(Event Runner)의 무제한 적체 및 지연 처리 현상
- 특정 시점(예: 네트워크 불안정 등)에 다수의 사이트 연결/끊김 이벤트가 발생하면, DB(Logs 테이블)의 UPDATE 처리 속도가 느려지면서 `servd` 프로세스 메모리에 미처리 이벤트 큐가 수십만 건(메모리 120MB 이상) 적체됩니다.
- 이후 사이트 연결이 아예 해제된 상태에서도, `servd`가 일주일 전에 메모리에 쌓인 과거 `connected / disconnected` 이벤트를 초당 수십 건씩 꺼내어 며칠 내내 DB에 계속 INSERT 및 UPDATE를 반복(실측: 10월 19일 시점에 10월 12일 자 이벤트를 계속 DB에 기록)하며 디스크에 하루 1TB 이상의 쓰기를 유발합니다.
- 동일한 사이트(s_id)의 연결/끊김 상태 변경 이벤트는 큐에 중복 적재하지 않고 최신 상태만 반영(Debounce / Coalesce)하도록 개선이 필요합니다.
[원인 4] 컨테이너 내 MariaDB 5.5 기본 설정 파일(/etc/my.cnf) 부재
- 컨테이너 내부에 `/etc/my.cnf` 파일이 없어 MariaDB 5.5 초기 기본값으로 구동됩니다.
* `innodb_log_file_size = 5M` (기본값): Redo 로그가 5MBx2=10MB에 불과해 수 초마다 강제 체크포인트(Checkpoint)가 발생하며 `ibdata1`에 과도한 플러시 발생
* `innodb_buffer_pool_size = 128M` (기본값): `ibdata1` 크기가 커지면 메모리 부족으로 디스크 스래싱 발생
* `innodb_flush_log_at_trx_commit = 1` (기본값): 매 이벤트 쿼리마다 물리 디스크 강제 fsync() 호출
3. 패치 및 개선 제안 사항
(1) plugin_icc.so 내 SQL 쿼리 및 테이블 스키마 개선
- `Logs` 업데이트 쿼리에 미확인 건만 갱신하도록 조건 추가:
UPDATE `Logs` SET checked = 'System' WHERE type = 'event' AND s_id = '%d' AND device_mac IS NULL AND `key` = 'event.site.disconnected' AND checked = 'Active';
- `Logs` 테이블에 복합 인덱스 기본 추가:
ALTER TABLE `Logs` ADD INDEX `idx_site_event` (`s_id`, `type`, `key`, `checked`, `device_mac`);
- 15일 경과 로그 삭제 쿼리에 `event.site.%`도 포함되도록 수정:
DELETE FROM `Logs` WHERE (`key` LIKE 'event.station%%' OR `key` LIKE 'event.site%%') AND date < DATE_SUB(NOW(), INTERVAL 15 DAY);
(2) servd 사이트 연결/끊김 이벤트 디바운싱(Debounce) 또는 큐 중복 제거
- 짧은 시간 내 반복되는 `event.site.connected` / `event.site.disconnected` 이벤트가 큐에 수만 건씩 쌓여 뒷북 처리되지 않도록 큐 크기 제한 또는 상태 병합 처리
(3) 도커 이미지 내 `/etc/my.cnf` 기본 튜닝값 탑재
--------------------------------------------------
[mysqld]
innodb_buffer_pool_size = 512M innodb_log_file_size = 128M innodb_flush_log_at_trx_commit = 2 innodb_doublewrite = 0
--------------------------------------------------
------
```







































