serverless framework .yml을 작성해보았다.

이번 Developers.IO 한국 블로그를 준비하면서 serverless framework를 도입해보려고 하고 있습니다. 이번에는 "AWS Lambda와 DynamoDB Streams를 활용한 트위터 포스팅봇 만들기"의 기사를 참고로 하여 serverless framework의 .yml(YAML) 파일을 간단히 작성해보도록 하겠습니다.
2020.06.15

이번 Developers.IO 한국 블로그를 준비하면서 serverless framework를 도입해보려고 하고 있습니다. 이번에는 홍기님이 작성하신 AWS Lambda와 DynamoDB Streams를 활용한 트위터 포스팅봇 만들기 을 기준으로 serverless framework의 .yml 파일을 간단히 작성해보도록 하겠습니다.

serverless framework의 .yml(YAML)이란

serverless framework에서는 serverless.yml라는 구성 파일을 작성하여 여러 Resource 등을 설정하고 생성할 수 있습니다. 간단하게 필요한 Lambda 함수에 연결할 여러 Resource 등을 설정하는 파일이라고 생각하시면 됩니다.

그럼 어떻게 이런 동작이 가능할까요?

AWS - Deploying

위의 내용에 따르면 AWS에 deploy(배포)시

실제로는 serverless framework는 serverless.yml을 AWS cloudFormation 템플릿으로 변환 후

cloudFormation을 통해 필요한 Lambda function과 Resource등을 생성하게 됩니다.

더욱 자세한 동작 방식에 대해서 알고 싶으시다면

AWS - Deploying

AWS CloudFormation 작동 방식

두 개의 링크를 참고해 주세요.

.yml(YAML)작성

Lambda functions작성

먼저 필요한 두 개의 함수를 작성하겠습니다.

  • request.js
module.exports.handler = async (event) => {
    return {
        statusCode: 200,
        body: JSON.stringify(
            {
                message: '첫번째 function'
            },
            null,
            2
        )
    }
}
  • response.js
module.exports.handler = async (event) => {
    return {
        statusCode: 200,
        body: JSON.stringify(
            {
                message: '두 번째 function'
            },
            null,
            2
        )
    }
}

첫 번째 Lambda

functions:
  request:
    handler: request.handler
    runtime: nodejs12.x
    memorySize: 512
    timeout: 6
    environment:
      developerIO_Korea_rss: "rss"
    events:
     - schedule:
        name: developerIOKorea-scheduled
        rate: cron(----)
        enabled: true
  • handler: 처음설정한 reuqest.js function을 연결합니다.
  • runtime: nodejs12.x로 설정합니다.
  • momoerySize: Lambda의 memorySize를 설정합니다. 여기서는 512MB로 설정하도록 하겠습니다.
  • timeout: lambda가 한번 호출 할 때 마다 실행되는 시간입니다. 여기서는 6초로 설정하도록 하겠습니다.
  • environment: lambda의 환경변수를 설정할 수 있습니다. (블로그 기사를 가져오기 위한 RSS를 설정하도록 하겠습니다.)
  • event: lambda에 필요한 이벤트를 설정 할 수 있습니다. 첫 번째 lambda에는 AWS Lambda와 DynamoDB Streams를 활용한 트위터 포스팅봇 만들기를 토대로 필요한 cloudWatch의 rules을 설정하였습니다.

두 번째 Lambda

functions:
  response:
    handler: response.handler
    runtime: nodejs12.x
    memorySize: 512
    timeout: 6
    environment:    <- Twitter을 이용하기 위한 token등을 환경변수로 등록 하였습니다.
      access_token_key: "---"
      access_token_secret: "---"
      consumer_key: "---"
      consumer_secret: "---"
    events:
      - stream:
          type: dynamodb
          arn:
            Fn::GetAtt: [usersTable, StreamArn]

두 번째 Lambda는 첫 번째 Lambda와 다른 events에 대해서 알아보겠습니다.

AWS Lambda와 DynamoDB Streams를 활용한 트위터 포스팅봇 만들기 내용과 마찬가지로 dynamodb stream을 연결해야 합니다.

그럼 먼저 dynamodb을 설정해보도록 하겠습니다.

resources:
  Resources:
    usersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: devloperIO_Korea
        AttributeDefinitions:
          - AttributeName: link
            AttributeType: S
        KeySchema:
          - AttributeName: link
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        StreamSpecification:
          StreamViewType: NEW_AND_OLD_IMAGES
  • Type: db의 type입니다. dynamodb로 설정하도록 하겠습니다.
  • AttributeDefinitions: 속성을 설정합니다.
  • KeySchema: dynamodb의 key를 설정합니다.
  • ProvisionedThroughput: 읽기/쓰기 용량을 설정합니다.(dynamodb의 용량란에서 확인할 수 있습니다.)
    • ReadCapacityUnitys: dynamodb의 읽기 용량 설정
    • WriteCapacityUnitys: dynamodb의 쓰기 용량 설정
  • StreamSpecification: dynamodb stream을 설정합니다.
    • StreamViewType: dynamodb stream의 type을 설정합니다. 여기서는 New Image 와 OLD Image로 설정하도록 하겠습니다.
