클라우드 컴퓨팅 서비스가 점차 다양한 형태로 발전하면서 단순히 공유 인프라를 제공하던 IaaS (Infrastructure as a Service)를 뛰어넘어 인프라 관리를 신경 쓸 필요가 없는 PaaS (Platform as a Service) 형태의 서비스가 널리 퍼져나가고 있습니다. 개발자가 컴퓨팅 자원, 즉 서버에 대한 관리를 할 필요가 없기 때문에 흔히 Serverless라고 불립니다.

이처럼 애플리케이션을 개발/운영하는데 인프라의 경계가 사라지고 Serverless가 등장함으로써 개발 방법론도 기존의 방식에서 벗어나서 Serverless 아키텍처에 걸맞는 형태로 변화할 필요가 있습니다. 그래서 다양한 툴들이 등장하여 클라우드 네이티브하게 개발이 가능하도록 도와주고 있는데, 그 중에 하나가 바로 AWS의 SAM (Serverless Application Model) 입니다.

SAM은 로컬 환경에서 Serverless 자원에 대한 개발을 손쉽게 할 수 있도록 도와줍니다. CloudFormation의 YAML을 이용한 IaC (Infrastructure as Code) 기능을 포함하고 있으며, 리소스에 배포할 소스를 함께 관리할 수 있습니다. 이와 더불어, 클라우드 서비스에 대한 로컬 테스트 기능을 제공하여 배포하기 전에도 충분한 테스트가 가능합니다.

예를 들어, AWS Lambda 함수를 개발하려고 하면, 기존 환경에서는 소스 개발만 가능하고 의존성 패키지를 함께 패키징하는 기능이나 로컬 테스트를 진행하기가 매우 어렵습니다. SAM을 이용하면 도커 (Docker)를 이용하여 가상의 AWS Lambda 환경을 구성하고 그 안에서 함수 테스트가 가능하며, 의존성 패키지를 별도로 관리할 필요 없이 자동으로 패키징을 해서 배포 패키지를 구성할 수 있습니다.

개발 환경 설정

AWS SAM은 다양한 IDE 환경에 대해서 확장 플러그인을 제공하고 있습니다. 저는 개인적으로 VS Code를 선호하므로 이를 기준으로 글을 작성하도록 하겠습니다. VS Code 설치 후에, Extensions에서 AWS Toolkit을 설치하면 AWS CLI의 자격 증명, SAM과 연동되어 다양한 기능을 사용할 수 있습니다.

IDE에서 리소스를 확인하고 실행도 가능합니다!

새로운 SAM 애플리케이션을 생성하면 사용할 언어와 기본 템플릿을 지정하고 프로젝트가 저장될 경로를 설정할 수 있습니다. Python 3.8 버전과 AWS SAM Hello World 템플릿을 선택하여 SAM 프로젝트를 생성해봅니다. 그러면 아래와 같은 구조로 파일들이 생성됩니다.

.
├── README.md
├── events
│ └── event.json
├── hello_world
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── template.yaml
└── tests
└── unit
├── __init__.py
└── test_handler.py
Code language: CSS (css)
  • events 폴더는 로컬 테스트용 JSON 이벤트 소스를 담고 있습니다.
  • hello_world는 Lambda 함수로 배포될 Python 소스입니다.
    • requirements.txt는 SAM을 통해서 빌드/패키지를 진행할 경우에 함께 패키징될 의존성 패키지를 정의할 수 있습니다.
  • template.yaml은 아래와 같이 CloudFormation 배포를 위한 IaC 소스이며 본 예제에서는 Lambda 함수와 IAM 롤, API Gateway를 배포하게 됩니다.
자동으로 생성된 Hello World 프로젝트.

빌드 및 로컬 테스트

SAM CLI를 통해서 빌드를 진행합니다. --use-container 옵션을 사용하면 도커 이미지를 불러와서 빌드를 진행하고 의존성 패키지도 함께 묶어줍니다.

sam build --use-container

Starting Build inside a container
Building function 'HelloWorldFunction'

Fetching amazon/aws-sam-cli-build-image-python3.8 Docker container image...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Mounting /workspace/workspace/hello_world as /tmp/samcli/source:ro,delegated inside runtime container

Build Succeeded

Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource
Code language: Bash (bash)

이제 로컬 테스트를 해볼 수 있습니다. CloudFormation 템플릿에서 함수의 논리적인 이름으로 HelloWorldFunction 라고 명명하였으므로 이것을 호출하면 됩니다.

sam local invoke HelloWorldFunction --event events/event.json

