#!/bin/bash # 이 파일이 하는 일: 학습 플랫폼이 살아 있는지 밖에서 확인하고, 상태가 "바뀔 때만" 텔레그램으로 알린다. # systemd 타이머가 2분마다 실행한다(mirim-health.timer). # # 학습 포인트 ① — 왜 "앱 밖에서" 감시하나? # 백엔드가 죽으면 백엔드 스스로는 "나 죽었어요"를 못 보낸다(죽었으니까!). # 그래서 감시는 반드시 앱 바깥의 별도 프로세스가 해야 한다. 여기선 호스트의 이 스크립트가 # 실제 사용자와 똑같이 공개 URL(HTTPS)을 두드려 본다 — 이게 되면 Caddy·백엔드·인증서가 다 정상. # # 학습 포인트 ② — "상태 변화"에만 알린다. # 2분마다 "정상입니다"를 보내면 알림이 스팸이 돼 아무도 안 본다. # 그래서 직전 상태를 파일에 적어 두고, UP→DOWN(장애 발생)과 DOWN→UP(복구)의 # "바뀌는 순간"에만 한 번씩 보낸다. 좋은 알림은 조용하다가 필요할 때만 운다. set -uo pipefail # -e는 안 쓴다: curl 실패(=사이트 다운)는 "에러"가 아니라 우리가 감지하려는 "정상 흐름"이다. # ── 설정 ── URL="https://edu.awesomedevapp.com/" STATE_FILE="/home/ec2-user/.mirim-health-state" # 직전 상태(UP/DOWN)를 기억 FAIL_THRESHOLD=2 # 연속 N회 실패해야 DOWN으로 본다(일시적 깜빡임 무시) TIMEOUT=10 # 텔레그램 설정은 백엔드 .env에서 그대로 가져온다(같은 봇·같은 방). ENV_FILE="/home/ec2-user/mirim-app/deploy/.env" BOT_TOKEN="$(grep -E '^TELEGRAM_BOT_TOKEN=' "$ENV_FILE" 2>/dev/null | cut -d= -f2- || true)" CHAT_ID="$(grep -E '^TELEGRAM_CHAT_ID=' "$ENV_FILE" 2>/dev/null | cut -d= -f2- || true)" notify() { # 봇 설정이 없으면 조용히 넘어간다(알림만 꺼지고 감시 자체는 계속). [ -z "$BOT_TOKEN" ] || [ -z "$CHAT_ID" ] && return 0 curl -s -m "$TIMEOUT" -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \ -H "Content-Type: application/json; charset=utf-8" \ --data-binary "$(printf '{"chat_id":%s,"text":%s}' "$CHAT_ID" "$(printf '%s' "$1" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')")" >/dev/null || true } # ── 현재 상태 확인 ── # 연속 FAIL_THRESHOLD회 실패해야 DOWN. 한 번이라도 성공하면 즉시 UP. now="DOWN" for i in $(seq 1 "$FAIL_THRESHOLD"); do if code="$(curl -s -o /dev/null -m "$TIMEOUT" -w '%{http_code}' "$URL")" && [ "$code" = "200" ]; then now="UP" break fi sleep 3 done # ── 직전 상태와 비교, 바뀌었을 때만 알림 ── prev="$(cat "$STATE_FILE" 2>/dev/null || echo "UP")" # 첫 실행은 UP으로 가정(정상 시작 전제) if [ "$now" != "$prev" ]; then if [ "$now" = "DOWN" ]; then notify "🔴 학습 플랫폼 응답 없음 — $URL 이 열리지 않습니다. 서버를 확인해 주세요. (연속 ${FAIL_THRESHOLD}회 실패)" else notify "🟢 학습 플랫폼 정상 복구 — $URL 이 다시 열립니다." fi echo "$now" > "$STATE_FILE" fi