events:
  - stream:
      type: dynamodb
      arn:
        Fn::GetAtt: [usersTable, StreamArn]

이후 위처럼 설정하면 끝입니다.

events: arn:~

와 같이 설정해도 괜찮습니다.

이번에는 Fn::GetAtt 을 참고로 작성해 보았습니다.

마지막으로 필요한 Role(역할)을 설정하도록 하겠습니다.

Role(역할)작성

Role(역할)은 AWS Lambda와 DynamoDB Streams를 활용한 트위터 포스팅봇 만들기의 Role을 기준으로 2개로 나뉘어 각 Lambda에 연결해 주도록 하겠습니다.

policy에 관한 내용도 홍기님이 작성하신 policy에 맞춰서 작성하였습니다.

2개의 Role과 관련된 정책을 작성해 줍니다.

devKoreaRequestRole:
      Type: AWS::IAM::Role
      Properties:
        Path: /my/default/path/
        RoleName: devKoreaRole
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: devloperIO_Korea_policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: 
                    - logs:CreateLogGroup
                    - logs:CreateLogStream
                    - logs:PutLogEvents
                  Resource: "*"
                - Effect: Allow
                  Action:
                    - dynamodb:PutItem
                  Resource: 
                    Fn::GetAtt: [usersTable, Arn]
    devKoreaResponseRole:
      Type: AWS::IAM::Role
      Properties:
        Path: /my/default/path/
        RoleName: devKoreaRole
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: devloperIO_Korea_policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: 
                    - logs:CreateLogGroup
                    - logs:CreateLogStream
                    - logs:PutLogEvents
                  Resource: "*"
                - Effect: Allow
                  Action:
                    - dynamodb:DescribeStream
                    - dynamodb:GetRecords
                    - dynamodb:GetShardIterator
                    - dynamodb:ListStreams
                  Resource: 
                    Fn::GetAtt: [usersTable, StreamArn]

크게 두 부분으로 나누어져 있습니다.

  • Role: lambda에 연결할 Role(역할)을 정의합니다.
    • RoleName: Role의 이름을 설정
    • AssumeRolePolicyDocument: 자신이 원하는 역할의 policy를 설정하시면 됩니다.
  • Policies: 원하시는 policy를 정의하시면 됩니다. 구조는 기존 policy를 작성 할 때와 비슷합니다.
    • PolicyName: policy의 이름을 설정
    • PolicyDocument: 자신이 원하는 policy설정하시면 됩니다.
  • Reousrce: arn은 Fn::GetAtt을 참고로 작성하였습니다.

Lambda에 연결해 줍니다.

functions:
  request:
    handler: request.handler
    ...
    role: devKoreaRequestRole <- Role(역할)을 연결
  response:
    handler: response.handler
    ...
    role: devKoreaResponseRole <- Role(역할)을 연결

전체

마지막으로 전체 내용입니다.

service: developerIO

provider:
  name: aws
  stage: dev
  region: ap-northeast-2

functions:
  request:
    handler: request.handler
    runtime: nodejs12.x
    memorySize: 512
    timeout: 6
    environment:
      developerIO_Korea_rss: "---/"
    events:
     - schedule:
        name: developerIOKorea-scheduled
        rate: cron(---)
        enabled: true
    role: devKoreaRequestRole

  response:
    handler: response.handler
    runtime: nodejs12.x
    memorySize: 512
    timeout: 6
    environment:
      access_token_key: "---"
      access_token_secret: "---"
      consumer_key: "---"
      consumer_secret: "---"
    events:
      - stream:
          type: dynamodb
          arn:
            Fn::GetAtt: [usersTable, StreamArn]
    role: devKoreaResponseRole

resources:
  Resources:
    usersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: devloperIO_Korea
        AttributeDefinitions:
          - AttributeName: link
            AttributeType: S
        KeySchema:
          - AttributeName: link
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        StreamSpecification:
          StreamViewType: NEW_AND_OLD_IMAGES
        
        
    devKoreaRequestRole:
      Type: AWS::IAM::Role
      Properties:
        Path: /my/default/path/
        RoleName: devKoreaRequestRole
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: devloperIO_Korea_policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: 
                    - logs:CreateLogGroup
                    - logs:CreateLogStream
                    - logs:PutLogEvents
                  Resource: "*"
                - Effect: Allow
                  Action:
                    - dynamodb:PutItem
                  Resource: 
                    Fn::GetAtt: [usersTable, Arn]
    devKoreaResponseRole:
      Type: AWS::IAM::Role
      Properties:
        Path: /my/default/path/
        RoleName: devKoreaResponseRole
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: devloperIO_Korea_policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: 
                    - logs:CreateLogGroup
                    - logs:CreateLogStream
                    - logs:PutLogEvents
                  Resource: "*"
                - Effect: Allow
                  Action:
                    - dynamodb:DescribeStream
                    - dynamodb:GetRecords
                    - dynamodb:GetShardIterator
                    - dynamodb:ListStreams
                  Resource: 
                    Fn::GetAtt: [usersTable, StreamArn]

참고자료

AWS Lambda와 DynamoDB Streams를 활용한 트위터 포스팅봇 만들기

serverless doc

AWS - Deploying

AWS CloudFormation 작동 방식

Fn::GetAtt