Node, Express 및 MongoDB를 사용한 간단한 API HealthCheck

우리 애플리케이션Promyze은 ReactJS 프런트엔드와 API용 Node/Express/Mongoose/MongoDB 스택이 있는 표준 웹 애플리케이션입니다. 우리는 최근 사용자가 서비스가 작동 중인지 문제가 있는지 알 수 있도록 공개 상태 페이지를 구축하는 것을 반영하고 있습니다. 시장에는 많은 모니터링 도구가 있습니다. 우리의 경우에는 MonSpark을 선택했습니다. 사용이 매우 간단하고 Slack과의 통합 및 공개 및 비공개 상태 페이지(내부 팀용)와 같은 요구 사항을 충족하기 때문입니다. 이후 게시물에서 MonSpark의 구성을 다루겠지만 지금까지는 API HealthCheck 엔드포인트 설정에 중점을 두었습니다.

NB: 우리는 이것이 올바른 방법인 척하지 않습니다. 많은 구현이 있으며 여기서 제시하는 구현에는 몇 가지 결함이 있을 수 있습니다. 우리는 단지 우리의 생각을 공유할 뿐입니다 ;)

이 모니터링을 수행하는 이유와 무엇을 모니터링해야 합니까?



모니터링은 소프트웨어 개발에서 매우 중요하며 불행히도 많은 팀이 해당 주제에 투자하지 않는 것 같습니다. 귀하의 시스템에 중대한 정전이 발생하거나 일부 서비스가 다운된 경우, 우리 고객이 아닌 것을 가장 먼저 관찰해야 합니다. 또한 오늘날에는 기존 도구의 수로 설정 모니터링이 매우 쉽습니다.

우리의 맥락에서 다음과 같은 경우 API가 작동하는 것으로 간주합니다.
  • 노드 서버가 실행 중입니다
  • .
  • 익스프레스 프레임워크가 시작되었습니다
  • .
  • 데이터베이스를 사용할 수 있으며 쿼리할 수 있습니다
  • .

    그래서 우리는 이러한 요구 사항을 충족하는 엔드포인트를 원했습니다. 익스프레스 서버가 시작되어 API가 노출되지만 데이터베이스 연결이 작동하지 않는 경우가 발생할 수 있습니다. 따라서 API가 정상인지 확인하려면 전체 그림이 필요합니다.

    모니터링하는 방법?



    잘 작동하는 이런 종류의 솔루션을 제안하는 많은 블로그 게시물을 읽었습니다.

    const express = require("express");
    const router = express.Router({});
    router.get('/healthcheck', async (_req, res, _next) => {
        res.status(200).send({'message':'OK');
    });
    // export router with all routes included
    module.exports = router;
    


    데이터베이스 부분이 누락되었습니다. 이 루트 포인트 예제를 사용하여 MongoDB 컬렉션을 쿼리하고 그 안에서 1개의 요소를 찾을 수 있는 경우에만 200 코드를 반환하도록 선택했습니다. 그게 다야.

    기본적으로 구현은 다음과 같습니다. 전체 코드를 추가하지 않았지만 논리를 쉽게 이해할 수 있습니다.

    // Healtcheck.ts
    export class HealthCheck {
        constructor(public event: string) {}
    }
    
    // HealthCheckMongo.ts
    const HealthCheckSchema = new mongoose.Schema(
        {
            event: String,
        },
        {
            collection: 'HealthCheck',
            minimize: false,
        },
    );
    export default mongoose.model('HealthCheck', HealthCheckSchema);
    
    // HealtcheckRepositoryMongo.ts
    async getOrCreate(): Promise<HealthCheck> {
          const data = await this.model.findOneAndUpdate({"event" : "check"}, 
                    {"event" : "check"}, {
                  new: true,
                  upsert: true,
              });
          return data;
    }
    
    //server.ts
    router.get('/healthcheck', async (_req, res, _next) => {
        try {
          const healthCheckData: HealthCheck = await this._healthCheckRepo.getOrCreate();
          const isUp: boolean = healthCheckData !== undefined;
          if (isUp) {
              res.status(200).end();
          } else {
              res.status(502).end();
          }
      } catch(error) {
          res.status(502).end();
      }
    });
    


    "findOneAndUpdate"호출은 컬렉션의 첫 번째 요소를 만드는 데 사용됩니다. 특히 논리가 매우 간단하기 때문에 이것을 단일 파일에 명확하게 넣을 수 있습니다. 그러나 우리는 응용 프로그램에서 육각형 아키텍처를 일관되게 유지하려고 노력하므로 HealthCheck를 위한 육각형이 거의 없습니다! 🙂

    데이터베이스에 미치는 영향?



    "쓸모 없는"쿼리를 실행하면 데이터베이스가 과부하될 수 있다고 생각할 수 있습니다. 솔직히 전용 컬렉션에 대한 이 간단한 쿼리를 분당 한 번 감당할 수 없다면… 먼저 해결해야 할 더 큰 문제가 있다고 생각합니다! 더 나아가 실제 비즈니스 데이터를 쿼리할 수도 있습니다.

    HealthCheck 엔드포인트의 응답 시간은 연결 속도 저하 문제가 있는 경우 데이터베이스 문제를 감지하는 데에도 유용합니다. 예를 들어 응답 시간이 10초를 초과하면 알림을 받도록 모니터링 도구를 조정하여 시간 초과 설정을 조정할 수 있습니다.

    보안 계층 ​​추가



    애플리케이션을 배포한 방법에 따라 엔드포인트가 공개될 수도 있고 공개되지 않을 수도 있습니다. 공개란 나 같은 사람이 엔드포인트를 ping할 수 있다는 의미입니다. 이 끝점은 웹 사이트에 나열되지 않아야 하지만 누군가가 여전히 그 존재를 인식하고 공격을 실행할 수 있습니다. 몇 가지 전략이 존재하며 그 중 하나는 개인 키를 헤더로 추가하는 것입니다.

    컨텍스트에서 코드 PRIVATE_AUTH_HEADER_KEY라는 헤더를 추가합니다.

    router.get('/', privateKeyMiddleware, async (_req, res, _next) => {
        res.status(200).send({'message':'OK');
    });
    
    function privateAuthMiddleware(req: Request, res: Response, next: NextFunction) {
        const key = req.headers[PRIVATE_AUTH_HEADER_KEY];
    
        if (key && key === getPrivateAuthKey()) {
            return next();
        }
    
        return res.sendStatus(401);
    }
    
    function getPrivateAuthKey(): string {
        return process.env.PRIVATE_AUTH_KEY || PRIVATE_AUTH_KEY.default;
    }
    


    물론 이 접근 방식은 SQL 엔진이나 다른 데이터베이스에 대해 동일한 방식으로 적용할 수 있습니다.

    그게 다야, 당신의 방법과 팁을 우리와 자유롭게 공유하십시오 :)

    좋은 웹페이지 즐겨찾기