점검 뒤 홈서버 서비스를 다시 올리는 순서
맥미니에서 Home Assistant와 여러 앱, 봇을 함께 실행하다 보니 OS 업데이트도 서비스 점검 작업이 됐습니다. 시작 방식이 서로 달라 재부팅 후 모두 정상으로 돌아온다고 기대하기 어려웠습니다.
그래서 점검 중 보여줄 안내와 자동 시작 설정을 먼저 준비하고, 업데이트 후에는 의존 관계에 따라 서비스를 확인하도록 정리했습니다. 실제 복구 과정에서는 Home Assistant가 내부에서 정상인데 외부에서는 HTTP 400을 반환하는 문제도 있었습니다.
맥미니 외부의 점검 안내
맥미니가 꺼진 동안에도 안내를 보여주기 위해 Cloudflare Worker에 점검 응답을 뒀습니다. 지정한 호스트에 대해 수동 점검, 예약 점검, 정상 원본 전달을 선택할 수 있게 했습니다.
원본이 응답하지 않는 GET·HEAD 요청에는 503 안내를 반환할 수 있도록 했습니다. 다만 모든 요청을 같은 안내로 바꾸거나 임의로 다시 보내지는 않습니다. POST와 WebSocket, 인증과 리디렉션, 원본이 반환한 오류 응답은 각각의 처리를 유지하도록 구성했습니다.
안내 페이지는 서비스가 정상이라는 신호가 아닙니다. 응답 코드도 503이므로 모니터링에서 장애로 감지될 수 있습니다. 사용자에게 보여줄 설명과 모니터링의 점검 일정은 함께 관리해야 했습니다.
점검 응답의 최소 구현
점검 안내는 원본 서버가 꺼져 있어도 응답할 위치에 있어야 합니다. 아래는 Worker에서 특정 호스트의 GET·HEAD에만 수동 점검 응답을 주는 최소 예시입니다. 실제로 만든 예약·오류 처리 전체를 옮긴 코드는 아닙니다.
export default {
async fetch(request, env) {
const url = new URL(request.url);
const isRead = request.method === 'GET' || request.method === 'HEAD';
const isWebSocket = request.headers.get('Upgrade')?.toLowerCase() === 'websocket';
if (env.MAINTENANCE === 'on' && url.hostname === 'service.example.com'
&& isRead && !isWebSocket) {
const body = request.method === 'HEAD' ? null : '점검 중입니다. 잠시 후 다시 확인해주세요.';
return new Response(body, {
status: 503,
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'no-store',
'Retry-After': '300',
},
});
}
return fetch(request);
},
};503으로 반환하는 것은 정상 페이지가 준비됐다는 의미로 캐시되거나 모니터링되는 것을 피하기 위해서입니다. HEAD에는 본문을 보내지 않고, 이 예시의 POST와 WebSocket은 정상 원본 경로를 유지합니다. Retry-After는 300초 뒤 무조건 복구된다는 약속이 아니라 재요청 시점의 안내입니다.
실제 적용에서는 Worker의 route와 원본 전달 설정이 맞아야 합니다. 코드만 복사해 배포했다고 원본 요청이 연결되는 것은 아닙니다. Worker 응답 API는 Cloudflare 문서 를 참고했습니다. 이 예제는 로컬에서 분기와 응답 형식을 설명하기 위한 것으로 운영 Worker를 새로 배포하지 않았습니다.
복구 순서와 준비 상태
재부팅 후에는 프로세스 ID가 있다는 이유만으로 다음 단계로 넘어가지 않았습니다. Docker가 켜져도 앱이 사용하는 데이터와 포트가 아직 준비되지 않았을 수 있습니다.
Colima·Docker → 컨테이너 상태·볼륨 확인
HA VM → 내부 HTTP·준비 상태 확인
앱·API → 각 앱의 HTTP 응답 확인
게이트웨이 → 앱 경로를 통한 응답 확인
봇·예약 작업 → 실행 환경·인증·일정 확인
Tunnel → 공개 HTTP·WebSocket 확인
점검 안내 → 공개 경로 확인 후 해제이 순서는 해당 홈서버의 의존 관계를 설명한 것입니다. 모든 시스템에서 Tunnel을 마지막에 시작해야 한다는 일반 규칙은 아닙니다. 중요한 것은 앞의 의존 서비스가 준비됐는지 확인한 뒤 뒤쪽 연결을 보는 것입니다. 일부가 정상이라면 전체를 다시 시작하기보다 실패한 구간만 복구할 수 있습니다.
자동 시작 설정 백업과 복원
관리할 LaunchAgent 목록을 정하고 점검 전의 활성화 상태를 저장했습니다. 일부 상시 서비스는 자동 시작을 끄고 예약 작업은 유지하는 식으로 선택할 수 있게 했습니다.
자동 시작 설정과 현재 실행 중인 프로세스는 구분했습니다. 다음 시작을 막는다고 이미 동작 중인 VM까지 자동으로 종료되는 것은 아니기 때문입니다. 점검할 때는 실행 중인 서비스를 멈추는 절차도 따로 필요했습니다.
| 구분 | 확인할 내용 |
|---|---|
| 자동 시작 설정 | 이후 자동으로 실행하도록 설정됐는지 |
| 프로세스 | 현재 실행 중인지 |
| 준비 상태 | 의존 서비스와 연결하고 요청을 받을 수 있는지 |
| 공개 경로 | 외부에서 HTTP·WebSocket으로 연결되는지 |
업데이트 뒤에는 저장한 설정을 복원하고 Colima·Docker, HA, 앱, 게이트웨이, 봇, Tunnel 순서로 준비 상태를 확인했습니다. 선행 서비스가 준비되지 않았는데 후속 서비스를 반복 실행하지 않도록 했고, 이미 정상인 서비스는 다시 시작하지 않았습니다.
HA 외부 접속의 HTTP 400 오류
복구 중 Home Assistant의 내부 주소는 정상이었지만 공개 주소는 HTTP 400을 반환했습니다. 서비스가 켜졌는지보다 요청이 어떤 경로로 들어가는지를 확인해야 하는 상황이었습니다.
프록시 헤더가 포함된 요청을 유선과 Wi-Fi 출발 주소로 나눠 보내 보니 결과가 달랐습니다. 실제 요청이 Wi-Fi 경로로 나가면서 Home Assistant의 신뢰 프록시 설정과 맞지 않았습니다.
유선 경로로 돌아온 뒤 HTTP와 WebSocket 연결을 다시 확인하고 점검 안내를 해제했습니다. 이 경우에는 Home Assistant를 반복해서 재시작하기보다 프록시에서 보낸 요청의 출발 주소를 확인하는 것이 필요했습니다.
복구 확인 항목
마지막에는 자동 시작 설정이 원래대로 복원됐는지, 점검 모드가 해제됐는지 다시 조회했습니다. 공개 HTTP와 WebSocket 연결도 확인했습니다. 장치별 제어와 모든 정기 메시지 재전송은 이 점검에 포함하지 않았습니다.
서비스 복구 순서를 정리하면서 시작 명령의 성공과 요청을 받을 수 있는 상태를 따로 보게 됐습니다. 다음 점검에서도 같은 기준으로 확인할 수 있도록 설정 상태와 연결 확인 결과를 함께 남겼습니다.