
서버 터지는 문제를 완전히 해결한 줄 알았는데
간헐적으로 서버가 터지는 현상이 계속 발생하였다.
터지는 이유 추적을 위해서 winston 라이브러리를 사용, 다양한 로깅을 시도하고
또 에러 핸들러를 더 구체적으로 작성하여 이유를 잡아보려 했는데 쉽지 않았다.
정말 우연히 서버가 터지는 원인을 찾았다.
테스트 하던 과정에서 우연히 rateLimiter가 걸려있지 않던 요청에 대해, 마침 또 프론트에도 디바운싱이 안 걸려있던 API 요청이 여러 개 동시에 밀려 들어오면서
순간적으로 서버에 부하가 발생하고, 그래서 또 밀리고, 그러다가 점유율 100%를 찍고 서버가 터지는 현상을 확인하였다.
실제 라이브 서버에서는 단 몇 명의 유저만 저런 행위를 하더라도
즉시 100개 200개 이상의 스택이 쌓이면서 서버에 큰 무리가 갈 수 있겠다 싶었다.
먼저 클라이언트 코드를 전체적으로 확인하여
반복 API호출이 가능한 모든 핸들러에 디바운싱/쓰로틀링 중 적절한 처리를 모두 해주었다.
처리에는 lodash 라이브러리의 debounce와 throttle함수를 사용하였다.
하지만 여기서 안심하면 안 되겠지?
원래는 app.use()를 통해 모든 API에 대해 일괄적인 제한을 적용하였으나,
그동안 운영하며 수집한 로그를 분석하여 각 router별로 적절한 제한 빈도를 좀더 타이트하게 설정해 주었다.
// 보안 상 실제 제한 빈도는 ?로 처리하였다.
export const apiRateLimiter_warn = rateLimit({
windowMs: ? * ? * ? * 1000,
max: ?,
message: 'Too many requests.',
keyGenerator: (req, res) => {
const ip = req.clientIp as string;
return ip;
},
handler: blockHandler_warn,
});
다음과 같이 handler를 이용하여 rateLimiter 의 기능을 강화하였다.
const blockHandler_warn = (req: Request, res: Response, next: NextFunction): void => {
const ip = req.clientIp as string;
blockedIps.add(ip);
setTimeout(() => unblockIp(ip), ? * ? * ? * 1000);
logger.warn(`...로그 메시지`);
}
};
이렇게 만든 미들웨어를 router에 부착해주면 되는 것이다.
위와 같이 처리를 하더라도 정말 재수가 없이 요청이 몰리면 또 서버가 위험해진다.
서버 사양을 올리는 방법이 있겠으나, 예산에 부담이 가기 때문에..
결국 서버 개발자의 숙명은 "어떻게 하면 더 싼 서버로 커버할 것인지" 고민하는 것이 되는 것 같다.
또, 서버가 터지는 일이 아주 잦은 일은 아니었고,
또 언제 터질 지 알 수 없다는 점이 큰 부담이었다.
(업무 중에는 서버를 볼 수 없어서, 업무 중이나 수면 중에 서버가 터지면 속수무책이었다.
따라서 이 설정은 반드시 필요했다.

EC2 인스턴스에서 경보 상태를 클릭하면 경보를 생성하고, 경보 발생 시 처리할 로직을 설정해줄 수 있다.
아래와 같이 경보 임계값을 설정 해주고,

다시 경보 알림과 경보 발생 시 작업을 설정해준다.

하지만 인스턴스가 재부팅 되더라고, NginX 서버는 재실행되지 않는다는 문제가 있다.
AWS 에는 또 좋은 기능이 있다.
사용자 데이터 편집을 통해 인스턴스가 시작될 때 실행될 코드를 설정할 수 있다.

아래와 같이 설정을 해주면 된다.
Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"
#cloud-config
cloud_final_modules:
- [scripts-user, always]
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"
#!/bin/bash
===여기에 사용할 bash/shell script를 작성한다===
이제 내가 정한 비율을 기준으로, 이상 현상이 감지될 경우
EC2 인스턴스가 자동으로 재부팅 되고,
EC2 인스턴스가 새로 실행되면 사용자 데이터에 있는 코드가 실행되면서
자동으로 NginX server가 실행되도록 설정이 완료되었다.