점검 페이지 뒤에서 앱이 995만 번 재시작하고 있었다
홈페이지에 접속하면 모든 주소에서 「점검중」 화면만 나오는 상태였습니다. 응답 코드는 503이었고, 검색엔진에는 색인하지 말라는 표시까지 붙어 있었습니다. 점검을 하려고 일부러 막아 둔 것인지, 앱이 죽어서 대체 화면이 나오는 것인지는 바깥에서는 구별할 수 없습니다. 두 설정은 응답이 똑같기 때문입니다. 그래서 서버 안으로 들어가 확인했습니다.
점검 모드가 아니었다
서버의 프로세스 관리자(PM2)를 보니 홈페이지 앱이 약 995만 번 재시작된 상태였고, 가동 시간은 0초였습니다. 로그에는 한 줄만 반복되고 있었습니다.
You are using Node.js 16.20.2. For Next.js, Node.js version >= v18.17.0 is required.
사이트가 쓰는 Next.js 버전은 Node.js 18.17 이상이 필요했는데, 서버에 깔린 Node.js는 16.20.2였습니다. 앱은 뜨자마자 종료되고, 프로세스 관리자는 그것을 다시 띄우고, 앞단의 웹 서버(nginx)는 연결이 안 되니 점검 화면을 내보내고 있었습니다. 점검 화면이 「정상 동작하는 대체 페이지」 역할을 해서 문제가 겉으로 드러나지 않았던 셈입니다.
복구한 순서
- 재시작 루프부터 멈췄습니다. 죽는 앱을 계속 다시 띄우면 로그와 CPU만 소모합니다.
- 서버 전체의 Node.js를 올리지 않고, 계정 홈 디렉터리에 Node.js 20.10.0 바이너리를 따로 설치했습니다. 같은 서버에서 Node 16으로 정상 동작하던 다른 서비스를 건드리지 않기 위해서입니다.
- 새로 빌드하기 전에, 기존 빌드 결과물을 Node.js 20으로 띄워 응답을 확인했습니다. 정상 응답이 나와 재빌드가 필요 없다는 것을 먼저 확인했습니다.
- 프로세스 관리자에서 앱을 삭제하고 Node.js 20 실행 경로를 명시해 다시 등록한 뒤 설정을 저장했습니다. 저장하지 않으면 서버를 재부팅할 때 이전 상태로 돌아갑니다.
- 바깥 인터넷에서 홈페이지가 200으로 응답하는지, 모바일 화면과 문의 페이지가 열리는지 확인했습니다.
복구 후에는 앱이 online 상태이고 재시작 횟수가 0으로 유지되는 것까지 확인했습니다.
같은 원인의 서비스가 더 있었다
같은 서버의 다른 서비스 네 개도 같은 Node.js 버전 문제로 재시작을 반복하고 있었습니다. 두 개는 같은 방법으로 복구했습니다. 다른 하나는 Node.js 버전이 아니라, 프로세스 관리자가 재부팅하면 사라지는 임시 경로를 실행 파일로 붙잡고 있는 것이 원인이어서 설정을 새로 만들어 다시 등록했습니다. 겉으로는 같은 증상이어도 원인이 다를 수 있다는 예입니다.
자동화 도구 한 개는 일부러 켜지 않고 정지 상태로 남겼습니다. 오래 멈춰 있던 자동화 도구를 켜면 잠들어 있던 워크플로가 한꺼번에 실행될 수 있기 때문입니다. 활성 상태였던 워크플로 목록을 먼저 읽기 전용으로 정리하고, 켤 것을 하나씩 정해서 개별로 켜는 순서로 정했습니다.
남긴 교훈
- 503 점검 화면은 「점검 중」의 증거가 아닙니다. 앱이 죽었을 때도 똑같이 보입니다. 서버 안의 프로세스 상태를 봐야 구별됩니다.
- 재시작 횟수가 수백만 번까지 쌓이는 동안 알아채지 못했다는 것은, 재시작 횟수에 대한 알림이 없었다는 뜻입니다.
- 프레임워크를 올릴 때는 서버의 런타임 버전을 같이 확인해야 합니다. 프로젝트에는 이미 요구 버전(
.nvmrc의 20.10.0)이 적혀 있었습니다.