집 서버 마크 운영 자동화 (DDNS, 모니터링, 알림 한글화)
마크 서버를 도메인으로 접속하게 해놨는데, 집 회선은 공인 IP가 유동이다. IP가 바뀌면 DNS가 옛날 IP를 가리키고, 친구들은 서버 터졌다고 연락이 온다. 이걸 사람이 챙기는 건 말이 안 되니 자동화했다.
DDNS는 크론 하나면 된다
거창한 DDNS 클라이언트 안 깔고 쉘 스크립트 하나로 해결했다. 5분마다 크론이 도는 구조.
- 공인 IP를 조회한다. 1차는 Cloudflare whoami(DNS 조회라 빠름), 실패하면 ipify로 폴백
- 지난번 IP를 적어둔 파일과 비교해서 같으면 그냥 끝. 그래서 평상시 API 호출은 0회다
- 바뀌었으면 Cloudflare API로 A레코드를 갱신하고, 디스코드 웹훅으로 알림을 쏜다
포인트는 레코드 구조다. apex(moon-core.com) 하나만 A레코드로 두고, mc. 같은 서브도메인은 전부 apex로 가는 CNAME으로 걸었다. 그러면 IP가 바뀌어도 갱신할 레코드는 딱 하나임. 서버가 늘어날 때마다 DDNS 스크립트 고칠 일이 없다.
내부 문제도 하나 있는데, 집 안에서 도메인으로 접속하면 공유기 NAT를 한 바퀴 돌아야 한다(헤어핀). 이건 내부 DNS(AdGuard Home)에 리라이트를 걸어서 집 안에서는 도메인이 바로 서버 내부 IP로 풀리게 했다.
모니터링에 편입
서버 상태 감시는 원래 Prometheus + Grafana + Alertmanager 스택이 돌고 있어서, 마크 쪽은 mc-monitor(itzg)라는 익스포터만 붙이면 됐다. 서버 주소를 환경변수로 주면 마크 프로토콜로 핑을 쏴서 상태/접속자수/응답시간을 메트릭으로 내준다. 알림 규칙은 이거 하나면 충분하다.
- alert: MinecraftServerDown
expr: minecraft_status_healthy == 0
for: 3m
labels: { severity: critical }
모드팩 서버를 새로 파고 나서 감시 대상에 추가했는데, 추가하자마자 디스코드에 빨간 알림이 떴다. 어? 하고 봤더니 오탐이 아니었음. 마침 그 시간대에 서버를 재시작하고 있었고, 모드 305개짜리는 부팅이 오래 걸려서 그동안 진짜로 응답이 없던 거다. 부팅이 끝나자 초록색 해결 알림이 따라왔다. 첫 실전에서 제대로 작동한 것.
근데 해결됐다는데 왜 critical이지?
그 초록색 알림을 보다가 잠깐 의심했다. RESOLVED라면서 본문에는 여전히 critical이니 "서버 응답 없음"이니 하는 문구가 박혀있는 거다. 해결됐다면 이런 게 왜 붙어있지? 버그인가?
버그가 아니라 Alertmanager 사양이다. 해결 알림에도 원래 알림의 라벨과 문구를 그대로 첨부해서 보낸다. "어떤 알림이 해제됐는지" 식별시키려는 거지 현재 상태를 말하는 게 아님. 알고 나면 당연한데 모르고 보면 헷갈린다.
이럴 거면 한글로 쓰자
기본 알림 포맷이 저렇게 헷갈리니 템플릿을 직접 썼다. Alertmanager는 디스코드 알림의 제목/본문을 Go 템플릿으로 바꿀 수 있다.
{{ if eq .Status "firing" }}🚨 문제 발생{{ else }}✅ 문제 해결됨{{ end }}
🔴 마인크래프트 서버 응답 없음 (:25585) → 발생함
🟢 마인크래프트 서버 응답 없음 (:25585) → 해결됨
07/17 15:25 ~ 16:06 동안 지속됨
발생함/해결됨을 문장으로 박아버리니 색깔 안 보고도 헷갈릴 일이 없다. 해결 알림에는 문제가 지속된 시간대도 같이 나오게 했다.
시각은 KST로 나와야 해서 컨테이너에 TZ만 넣었는데 안 됐다. prom 계열 이미지는 tzdata가 없어서 TZ를 줘도 UTC로 나옴. 호스트의 /usr/share/zoneinfo를 읽기전용으로 마운트해주니 해결됐다.
덤으로 기본 알림에 붙던 Source 링크도 없앴다. http://af6c2d1d875f:9090 같은 컨테이너 ID 주소라 클릭해봐야 아무데도 못 가는 링크였음.
마치며
IP 갱신도 자동, 다운 감지도 자동, 알림도 읽자마자 이해되는 한글. 이제 서버 터졌다는 건 친구가 아니라 봇이 먼저 알려준다.