Serverless의 이해 (API Gateway, AWS Lambda)

손연주·2022년 3월 3일
8

Deploy a REST API using Serverless, typeScript, Express and Node.js

Getting Started

Serverless를 사용하기 위해선 Serverless Framework 설치와, AWS credentials이 필요하다.

Creating and deploying a single endpoint

$ mkdir my-express-application && cd my-express-application
$ npm init -f

새로운 package.json file과 함께 directory 생성한다.

install express and serverless-http

$ npm install --save express serverless-http

express framework와 serverless-http를 설치한다. serverless-http packages는 Node.js 어플리케이션과 API Gateway 간의 인터페이스를 처리하는 미들웨어다.

Amazon API Gateway란?

API를 쉽게 생성, 게시, 유지 관리, 모니터링 및 보호할 수 있는 완전 관리형 서비스이다. API는 애플리케이션이 백엔드 서비스의 데이터, 비즈니스 로직 또는 기능에 액세스할 수 있도록 하는 창구 역할을 한다. 자세하게 설명하자면, API Gateway는 API 백엔드 서비스와 API 사용자 사이에 위치하여 API 엔드포인트에 대한 HTTP 요청을 처리하고 올바른 백엔드로 라우팅한다. API 정의 및 엔드포인트와 해당 백엔드 서비스 간의 매핑을 관리하는 데 도움이 되는 도구 세트를 제공한다. 또한 API 참조를 생성하고 API 문서로 사용자에게 제공할 수 있다. AWS Lambda, AWS SNS, AWS IAM 등과 같은 다른 많은 AWS 서비스와 통합된다.

이렇듯 API Gateway는 서버리스 기능과 API 정의를 연결하는 부분이다. HTTP 요청에 대한 응답으로 직접 서버리스 기능 실행을 트리거할 수 있기 때문에 서버리스 설정에서 필수적인 이유이다.

  • AWS Lambda: Lambda 함수를 실행하여 HTTP API 응답을 생성
  • AWS SNS: HTTP API 엔드포인트에 액세스할 때 SNS 알림을 게시
  • Amazon Cognito: HTTP API에 대한 인증 및 권한 부여를 제공

API Gateway는 Serverless Framework와 어떻게 작동할까?

간단한 HTTP API의 경우 serverless.yml 파일에서 바로 서버리스 기능에 연결할 API 게이트웨이 엔드포인트를 지정한다.

serverless.yml 
functions:
 index:
  handler: handler.hello
  events:
   - http: GET hello # this is the API Gateway event

여기서, API request는 handler.hello handler로 전달되고 Serverless function을 이용가능하게 만들어준다.

API Gateway 해당 주소에서 API Gateway 예시를 더 많이 확인할 수 있다.

index.js

const serverless = require('serverless-http');
const express = require('express')
const app = express()

app.get('/', function (req, res) {
  res.send('Hello World!')
})

module.exports.handler = serverless(app);

root path /에 요청이 들어오면 간단하게 “Hello World”를 띄울 수 있는 코드다. serverless-http package를 첫번째 줄에서 import하였고, handler 함수를 export 하고 있다.

Application Deploy

배포를 위해서 serverless.yml 파일을 설정해야 한다.

# serverless.yml

service: my-express-application // project 이름

provider:
  name: aws
  runtime: nodejs6.10
  stage: dev
  region: us-east-1 // 배포할 region 

functions:
  app:
    handler: index.handler
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'

기본적인 구성이다. app이라는 함수를 만들었고 이 함수는 index.js에서 exported된 함수인 handler를 사용하고 있다. 이 과정에서 API Gatway와 연결이 되었고, HTTP trigger로 구성되었다.

$ sls deploy
... snip ...
Service Information
service: my-express-application
stage: dev
region: us-east-1
stack: my-express-application-dev
api keys:
  None
endpoints:
  ANY - https://bl4r0gjjv5.execute-api.us-east-1.amazonaws.com/dev
  ANY - https://bl4r0gjjv5.execute-api.us-east-1.amazonaws.com/dev/{proxy+}
functions:
  app: my-express-application-dev-app

deploy를 하면 endpoints가 생성되고, 해당 주소로 테스트 할 수 있다.

함수가 여러개일 때의 Serverless.yml 예시

# serverless.yml

functions:
  app:
    handler: index.handler
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'
  getUser:
    handler: index.handler
    events:
      - http: 'GET /users/{proxy+}'
  createUser:
    handler: index.handler
    events:
      - http: 'POST /users'

GET /users/:userId 로 들어오는 모든 request들은 getUser instance에 의해 처리되고, POST /users/ 로 들어오는 모든 request들은 createUser instance에 의해 처리된다. 다른 request들은 main 함수인 app instance에 의해 처리될 것이다.

API Gateway REST API

HTTP 엔드포인트를 AWS Lambda 함수의 이벤트 소스로 생성하려면 Serverless Framework의 간편한 AWS API Gateway Events 구문을 사용할 수 있다. AWS Lambda 함수와 통합하도록 HTTP 엔드포인트를 구성할 수 있는 방법은 5가지(lambda-proxy/ aws-proxy/ aws_proxy(권장), lambda/aws, http, http-proxy/http_proxy, mock)가 있지만, default로 lambda-proxy 방법을 사용하는 것을 권장한다. lambda-proxy는 HTTP request를 자동적으로 AWS Lambda function(headers, body, etc.)으로 전달하고 response(headers, status code, body)를 AWS Lambda 함수의 코드에 담을 수 있도록 허락한다. 반면에 lambda 의 방식은 header, status codes 등을 각각 API Gateway Endpoint(not in code)에 정의하도록 하기 때문에 지양하는 것을 권한다.

