본문 바로가기
IT/Serverless Stack

[AWS] 서버리스 스택-1

by 밤톨엽 2022. 3. 21.

현재 문서는 Serverless-Stack 가이드를 기반으로 문서를 작성하게 되었습니다.

서버리스 스택 가이드를 따라 모든 과정을 진행하면 AWS 서비스를 사용하여 풀스택(full-stack) 개발자 또는 풀스택 서버리스 응용 프로그램을 빌드하는 과정에 대해 전반적으로 경험을 해볼 수 있습니다.

현재 회사에서 사용중인 주 언어가 자바스크립트, 타입스크립트 이며, 현재 작성하는 문서는 자바스크립트를 사용하시는 모든 개발자분들과 프런트엔드, 백엔드를 아울러서 전체 응용 프로그램을 빌드하는 것을 경험할 수 있는 좋은 경험이 되길 바라며 문서를 작성합니다.

프론트엔드 풀스택 웹 어플리케이션이기 때문에 기본적으로 HTML CSS Javscript Typescript에 대해 어느정도 지식이 있는 상태에서 진행하시는 걸 추천드립니다.

기본 목표는 백엔드, 프론트엔드 모두 갖추어져 있는 서버리스 메모 웹 애플리케이션을 빌드하는 것이 목표이며, 모든 과정이 끝났을 때 웹 애플리케이션의 전체적인 이해를 돕는 좋은 경험이 되길 바라며 문서 작성을 시작합니다.


서버리스를 어플리케이션을 구축하기 이전에..

일반적으로 HTTP 요청을 처리하기 위해 어느 정도 제어할 수 있는 서버를 통해 웹 응용프로그램을 구축하고 배포를 진행해야 한다. 이렇게 배포된 응용프로그램은 해당 서버에서 실행되며 해당 서버의 자원을 구성하고 관리하는 것은 비용과 책임이 발생한다.

  • 요청을 처리하지 않는 시간대에도 서버 유지 비용을 지불해야 한다.
  • 서버 및 모든 리소스의 가동 시간 및 유지 보수를 책임져야 한다.
  • 서버에 적절한 보안 업데이트를 적용할 책임이 있다.
  • 사용 규모가 커짐에 따라 서버의 규모를 키워야 하며 사용량이 많지 않을 때는 규모를 축소해야 한다.
위에 리스트에 적힌 내용만 해도 관리 및 비용 측면에서 소홀히 할 수 없는 중요한 일이다.

보통 소규모의 기업 및 개별 개발자들이 처리한다고 가정한다면 위에 리스트에 있는 일만 하더라도 많은 비용과 공수가 들어갈 수 있는 일이다.

하지만, 보통은 실제 응용프로그램을 작성하고 관리해야 할 일들 때문에 최소한의 비용으로 서버를 작성하거나 혹은 서버를 운영하는 상태에서 서버 쪽 자원은 크게 신경 쓰지 못한 채 서비스가 운영될 수 있는 상황들이 실질적으로 많이 발생하곤 한다.

현재 회사에서도 인력이 부족하기 때문에 굉장히 공감되는 말이다.

어느정도 규모가 있는 기업의 경우에는 보통은 인프라팀이 따로 구성되어있어서 해당 팀에서 따로 관리될 확률이 높아 어느정도 안정성을 가져갈 수도 있다.

하지만, 이를 지원하는 데 필요한 프로세스들이 요구될 것이며 여러 상황들은 개발 시간을 지연시킬 수도 있다.

실제로 애플리케이션을 구축하고 실행하기 위해서는 인프라팀과 협력하는 것은 필수라고 생각된다. 이에 전세계의 여러 개발자들이 이런 문제들에 대한 해결책을 찾고 있었으며 이에 서버리스가 등장하게 된 주 배경이기도 하다.

즉, 서버리스(serverless)란 개발자가 서버를 관리할 필요 없이 애플리케이션을 빌드하고 실행할 수 있도록 하는 클라우드 네이티브 개발 모델이라고 한다.

