Zabbix 버전 업그레이드 시 'DB 변환 및 모니터링 중단'을 AI로 과거화하기: AL2(MariaDB) →
요약
본 기사는 Zabbix 메이저 버전 업그레이드 시 발생하는 DB 스키마 변환 및 모니터링 중단 문제를 다룹니다. AI 에이전트와 결합하여 5년 치 데이터를 손실 없이 이관하는 과정을 설명하며, 실제로는 건전한 인프라 설계 원칙(Blue/Green 배포)과 정교한 스크립팅으로 가능함을 강조합니다.
핵심 포인트
- Zabbix 업그레이드 시 모니터링 중단 및 데이터 손실을 방지하는 방법을 제시함.
- Blue/Green 배포 전략과 온라인 덤프, 차분 데이터 병합(Upsert) 기법이 핵심 원리임.
- AI는 마법이 아닌, 복잡한 인프라 설계와 스크립팅의 조합으로 문제를 해결했음.
Zabbix의 메이저 버전 업그레이드에서 인프라 엔지니어가 항상 골머리를 앓는 부분이 바로 **'DB 스키마 변환 및 히스토리 이관에 따른 모니터링 중단'**입니다.
게다가 OS를 새로 하거나 데이터베이스 엔진까지 변경이 겹치면, '수 시간의 유지보수 중단을 감수할 것인가' 아니면 '과거 히스토리 데이터를 포기하고 신규 구축할 것인가'라는 두 가지 선택지 앞에서 고민하는 것이 이전의 상식거리였습니다.
이번에는 5년 치(약 2,322만 행)의 자가 기상 데이터를 축적해 온 개인 Zabbix 환경을 대상으로, 에이전트형 AI(Gemini / Antigravity + TypeSafe AI Jev)와 페어를 이루어 다음의 **'삼중 이관 + 디스크 축소'**를 동시에 실시했습니다.
- Zabbix 본체 메이저 업그레이드: Zabbix 5.0 LTS (5.0.14) → Zabbix 7.0 LTS (7.0.31)
- OS/CPU 아키텍처 개편: Amazon Linux 2 (
x86_64/t3a.small) → Amazon Linux 2023 (ARM64 Graviton/t4g.small) - 이종 DB 엔진 간 이관: MariaDB 10.2 (MySQL 호환) → PostgreSQL 15
- 루트 EBS 볼륨 축소: 15GB (사용률 96%) →
gp310GB (사용률 49%)
결과적으로, 모니터링 중단 0초, 데이터 손실 0초로 모든 데이터 이관을 완수했으며, 실제 장비 리허설의 철저한 검증을 통해 '과거 자신이 6~8년 전에 심어두었던 설정의 함정'까지 특정할 수 있었습니다.
본 기사에서는 그 설계 및 검증, 무중단 이관의 전 과정과 AI 시대 인프라 엔지니어의 역할에 대해 공유합니다.
먼저 비밀을 풀자면, 이번에 실시한 '모니터링 중단 0초 버전 업그레이드 & 이종 DB 이관'이 AI만이 할 수 있는 마법을 사용해서가 아닙니다.
진행하는 것 자체는 다음과 같은 매우 건전한 인프라 설계의 조합입니다.
- 현재 장비(Blue)를 가동한 상태로, 신규 환경(Green)을 병행 구축한다.
- 기준 시점 $T_0$를 기록하고, 락킹 없는 온라인 덤프로 신규 환경의 임시 DB에 데이터를 선행 전송한다.
- 신규 환경 측에서 Zabbix 5.0 → 7.0의 DB 스키마 변환과, MariaDB → PostgreSQL의 데이터 변환을 완료한다. - 전환 직전에 현재 장비의 Zabbix를 중단(시점 $T_1$)하고, 이관 작업 중에 발생한 수천 행의 차분 데이터만 추출하여 신규 환경의 PostgreSQL에
ON CONFLICT로 공집합 병합(백필)한 후 Elastic IP를 교체한다.
즉, 원리적으로는 AI 의존도가 없는 (인간의 수작업과 스크립트 작성으로도 가능한) 방법입니다.
그렇다면 왜 지금까지 현장에서는 이것이 거의 이루어지지 않았고, '버전 업그레이드 때는 모니터링을 중단하거나' 혹은 '과거 데이터를 버리는 것'이 정석이었던 걸까요?
이유는 간단합니다. '단 한 번의 이관을 위해, 이종 DB 간의 타입 변환이나 인덱스 재구축, 나아가 이관 중에 발생한 차분 데이터의 Upsert 스크립트까지 직접 짜서 검증하는 공수(ROI)가 전혀 맞지 않았기 때문입니다.'
저 자신도 지금까지 Zabbix를 Amazon Linux 환경에서 쾌적하게 사용하기 위해, 리포지토리 정비나 구축 자동화에 매진해 왔습니다.
당시에는 Amazon Linux / Amazon Linux 2용 공식 패키지가 존재하지 않았기 때문에, 공식 SRPM의 릴리스를 수동으로 확인하고, .spec 파일을 직접 조정하여 오리건 리전의 EC2에서 빌드하고 S3에 배치하는 독자적인 리포지토리를 운영했습니다.
나아가, 그 독자적인 리포지토리를 사용하여 EC2의 UserData에 셸 스크립트를 흘려 넣는 것만으로 약 2분 만에 튜닝된 Zabbix Server를 전자동 구축할 수 있는 메커니즘도 공개했습니다.
하지만, 이러한 기존의 셸 스크립트에 의한 자동화는 어디까지나 **'아무것도 없는 환경을 정형 절차로 제로부터 올리는 것'**에 한정된 것이었습니다.
오랫동안 가동되어 2,300만 행 이상의 데이터가 쌓인 환경을 OS와 DB 엔진까지 변경하면서도 중단 없이 마이그레이션하는 것 같은 '비정형적이고 일회성인 복잡한 퍼즐'은, 사전에 스크립트를 개발하는 비용이 너무 커서 시도조차 할 수 없었습니다.
AI 에이전트의 본질적인 가치는 바로 이 '이론적으로는 가능하지만, 인간이 수작업으로 처리하기에는 지나치게 번거로웠던 무중단 마이그레이션 파이프라인'을 설계/구현/자가 복구하는 비용을 한순간에 0에 가깝게 줄여 현실적인 선택지로 만들었다는 점에 있습니다.
마이그레이션 작업을 시작하기 전에, 먼저 AWS CLI와 SSM Run Command를 사용하여 현재 환경(ZabbixServer5.0.amzn2)에 대한 비파괴적 조사를 AI가 실행하게 했습니다.
기쁜 우연이라고 할 수 있거나 시대의 진화로 볼 수 있는 점은, Zabbix 7.0 LTS에서 Amazon Linux 2023용 공식 리포지토리(x86_64 및 Graviton용 aarch64)가 repo.zabbix.com에서 공식 제공되고 있다는 것입니다.
과거에는 수동으로 릴리스를 확인하고, WinSCP와 EmEditor로 .spec 파일의 %is_amzn2 분기를 손수 조정하여 빌드했던 고생은 완전히 과거가 되었으며, 표준 dnf 패키지 관리로 깨끗하게 회귀할 수 있음을 확인할 수 있었습니다.
Zabbix 자체 모니터링을 통해 현재 장비의 15GB 루트 EBS(gp2) 여유 공간이 점차 줄어들어 거의 남지 않았다는 것은 이전부터 인지하고 있던 사실이었습니다 (애초에 이번 교체 결정을 내린 큰 동기 중 하나이기도 합니다).
마이그레이션 계획을 세우면서, 현재 장비의 디스크/메모리 상태와 MariaDB 10.2 내부 구성을 다시 확인했습니다.
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p1 15G 15G 751M 96% /
total used free shared buff/cache available
...
15GB 루트 EBS 중 96%가 사용되어 여유 공간이 751MB밖에 남지 않았기 때문에, 마이그레이션을 위해 구 서버의 로컬 디스크에 mysqldump를 작성하는 것조차 불가능한 상태였습니다.
마이그레이션 설계 과정에서 MariaDB 10.2 내부 테이블별 크기를 정확하게 집계해 본 결과, 논리 데이터량은 총 약 2.98GB에 머물러 있음을 확인할 수 있었습니다. 하지만 여기서 실제 디스크 소비량과의 큰 모순에 직면합니다.
| 테이블명 | 행 수 (table_rows ) | 데이터 용량 (data_mb ) | 인덱스 (idx_mb ) | 합계 (total_mb ) |
|---|---|---|---|---|
history | 16,248,930 | 1,323.02 MB | 481.03 MB | 1,804.05 MB |
history_uint | 5,952,444 | 487.02 MB | 161.31 MB | 648.33 MB |
sessions | 431,501 | 46.61 MB | 141.98 MB | 188.59 MB |
auditlog | 568,088 | 38.08 MB | 37.09 MB | 75.17 MB |
trends | 781,964 | 55.13 MB | 0.00 MB | 55.13 MB |
history_text | 41,060 | 47.05 MB | 2.02 MB | 49.06 MB |
history_log | 144,631 | 36.06 MB | 4.02 MB | 40.08 MB |
trends_uint | 262,650 | 40.06 MB | 0.00 MB | 40.06 MB |
events | 265,865 | 20.06 MB | 18.06 MB | 38.13 MB |
| DB 전체 합계 | 약 2,322만 행 | — | — | 2,982.39 MB |
DB 내부의 실제 데이터는 총 약 2.98GB (게다가 sessions의 188MB는 불필요한 과거 세션 정보)밖에 없었음에도 불구하고, /var/lib/mysql 전체로는 9.3GB라는 디스크를 소비하고 있었습니다(※이 원인은 후속 리허설 검증에서 밝혀지게 됩니다).
DB의 실데이터가 3GB 미만이라면, 현재 사용 중인 15GB EBS를 유지할 필요조차 없습니다.
AWS의 EBS는 '나중에 온라인 확장은 언제든지 할 수 있지만, 축소는 마이그레이션 시점에만 가능하다' 라는 특성이 있기 때문에, 신규 환경의 루트 EBS는 **10GB (gp3)**로 축소하고 싶었습니다.
여기서 판단의 오류를 없애기 위해 초고속 판정 모델인 **TypeSafe AI (Jev)**에 조사 메트릭과 제약 조건을 입력하여 아키텍처와 마이그레이션 방식의 확률 판정을 받았습니다.
| 설계/판단 항목 | Jev 채택 결과 (확률 / 스코어) | 채택 이유/아키텍처상의 목표 |
|---|---|
| OS/인스턴스 선정 | AL2023 ARM64 (t4g.small) 확률 82% (Confidence: 0.73) | 공식 aarch64 RPM을 이용할 수 있고, t3a.small 대비 약 20% 비용 절감 및 메모리 2GB 확보 가능. |
| 데이터 스토어 배치 | EC2 단독 포함 (RDS 미사용) RDS 부적합 확률 88% | 개인 환경이므로 RDS 분리는 비용 과다. gp3 (3,000 IOPS)로 변경하여 I/O 성능도 30배 향상. |
| 10GB EBS 축소 전략 | 본방 10GB + 작업용 15GB 임시 EBS 병용 확률 100% (Confidence: 0.99) | 10GB 단독으로 임시 MariaDB와 본방 PostgreSQL을 공존시키면 여유 공간이 고갈되고, 불필요한 쓰기 블록이 AWS Backup 스냅샷 용량을 부풀립니다. 마이그레이션 중 2시간 동안만 15GB 임시 EBS(/mnt/scratch, 과금 약 0.6엔)를 연결하여 작업 영역을 완전히 분리합니다. |
| 무정지/누락 제로 방식 | 선행 스트림 전송 + 차분 백필 확률 100% (Confidence: 0.99) | 구 기기의 디스크를 1바이트도 사용하지 않고 네트워크를 통해 임시 MariaDB로 흘려보내고, 7.0 업그레이드 및 PG 변환 후 마이그레이션 중의 차분 데이터만 병합합니다. |
| 시계열 확장 (초기 판정) | TimescaleDB 도입 권장 (책상 위 단계) Score 1.94 / 2.0 (확률 95%) | ※책상 위의 일반론에서는 '시계열 데이터 = TimescaleDB'라고 고평가되었으나, 다음 장의 리허설 검증에서 이 판정이 뒤집히게 됩니다. |
AI 덕분에 작업 시간이 몇 분으로 단축되는 시대이기에, 무작정 본방을 전환하는 도박은 하지 않습니다.
우선 본방기 (52.194.38.180)에 일절 손대지 않고, 동일 서브넷 내에 검증용 신규 EC2 (ZabbixServer7.0.al2023-rehearsal)를 띄워 실데이터를 한 바퀴 흘려보내는 **마이그레이션 리허설 (사전 검증)**을 실시했습니다.
리허설 자체는 채 30분도 안 되어 완료되었지만, 정말 중요한 것은 여기서부터입니다.
저는 리허설 환경을 바로 삭제하지 않고, **'현행기와 리허설기가 지금 실제로 어떤 상태인지, 예상 결과와 실측 결과의 차이는 무엇인지, 왜 그 차이가 발생했는지'**를 AI와 함께 철저하게 대조했습니다.
먼저 검증한 것은 사전 설계에서 Jev가 1.94 / 2.0로 높게 권장했던 TimescaleDB의 적합성입니다.
리허설기에 들어간 Zabbix 7.0 포함 /usr/share/zabbix-sql-scripts/postgresql/timescaledb/schema.sql과, 마이그레이션된 config/items 테이블의 보존 기간 설정을 교차 분석한 결과, 책상 위의 일반론으로는 절대 파악할 수 없는 중대한 함정이 발견되었습니다.
timescaledb/schema.sql의 끝부분에는 다음 SQL이 하드코딩되어 있습니다.
UPDATE config SET db_extension='timescaledb', hk_history_global=1, hk_trends_global=1;
TimescaleDB는 시간 청크 단위로 테이블을 일괄 삭제(drop_chunks())하는 아키텍처이므로, 활성화되는 순간 hk_history_global=1 및 hk_trends_global=1가 강제 적용됩니다.
즉, 아이템별로 설정했던 보존 기간(items.history, items.trends)이 모두 무시되고, 모든 아이템에 일률적인 글로벌 보존 기간으로 강제 덮어쓰여지는 것입니다.
반면, 제 환경에서 실제로 설정되어 있던 아이템별 보존 기간을 집계해 보니 다음과 같은 뚜렷한 차이가 있었습니다.
글로벌 설정 (:config
)
hk_history_global = 0
(90d
),
hk_trends_global = 0
(365d
) -
히스토리 개별 설정 (:items.history
)-
1w
(7일 보존): 635개 아이템 (OS 모니터링 등 고빈도 아이템)
30d / 31d: 305개 아이템 -
90d: 445개 아이템 (기상 데이터 등)
트렌드 개별 설정 (:items.trends
)-
90d: 144개 아이템 -
365d
(1년 보존): 1,054개 아이템 -
:1827d
(5년 보존): 18개 아이템 (거실 온도/습도, 북측/남측 실외 온도, 머신룸 온도/습도, 침실 온도/습도, 자택 기압)
만약 책상 위의 AI 추천을 맹신하여 그대로 TimescaleDB를 활성화했다면, 다음과 같은 궁극의 두 가지 선택지 중 하나를 강요받았을 것입니다.
현재 글로벌 설정(:hk_trends = 365d
)을 유지한 채 활성화할 경우
Housekeeper가 작동하는 순간, 1년보다 오래된 소중한 기상 트렌드 데이터(과거 4년간의 거실 온도나 기압 등)가 drop_chunks()로 일괄 삭제됩니다. -
기상 데이터를 보호하기 위해 글로벌 설정을:hk_trends = 1827d
(5년), hk_history = 90d
으로 상향 조정할 경우
원래 7일 만에 버리던 635개의 고빈도 OS 모니터링 히스토리가 모두 90일간(약 13배) 강제 보존되고, 1,198개의 비기상 트렌드 역시 5년간 보존되어 불필요 데이터로 인해 디스크가 오히려 비대화합니다.
게다가, Packagecloud(el/9)의 TimescaleDB 리포지토리에는 x86_64용 패키지만 존재하여, aarch64 (Graviton) 환경에서는 소스에서 cmake로 계속 빌드해야 한다는 사실도 밝혀졌습니다.
'직접 만든 RPM의 유지보수에서 벗어나고 싶다'는 이번 목적에 비추어 볼 때, 이는 본말전도입니다.
이 실측 팩트를 컨텍스트로 **TypeSafe AI (Jev)**에 재입력하여 판정하게 했더니, 초기 판정과 정반대로
**확신도 0.99(확률 100%)로
원래 압축되어 있어야 할 MariaDB가 왜 표준 PostgreSQL 15보다 용량이 작아지는 걸까요? 과거의 MariaDB 설정에 뭔가 결함이 있었던 것은 아닐까요?
이 의문을 AI에게 던지고, 현행 시스템과 리허설 시스템 양쪽을 철저히 분석한 결과, 8년 이상, 심지어 6년 이상 지난 두 가지 충격적인 단서를 확보했습니다.
현행 시스템의 MariaDB에서 information_schema.TABLES의 CREATE_OPTIONS(DDL로 지정된 옵션)와 실제 물리 포맷을 나타내는 ROW_FORMAT 컬럼을 비교한 결과는 다음과 같습니다.
TABLE_NAME ENGINE ROW_FORMAT CREATE_OPTIONS data_mb idx_mb
history InnoDB Compact row_format=COMPRESSED key_block_size=8 1323.02 481.03
history_uint InnoDB Compact row_format=COMPRESSED key_block_size=8 487.02 161.31
...
놀랍게도, CREATE_OPTIONS에는 row_format=COMPRESSED key_block_size=8로 기록되어 있음에도 불구하고, 실제 물리 포맷(ROW_FORMAT)은 모두 Compact(비압축) 상태였습니다.
더 나아가 /var/lib/mysql/zabbix/ 디렉토리를 확인했을 때, 개별 .ibd 파일은 단 하나도 존재하지 않았고, 2018년~2020년 당시의 .frm(정의 파일)만 있었습니다. 왜 이런 일이 발생했을까요?
이 환경이 처음 구축된 2018년 10월( Amazon Linux 2 표준 MariaDB 5.5.64 시기)에는 기본값이 innodb_file_per_table = OFF(공유 테이블스페이스 /var/lib/mysql/ibdata1에 저장)였으며, innodb_file_format = Antelope였습니다.
InnoDB 사양상, 공유 테이블스페이스 내부나 Antelope 포맷에서는 ROW_FORMAT=COMPRESSED를 사용할 수 없으므로, 사전에 innodb_file_per_table = 1과 Barracuda를 활성화해야 합니다.
그런데 당시 기본값이 innodb_strict_mode = OFF였기 때문에, ALTER TABLE ... ROW_FORMAT=COMPRESSED 명령을 실행해도 에러로 중단되지 않고 경고(Warning)만 발생하며 .frm 옵션 문자열에 기록만 남기고, 실제 테이블은 비압축의 Compact 상태 그대로 ibdata1 (3.2GB) 내부에서 재구축되었던 것입니다.
즉, 저는 8년 동안
이것을 보고 나도 모르게 이상한 웃음이 나왔습니다.
앞서 말씀드린 2020년의 제 Qiita 기사 「## Zabbix 5.0에서의 주의점」에서, 저는 이렇게 작성했었습니다.
amazon-linux-extras install lamp-mariadb10.2-php7.2 epel php7.2 -y
Zabbix 5.0에 필수화된 PHP 7.2를 넣기 위해 이 amazon-linux-extras 명령어를 실행했을 때, 함께 mariadb-server가 5.5.64 ➔ 10.2.10으로 업데이트되었고, Amazon Linux 2 측의 패키지 의존 관계(Dep-Install)로서 mariadb-rocksdb-engine이 자동으로 설치되었습니다.
게다가 이 패키지는 단순히 설치되는 것만으로도 /etc/my.cnf.d/rocksdb.cnf에 plugin-load-add=ha_rocksdb.so를 배치하여 기본적으로 활성화하는 사양이었습니다. 그 결과, 누구도 알아차리지 못한 채 6년 동안 백그라운드에서 계속 구동하며 6.1GB나 되는 덤프(LOG.old.*)를 쌓아두고 있었다는 결말이었습니다. 진실을 알면 대책은 명확합니다.
PostgreSQL 15에서는 고정 길이 수치 테이블(history / history_uint)에 대해서 주로 **히프 헤더 경량화(고밀도 배치)와, PostgreSQL 13 이후 도입된 B-tree 인덱스의 중복 제거(Deduplication)**가 표준으로 기능하기 때문에, 동일한 itemid가 반복되는 시계열 데이터의 크기가 극적으로 줄어듭니다.
더 나아가 가변 길이 텍스트 테이블(history_text, history_log, history_str 또는 items 등`)에 대해서는 표준 TOAST 압축 알고리즘을 고속의 lz4로 변경하고, 기본값으로 약 2KB(2,040바이트)를 넘지 않으면 발동하지 않는 압축 임계치를 128B로 낮추었습니다.
toast_tuple_target = 128 (128바이트 이상에서 압축 발동) | 테이블명 | 구 MariaDB 10.2 (명목 COMPRESSED / 실체 Compact) | 신 PostgreSQL 15 (TOAST lz4 + toast_tuple_target=128 + B-tree 중복 제거) |
| 삭감률・기술적 요인 | |||
|---|---|---|---|
history | 1,804.05 MB | 1,384.52 MB (Data 908MB + Idx 476MB) | -23.3% (히프 고밀도 배치 + B-tree 중복 제거) |
history_uint | 648.33 MB | 466.62 MB (Data 296MB + Idx 171MB) | -28.0% (동상) |
history_text | 49.06 MB | 16.69 MB | -66.0% (TOAST lz4 + 128B 임계치 압축) |
history_log | 40.08 MB | 4.22 MB | -89.5% (TOAST lz4 + 인덱스 최적화) |
items | 2.66 MB | 0.94 MB (960 kB) | -64.7% (toast_tuple_target=128 효과) |
| DB 전체 합계 | 2,982.39 MB(+ ibdata1 / #rocksdb로 실소비 9.3GB ) | 2,099.80 MB (약 2.05 GB) | 논리 크기 -30.1% 삭감실 디스크 소비 -77% 삭감 |
게다가 리허설에서는 데이터 투입 후에 ALTER TABLE과 VACUUM FULL을 시도했기 때문에 일시적으로 WAL이 부풀었지만, 이 지식을 활용하여 본 배포에서는 **
새 본 서버(t4g.small)는 처음부터 운영용 10GB gp3 루트 EBS와, 작업 시에만 사용하는 15GB 임시 EBS (gp3/mnt/scratch`, 2시간당 AWS 요금은 약 0.6엔) 두 개를 연결하여 부팅했습니다.
스키마 승격용 임시 MariaDB는 모두 /mnt/scratch/mysql 위에서 작동하므로, 운영용 10GB 루트 EBS에는 처음부터 깨끗한 PostgreSQL 15 데이터만 기록됩니다.
또한, 구기에서 신기의 임시 MariaDB로 데이터를 전송할 때, AI는 보안 그룹을 전혀 변경하지 않고, Zabbix Trapper용으로 이미 열려 있던 10051/TCP 포트를 통해 신기 임시 MariaDB를 리스닝하게 하여 네트워크를 거쳐 직접 스트림 전송하는 방식을 채택했습니다.
T0=$(date +%s)
echo $T0 > /tmp/zabbix_migration_t0.txt
DBPASS=$(grep -E '^DBPassword=' /etc/zabbix/zabbix_server.conf | cut -d= -f2)
...
이로써 남은 공간이 751MB밖에 없는 구기 디스크를 1바이트도 소비하지 않고, 모니터링을 1초도 중단하지 않은 채 약 11분(664초) 만에 전체 베이스라인 전송을 완료했습니다.
이종 DB 간 마이그레이션의 철칙은 **'마이그레이션 원본과 마이그네이션 대상 Zabbix DB 스키마 버전을 완전히 일치시킨 후에 데이터를 옮기는 것'**입니다.
신기의 임시 MariaDB에 대해 zabbix-server-mysql
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기