라즈베리파이5 재부팅 후 원격 접속 자동 복구 + Discord 헬스체크 알림 구축

재부팅 후 Raspberry Pi Connect가 오프라인으로 뜨는 문제, 원인은 systemd 서비스 기동 순서였습니다. 네트워크 연결 후 자동 재시작하는 서비스 하나로 해결하고, 부팅 완료 시 Discord로 전체 상태를 자동 보고하는 시스템까지 구축한 과정을 공유합니다.

Pi 5 홈서버를 운영하다 보면 재부팅이 필요한 순간이 생깁니다. 그런데 재부팅 후 connect.raspberrypi.com에 접속해보면 기기가 회색 오프라인 상태로 표시되는 일이 반복됐습니다. SSH로 직접 들어가서 수동으로 서비스를 재시작해야만 온라인으로 바뀌는 불편함이 있었죠. 이 문제의 원인을 찾아 자동으로 복구하고, 재부팅 후 전체 상태를 Discord로 자동 보고하는 시스템까지 구축한 과정을 정리합니다.

✅ 최종 완성된 기능

  • 재부팅 후 rpi-connect 자동 재연결 (connect.raspberrypi.com 온라인 유지)
  • 부팅 완료 시 Docker 컨테이너 전체 상태 Discord 알림
  • 시스템 서비스(cloudflared, rpi-connect) 정상 여부 자동 보고
  • 메모리 / 디스크 사용량 요약 전송

🔍 문제 원인 — systemd 서비스 기동 순서

rpi-connect 서비스 로그를 확인했더니 부팅 직후에 이런 에러가 찍혀 있었습니다.

Bash
WARN [SYNC]: API call unsuccessful
error="Post https://api.connect.raspberrypi.com/delta:
dial tcp: lookup api.connect.raspberrypi.com on 168.126.63.2:53:
dial udp 168.126.63.2:53: connect: network is unreachable"

rpi-connect 서비스가 시작될 때 아직 네트워크가 완전히 올라오지 않은 상태여서 DNS 조회 자체가 실패한 것입니다. 서비스는 오류를 기록하고 그냥 실행 중 상태로 남아 있지만, Raspberry Pi Connect 서버에는 온라인으로 등록되지 않습니다. 결과적으로 웹사이트에서 기기가 영원히 오프라인으로 표시되는 것이었습니다.

같은 이유로 Cloudflare Tunnel(cloudflared)도 부팅 시 한 번 실패 후 자동 재시작으로 복구되고 있었습니다. cloudflared는 실패 시 프로세스가 종료되어 systemd가 자동 재시작해주지만, rpi-connect는 오류만 기록하고 계속 살아있어서 자동 복구가 안 되는 차이가 있었습니다.

🛠 해결 1 — 네트워크 연결 후 rpi-connect 재시작 서비스

핵심 아이디어는 간단합니다. network-online.target 이후에 실행되는 별도 systemd 서비스를 만들어서 rpi-connect를 재시작하면 됩니다.

Bash
sudo nano /etc/systemd/system/rpi-connect-restart.service
Bash
[Unit]
Description=Restart rpi-connect after network is online
After=network-online.target
Wants=network-online.target
After=[email protected]
Requires=[email protected]

[Service]
Type=oneshot
User=pi5
Environment="XDG_RUNTIME_DIR=/run/user/1000"
Environment="DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus"
ExecStart=systemctl --user restart rpi-connect
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

📌 rpi-connect는 user 레벨 systemd 서비스입니다. 시스템 서비스에서 user 서비스를 제어하려면 XDG_RUNTIME_DIRDBUS_SESSION_BUS_ADDRESS 환경 변수를 명시해야 합니다. 1000은 pi5 유저의 UID입니다.

Bash
sudo systemctl daemon-reload
sudo systemctl enable rpi-connect-restart.service

이후 재부팅하면 네트워크가 완전히 연결된 후 rpi-connect가 재시작되어 connect.raspberrypi.com에 정상 등록됩니다.

🛠 해결 2 — 부팅 후 Discord 헬스체크 알림

어차피 부팅 후 서비스 상태를 직접 확인해야 한다면, 자동으로 전체 점검 결과를 Discord로 받으면 더 편합니다. 스크립트 하나와 systemd 서비스 하나로 구현했습니다.

헬스체크 스크립트

Bash
sudo nano /usr/local/bin/boot-healthcheck.sh
Bash
#!/bin/bash
# Pi 5 부팅 후 전체 상태 점검 → Discord 전송

WEBHOOK="https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
NOW=$(date '+%Y-%m-%d %H:%M KST')

ok="✅"
ng="❌"

# 1. Docker 컨테이너
DOCKER_LINES=""
ALL_DOCKER_OK=true

while IFS= read -r line; do
  name=$(echo "$line" | awk '{print $1}')
  status=$(echo "$line" | cut -d' ' -f2-)
  if echo "$status" | grep -qiE "^Up"; then
    DOCKER_LINES="${DOCKER_LINES}${ok} ${name} — 정상\n"
  else
    DOCKER_LINES="${DOCKER_LINES}${ng} ${name} — 비정상 (${status})\n"
    ALL_DOCKER_OK=false
  fi
done < <(docker ps -a --format "{{.Names}} {{.Status}}" 2>/dev/null | sort)