물론, 서버리스 모델에도 서버가 존재하긴 하지만, 애플리케이션 개발에서와 달리 추상화되어 있으며 클라우드 제공업체가 아래 리스트 외에도 서버 인프라에 대한 여러가지를 관리해준다.

  • 프로비저닝 유지 관리
  • 스케일링
  • 그 외 일상적인 작업을 처리
개발자는 배포를 위해 코드를 컨테이너에 패키징하기만 하면 되는 장점이 있다.

서버리스 컴퓨팅

서버리스 컴퓨팅(줄여서 서버리스)은 클라우드 서비스 공급자들(AWS, Azure, Google Cloud)이 리소스를 동적으로 구성하여 코드 실행을 책임지는 실행 모델이다.

코드를 실행하는 데에 사용된 리소스의 양에 따라서 요금을 부과되며, 이러한 코드는 일반적으로 HTTP요청, 데이터베이스 이벤트, 대기열 서비스, 모니터링 경고, 파일 업로드, 예약된 이벤트(cron 작업) 등 다양한 이벤트에 의해 트리거 되는 스테이트리스(Stateless: 특정 상태를 저장하지 않는) 컨테이너 내부에서 실행된다.

실행을 위해 클라우스 서비스 공급자에게 보내지는 코드는 보통 함수 형태로 구성되는데 그래서 서버리스는 가끔 "서비스로 제공되는 함수(Functions as a Service)" 또는 "FaaS" 라고도 불린다.

Faas를 제공하는 주요 클라우드 업체

서버리스(Serverless)는 기본 인프라를 개발자로부터 추상화하는 반면, 서버는 여전히 우리의 함수를 실행하는 데 관여한다.

코드가 개별 함수로 실행되기 때문에 우리가 미리 알아 둬야 할 몇 가지를 보도록 하자.

아래 내용은 서버리스 아키텍쳐의 특징과 서버리스 아키텍쳐를 사용하면서 생기는 특징들로 한번정도 읽어보는 것을 추천한다.

마이크로서비스(Microservices)

Serverless로 전환하면서 큰 변화중 한개는 애플리케이션의 기능을 구조화해야 한다는 것이다. 물론, 하나의 모노리스식 응용 프로그램을 기존처럼 단순히 배포하는 데 사용할 수도 있다.

그러나 Serverless 에서는 일반적으로 더욱 마이크로 서비스 기반의 아키텍처를 채택하는 것이 구조상 좋다. 모노리스처럼 하나의 함수로 전체 애플리케이션을 실행하고 라우팅을 직접 처리할 수도 있겠지만 함수의 크기를 줄이는 것이 좋기 때문에 권장하지는 않는다.

실제 가이드에 위에 내용처럼 조금 난이도 있게 설명이 되어있지만, 서버리스라는 아키텍처에서는 애플리케이션에 필요한 기능들을 담당하는 여러 서비스들을 각 기능에 맞게 작게 나누거나 모듈화 시켜 개별적으로 잘 작동할 수 있도록 구성하는 방향이 보통은 좋은 패턴이라고 생각하면 좋을 것 같다.

스테이트리스 함수(Stateless Functions)

함수는 일반적으로 상태가 저장되지 않는(stateless) 컨테이너에서 보안적으로 안전하게 실행된다. 즉, 이벤트가 완료된 이후 한 참이 지난 응용 프로그램 서버에서 코드를 재실행하거나 이전 실행 콘텍스트를 사용하여 요청을 제공하는 코드를 실행시킬 수 없다.

이것은 우리가 구성한 함수가 매번 새로운 컨테이너를 통해 호출되는 것이라고 생각하면 되겠다. 
(사실 여기에 약간의 미묘한 차이가 있으며 밑에 Lambda 부분에서 좀 더 자세히 다룬다.)

콜드 스타트(Cold Starts)

함수(Function)는 이벤트에 응답하도록 설정된 컨테이너 내부에서 실행되기 때문에 이벤트와 관련된 어느 정도의 대기 시간을 필요로 하며, 이를 "콜드 스타트(Cold Start)"라고 한다. 함수 실행이 완료된 이후에도 한동안 유지가 되는데 만일 이 시간 동안 다른 이벤트가 발생하면 훨씬 빨리 응답하게 된다. 이것을 "웜 스타트(Warm Start)"라고 한다.

