엔지니어링


330개의 “또” 수작업을 코드로 옮기기까지

한국신용데이터
2026-05-27
조회수 291


adb9fea8a1f48.png

“DB 새로 추가됐는데 알람 설정 또 해야해요?”


어느 날 팀에서 이런 말이 나왔습니다. ‘또’라는 단어가 걸렸습니다. 25대 DB, 330개 알람, 전부 Datadog 콘솔에 로그인해서 사람이 하나씩 만든 것들이었으니까요. 


안녕하세요. 한국신용데이터 인프라보안실 SRE팀의 DBA Andrew입니다. 이 글은  바로 그 ‘또’를 없애기까지의 과정입니다.


캐시노트는 220만 사업장의 매출과 경영 데이터를 다루는 서비스입니다. 서비스가 커지면서 DB도 자연스럽게 늘었고, 현재는 Aurora MySQL 14대, RDS MySQL 1대, Aurora PostgreSQL 8대, RDS PostgreSQL 2대로 총 25대를 Datadog으로 모니터링하고 있습니다. 


DB 한 대당 CPU, 메모리, 커넥션, 슬로우 쿼리, 가용성, 디스크, 업타임 등 12~14종의 알람을 걸어야 하니, 모두 합치면 모니터 개수만 330개입니다.Datadog이 정해진 지표를 계속 들여다보다 임계치를 넘으면 담당자에게 알람을 보내는 방식으로요.

문제는 이 330개가 전부 Datadog 콘솔에서 사람의 손으로 만들어져 있었다는 점입니다.


문제는 4가지였습니다. 같은 종류의 모니터인데 DB마다 임계치가 달랐습니다. 어떤 DB는 CPU 80%, 어떤 DB는 90%. 누가 언제 왜 바꿨는지 알 수 없었습니다. 

모든 알람은 하나의 Slack 채널로 몰렸고 P1 장애 알람과 P4 참고  알람이 같은 곳에 쌓였습니다. 새 DB가 추가될 때마다 12개 이상의 항목을 일일이 채워야 했고, 복붙하다 호스트명을 안 바꾸거나 임계치를 잘못 넣는 실수도 잦았습니다. 

모니터 설정 변경에 PR 리뷰가 없으니, 한 사람이 임계치를 올려도 그게 맞는 결정인지 검증할 방법이 없었습니다.

"코드로 관리해야 한다"는 공감대는 팀 안에 있었습니다. 그런데 막상 운영 중인 수백 개 모니터를 건드리는 건 다른 문제였습니다. 잘못 전환하면 장애 감지를 놓칠 수 있으니까요.



설계: 핵심 아이디어 한 줄


4가지 이슈를 동시에 잡아야 했습니다.

  1. 일관성(같은 모니터는 동일한 기준)

  2. 확장성(DB 추가 시 코드 몇줄)

  3. 유연성(DB마다 예외 설정 가능)

  4. 안전성(기존 대비 검증 가능).

여기서 핵심 아이디어가 나왔습니다.

  • 모니터 = 템플릿(what to monitor) × DB 목록(whom to monitor)

이 곱을 Terraform의 반복 문법으로 전개해 봤습니다. 개별 모니터를 하나씩 만들 필요는 없습니다. Terraform은 "어떤 모니터를 어떤 DB에 걸지"를 텍스트로 정의하면 Datadog에 자동으로 반영해주는 도구입니다. 코드로 관리하니 Git에 이력이 남고, PR 리뷰가 가능하고, 똑같은 설정을 복제하기도 쉬워집니다.



실제로 어떻게 작동하는가


구조는 두 파일이 핵심입니다.

모니터 템플릿 파일 (monitor_templates.tf): 모든 모니터의 "설계도"가 모여 있는 파일입니다. CPU, 메모리, 슬로우 쿼리 같은 모니터의 원형을 정의합니다. 쿼리와 메시지 안에는 빈칸(${db_id}, ${host} 같은 자리 표시자)을 넣어두고, 실제 모니터를 만들 때 각 DB 정보가 빈칸에 들어갑니다.

DB 목록 파일 (db_mysql.tf / db_postgresql.tf): 모니터링 대상 DB 25대의 명단입니다. DB마다 엔진 타입(Aurora MySQL인지 PostgreSQL인지)과 인스턴스 크기(서버 사양)를 지정합니다. 필요하면 DB마다 두 가지 예외 설정을 추가할 수 있습니다.

  • threshold_overrides (임계치 예외 설정): 해당 DB만 임계치를 기본값과 다르게 적용하고 싶을 때

  • disabled_monitors (특정 모니터 끄기): 해당 DB에는 특정 모니터를 적용하지 않을 때


인스턴스 타입별 임계치 자동 매핑도 여기서 해결했습니다.db.t4g.medium과 db.r6i.12xlarge에 같은 메모리 임계치를 쓰면 의미가 없습니다. 템플릿에 인스턴스 타입별 임계치 맵을 정의해두면, 각 DB의 instance_type에 맞는 값이 자동 적용됩니다. 기본값의 일관성을 유지하면서도 예외 처리가 가능한 구조입니다.



Python 스크립트 기반 검증 도구 활용


