MeetBack 2026.08.31 6 min read

cAdvisor Dashboard와 Telegram Alert까지 연결한 운영 가시성

cAdvisor 지표와 Telegram 알림을 연결했다. 파이프 때문에 생긴 curl 오류와 Grafana 비밀번호 변경 문제를 확인하고, 알림 발생·해제와 남은 관측 항목을 정리했다.

프로젝트 기간: 2026-08-18 ~ 2026-09-02
글 범위: cAdvisor Dashboard와 Alertmanager Telegram 알림

node_exporter로 호스트 전체의 CPU와 메모리는 확인할 수 있었다. 하지만 어느 컨테이너가 자원을 사용하는지는 더 세분화해서 볼 필요가 있었다. Swarm 서비스와 모니터링 컨테이너를 함께 운영하고 있어 cAdvisor를 추가했다.

Docker 호스트마다 cAdvisor를 실행하고 Prometheus 수집 대상으로 등록했다. Swarm 5개 노드에는 노드별로 배치하고, 클러스터 밖의 모니터링 서버에는 별도 컨테이너로 실행했다.

배포 전에 host 조건을 확인했다

cAdvisor는 Docker 메타데이터, cgroup, 호스트 파일시스템 일부를 읽어야 한다. 배포 전에 각 서버에서 아래 조건을 확인했다.

  • Docker Engine 상태와 루트 디렉터리
  • cgroup v2 사용 여부
  • /dev/kmsg 접근 가능 여부
  • 서비스 주소와 수신 포트
  • 모니터링 서버에서 수집 주소까지 접근 가능 여부

필요한 호스트 경로를 읽기 전용으로 연결하고 재시작 정책과 healthcheck를 설정했다. 수집 포트는 내부 서비스망 주소에만 공개했다. 최종 방화벽 작업에서는 모니터링 서버에서만 접근하도록 제한할 계획이었다.

Metric이 보이는데 curl: (23)이 발생했다

cAdvisor 지표 중 첫 번째 일치 항목만 보려고 다음 명령을 실행했다.

curl -fsS http://<node>:8080/metrics |
grep -m 1 '^container_cpu_usage_seconds_total'

지표 한 줄이 출력됐지만 마지막에는 오류가 붙었다.

curl: (23) Failure writing output to destination

grep -m 1은 첫 번째 일치를 찾으면 종료한다. curl은 남은 응답을 계속 쓰다가 닫힌 파이프 때문에 오류 23을 반환했다. cAdvisor 응답과 파이프 처리를 구분해서 봐야 했다.

상태 확인 요청과 지표 확인을 따로 실행했다.

curl -fsS http://<node>:8080/healthz
curl -fsS http://<node>:8080/metrics -o /tmp/cadvisor.metrics
grep -m 1 '^container_cpu_usage_seconds_total' /tmp/cadvisor.metrics

컨테이너는 healthy였고 /healthz는 ok를 반환했다. 저장한 지표 파일에 시계열이 있었으며 Prometheus 대상도 UP이었다. 이 결과를 확인하고 서비스 재시작이 필요한 장애는 아니라고 판단했다.

Container dashboard는 node와 workload를 함께 봤다

Grafana 화면에는 아래 항목을 배치했다.

  • 노드별 실행 중인 컨테이너 수
  • 컨테이너 CPU 사용량
  • working set 메모리와 제한량 대비 비율
  • 네트워크 수신·송신량
  • 파일시스템 읽기·쓰기
  • 재시작과 지표 수집 상태

시스템 컨테이너 중에는 Swarm 서비스 이름이나 Compose 레이블이 없는 경우도 있었다. 특정 레이블이 항상 있다고 가정하지 않고 name, id, 노드 레이블을 함께 사용할 수 있도록 쿼리를 구성했다.