콜드 스타트 기간은 특정 클라우드 공급 업체의 구현 방식에 따라 다르지만, AWS Lambda에서는 몇 백 밀리 초에서 몇 초 사이의 범위가 될 수 있다. 사용된 런타임(또는 언어), 기능의 크기(패키지) 및 클라우드 공급 업체에 따라 다르다.

최근에는 콜드 스타트와 관련해서 클라우드 공급자가 대기 시간을 줄이기 위해 수년간 최적화가 개선되어 왔기 때문에 정말 빠른 응답속도가 요구되지 않는다면 사실상 큰 영향 없이 사용이 가능하다.

서버리스 메모 웹 애플리케이션

서버리스 기반의 메모 웹 애플리케이션을 단계별로 구축할 것이며 기본적인 요구사항은 아래와 같다.

  • 사용자가 가입하고 계정에 로그인할 수 있다.
  • 사용자는 일부 콘텐츠가 포함된 메모를 만들 수 있다.
  • 각 메모에는 첨부 파일로 업로드된 파일이 있을 수도 있다.
  • 사용자가 메모 및 첨부 파일을 수정할 수 있다.
  • 사용자는 메모를 삭제할 수 있다.
  • 앱에서 신용 카드 결제를 처리할 수 있어야 한다.
  • 사용자 도메인에서 HTTPS를 통해 앱을 서비스해야 한다.
  • 백엔드 API는 안전해야 되며 인증된 요청만 처리한다.
  • 앱은 반응형을 동작해야 한다.

기술과 서비스

아래에 정의된 기술과 서비스를 사용하여 서버리스 애플리케이션을 구축할 것이다.

AWS Lambda?

앞서 이전에 AWS Lambda에 대해 언급했지만 람다란 무엇인지 잠깐 언급해보겠다.

AWS Lambda (또는 줄여서 Lambda)는 AWS에서 제공하는 서버리스 컴퓨팅 서비스이다.

AWS Lambda가 내부적으로 어떻게 자세히 작동하는지 까지는 너무 많은 분량을 차지할 것 같아 이번 문서에서는 기본적으로 로직을 구성해야 하는 Lambda 안에서 함수가 어떻게 실행되는지 간단히 알아보겠다.

Lambda 스펙

우선 Lambda는 다음과 같은 런타임(Runtime)을 지원한다.

  • Node.js
  • Java
  • Python
  • .NET Core
  • Go
  • Ruby
  • Rust

각 기능은 64 비트의 Amazon Linux AMI가 설치된 컨테이너에서 실행되며, 그리고 실행 환경은 다음과 같다.

  • 메모리: 128MB - 3008MB, 64MB씩 증가
  • 임시 저장용 디스크 공간: 512MB
  • 최대 실행 시간: 900 초
  • 압축 시 패키지 크기: 50MB
  • 압축을 풀었을 경우 패키지 크기: 250MB

Lambda 함수 모양
Lambda 함수 모양

  • myHandler는 Lambda 함수의 이름이다.
  • event 객체는 이 Lambda를 트리거 한 이벤트에 대한 모든 정보를 포함한다.
  • 이벤트가 HTTP 요청인 경우, 해당 HTTP 요청에 대한 모든 정보가 들어있다.
  • context 객체는 Lambda 함수가 실행되는 런타임에 대한 정보를 포함한다.
  • Lambda 함수 내부에서 모든 작업을 수행한 후에는 그에 대한 결과(또는 오류)와 함께 callback 함수를 호출하고 이를 AWS가 HTTP 요청에 대한 응답으로 처리한다.

Stateless 함수

  • Lambda 함수는 상태를 저장하지 않는다.
  • 각 요청(또는 이벤트)은 하나의 Lambda 함수를 지닌 단일 인스턴스에서 제공합니다.
