AWS Nuxt인스턴스에 x-ray(분산추적) 적용하기

Server

(Update : 2022-08-30)

Language :

안녕하세요 Lovefield입니다.

이번에 Nuxt로 넘어오면서 개발 구조가 마이크로 서비스화 되었습니다. 마이크로 서비스화가 되면서 사이트 접속시 요청에 대해서 `front-end -> api -> back-end` 순으로 실행됩니다. 최소 3번의 요청이 합쳐져야 페이지 하나를 표시 할 수 있게 되었죠, 이 상태에서 AWS x-ray를 연결하는 방법에 대한 글입니다.

먼저 Nuxt에 “server middleware”를 등록하고 Axios 설정을 합니다.

…
modules: ["@nuxtjs/axios"],

axios:{
    proxy: true
},

proxy: {
       "/api/": {
           target: "BACKEND-URL",
           pathRewrite: {
               "^/api/": "/api/v1/",
           },
       },
   },

serverMiddleware : ["serverMiddleware/x-ray"]

클라이언트는 프론트서버에 화면에 대한 요청과 API에 대한 요청을 하게됩니다. 즉 이 두가지를 구분해야함에 유의해서 코드를 작성합니다. x-ray역할을 할 파일인 “x-ray.js”를 “serverMiddleware”폴더에 만들어줍니다.

// x-ray는 소캣통신을 하기때문에 “dgram” 모듈을 사용합니다. (노드 기본 모듈)
import dgram from "dgram";

// 구분자 아이디 생성용 함수 a~f + 0~9로 이루어진 16자리여야 합니다.
function createRequestId() {
   let possible = "abcdef0123456789";
   let text = "";
   for (let i = 0; i < 16; i += 1) {
       text += possible.charAt(Math.floor(Math.random() * possible.length));
   }
   return text;
}

// 실제 작동 코드 부분입니다.
export default function (req, res, next) {
   let headers = req && req.headers ? Object.assign({}, req.headers) : {};
   let traceHeader = headers["x-amzn-trace-id"]; // aws는 LB가 리퀘스트 헤더에 x-amzn-trace-id를 항상 붙여줍니다.
   let requestURL = req.url;
   let apiReg = new RegExp("\\/api", "i");

   if (traceHeader !== undefined) {
       let server = dgram.createSocket("udp4");
       let requestDate = new Date().getTime() / 1000;
       let requestId = createRequestId();
       let isApiRequest = apiReg.test(requestURL);
       let traceId = traceHeader.substr(5, 35);

       if (isApiRequest == false) { // api 일경우 request에 데이터를 추가합니다. (axios plugin에서 사용)
           req.requestId = requestId;
           req.traceId = traceId;
       }

       // response가 끝나면 실행합니다.
       res.on("finish", () => {
           let jsonData = {
               name: `appname`,
               id: requestId,
               origin: "AWS::EC2::Instance",
               trace_id: traceId,
               start_time: requestDate,
               end_time: new Date().getTime() / 1000,
               http: {
                   request: {
                       url: req.url,
                       method: req.method,
                       user_agent: headers["user-agent"],
                       x_forwarded_for: true,
                   },
                   response: {
                       status: res.statusCode,
                   },
               },
           };

           if (isApiRequest == true) {
               jsonData.type = "subsegment";
               jsonData.parent_id = headers["requestid"];
           }

           let message = Buffer.from(`{"format": "json", "version": 1}\n` + JSON.stringify(jsonData));

           server.send(message, 2000,"localhost", (err) => { // x-ray 는 로컬로 정보를 정송합니다.
               server.close();
           });
       });
   }

   next();
}

기본적인 동작 코드는 위와 같습니다.
이제 axios plugin에서 몇가지 설정을 더 해야합니다.

export default function ({ req, $axios }) {
   let requestId = req ? req.requestId : undefined;

   $axios.onRequest((config) => {
       if (requestId !== undefined) {
           config.headers.common["requestid"] = requestId;
           config.headers.common["X-Amzn-Trace-Id"] = `Root=${req.traceId};Parent=${requestId}`;
       }
   });
}

리퀘스트를 행할때 `x-ray.js` 에서 설정한 `requestId`가 있을경우 api request에도 해당 값을 넣어줍니다.

앱에서 설정할 사항은 전부 끝났습니다. 이제 AWS 인스턴스 에 접속해서 x-ray를 실행해줘야 하는데요.
인스턴스에 x-ray 설치 를 진행한 후에 `sudo service xray start`후 `sudo service xray status` 로 상태를 체크합니다.x-ray 기록은 CludWatch -> x-ray 기록 에서 보실수 있습니다.

Lovefield

Web Front-End developer

하고싶은게 많고, 나만의 서비스를 만들고 싶은 변태스러운 개발자입니다.