루트 cgroup 지표를 App 컨테이너 집계에 섞으면 호스트 전체 사용량과 중복될 수 있었다. 서비스 패널에서는 이미지가 비어 있거나 루트 ID인 항목을 제외하고, 호스트 관점의 패널에서는 따로 유지했다.

Grafana 비밀번호를 .env에서 바꿔도 401이 계속됐다

대시보드를 API로 등록하던 중 관리자 비밀번호를 바꾸려고 .env를 수정하고 컨테이너를 재시작했다. 그런데 Basic Auth API 응답은 계속 HTTP 401이었다.

Grafana의 계정은 영구 저장된 DB에 있었다. 관리자 초기화 환경변수는 DB를 처음 만들 때 쓰이므로 SQLite DB가 이미 있는 상태에서 .env만 수정해도 저장된 비밀번호는 바뀌지 않았다.

컨테이너의 Grafana CLI로 계정 비밀번호를 변경한 뒤 API health와 search 요청을 다시 확인했다.

docker exec <grafana> \
  grafana cli admin reset-admin-password '<new-secret>'

실제 비밀번호는 문서나 셸 기록에 남기지 않았다. 이후에는 컨테이너에 주입한 초기 설정과 애플리케이션 DB에 저장된 값을 따로 확인했다.

Alert rule을 상태별로 나눴다

Prometheus 알림은 아래 세 그룹으로 나눴다.

Basic
  → scrape target down

MySQL
  → exporter/server down, replication thread, lag,
     read-only role, connection, backup freshness

NFS
  → exporter/service down, mount missing, read-only,
     capacity, inode

대응을 준비해야 하는 상태는 warning, 서비스나 데이터 경로에 위험이 있는 상태는 critical로 구분했다. 순간적으로 값이 튀었다는 이유로 바로 알림이 가지 않도록 일정 시간 유지되는 조건도 넣었다.

다만 warning과 critical의 알림 이름이 다르면 Alertmanager의 억제 규칙이 중복 알림을 예상대로 처리하지 못할 수 있다. 같은 사건을 묶을 레이블 설계는 실제 알림을 보면서 더 정리할 부분으로 남겼다.

Telegram credential은 file로 분리했다

Alertmanager 알림은 Telegram으로 받도록 했다. 봇 토큰과 채팅 ID는 compose.yaml이나 Git에 쓰지 않고 접근을 제한한 파일에 보관했다. 컨테이너의 /run/secrets 아래에 읽기 전용으로 연결하고 설정에는 파일 경로만 참조했다.

아래 순서로 전송과 알림 상태를 확인했다.

  1. Telegram Bot API로 직접 메시지 전송
  2. Alertmanager 설정 문법 검사
  3. 실행 중인 Alertmanager가 읽은 설정 확인
  4. 수신 설정에서 Secret 파일 경로를 읽는지 확인
  5. 비핵심 수집 대상을 의도적으로 중단해 pending → firing 확인
  6. 대상 복구 후 resolved 확인

Bot API 전송은 토큰과 채팅 대상이 맞는지 확인하는 단계였다. 그다음 실제 수집 상태 변화가 Prometheus와 Alertmanager를 거쳐 알림으로 이어지는지도 확인해야 했다. 단계별 결과를 따로 기록했다.

운영 가시성의 현재 한계

이 단계에서는 노드, 컨테이너, MySQL, NFS 지표와 알림을 구성했다. App 배포 전이라 아래 항목은 후속 작업으로 남았다.

  • HTTP 요청 지연과 오류 비율
  • Spring Boot JVM·커넥션 풀 지표
  • 업무 처리 실패
  • 애플리케이션 로그와 추적 정보 연결
  • 복제본 감소와 롤링 업데이트의 영향
  • 외부 사용자 경로 점검

이번 작업으로 노드 지연, 컨테이너 자원 사용, DB 복제 중단, NFS 마운트 누락을 각각 확인할 수 있게 됐다. App의 실제 요청 결과까지 보려면 배포 후 지표와 로그를 더 연결해야 했다.

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동