AWS Lambda는 새로운 요청이 있을 때마다 컨테이너를 띄운다. 여기에서 최적화를 수행하게 되는데 몇 분(컨테이너의 부하에 따라 5분~15분) 동안 기다리며 이후에는 콜드 스타트 없이 요청에 바로 응답할 수 있는 실행 모델을 가지고 있다.

위 실행 모델은 Lambda 함수를 효과적으로 상태를 저장하지 않도록 한다.

위에서 의미하는 것은 Lambda 함수가 이벤트에 의해 트리거 될 때마다 완전히 새로운 환경에서 호출된다는 뜻이며, 이전 이벤트의 실행 콘텍스트에 대한 액세스 권한이 없는 것을 알 수 있다.

만약 컨테이너가 후속 요청에 계속 사용할 수 있는 경우에는 핸들러 함수는 다시 호출되지만 주위의 코드는 호출되지 않는 특징을 가지고 있다.

예를 들어, 아래의 createNewDbConnection 메서드는 Lambda 함수가 호출될 때마다가 아니라 인스턴스화 된 컨테이너 당 한 번 실행된다. 반면에 myHandler 함수는 호출될 때마다 실행된다.

var dbConnection = createNewDbConnection();

exports.myHandler = function(event, context, callback) {
  var result = dbConnection.makeQuery();
  callback(null, result);
};

준비사항

AWS의 많은 서비스를 특정 OS에서 명령어로 쉽게 사용하기 위해서 AWS CLI 설치가 필요하다.

AWS CLI는 Python 2 버전 2.6.5+ 또는 Python 3 버전 3.3+ 와 Pip가 필요하다.
Python 또는 Pip 설치가 필요하면 아래 링크를 참고해서 설치를 진행한다.

맥 사용자는 아래 Python과 Pip을 설치해야 합니다.

MAC OS 사용자

Python, Pip 인스톨 후 아래 명령어를 터미널에서 실행합니다.
(달러는 명령어에 포함되지 않습니다.)

$ sudo pip install awscli

또는 Homebrew를 사용하고 있다면 아래 명령어를 터미널에서 실행합니다.

$ brew install awscli

Window OS 사용자

윈도우 사용자는 아래 msi로 설치를 진행합니다.

AWSCLIV2 msi 다운로드

설치 이후

AWS 서비스를 사용할 것이기 때문에 AWS 계정이 필요하며 계정 관련 세팅은 아래를 참고한다.

https://serverless-stack.com/chapters/ko/create-an-iam-user.html

터미널에서 aws 명령어를 통해 계정 설정을 진행해준다.

$ aws configure
$ Access key ID "AWS IAM 계정 ID값"
$ Secret access key "AWS IAM 계정 KEY값"

SST Framework

AWS Lambda , Amazon API Gateway 및 기타 AWS 서비스 호스트를 사용하여 애플리케이션을 생성할 예정이다. 여기서 AWS Lambda, API Gateway 및 기타 AWS 서비스와 직접 작업하는 것은 다소 번거로움 점이 발생할 수 있다.

이러한 서비스는 AWS에서 실행되기 때문에 로컬에서 테스트하고 디버그 하기가 어려운 점이 있다. 그리고 서버리스 애플리케이션 구축의 큰 부분은 인프라를 코드로 정의할 수 있다는 것이 장점일 수 있다.

SST Framework를 통해 인프라를 프로그래밍 방식으로 정의할 수 있으며, 인프라를 생성하기 위해 AWS 콘솔을 클릭할 필요가 없다.

SST를 사용하면 개발자가 다음을 수행할 수 있으므로 서버리스 애플리케이션을 쉽게 구축이 가능하다.

  1. AWS CDK 를 사용하여 인프라 정의
  2. Live Lambda Development 를 사용하여 애플리케이션을 실시간으로 테스트
  3. Visual Studio Code에서 중단점 설정 및 디버그
  4. 여러 환경 및 지역에 배포
  5. 서버리스 앱을 위해 특별히 설계된 상위 수준 구성 사용
  6. JS 및 TS( esbuild 사용 ), Go, Python, C# 및 F# 으로 Lambda 함수 구성

