본문 바로가기
AWS

node.js(express) app을 AWS 서버리스 환경(lambda, API gateway)으로 배포

by misankim 2023. 4. 1.

최근 게시물을 통해 api 서버 구성 관련 내용을 공유해드렸는데요.
오늘은 지난번 서버(컨테이너)에 구성한 node.js app을 AWS 서버리스 환경(lambda+API gateway)으로 배포하는 내용으로 학습 내용 공유드립니다.

1. express 웹 프레임워크로 구성된 api 서버

지난 게시글에서 api 서버를 구성하기 위해 express 웹 프레임워크를 사용했습니다. node.js로 http 서버를 구성하기 위해서는 "server.listen(3000);"과 같이 트래픽 수신을 포함하여 웹서버 구성에 필요한 여러 소스들을 일일 설정해줘야합니다. 이런 웹서버를 구성하기 위한 기본 뼈대를 만들어주는 역할이 웹 프레임워크로, express는 가장 널리 쓰이는 node.js 웹 프레임워크라고 합니다.

express 프레임워크를 이용하여 기본 웹서버 구성 시 생성되는 "routes/index.js" 파일은 웹을 통해 접속이 이뤄졌을 때, 메소드(GET, POST 등) 및 경로(/, /select, /api 등)에 따라 어떻게 응답할 것인지 정의가 되어 있습니다. 아래는 기본 파일로 / 경로에 대해서만 index 파일을 렌더링하여 응답하도록 설정되어 있습니다.

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

아래는 지난번 게시글을 통해 구성한 node.js 백업 결과 api 서버의 소스 들의 역할을 간단히 설명한 것입니다.

routes/index.js : 메소드 및 경로에 따라 어떤 함수를 호출할지 정의
controllers/index.js : 각 함수를 정의
config/database.js : 데이터베이스 접속 정보를 정의

2. API gateway와 lambda 의 역할

일반적으로 API gateway의 리소스 메뉴에는 node의 routes/index.js 파일과 같이 어떤 메소드로, 경로로 트래픽이 유입되었을 때 어떤 람다 함수로 트래픽을 라우팅할지 정의되어 있습니다.

람다 함수는 controllers/index.js, config/database.js 등 실행될 함수의 내용을 포함하고 있습니다.

3에서 다룰 express를 이용하여 구성된 node 앱을 AWS의 서버리스로 배포하기 위해서는 claudia 라는 node 모듈을 이용합니다. claudia를 이용하여 구성된 API gateway는 메소드, 경로에 따라 각각의 람다 함수로 연결하는 것이 아닌, 람다 프록시를 이용하여 모든 트래픽을 단일 람다 함수로 라우팅합니다.

https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html

Amazon API Gateway의 Lambda 프록시 통합은 단일 API 메서드의 설정을 통해 API를 구축하기 위한 간단하고 강력하며 민첩한 메커니즘입니다. Lambda 프록시 통합에서 클라이언트가 API 요청을 제출하면 API Gateway는 통합된 Lambda 함수로 원래 요청을 있는 그대로 전달합니다.

람다 프록시에 대한 내용은 일반적인 API gateway와 lambda를 통한 구성과는 조금 다르기 때문에 언급하였습니다.

3. claudia를 통해 app을 서버리스 환경으로 배포

앞선 내용들은 개념적인 내용으로, 실질적인 구성은 지금부터 시작됩니다. 먼저 지난 게시물을 통해 구성이 완료된 testapp(임의로 정한 앱 이름)이 있다는 상황을 가정하고 진행합니다. 아직 서버에 구성된 앱이 없는 경우 지난 게시물을 참고하여 앱을 구성하고 진행하면 됩니다. claudia는 express 웹 프레임워크로 제작된 앱을 AWS 서버리스로 배포하는 aws-serverless-express 모듈을 기반으로 작동하는 모듈입니다. 먼저 claudia를 설치합니다.

# 설치(전역 설치)
npm install claudia -g

# 버전 확인
claudia --version

그리고 AWS API 사용을 위한 인증을 설정하기 위해 "~/.aws/credentials" 파일 혹은 환경변수로 인증을 구성합니다. 해당 AWS 사용자 권한으로는 아래와 같은 권한이 필요합니다.