Lambda Proxy Integration

AWS - Events

이벤트는 함수가 실행되도록 하는 trigger다. AWS를 사용 중이라면 AWS 안에 있는 모든 events는 AWS Lambda function을 실행시킬 수 있다. (e.g. S3 bucket upload, HTTP endpoints created via API Gateway)

Configuring endpoint types

API Gateway REST API를 특정 리전과 연결하기 위한 리전 엔드포인트를 지원한다.

service:  my-service 
provider: 
  name:  aws 
  endpointType:  REGIONAL 
functions: 
  hello: 
    events: 
      -  http: 
          path:  user/create 
          method:  get

API Gateway HTTP API

API Gateway를 사용해 HTTP API를 배포할 수 있다.

# Event Definition 
# General setup
functions:
  simple:
    handler: handler.simple
    events:
      - httpApi: 'PATCH /elo'
  extended:
    handler: handler.extended
    events:
      - httpApi:
          method: POST
          path: /post/just/to/this/path
# Parameters
functions:
  params:
    handler: handler.params
    events:
      - httpApi:
          method: GET
          path: /get/for/any/{param}

정리

functions:
  # lambda에서 표시되는 함수 이름
  function1:
    # 등록할 함수 경로
    handler(임의): src/controllers/...
    description: if needed
    # API Gateway 설정
    events:
      - httpApi:
          method: POST
          path: /user/mypage (example)
  function2:
    handler: src/service/..
    description: ...

함수 이름을 지정하고, 등록할 함수 경로를 설정한다. 필요에 의해 description을 달 수도 있다. events 설정을 하여 methodpath를 지정해줄 수 있다.

Serverless plugins

serverless-offline

Local development configuration with Serverless offline plugin
로컬 개발과 테스트를 위해 API 게이트웨이 환경을 설정한다.

$ npm install --save-dev serverless-offline

install 한 다음 serverless.yml에 plugin을 추가해준다.

# serverless.yml

plugins:
  - serverless-offline

start the serverless-offline server:

$ sls offline start
Serverless: Starting Offline: dev/us-east-1.

Serverless: Routes for app:
Serverless: ANY /
Serverless: ANY /{proxy*}

Serverless: Routes for getUser:
Serverless: GET /users/{proxy*}

Serverless: Routes for createUser:
Serverless: POST /users

Serverless: Offline listening on http://localhost:3000$ sls offline startServerless: Starting Offline: dev/us-east-1. Serverless: Routes for app:Serverless: ANY /Serverless: ANY /{proxy*} Serverless: Routes for getUser:Serverless: GET /users/{proxy*} Serverless: Routes for createUser:Serverless: POST /users Serverless: Offline listening on http://localhost:3000

serverless-dotenv-plugin

환경변수를 호출하여 쓸 수 있다.

plugins:
  - serverless-dotenv-plugin
...
...
provider:
  name: aws
  runtime: nodejs6.10
  stage: ${env:STAGE}
  region: ${env:AWS_REGION}
...

serverless-plugin-typescript

Typescript 지원을 위한 서버리스 플러그인

yarn add --dev serverless-plugin-typescript typescript
# or
npm install -D serverless-plugin-typescript typescript
# Add the following plugin to your serverless.yml:
plugins: 
  - serverless-plugin-typescript
# tsconfig.json
{
  "compilerOptions": {
    "preserveConstEnums": true,
    "strictNullChecks": true,
    "sourceMap": true,
    "allowJs": true,
    "target": "es5",
    "outDir": ".build",
    "moduleResolution": "node",
    "lib": ["es2015"],
    "rootDir": "./"
  }
}

serverless-ignore

.gitignore와 같은 파일을 무시하는 서버리스 플러그인

  • serverless.yml에서 하드코딩된 제외 파일의 긴 목록 제거
  • 다른 serverless.yml 간의 관리 개선(다른 .slsignore 추가)
  • .gitignore와 동일한 접근 방식
    $ npm install --save-dev serverless-ignore
    Usage

Add serverless-ignore to your plugins list (serverless.yml)

plugins:
  - serverless-ignore

Add a .slsignore in your root folder with a .gitignore-like syntax with all the files you want to ignore

Example:

# it works like a .gitignore
# for sls
README.md
*.log

# ignore aws-sdk
node_modules/aws-sdk/*

.env.example
.git/*
__tests__/*
# it works like a .gitignore# for slsREADME.md*.log # ignore aws-sdknode_modules/aws-sdk/* .env.example.git/*__tests__/*

serverless-domain-manager

AWS Lambda가 Serverless로 배포할 수 있는 사용자 지정 도메인 이름을 만든다. 도메인 이름을 배포 및 삭제할 때 기본 경로 매핑을 허용한다.

$ npm install serverless-domain-manager --save-dev

plugins:
  - serverless-domain-manager # Add the plugin configuration 
  
custom:
  customDomain:
    domainName: serverless.foo.com
    stage: ci
    basePath: api
    certificateName: '*.foo.com'
    createRoute53Record: true
    endpointType: 'regional'
    securityPolicy: tls_1_2
    apiType: rest
    autoDomain: false

Running

To create the custom domain:
$ serverless create_domain

To deploy with the custom domain:
$ serverless deploy

To remove the created custom domain:
$ serverless delete_domain

profile
할 수 있다는 생각이 정말 나를 할 수 있게 만들어준다.

2개의 댓글

comment-user-thumbnail
2022년 3월 25일

위와 같은 구성으로 서비스에서 실제 사용중이신가요?
Lambda의 coldsatrt로 인한 답답함이 느껴지진 않으신가요?

1개의 답글