코드로서의 인프라

AWS 콘솔을 이용해서 인프라를 구성할 수도 있지만, 수많은 클릭이 필요하며 같은 환경을 구성하는데 어려움이 발생한다.

프로그래밍 방식으로 인프라를 구현하면 몇 가지 엄청난 이점이 있다.

  • 몇 가지 간단한 명령으로 설정을 복제할 수 있음
  • 손으로 하는 것만큼 오류가 발생하지 않음
  • 전체 인프라를 코드로 설명하면 여러 환경을 쉽게 만들 수 있음
  • 사용자가 상호 작용하는 프로덕션 환경과 별도로 유지될 수 있음

코드로 인프라를 구성하기 위해  CloudFormation 템플릿(JSON 또는 YAML으로 작성)을 사용할 것이다.

CloudFormation 템플릿은 해당 템플릿에 정의된 데이터 기반으로 리소스를 프로비저닝 하는 AWS 서비스이다.

cloud formation template
cloud formation template

아래는 DynamoDB 테이블을 생성하기 위한 CloudFormation 템플릿의 모양이다.

Resources:
  NotesTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: ${self:custom.tableName}
      AttributeDefinitions:
        - AttributeName: userId
          AttributeType: S
        - AttributeName: noteId
          AttributeType: S
      KeySchema:
        - AttributeName: userId
          KeyType: HASH
        - AttributeName: noteId
          KeyType: RANGE
      BillingMode: PAY_PER_REQUEST

CloudFormation은 AWS 리소스를 정의하는 데 적하지만, 몇 가지 주요 단점이 있다.

  • CloudFormation 템플릿에서 앱에 필요한 모든 리소스를 정의해야 함
  • 직접 상호 작용하지 않을 많은 수의 사소한 리소스가 포함됨
  • 템플릿은 쉽게 수백 줄이 될 수 있음
  • 사실상 단순한 정의 파일이기 때문에 재사용 및 구성에 어려운 점 발생

YAML과 JSON은 시작하기는 쉬우나 대규모 CloudFormation에 대한 템플릿을 유지 관리하는 것은 정말 어려울 수 있다.

AWS CDK 소개

이러한 문제를 해결하기 위해 AWS는 2018년 8월에 AWS CDK 프로젝트를 시작했는데, YAML 또는 JSON 대신 JavaScript 또는 Python과 같은 최신 프로그래밍 언어를 사용하여 CloudFormation을 구성할 수 있다.

AWS CDK (클라우드 개발 키트)는 TypeScript, JavaScript, Java, .NET, Python을 사용하여 AWS 인프라의 리소스를 정의하고 생성할 수 있다.

예를 들어 DynamoDB 테이블을 생성하는 CloudFormation 템플릿은 아래와 같다.

const table = new dynamodb.Table(this, "notes", {
    partitionKey: {
    	name: 'userId',
        type: dynamodb.AttributeType.STRING
    },
    sortKey: {
    	name: 'noteId',
        type: dynamodb.AttributeType.STRING
   },
   billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});

결국 AWS CDK는 프로그래밍 언어를 사용하여 CloudFormation 템플릿을 생성하고 AWS에 리소스를 생성하는 방식으로 동작한다.

aws cdk
aws cdk

CDK 및 SST

SST 는 서버리스 앱을 쉽게 구축할 수 있도록 설계된 상위 수준 CDK 구성 목록과 함께 제공된다.

제로베이스 구성으로 시작하기도 간단하며 사용자 정의 또한 쉽게 할 수 있으며 테스트를 위한 로컬 개발 환경도 함께 제공된다.

SST APP 만들기

  • Node.js >= 10.15.1
  • TypeScript 사용
  • 로컬 환경에 aws cli 설치 및 aws configure 설정
$ npx create-serverless-stack@latest --language typescript notes-app-test
$ cd notes-app-test

기본적으로 앱은 AWS 리전 dev에서 호출되는 환경(또는 단계)에 배포된다. us-east-1이것은 sst.json프로젝트 루트에서 변경할 수 있다.

