홈서버 장애를 텔레그램으로 받기 — 모니터링·알림 구축

감시 스크립트가 이상을 감지하면 텔레그램 봇 API를 거쳐 휴대폰 푸시 알림으로 전달되는 파이프라인

홈서버 장애의 진짜 문제는 장애 자체가 아니라 모른 채 지나가는 시간이다. 백업이 조용히 실패하고, 디스크가 가득 차가고, 마운트가 풀려 있는데 — 서버는 비명을 지르지 않는다. 그래서 모니터링의 시작은 거창한 대시보드가 아니라 “이상할 때 내 폰이 울리게 만드는 것”이다. 텔레그램 봇이 이 용도에 잘 맞는다: 무료이고, API가 단순해서 curl 한 줄이면 되고, 푸시 알림 전달이 빠르다.

1. 봇 만들기 — 5분

  1. 텔레그램에서 @BotFather를 검색해 대화를 열고 /newbot 명령으로 봇을 만든다. 끝나면 봇 토큰을 준다 (공식 Bot API 문서).
  2. 만든 봇과 대화를 시작한다(아무 메시지나 전송 — 봇은 먼저 말을 걸 수 없다).
  3. chat_id를 확인한다: 브라우저에서 https://api.telegram.org/bot<토큰>/getUpdates를 열면 방금 보낸 메시지의 chat.id가 보인다.

토큰과 chat_id 두 값이 전부다. 이 둘은 비밀값이므로 스크립트에 하드코딩하지 말고 권한을 조인 별도 파일(chmod 600)에 둔다.

2. 전송 함수 — curl 한 줄

#!/bin/bash
# notify.sh — 텔레그램 알림 전송
source /home/user/scheduler/.telegram-env   # TOKEN, CHAT_ID 정의

curl -s -X POST "https://api.telegram.org/bot${TOKEN}/sendMessage" \
  -d chat_id="${CHAT_ID}" \
  --data-urlencode text="$1" >/dev/null

이제 서버 어디서든 ./notify.sh "메시지"로 폰에 알림을 보낼 수 있다. 모든 자동화 스크립트가 이 한 파일을 공유하게 만드는 것이 유지보수의 요령이다.

3. 무엇을 감시할 것인가

홈서버에서 “조용히 죽는” 대표 항목 네 가지를 감시 스크립트 하나로 묶는다.

#!/bin/bash
# server-check.sh — 이상 시에만 알림
source /home/user/scheduler/.telegram-env
ALERT=""

# ① 디스크 사용량 90% 초과 (tmpfs류 제외)
while read -r target pcent; do
  usage=${pcent%\%}
  [ "$usage" -ge 90 ] && ALERT+="⚠️ 디스크 ${target} ${pcent} 사용 중\n"
done < <(df --output=target,pcent -x tmpfs -x devtmpfs -x overlay | tail -n +2)

# ② 필수 마운트 확인
for m in /mnt/backup /mnt/nas-media; do
  mountpoint -q "$m" || ALERT+="⚠️ 마운트 해제: $m\n"
done

# ③ 컨테이너 생존 확인
for c in jellyfin vaultwarden; do
  [ "$(docker inspect -f '{{.State.Running}}' "$c" 2>/dev/null)" = "true" ] || ALERT+="⚠️ 컨테이너 중지: $c\n"
done

# ④ dmesg I/O 에러 (최근 부팅 이후)
dmesg | grep -qiE "i/o error" && ALERT+="⚠️ dmesg에 I/O 에러 존재\n"

[ -n "$ALERT" ] && /home/user/scheduler/notify.sh "$(echo -e "🚨 서버 점검 경보\n$ALERT")"

이를 중앙 관리 crontab에 5분 또는 10분 주기로 등록하면 기본 감시망이 완성된다. ①은 로그 폭주나 백업 누적으로 디스크가 차는 것을, ②는 백업이 허공에 쓰이는 사고를, ④는 외장 디스크 연결 장애를 조기에 잡는다.

