Serverless 프레임워크 빠르게 배우기 (1)

Serverless 프레임워크 빠르게 배우기 (1)

인프라에 대한 걱정을 줄여주는 서버리스는 배우고 나면 정말 편하겠다고 생각한 지가 대충 한 3개월 정도 되어가는 것 같다. 처음 람다를 경험한 건 AWS 아마톤 행사에서 사용했는데, 배포가 정말 코드 한 줄로 이루어 지는 것을 경험하고 (같이 하신 분이 환경 구성을 정말 잘 해주신 것이라고 생각하지만) 나중에 따로 프로젝트에서 꼭 써봐야지 했는데, 이제서야 점점 공부를 시작한다. 우선 람다에 대한 개념은 대충 알고 있으니 서버리스 배포를 편하게 도와주는 몇 서버리스 프레임워크 중에, 그 당시에 그나마 진입 장벽이 낮다고 추천 해주셨던 Serverless 라는 서버리스 프레임워크를 사용해보려고 한다.

일단 이 글은 AWS provider를 중심으로 공부한 내용을 정리할 예정이고, 내가 실습한 내용 보다는 Serverless에서 제공해주는 유저 가이드를 공부한 내용 위주로 정리할 생각이다.

핵심 개념들

Functions

여기서 함수들은 AWS 람다를 의미한다. 이것들은 마이크로서비스처럼 독립적인 배포 단위이다. 그리고 클라우드에서 배포되는 단순한 코드이다. 또 보통 단일한 역할을 수행하도록 설계된다.

Events

AWS 람다를 실행하도록 트리거 역할을 하는 것을 말한다. AWS에서는 보통 아래와 같은 이벤트가 있다고 보면 된다.

  • API Gateway
  • S3
  • CloudWatch timer
  • AWS SNS topic
  • CloudWatch Alert
  • 기타 등등

Serverless 프레임워크(이하 serverless)에다가 이벤트를 정의하면 serverless는 알아서 AWS 이벤트를 생성하고 이벤트를 람다와 연결되게 설정한다.

Resources

리소스는 람다가 사용하는 AWS 인프라라고 볼 수 있다. 아래와 같은 것들을 의미한다.

  • DynamoDB Table
  • S3
  • SNS Topic
  • CloudFormation에서 정의될 수 있는 어떤 것이든 가능함

마찬가지로 리소스들도 serverless를 사용해서 구성할 수 있다.

Services

함수들을 모아둔 것이다. 함수는 위의 리소스와 이벤트를 가지고 하나의 단일 역할을 한다면 서비스는 이런 역할들을 모아서 하나의 서비스를 이룰 수 있다.

Plugins

serverless의 기능을 확장하거나 덮어 씌울 때 사용한다. 모든 serverless.yml은 plugins를 포함할 수 있고 여러개를 사용할 수 있다.

설치

일단 serverless는 Node 패키지로 구성되어있다. 아래 명령어로 CLI를 설치할 수 있다.

1
2
3
4
5
6
7
8
9
$ npm i -g serverless

$ serverless --version

Framework Core: 1.58.0
Plugin: 3.2.5
SDK: 2.2.1
Components Core: 1.1.2
Components CLI: 1.4.0

설치 한 다음 AWS 계정을 연결해주면 되는데 간단하게 프로젝트를 해 봤을 때 serverless.yml에 aws cli에 등록된 profile을 적어주는 방법으로 해서 따로 설정해주지 않았다. aws cli에 등록된 profile을 이용하거나 default profile를 serverless에 등록해줄 수도 있다. 여기 링크에서 AWS 계정을 연결하는 방법을 설명하고 있다. 만약 그 전에 AWS IAM을 사용해 사용자를 추가하는 방법을 모른다면 간단하게 벨로퍼트님의 자료를 확인해보면 좋을 것 같다.

Services 알아보기

서비는 하나의 프로젝트같은 것이다. serverless를 사용하기 위해서는 service를 만들어야 한다. serverless에서 service를 만드는 것은 yml 파일을 조작하는 것으로써 가능하다. 간단한 예제를 만들어보자.

프로젝트 폴더를 만들고, 최상단에 serverless.yml를 만들었다.

1
2
myProject/
serverless.yml

위 파일에 모든 함수들과 리소스들을 담아내면 된다. 그런데 앱은 계속 커지기 마련이니, 하나의 파일에 모든 함수들을 정의하다 보면 정말 복잡한 파일이 구성된다. serverless는 yml파일을 서비스 단위로 쪼갤 수 있다. 예를 들어서 아래와 같이 구성할 수도 있다.

1
2
3
4
5
6
users/
serverless.yml
posts/
serverless.yml
comments/
serverless.yml

위와 같이 서비스를 만들 때 create 명령어를 사용할 수 있는데, 아래와 같이 사용할 수 있다. (수동으로 구성할 수도 있겠지만, serverless에서는 강하게 권하고 있는 것 같다.)