AWSLambdaFullAccess
IAMFullAccess
AmazonAPIGatewayAdministrator

이제 설치가 완료되었으니 아래 명령어를 통해 기존 express 앱에 aws-serverless-express 모듈을 추가하고 람다 함수 파일을 생성합니다.

claudia generate-serverless-express-proxy --express-module app

명령어의 출력은 아래와 같습니다.

마지막으로 아래 명령어를 통해 람다 함수와 API gateway를 AWS 상에 배포합니다.

claudia create --handler lambda.handler --deploy-proxy-api --region ap-northeast-2

이제 AWS 콘솔에서 생성된 API gateway를 확인합니다. 접속 주소는 "https://u15hv2gqs4.execute-api.ap-northeast-2.amazonaws.com/latest" 와 같이 나타납니다.

생성된 URL로 접속 시 아래와 같이 정상적으로 접속이 됩니다.

지난 게시글에서의 테스트 환경은 데이터베이스를 도커 컨테이너로 구성했지만 이번에는 RDS를 생성하여 람다 함수를 통해 데이터를 쌓도록 설정했습니다. 람다 함수가 RDS에 접근할 수 있도록 AWS 람다 콘솔을 통해 데이터베이스 접속 정보를 수정해줍니다.

소스 수정은 완료되었으니 람다 함수가 VPC 네트워크 인터페이스를 제어할 수 있도록 AWSLambdaVPCAccessExecutionRole 정책을 람다 함수 실행 역할에 추가해줍니다. 기존 람다 함수의 실행 역할은 함수의 권한 탭에서 확인 가능합니다.

AWSLambdaVPCAccessExecutionRole 정책을 추가한 모습입니다.

AWSLambdaVPCAccessExecutionRole 정책에 포함된 세부 정책 내용입니다.

마지막으로 람다 함수가 VPC 내부의 RDS와 통신할 수 있도록 람다 함수의 구성 탭에서 VPC 설정을 수정해줍니다. 서브넷 및 보안그룹 설정을 적절히 해줍니다.

이제 구성은 완료되었습니다. 테스트를 진행합니다.

4. 작동 테스트

echo 테스트(전송한 json 코드를 그대로 응답)

curl -H "Content-Type: application/json" -X POST -d "{\"Date\": \"`date +%Y%m%d`\",\"Hostname\": \"$HOSTNAME\", \"PRI_IP\": \"`ifconfig eth0 | head -2 | tail -1 | sed 's/ //g' | awk -Finet '{ print $2 }' | awk -Fnetmask '{ print $1 }'`\", \"PUB_IP\": \"`curl -s ifconfig.me`\", \"Result\":\"running\"}" https://u15hv2gqs4.execute-api.ap-northeast-2.amazonaws.com/latest/backup_result/api | jq

백업 시작

curl -H "Content-Type: application/json" -X POST -d "{\"Date\": \"`date +%Y%m%d`\",\"Hostname\": \"$HOSTNAME\", \"PRI_IP\": \"`ifconfig eth0 | head -2 | tail -1 | sed 's/ //g' | awk -Finet '{ print $2 }' | awk -Fnetmask '{ print $1 }'`\", \"PUB_IP\": \"`curl -s ifconfig.me`\", \"Result\":\"running\"}" https://u15hv2gqs4.execute-api.ap-northeast-2.amazonaws.com/latest/backup_result/api_start | jq

RDS에 입력된 데이터

백업 종료

curl -H "Content-Type: application/json" -X POST -d "{\"Date\": \"`date +%Y%m%d`\",\"Hostname\": \"$HOSTNAME\", \"PRI_IP\": \"`ifconfig eth0 | head -2 | tail -1 | sed 's/ //g' | awk -Finet '{ print $2 }' | awk -Fnetmask '{ print $1 }'`\", \"PUB_IP\": \"`curl -s ifconfig.me`\", \"Result\": \"success\", \"File_check\": \"300/300,100%\"}" https://u15hv2gqs4.execute-api.ap-northeast-2.amazonaws.com/latest/backup_result/api_end | jq

RDS에 입력된 데이터