Invoking app.lambda_handler (python3.8)
Image was not found.
Building image........................
Failed to download a new amazon/aws-sam-cli-emulation-image-python3.8:rapid-1.1.0 image. Invoking with the already downloaded image.
Mounting /workspace/workspace/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: 4c3d5d53-87b6-1ded-73a8-ab221b0fd705 Version: $LATEST
END RequestId: 4c3d5d53-87b6-1ded-73a8-ab221b0fd705
REPORT RequestId: 4c3d5d53-87b6-1ded-73a8-ab221b0fd705 Init Duration: 138.15 ms Duration: 3.19 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB

{"statusCode":200,"body":"{\"message\": \"hello world\"}"}
Code language: Bash (bash)

마찬가지로 도커 이미지를 생성하고 그 안에서 테스트가 이루어집니다. 마운팅이 끝난 이후에 START RequestId로 시작하는 로그는 Lambda에서 제공하는 것과 동일한 형태로 구성된 것을 확인할 수 있습니다.

API 테스트도 가능합니다. 먼저 API 서비스를 실행합니다.

sam local start-api

Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET] You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-10-17 06:08:51 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

Code language: Bash (bash)

이후에 http://localhost:3000/으로 접근하면 위 터미널에는 로그가 남고 요청에는 응답이 남는 것을 확인할 수 있습니다.

이처럼 IDE에서 소스 개발을 진행하고, SAM으로 빌드 후에 로컬 테스트를 반복함으로써 Serverless 애플리케이션에 대한 개발을 수행할 수 있게 됩니다.

서비스 배포

기본적으로 SAM의 서비스 배포는 CloudFormation을 통해서 한 번에 이루어집니다. 간단하게 다음 명령을 통해서 배포를 수행해볼 수 있습니다.

sam deploy --guided

Configuring SAM deploy
======================

Looking for samconfig.toml : Not found

Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]: cloudformation_name
AWS Region [us-east-1]: ap-northeast-2
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]: y
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: y
HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
Save arguments to samconfig.toml [Y/n]: n

Looking for resources needed for deployment: Found!

Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-************
A different default S3 bucket can be set in samconfig.toml
Uploading to cloudformation-name/449a6ea89fa18192439837a8c6be2b52 538937 / 538937.0 (100.00%)

Deploying with following values
===============================
Stack name : cloudformation-name
Region : ap-northeast-2
Confirm changeset : True
Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-5mu3qsrakhxp
Capabilities : ["CAPABILITY_IAM"] Parameter overrides : {}

Initiating deployment
=====================
HelloWorldFunction may not have authorization defined.
Uploading to cloudformation-name/342a5707a3ec1ecc9d6ac0ba37a69101.template 1105 / 1105.0 (100.00%)

Waiting for changeset to be created..

CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType
---------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add HelloWorldFunctionHelloWorldPermissionProd AWS::Lambda::Permission
+ Add HelloWorldFunctionRole AWS::IAM::Role
+ Add HelloWorldFunction AWS::Lambda::Function
+ Add ServerlessRestApiDeployment47fc2d5f9d AWS::ApiGateway::Deployment
+ Add ServerlessRestApiProdStage AWS::ApiGateway::Stage
+ Add ServerlessRestApi AWS::ApiGateway::RestApi
---------------------------------------------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-2:************:changeSet/samcli-deploy1602915546/deeaa13e-f055-40eb-9f66-0b70287b9e60
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]:
Code language: Bash (bash)

여기서 중요한 점은, 배포할 때 IAM 롤이나 정책을 생성해야 하는 경우에는 IAM 권한이 반드시 필요하다는 것입니다. 적절한 권한을 가진 자격 증명을 이용하고 있는지 확인해야 오류가 나지 않습니다. 마지막에 changeset을 배포할 것인지 여부만 결정하면, 그 즉시 개발 소스와 리소스가 AWS로 배포됩니다. 그리고 이렇게 배포된 리소스들은 모두 CloudFormation을 통해서 관리됩니다.

배포 후에 실행된 로그에 대해서는 AWS Toolkit의 CloudWatch Logs에서 확인 가능합니다. 콘솔에 접근하지 않고도 아래와 같이 VS Code 내에서 확인됩니다.

로그도 VS Code에서 바로 볼 수 있습니다.

지금까지 SAM을 이용한 Serverless 애플리케이션 개발에 대해서 살펴보았습니다. SAM을 이용하면 별도의 콘솔 접근 없이도 개발 소스와 리소스를 로컬 환경에서 손쉽게 빌드하고 배포가 가능하다는 것을 확인할 수 있었습니다. 코드를 통해서 모든 것들이 관리되기 때문에 CodeDeploy와 CodePipeline 등을 사용하면 자동화된 CI/CD 구축도 매우 간단하게 이루어질 수 있습니다.

여러분도 클라우드 환경에서는 클라우드 네이티브한 개발을 시도해보는 것은 어떨까요?