Amazon Web Services 한국 블로그

대규모 서버리스 애플리케이션 구성을 위한 모범 사례

잘 설계된 서버리스 애플리케이션은 분리(decoupled) 되어 있고, 상태를 저장(stateless)하지 않으면서 최소한의 코드를 사용합니다. 프로젝트가 성장함에 따라 개발 관리자의 목표는 설계의 단순성과 로우 코드 구현을 유지하는 것입니다. 이 글은 대규모 서버리스 프로젝트에서 코드 리포지토리를 설계 및 관리하기 위한 권장 사항과 프로덕션 시스템 및 출시 배포를 위한 모범 사례를 제공합니다.

1. 함수 규모에 따라 코드 저장소 구성하기

대부분 서버리스 애플리케이션은 초기에 모놀리식 애플리케이션으로 시작합니다. 이는 간단한 애플리케이션이 시간이 지남에 따라 더 복잡해지거나 개발자가 기존 개발 관행을 따르기 때문에 발생할 수 있습니다. 일반적으로 모놀리식 애플리케이션은 단일 AWS Lambda 함수로 만들어지고, 여러 작업을 수행하지만 전체 애플리케이션 로직을 포함하는 단일 리포지토리입니다.

모놀리스는 cron 작업, 데이터 처리 작업 및 일부 비동기 프로세스와 같은 단일 목적 기능을 수행하는 가장 단순한 서버리스 애플리케이션에 적합합니다.  이러한 애플리케이션이 워크플로로 발전하거나, 새로운 기능을 개발함에 따라 코드를 더 작은 서비스로 리팩토링하는 것이 중요해집니다.

예를 들면, AWS Serverles Application Model (SAM) 또는 Serverless Framework 같은 프레임 워크를 사용하여 작은 서비스로 기능을 그룹 공통의 조각을 쉽게 만들 수 있습니다. 이들 각각은 별도의 코드 리포지토리를 가질 수 있습니다. SAM의 경우 template.yaml 파일에는 애플리케이션에 필요한 모든 리소스와 기능 정의가 포함됩니다. 따라서 별도의 템플릿을 사용하여 애플리케이션을 마이크로서비스로 나누는 것은 리포지토리와 리소스 그룹을 분할하는 간단한 방법입니다.

마이크로서비스를 위한 별도의 템플릿

서버리스 애플리케이션의 가장 작은 단위에서는 함수당 하나의 저장소를 생성하는 것도 가능합니다. 이러한 함수는 독립적이고 다른 AWS 리소스를 공유하지 않는 경우 적절할 수 있습니다. 도우미 함수와 간단한 이벤트 처리 코드는 이러한 종류의 repo 구조를 만드는 사례입니다.

대부분의 경우, 마이크로 서비스를 정의하는 기능 및 리소스 그룹 주위에 리포지토리를 만드는 것이 좋습니다. 전자 상거래의 예에서 “결제 처리”는 공통 리소스를 공유하는 여러 개의 소규모 관련 기능이 있는 마이크로 서비스입니다.

다른 소프트웨어와 마찬가지로 리포지토리 디자인은 개발 팀의 사용 사례와 구조에 따라 다릅니다. 하나의 큰 리포지토리는 개발팀이 다양한 기능을 작업하고 테스트 및 배포하기 어렵게 만듭니다. repo가 너무 많으면 중복 코드가 생성되고 repo 간에 리소스를 공유하기 어려울 수 있습니다. 프로젝트의 균형을 찾는 것은 애플리케이션 아키텍처를 설계하는 데 있어 중요한 단계입니다.

2. 번들 코드 패키지 대신 기존 AWS 서비스 사용하기

AWS 서비스는 서버리스 애플리케이션을 위한 중요한 빌딩 블록입니다. 이들은 유사한 기능을 가진 번들 코드 패키지보다 더 큰 규모, 성능 및 안정성을 제공할 수 있습니다.

예를 들어, Lambda로 마이그레이션되는 많은 웹 애플리케이션은 Flask (Python용) 또는 Express (Node.js)와 같은 웹 프레임워크를 사용합니다. 두 패키지 모두 라우팅 및 애플리케이션이 웹 서버에서 실행 중인 경우 적합한 별도의 사용자 컨텍스트를 지원합니다. Lambda 함수에서 이러한 패키지를 사용하면 다음과 같은 아키텍처가 생성됩니다.

Lambda 함수의 웹 서버

Amazon API Gateway를 사용하여 모든 요청 경로에 대해 Lambda 함수로 라우팅하기 위한 프록시 기능을 제공할 수 있습니다. 그런데, 애플리케이션이 더 많은 요청 경로를 만드면, Lambda 함수의 크기가 커지고 새 배포 버전이 전체 함수를 대체합니다. 이러한 경우, 여러 개발자가 동일한 프로젝트에서 작업하는 것이 더 어려워집니다.