1
2
# ./serverless-test 아래 Node 탬플릿을 가지고 서비스를 만든다.
serverless create --template aws-nodejs --path serverless-test

템플릿은 serverless에서 만들어둔 틀인데 노드를 선택한 것이다. (간단하게 테스트 해봤을 때 타입스크립트 템플릿에는 현재 날짜 기준으로 버그가 있는 것 같다. 웹팩이 handler export 이름을 없애서 람다에서 함수를 가져오지 못 하는 것 같다.)

create 커맨드에 대해서 자세하게 정리한 문서는 이 링크에서 확인 가능하다.

이렇게해서 만들어진 내용을 확인해 보면 아래와 같다.

1
2
3
serverless-test/
handler.js
serverless.yml

먼저 serverless.yml를 확인해보면 서비스 설정 값들이 관리되고 있는 파일이란 걸 알 수 있다. 정확하게는 아래와 같은 역할을 한다고 볼 수 있다.

  • 서버리스 서비스를 선언
  • 서비스에 들어갈 한 개 이상의 함수를 정의
  • Provider 정의 (AWS, GCP, Azure …, 여기선 AWS)
  • Plugin 정의
  • 함수들을 실행할 이벤트들을 정의함
  • 함수에서 사용되는 리소스들을 정의함
  • 이벤트 섹션에 나열된 이벤트들이 개발시에 이벤트들이 요구하는 리소스들을 자동적으로 생성하도록 허용함
  • 서버리스의 변수들을 사용해서 유연한 설정값을 만들도록 허용함

현재 만들어진 serverless.yml은 주석을 다 빼면 다음과 같은 모습을 하고 있다.

1
2
3
4
5
6
7
8
9
service: serverless-test

provider:
name: aws
runtime: nodejs12.x

functions:
hello:
handler: handler.hello

그리고 정말 고맙게도 사용 되지 않은 기능들을 비교적 구체적인 예시들로 주석을 채워놨다. 문서에서 보여주고 있는 사용 가능한 기능, 옵션 등은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# serverless.yml

service: users

provider:
name: aws
runtime: nodejs12.x
stage: dev # Set the default stage used. Default is dev
region: us-east-1 # Overwrite the default region used. Default is us-east-1
stackName: my-custom-stack-name-${self:provider.stage} # Overwrite default CloudFormation stack name. Default is ${self:service}-${self:provider.stage}
apiName: my-custom-api-gateway-name-${self:provider.stage} # Overwrite default API Gateway name. Default is ${self:provider.stage}-${self:service}
profile: production # The default profile to use with this service
memorySize: 512 # Overwrite the default memory size. Default is 1024
deploymentBucket:
name: com.serverless.${self:provider.region}.deploys # Overwrite the default deployment bucket
serverSideEncryption: AES256 # when using server-side encryption
tags: # Tags that will be added to each of the deployment resources
key1: value1
key2: value2
deploymentPrefix: serverless # Overwrite the default S3 prefix under which deployed artifacts should be stored. Default is serverless
versionFunctions: false # Optional function versioning
stackTags: # Optional CF stack tags
key: value
stackPolicy: # Optional CF stack policy. The example below allows updates to all resources except deleting/replacing EC2 instances (use with caution!)
- Effect: Allow
Principal: "*"
Action: "Update:*"
Resource: "*"
- Effect: Deny
Principal: "*"
Action:
- Update:Replace
- Update:Delete
Resource: "*"
Condition:
StringEquals:
ResourceType:
- AWS::EC2::Instance

functions:
usersCreate: # A Function
handler: users.create
events: # The Events that trigger this Function
- http: post users/create
usersDelete: # A Function
handler: users.delete
events: # The Events that trigger this Function
- http: delete users/delete

# The "Resources" your "Functions" use. Raw AWS CloudFormation goes in here.
resources:
Resources:
usersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: usersTable
AttributeDefinitions:
- AttributeName: email
AttributeType: S
KeySchema:
- AttributeName: email
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1

serverless.yml는 하나의 AWS CloudFormation stack을 구성하게 된다. 그렇다면 서비스마다 CloudFormation stack을 구성하는 셈이다.

handler.js는 순수 JS 코드를 담고 있다. Monolithic하게 구성된 백앤드에서도 services를 API 바로 전에 두는 편이라 전반적으로 serverless가 어떻게 구성되는지 이해하기 쉬웠다. handler.js는 여러 함수를 export 할 수 있고, 내보내진 함수를 serverless.yml이 가르키고 있는 구조이다.

마치며

배포 방법은 간단하게 deploy 명령어로 한다. 이와 관련된 가이드와 명령어 문서도 있긴 한데, 자세하게 공부하지는 않았다. 더 디테일한 내용은 필요에 따라서 보려고 한다. 일단 서버리스 프레임워크를 사용해도 기존에 Monolithic 사용하던 3 Layer architecture를 사용할 수 있을 것 같다(그게 베스트가 아닐 수도 있지만).

Reference

댓글

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×