4. 운영하며 알게 되는 세부 사항

메시지 길이 제한이 있다. 텔레그램 sendMessage의 텍스트는 한 번에 4096자까지다 (Bot API 문서 sendMessage 항목). 로그 전체를 메시지로 보내려 하면 잘리거나 실패하므로, 알림에는 “무엇이 문제인지 + 어디를 보면 되는지”만 담고 상세 로그는 서버에서 보는 것이 맞다.

전송 실패도 처리한다. 알림 전송 자체가 네트워크 문제로 실패할 수 있다. curl에 --max-time 10 정도의 타임아웃을 걸어 감시 스크립트가 전송 단계에서 매달리지 않게 하고, 위 스크립트들처럼 감시 로직과 전송 로직을 분리해두면 한쪽 문제가 다른 쪽을 무너뜨리지 않는다.

토큰은 유출되면 곧 스팸 통로가 된다. 봇 토큰이 git 저장소나 로그에 섞여 나가지 않도록 별도 env 파일(chmod 600)로 관리하고, 의심되면 BotFather의 /revoke로 즉시 재발급한다.

동작 확인은 정기적으로. 알림 시스템의 가장 나쁜 고장은 “안 울리는 고장”이다. 설정 직후뿐 아니라 가끔 일부러 조건을 만들어(테스트 컨테이너 중지 등) 끝까지 — 폰 진동까지 — 도달하는지 확인하자.

5. 알림 설계 원칙: 조용한 것이 좋은 것이다

기술보다 중요한 것이 알림의 신호 대 잡음비다.

  • 정상 보고를 보내지 않는다. “오늘도 백업 성공 ✅“이 매일 오면 2주 뒤부터 안 읽는다. 그리고 안 읽는 채널에 어느 날 진짜 경보가 묻힌다. 이상 시에만 울리는 것이 원칙이다.
  • 예외: 침묵이 곧 장애인 작업. 단, “알림 시스템 자체가 죽은” 경우를 잡기 위해 주 1회 정도의 하트비트(생존 신고)는 타협안이 된다.
  • 반복 경보는 묶는다. 같은 경보가 5분마다 도착하면 잡음이 된다. 상태 파일을 두고 “같은 경보는 N시간에 한 번만” 같은 억제 로직을 넣으면 좋다.

내 모니터는 5분 간격으로 돌면서 dmesg의 I/O 에러, 파일시스템 read-only 전환, 마운트 해제, 디스크 용량 초과를 본다. 이 목록은 처음부터 완성된 것이 아니라 장애를 겪을 때마다 자랐다. 외장 디스크 쓰기 실패를 겪은 뒤 read-only 감시가 들어왔고, 최근 NVMe 컨트롤러가 응답을 멈춰 서버 전체가 굳는 일을 겪고는 컨트롤러 타임아웃·리셋 로그를 전조 감시 항목으로 추가했다. 장애가 지나간 뒤 “같은 일이 또 오면 미리 알 수 있는가?”를 자문하고 항목을 하나 늘리는 것 — 운영해보니 모니터링은 그렇게 자라는 물건이었다.

마무리

이메일이나 다른 메신저로도 같은 구조를 만들 수 있지만, 텔레그램을 권하는 이유는 구축 비용이다 — SMTP 설정도, 앱 심사도 없이 HTTP 요청 하나로 끝난다. 도구가 단순해야 “나중에 해야지”가 “오늘 한다”가 된다.

여기까지 하면 서버가 “상태를 물어봐야 아는 기계”에서 “이상하면 먼저 말하는 기계”로 바뀐다. 모니터링의 다음 단계(메트릭 수집, 대시보드)는 그 후에 욕심내도 늦지 않다. 알림이 잘 동작하는지 검증하는 가장 좋은 타이밍은 재부팅 테스트와 함께다 — 일부러 컨테이너 하나를 멈추고 폰이 울리는지 확인해보자.