[ "$ALL_DOCKER_OK" = true ] && DOCKER_SUMMARY="${ok} 전체 정상" || DOCKER_SUMMARY="${ng} 일부 비정상"

# 2. 시스템 서비스
if systemctl is-active --quiet cloudflared; then
  CLOUDFLARED="${ok} cloudflared 터널 — 정상"
else
  CLOUDFLARED="${ng} cloudflared 터널 — 비정상"
fi

RPI_STATUS=$(XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
  su -s /bin/bash pi5 -c "rpi-connect status 2>/dev/null" 2>/dev/null)
if echo "$RPI_STATUS" | grep -q "Subscribed to events: yes"; then
  RPI_CONNECT="${ok} rpi-connect — 정상 (온라인)"
else
  RPI_CONNECT="${ng} rpi-connect — 비정상"
fi

# 3. 리소스
MEM_TOTAL=$(LC_ALL=C free -m | awk '/^Mem:/{print $2}')
MEM_USED=$(LC_ALL=C free -m | awk '/^Mem:/{print $3}')
MEM_PCT=$(python3 -c "print(int(${MEM_USED}/${MEM_TOTAL}*100))")
DISK_INFO=$(df -h / | awk 'NR==2{print $3"/"$2" ("$5")"}')
DISK_PCT=$(df / | awk 'NR==2{gsub(/%/,"",$5); print $5}')

[ "$MEM_PCT" -lt 80 ] \
  && MEM_LINE="${ok} 메모리: ${MEM_USED}M / ${MEM_TOTAL}M (${MEM_PCT}%)" \
  || MEM_LINE="${ng} 메모리: ${MEM_USED}M / ${MEM_TOTAL}M (${MEM_PCT}%) ⚠️ 높음"

[ "$DISK_PCT" -lt 85 ] \
  && DISK_LINE="${ok} 디스크: ${DISK_INFO}" \
  || DISK_LINE="${ng} 디스크: ${DISK_INFO} ⚠️ 부족"

# 4. Discord 전송
send_discord() {
  local tmpfile=$(mktemp)
  echo "$1" > "$tmpfile"
  python3 -c "
import sys, json
msg = open('$tmpfile').read()
print(json.dumps({'content': msg}))
" > "${tmpfile}.json"
  curl -s -X POST "$WEBHOOK" -H "Content-Type: application/json" --data-binary "@${tmpfile}.json" > /dev/null
  rm -f "$tmpfile" "${tmpfile}.json"
}

PART1="🔄 **Pi 5 재부팅 완료** — ${NOW}
━━━━━━━━━━━━━━━━━━━━━━

📦 **Docker 컨테이너** — ${DOCKER_SUMMARY}
$(echo -e "$DOCKER_LINES")"

PART2="🌐 **시스템 서비스**
${CLOUDFLARED}
${RPI_CONNECT}

💾 **리소스**
${MEM_LINE}
${DISK_LINE}"

send_discord "$PART1"
sleep 1
send_discord "$PART2"
Bash
sudo chmod +x /usr/local/bin/boot-healthcheck.sh

헬스체크 systemd 서비스

Bash
sudo nano /etc/systemd/system/boot-healthcheck.service
Bash
[Unit]
Description=Boot health check and Discord notification
After=network-online.target
Wants=network-online.target
After=rpi-connect-restart.service
After=docker.service

[Service]
Type=oneshot
ExecStartPre=/bin/sleep 15
ExecStart=/usr/local/bin/boot-healthcheck.sh
RemainAfterExit=yes
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

ExecStartPre=/bin/sleep 15는 Docker 컨테이너들이 완전히 올라올 때까지 기다리는 시간입니다. 컨테이너 수에 따라 조정하면 됩니다.

Bash
sudo systemctl daemon-reload
sudo systemctl enable boot-healthcheck.service

💡 전체 부팅 순서 흐름

이제 재부팅하면 아래 순서로 자동 진행됩니다.

Bash
재부팅
  └─ NetworkManager (인터넷 연결 대기)
       └─ rpi-connect-restart    rpi-connect 재시작 (온라인 등록)
       └─ boot-healthcheck       15 대기  전체 점검  Discord 전송

📱 Discord 수신 예시

재부팅 완료 후 Discord에 아래와 같이 두 개의 메시지가 자동으로 전송됩니다.

Bash
🔄 Pi 5 재부팅 완료  2026-04-02 09:00 KST
━━━━━━━━━━━━━━━━━━━━━━

📦 Docker 컨테이너   전체 정상
 n8n  정상
 nextcloud  정상
 wordpress  정상
 metabase  정상
...

🌐 시스템 서비스
 cloudflared 터널  정상
 rpi-connect  정상 (온라인)

💾 리소스
 메모리: 3200M / 8059M (39%)
 디스크: 90G/457G (21%)

📌 마치며

문제 자체는 단순한 부팅 순서 race condition이었지만, 이를 계기로 재부팅 후 자동 복구 + 상태 보고 체계를 갖추게 됐습니다. 이제 홈서버가 재부팅되면 몇 분 안에 Discord 알림이 오고, 어느 서비스가 정상인지 비정상인지 한눈에 파악할 수 있습니다. Pi 5 홈서버를 운영하신다면 이 두 가지 서비스를 꼭 설정해두시길 권장합니다.