새 모니터가 기존과 다르게 동작하면 두 가지 위험이 생깁니다. 멀쩡한 상황에 오알람이 울리거나, 반대로 기존 알람이 잡아주던 장애를 새 알람이 놓치는 경우입니다. 둘 다 즉각 대응에 실패할 수 있는 치명적인 상황이라, 이를 막기 위해 아래 4개의 검증 도구를 만들어 활용했습니다.

  • find_notification_handles.py: Datadog API로 기존 모니터에서 DB별 알림 수신자(@handle)를 추출

  • compare_monitors.py: 기존 수동 모니터와 Terraform 모니터의 쿼리, 임계치, 메시지를 항목별로 비교

  • mute_monitors.py: 전환 기간 중 기존 모니터를 일괄 뮤트/언뮤트

  • cleanup_old_monitors.py: 검증 완료 후 기존 수동 모니터 정리

compare_monitors.py는 단순 문자열 비교가 아니라 쿼리 구조, 임계치 값, 메시지 내 @handle 포함 여부까지 비교하도록 만들었습니다. 이 스크립트가 찾아낸 차이를 하나씩 맞춰가며 정합성을 확보했습니다.

마이그레이션은 5단계로 진행했습니다.

  1. [TEST] prefix를 붙여 Terraform 모니터를 기존 모니터와 나란히 생성

  2. compare_monitors.py로 기존 대비 쿼리/임계치/메시지 비교 및 수정

  3. 일정 기간 양쪽 모니터를 동시에 운영하며 알림 정합성 확인

  4. 기존 모니터 음소거 → Terraform 모니터에서 [TEST] prefix 제거

  5. 정상 결과 확인 후 기존 모니터 삭제

이렇게 단계적으로 진행한 덕분에 수백 개 알람을 전환하는 동안 알람 누락 없이 마이그레이션을 끝낼 수 있었습니다.



시도했지만 막혔던 사항


Aurora 공통 메트릭의 엔진 격리 실패

Aurora MySQL 전용으로 만든 Temp Storage 모니터가 Aurora PostgreSQL DB에도 뜨는 문제가 생겼습니다. HLL(rollback_segment_history_list_length)이나 Aurora Volume(aurora_volume_bytes_left_total)은 InnoDB 전용 메트릭이라 PostgreSQL에서 데이터가 없어 문제가 없었는데, Temp Storage가 쓰는 free_local_storage는 Aurora 엔진 공통 메트릭이라 PostgreSQL 호스트에도 모니터 그룹이 만들어졌습니다.

쿼리 레벨에서 해결을 시도했지만 전부 막혔습니다. host:FQDN 쉼표 나열은 API가 거부했고, dbinstanceidentifier 필터는 No Data가 발생했고, by {dbinstanceidentifier} 그룹핑은 DBM UI에 안 나왔습니다.

결국 모니터 코드단에 engine_filter = ["aurora-mysql"]을 설정해 Terraform 리소스 생성 단계에서 엔진을 필터링하는 방식으로 해결했습니다. Datadog 쿼리 문법으로 풀 수 없는 문제를 IaC 레벨에서 우회한 케이스입니다.



가장 뼈아팠던 실수


이름에 [TEST] prefix가 붙어 있으니 실제 알람으로 받아들여지지는 않을 거라고 막연히 생각했고, Downtime 설정 없이 배포했습니다. 하지만, 예상과는 다르게 새벽에 기존과 다른 임계치가 적용된 [TEST] 모니터들이 대량의 오알람을 쏟아냈고, 팀원분들께 피해를 입었습니다.


특히 PostgreSQL DB의 max transaction id처럼 수치가 계속 오르락내리락하는 메트릭은 warn 기준을 넘었다 회복했다를 반복하면서 알림 노이즈가 심했습니다.

Datadog의 Downtime 기능을 쓰면 alert/warn 알림 자체를 차단할 수 있었습니다. 다만 그러면 모니터 항목을 일일이 검색해가며 확인해야 하는 불편함이 있어 넘어갔던 건데, 제 편의 때문에 팀 전체에 피해를 끼친 건 분명한 잘못이었습니다.


이후로는 대규모 모니터 변경 전에 Downtime 설정을 체크리스트 항목으로 고정해뒀습니다. 이름에 [TEST]를 붙이는 건 안전장치가 아닙니다.



결과


구분

수량

설명

모니터링 대상 DB

25대

Aurora MySQL 14 / RDS MySQL 1 / Aurora PostgreSQL 8 / RDS PostgreSQL 2

모니터 템플릿 종류

14종

CPU, 메모리, 커넥션, 슬로우 쿼리, 디스크, 업타임 등

총 모니터링 포인트

330개

25대 × 12~14종, 엔진 필터 적용


새 DB 추가 시 변경량:

{

  db_id         = "aurora-mysql-new-service-prod-a-prod01"

  engine       &nbsp= "aurora-mysql"

  instance_type = "db.r6g.large"

  tags         &nbsp= ["env:prod", "type:db", "engine:aurora-mysql"]

}


이 한 블록을 추가하면 14종의 모니터가 자동 생성됩니다. 330개를 344개로 늘리는 데 걸리는 시간은 이 코드를 쓰고 PR을 올리는 시간뿐입니다.

330개의 알람을 코드로 옮기고 나니, 자연스럽게 다음 질문이 생겼습니다. DB 레이어에서 시작한 이 패턴을 애플리케이션 레이어로, ElastiCache와 ECS로 확장하면 어떨까. 운영 이력을 쌓아 임계치 튜닝을 자동화할 수 있을까.

330개의 알람을 손으로 관리하던 시절, 그 방어선은 사람의 근성에 의존하고 있었습니다. 이제는 코드가 그 자리를 대신합니다.



지금, KCD는 사장님의 더 나은 내일을 위해 함께할 동료를 찾고 있어요.

✨영입 중인 포지션 확인하기

✨KCD가 더 궁금하다면?




2 0

월간 인기글