이 때, API Gateway에서 사용할 수 있는 경로별 기본 라우팅 기능을 활용하는 것이 더 나은 경우가 많습니다. 대부분의 경우 Lambda 함수에 웹 프레임워크가 필요하지 않으므로 배포 패키지의 크기가 증가합니다. API Gateway는 매개변수를 검증할 수 있으므로, 사용자 지정 코드로 매개변수를 확인할 필요가 줄어듭니다. 또한 무단 액세스에 대한 보호와 서비스 수준에서 처리하기에 더 적합한 기타 기능을 제공할 수 있습니다. 이러한 방법으로 API Gateway를 사용할 때 새 아키텍처는 다음과 같습니다.

라우팅에 API 게이트웨이 사용

또한, Lambda 함수는 더 적은 수의 코드와 더 적은 수의 패키지 종속성으로 구성됩니다. 이렇게 하면 테스트가 더 쉬워지고 코드 라이브러리 버전을 유지 관리할 필요가 줄어듭니다. 팀의 다른 개발자는 별도의 라우팅 기능을 독립적으로 작업할 수 있으며 향후 프로젝트에서 코드를 재사용하는 것이 더 간단해집니다. 애플리케이션의 AWS SAM 템플릿에서 API Gateway의 경로를 구성할 수 있습니다.

Resources:
  GetProducts:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: getProducts/
      Handler: app.handler
      Runtime: nodejs12.x
      Events:
        GetProductsAPI:
          Type: Api 
          Properties:
            Path: /getProducts
            Method: get
YAML

마찬가지로 Lambda 함수 내에서 워크플로 오케스트레이션을 수행하는 것을 피해야 합니다. 워크플로는 다른 서비스 및 기능을 호출하고, 성공적인 실행 또는 실패에 따라 후속 작업을 수행하는 코드 섹션입니다.

워크플로 오케스트레이션이 포함된 Lambda 함수

이러한 방식의 워크플로는 빠르게 취약해지고 새로운 요구 사항에 맞게 수정하기 어렵습니다. 이는 Lambda 함수가 계속 실행되면서 기다리는 상태를 유발할 수 있습니다. 즉, 함수가 외부 소스의 반환 값을 기다리고 있으며 함수 실행 비용이 증가합니다.

더 나은 접근 방식은 AWS Step Functions를 사용하여 애플리케이션의 SAM 템플릿에서 복잡한 워크플로를 JSON 정의로 나타낼 수 있는 하는 것입니다. 이 서비스는 필요한 사용자 지정 코드의 양을 줄이고 Lambda 함수의 유휴 상태를 최소화하는 워크플로를 활성화합니다. 또한, 워크플로가 업그레이드됨에 따라 진행 중인 실행을 관리합니다. Step Functions 워크플로로 재설계된 위의 예는 다음과 같습니다.

오케스트레이션을 위한 Step Functions 사용

3. 개발 과정에 따라 다수 AWS 계정 사용하기

서버리스 애플리케이션을 프로덕션에 배포하는 방법에는 여러 가지가 있습니다. 애플리케이션이 성장하고 비즈니스에서 더욱 중요해짐에 따라 일반적으로 배포 프로세스의 견고성을 개선해야 합니다. AWS에는 서버리스 애플리케이션의 개발 및 배포를 관리하기 위한 다양한 옵션이 있습니다.

첫째, 개발팀에 따라 두개 이상의 AWS 계정을 사용하는 것이 좋습니다. AWS Organizations 을 사용하면, 멀티 계정의 결제, 규정 준수 및 보안을 중앙에서 관리할 수 있습니다. 사용자 지정 스크립트 및 수동 프로세스를 방지하기 위해 계정 그룹에 정책을 연결할 수 있습니다. 한 가지 간단한 접근 방식은 각 개발자에게 AWS 계정을 제공한 다음 베타 배포 단계 및 프로덕션에 대해 별도의 계정을 사용하는 것입니다.

배포 파이프라인의 여러 AWS 계정

개발자 계정에는 프로덕션 리소스의 복사본이 포함될 수 있으며, 개발자에게 이러한 리소스에 대한 관리자 수준 권한을 제공할 수 있습니다. 각 개발자는 계정에 대한 고유한 한도 세트가 있으므로 사용이 프로덕션 환경에 영향을 미치지 않습니다. 개별 개발자는 프로덕션 자산에 대한 위험을 최소화하면서 CloudFormation 스택 및 SAM 템플릿을 이러한 계정에 배포할 수 있습니다.

이 접근 방식을 통해 개발자는 개별 개발 계정의 AWS 리소스에 대해 개발 장비에서 로컬로 Lambda 기능을 테스트할 수 있습니다. 강력한 단위 테스트 프로세스를 생성하는 데 도움이 될 수 있으며, 개발자는 AWS CodeCommit 리포지토리에 코드를 푸시할 수 있습니다.