{
  "name": "notes",
  "region": "ap-northeast-2",
  "main": "stacks/index.ts"
}

Region 값을 서울 리전 ap-northeast-2으로 변경해주자

SST 프로젝트 레이아웃

SST 앱은 두 부분으로 구성된다.

  1. stacks/ - 앱 인프라
    서버리스 앱의 인프라를 설명하는 코드는 stacks/ 프로젝트 디렉터리에 있으며, SST는 AWS CDK 를 사용하여 인프라를 생성한다.
  2. src/ - 앱 코드
    API가 호출될 때 실행되는 Lambda 함수 코드는 src/ 프로젝트의 디렉터리에 존재한다.

나중에는 frontend/ 프런트엔드 React 앱을 위한 디렉터리를 추가할 예정이다.

Hello World API 만들기

SST 프로젝트를 명령어로 생성했다면 이미 Hello World 관련 API가 구성이 돼있어 코드 확인 후 배포만 하면 API를 만들어볼 수 있다.

stacks/MyStack.ts 파일에 정의되어 있다.

import * as sst from "@serverless-stack/resources";

export default class MyStack extends sst.Stack {
  constructor(scope: sst.App, id: string, props?: sst.StackProps) {
    super(scope, id, props);

    // Create a HTTP API
    const api = new sst.Api(this, "Api", {
      routes: {
        "GET /": "src/lambda.handler",
      },
    });

    // Show the endpoint in the output
    this.addOutputs({
      "ApiEndpoint": api.url,
    });
  }
}

여기에서는 하나의 경로, GET / 이 API가 호출되면 src/lambda.js 에 정의돼있는 함수 handler가 실행된다.

터미널에서 아래 명령어를 실행하여 로컬 개발 환경을 시작해보자.
(SST는 서버리스 앱에서 실시간으로 작업할 수 있는 Live Lambda 개발 환경을 제공한다.)

$ npx sst start --stage stage

이 명령을 처음 실행하면 앱과 디버그 스택 배포가 진행되며 Live Lambda 개발 환경을 구동하는 데 몇 분이 소요될 수 있다.

개발환경 구성 구동 시 예시 모양

===============
 Deploying app
===============

Preparing your SST app
Transpiling source
Linting source
Deploying stacks
dev-notes-my-stack: deploying...

 dev-notes-my-stack


Stack dev-notes-my-stack
  Status: deployed
  Outputs:
    ApiEndpoint: https://guksgkkr4l.execute-api.us-east-1.amazonaws.com

ApiEndpoint에 나와있는 주소를 인터넷 브라우저에 입력해보면 아래와 같이 API가 생성된 것을 확인할 수 있다.

인터넷 브라우저에서는 GET 요청밖에 테스트가 안되니, Postman과 같은 외부 API 테스트 툴을 사용하는 것도 추천한다.

hello-world-api
hello-world-api

위에는 로컬 환경에서 실행 중인 것이고 실제 AWS에서 운영환경에 배포하려면 아래 명령어로 할 수 있지만 진행은 하지말자.

$ npx sst deploy --stage prod

서버리스 스택-1 정리

  • 서버리스 탄생배경
  • 서버리스 컴퓨팅 및 간단한 설명
  • 서버리스 메모 웹 어플리케이션 소개
  • SST Framework 소개
  • SST App Hello World

이번 문서에서는 위와 같이 간단하게 서버리스에 대해 알아보고 SST Framework 사용해 로컬에 API환경을 구축해보고 Hello World API를 생성해보았다.

다음 내용은 DynamoDB 정의, S3 Bucket 정의, 메모 생성 API 작성 등에 대해서 [AWS] 서버리스 스택-2 파트에서 정리하도록 하겠습니다.

참고자료

'IT > Serverless Stack' 카테고리의 다른 글

[AWS] 서버리스 스택-4  (0) 2022.04.06
[AWS] 서버리스 스택-3  (0) 2022.03.24
[AWS] 서버리스 스택-2  (0) 2022.03.22