AWS Secrets Manager와 통합하면 각 개발 환경에 서로 다른 아이디/암호 세트를 저장할 수 있으며, 코드에 하드코딩할 필요가 없습니다. 코드가 개발자 계정에서 베타 및 프로덕션 계정으로 승격되면, 올바른 자격 증명 집합을 자동으로 사용할 수 있습니다. 즉, 개별 개발자와 개발 환경에 따라 아이디 및 암호 등을 개발자에게 공유할 필요가 없습니다.

코드가 배포될 때 빌드 파이프라인을 시작하는 CI/CD 프로세스를 구현하는 것도 가능합니다. 다중 계정 배포 흐름을 사용하여 샘플 애플리케이션을 배포하려면 서버리스 CI/CD 자습서를 살펴보세요.

4. CodeDeploy를 통한 배포 및 출시 과정 관리

서버리스 애플리케이션을 위한 CI/CD 파이프라인을 구현할 때 전체 애플리케이션 업그레이드보다 안전한 배포를 선호하는 것이 가장 좋습니다. 기존 소프트웨어 배포와 달리, 서버리스 애플리케이션은 Lambda 함수의 사용자 지정 코드와 AWS 서비스 구성의 조합으로 되어 있습니다.

출시 과정은 Lambda 함수의 버전 변경으로 구성될 수 있습니다. API Gateway에 다른 엔드포인트가 있거나 DynamoDB 테이블과 같은 새 리소스를 사용할 수 있습니다. AWS SAM에는 AWS CodeDeploy가 내장 되어, Canary 배포를 할 수 있습니다. 아래 YAML 구성을 참고하세요.

Resources:
 GetProducts:
   Type: AWS::Serverless::Function
   Properties:
     CodeUri: getProducts/
     Handler: app.handler
     Runtime: nodejs12.x

     AutoPublishAlias: live

     DeploymentPreference:
       Type: Canary10Percent10Minutes 
       Alarms:
         # A list of alarms that you want to monitor
         - !Ref AliasErrorMetricGreaterThanZeroAlarm
         - !Ref LatestVersionErrorMetricGreaterThanZeroAlarm
       Hooks:
         # Validation Lambda functions run before/after traffic shifting
         PreTraffic: !Ref PreTrafficLambdaFunction
         PostTraffic: !Ref PostTrafficLambdaFunction
YAML

CodeDeploy는 함수의 이전 버전과 버전을 가리키는 별칭을 자동으로 생성합니다. 카나리아 배포를 사용하면, 새 버전이 예상대로 작동한다고 확신할 수 있을때 트래픽을 이전 별칭에서 새 별칭으로 점진적으로 이동할 수 있습니다. 필요한 경우 업데이트를 롤백할 수 있습니다. PreTraffic PostTraffic 후크를 하여 트래픽 이동 전후에 Lambda 함수를 호출할 수도 있습니다

마무리

어떤 애플리케이션이라도 크기가 커지면 개발 관리자가 코드 저장소를 구성하고 배포 주기를 관리하는 것이 중요합니다. 대규모 서버리스 애플리케이션을 관리하는 데 도움이 되는 패턴을 알아보았습니다. 일반적으로 모놀리식 기능과 단일 리포지토리를 피하는 것이 가장 좋으며, 리포지토리의 범위를 마이크로서비스 또는 개별 기능 수준으로 지정해야 합니다.

잘 설계된 서버리스 애플리케이션은 Lambda 함수의 사용자 지정 코드를 사용하여 AWS의 관리형 서비스와 잘 연결합니다. 배포 크기를 최소화하고 코드 기반을 단순화하기 위해 서비스로 대체할 수 있는 라이브러리 및 패키지를 식별하는 것이 중요합니다. 이는 서버 기반 환경에서 마이그레이션된 응용 프로그램에서 특히 그렇습니다.

AWS Organizations를 사용하여 계정 그룹을 관리하여 개발자가 개발을 위한 자체 AWS 계정을 가질 수 있도록 합니다. 이를 통해 엔지니어는 코드를 작성하고 디버깅할 때 프로덕션 자산을 복제하고 AWS 클라우드에 대해 테스트할 수 있습니다. CI/CD 파이프라인을 사용하여 베타 환경을 통해 프로덕션으로 코드를 푸시하는 동시에 Secrets Manager를 사용하여 아이디/암호 등을 보호할 수 있습니다. 또한 CodeDeploy를 사용하여 카나리아 배포를 쉽게 관리할 수 있습니다.

AWS SAM 및 CodeDeploy를 사용하여 Lambda 함수를 배포하는 방법에 대해 자세히 알아보려면 자습서를 살펴보세요.

– James Beswick, AWS Serverless Developer Advocate

이 글은 AWS Compute Blog의 Best practices for organizing larger serverless applications 한국어 번역입니다.