BLOG

AWS Step Functions를 위한 CI/CD 파이프라인 테스트 및 생성
작성일: 2020-04-16

AWS Step Functions를 사용하면 고가용성, 서버리스 및 직관적인 워크 플로우를 쉽게 생성할 수 있습니다. Step Functions는 AWS LambdaAWS BatchAWS Fargate 및 Amazon SageMaker를 포함하여 다양한 AWS 서비스와 기본적으로 통합됩니다. Amazon States Language라고 하는 사용하기 쉬운 JSON 기반 언어를 통해 기본적으로 오류 처리, 재시도 로직 및 복잡한 브랜칭를 추가할 수 있습니다.

AWS CodePipeline 은 릴리스 파이프라인 자동화를 위한 쉽고 구성하기 쉬운 방법을 허용하는 완전 관리형 Continuous Delivery System입니다. CodePipeline을 통해 최종 사용자는 가장 중요한 애플리케이션 및 인프라를 안정적이고 반복 가능한 방식으로 구축, 테스트 및 배포할 수 있습니다.

AWS CodeCommit 은 완벽하게 관리되고 안전한 소스 제어 리포지토리 서비스입니다. 고가용성 및 중요 코드 리포지토리 시스템을 지원하기 위해 인프라를 서포트하고 확장할 필요가 없습니다.

따라서 오늘은 이러한 AWS의 CodeCommit, CodeBuild, CodePipeline과 Python을 사용하여 AWS Step Function 상태 머신을 처음부터 끝까지 종합적으로 테스트하기 위해 CI/CD 파이프라인을 생성하는 방법을 보여드리겠습니다.

 

 

CI/CD 파이프라인 단계

파이프라인에는 다음 다이어그램과 같은 단계가 포함됩니다.

  1. 소스 컨트롤에서 소스 코드를 가져옵니다.
  2. 구성 파일을 lint하세요.
  3. 코드베이스에서 AWS Lambda 함수에 대한 단위 테스트를 실행하세요.
  4. 테스트 파이프라인을 배포하세요.
  5. 테스트 파이프라인에 대해 엔드 투 엔드 테스트를 실행하세요.
  6. 테스트 상태 머신 및 테스트 인프라를 정리하세요.
  7. 승인자에게 승인을 보냅니다.
  8. 프로덕션에 배포하세요.

 

 

전제 조건

이 CI/CD 파이프라인을 구축하려면 몇 가지 전제 조건이 충족되어야 합니다.

  1. 기존 AWS 계정을 생성하거나 사용하세요. (계정 생성에 대한 지침은 여기를 참조하세요).
  2. AWS Step Function에서 언어 정의 (아래 참조) 예를 정의하거나 사용하세요.
  3. Lambda 함수에 적합한 단위 테스트를 작성하세요.
  4. AWS Step Function 상태 머신에 맞게 실행할 엔드 투 엔드 테스트를 결정하세요.

 

 

CodePipeline 프로젝트

다음 스크린 샷은 AWS Step Function 상태 머신을 프로덕션에 안전하고 안정적이며 확실하게 배포하기 위해 실행되는 단계를 포함한 CodePipeline 프로젝트의 모습을 보여줍니다.

 

CodeCommit 리포지토리 생성

시작하려면 AWS 콘솔로 이동하여 상태 머신을 위한 새로운 CodeCommit 리포지토리를 생성하십시오.

 

상태 머신 정의, Python 테스트 및 CodeBuild 구성을 포함하므로 이 예제에서 저장소의 이름은 CalculationStateMachine 입니다.

 

 

리포지토리 구조 내역

위의 CodeCommit 저장소에는 다음과 같은 폴더 구조가 있습니다.

  1. config – 여기에서 모든 Buildspec 파일이 AWS CodeBuild 작업에 사용됩니다.
  2. lambdas – 여기에 모든 AWS Lambda 함수가 저장됩니다.
  3. tests – 이것은 단위 및 엔드-투-엔드 테스트를 위한 최상위 폴더입니다. 두 개의 하위 폴더 (단위 및 e2e)가 있습니다.
  4. cloudformation – 여기서 추가 CloudFormation 템플릿을 추가합니다.

 

 

상태 머신 정의

CodeCommit 리포지토리 내에서 Amazon States Language에서 상태 머신을 정의하는 sm_def.json 이라는 상태 머신 정의 파일을 생성합니다.

 

이 예제는 Lambda 함수 모음을 호출하여 지정된 입력 값에 대한 계산을 수행하는 상태 시스템을 만듭니다. 또한, 특정 값에 대해 검사를 수행하고 선택 상태를 사용하여 파이프라인을 계속하거나 종료합니다.

 

sm_def.json file:

{

  “Comment”: “CalulationStateMachine”,

  “StartAt”: “CleanInput”,

  “States”: {

    “CleanInput”: {

      “Type”: “Task”,

      “Resource”: “arn:aws:states:::lambda:invoke”,

      “Parameters”: {

        “FunctionName”: “CleanInput”,

        “Payload”: {

          “input.$”: “$”

        }

      },

      “Next”: “Multiply”

    },

    “Multiply”: {

      “Type”: “Task”,

      “Resource”: “arn:aws:states:::lambda:invoke”,

      “Parameters”: {

        “FunctionName”: “Multiply”,

        “Payload”: {

          “input.$”: “$.Payload”

        }

      },

      “Next”: “Choice”

    },

    “Choice”: {

      “Type”: “Choice”,

      “Choices”: [

        {

          “Variable”: “$.Payload.result”,

          “NumericGreaterThanEquals”: 20,

          “Next”: “Subtract”

        }

      ],

      “Default”: “Notify”

    },

    “Subtract”: {

      “Type”: “Task”,

      “Resource”: “arn:aws:states:::lambda:invoke”,

      “Parameters”: {

        “FunctionName”: “Subtract”,

        “Payload”: {

          “input.$”: “$.Payload”

        }

      },

      “Next”: “Add”

    },

    “Notify”: {

      “Type”: “Task”,

      “Resource”: “arn:aws:states:::sns:publish”,

      “Parameters”: {

        “TopicArn”: “arn:aws:sns:us-east-1:657860672583:CalculateNotify”,

        “Message.$”: “$$”,

        “Subject”: “Failed Test”

      },

      “End”: true

    },

    “Add”: {

      “Type”: “Task”,

      “Resource”: “arn:aws:states:::lambda:invoke”,

      “Parameters”: {

        “FunctionName”: “Add”,

        “Payload”: {

          “input.$”: “$.Payload”

        }

      },

      “Next”: “Divide”

    },

    “Divide”: {

      “Type”: “Task”,

      “Resource”: “arn:aws:states:::lambda:invoke”,

      “Parameters”: {

        “FunctionName”: “Divide”,

        “Payload”: {

          “input.$”: “$.Payload”

        }

      },

      “End”: true

    }

  }

}

 

 

파이프라인이 완료된 후 다음 AWS Step Function 상태 머신이 생성됩니다.

 

 

CodeBuild 사양 파일

CI/CD 파이프라인은 CodePipelin을 통해 함께 연결된 CodeBuild BuildSpec 파일 모음을 사용합니다. 다음 섹션에서는 이러한 BuildSpec 파일이 어떻게 보여지는지, 그리고 파일을 함께 연결하고 전체 CI/CD 파이프라인을 구축하는 데에 어떻게 사용될 수 있는지를 보여줍니다.

 

 

AWS States Language Linter

상태 머신 정의의 유효성 여부를 판별하려면 CodePipeline 구성에 스테이지를 포함해 이를 평가하세요. statelint라는 Ruby Gem을 사용하여 다음과 같이 상태 머신 정의의 유효성을 확인할 수 있습니다.

lint_buildspec.yaml file:

version: 0.2

env:

 git-credential-helper: yes

phases:

 install:

    runtime-versions:

      ruby: 2.6

    commands:

      – yum -y install rubygems

      – gem install statelint

 

   build:

    commands:

      – statelint sm_def.json

 

 

구성이 유효하면 출력 메시지가 표시되지 않습니다. 구성이 유효하지 않으면 정의가 유효하지 않고 파이프라인이 종료된다는 메시지가 표시됩니다.

 

 

람다 단위 테스트

Lambda 함수 코드를 테스트하려면 테스트 세트를 통과했는지를 평가해야 합니다. 상태 머신 내에서 배포 및 사용된 개별 Lambda 함수를 테스트할 수 있습니다. Lambda 함수들에 다양한 인풋을 입력하고 아웃풋이 예상한 아웃풋임을 확인할 수 있습니다. 이 경우 Python pytest를 사용하여 테스트를 시작하고 결과를 검증합니다.

unit_test_buildspec.yaml file:

version: 0.2

env:  git-credential-helper: yes

phases:

  install:

    runtime-versions:

      python: 3.8

    commands:

      pip3 install r tests/requirements.txt

 

   build:

    commands:

      pytest s vvv tests/unit/ junitxml=reports/unit.xml

 

 reports:

  StateMachineUnitTestReports:

    files:

      “**/*”   

base-directory: “reports”

 

CodeCommit 리포지토리에는 tests / unit 이라는 디렉토리가 있으며 여기에는 Lambda 함수 코드에 대해 실행 및 검증되는 단위 테스트 모음이 포함되어 있습니다. 이 BuildSpec 파일의 또 다른 매우 중요한 부분은 보고서 섹션으로, 이는 결과, 트렌드 및 전반적인 테스트 성공에 대한 보고서를 생성합니다.

 

 

CodeBuild 테스트 보고서

단위 테스트를 실행한 후 실행 결과에 대한 보고서를 볼 수 있습니다. pytest 명령과 함께 실행되는 sectionjunitxml=reports/unit.xml 명령과 함께 BuildSpec 파일의 보고서 섹션을 확인해보세요. 이는 CodeBuild에서 시각화할 수 있는 일련의 보고서를 생성합니다.

검토하고자 하는 특정 CodeBuild 프로젝트로 이동하여 관심 있는 특정 실행을 클릭하세요. 다음 스크린 샷에서 볼 수 있듯이 Reports라는 탭이 있습니다.

 

다음 스크린 샷에 표시된 대로 실행 중인 테스트의 세부 사항을 보려면 관심 있는 특정 보고서를 선택하세요.

 

 

Report Groups 사용하면 시간이 지남에 따라 집계된 테스트 목록을 볼 수도 있습니다. 이 보고서에는 다음 스크린 샷과 같이 실행된 평균 테스트 사례 수, 평균 지속 시간 및 전체 통과율과 같은 다양한 기능이 포함됩니다.

 

 

 

AWS CloudFormation 템플릿 단계

다음 BuildSpec 파일은 상태 머신 정의를 AWS CloudFormation에 주입하는 AWS CloudFormation 템플릿을 생성하는 데 사용됩니다.

 

template_sm_buildspec.yaml file:

version: 0.2

env:

  git-credential-helper: yes

phases:

  install:

    runtime-versions:

      python: 3.8

 

   build:

    commands:

      python template_statemachine_cf.py

 

리포지토리에 sm_def.json 파일이 지정된 상태 머신 정의를 배포하도록 AWS CloudFormation을 템플릿화하는 Python 스크립트는 다음과 같습니다.

 

template_statemachine_cf.py file:

import sys

import json

 

def read_sm_def (

    sm_def_file: str

) -> dict:

    “””

    Reads state machine definition from a file and returns it as a dictionary.

 

    Parameters:

        sm_def_file (str) = the name of the state machine definition file.

 

    Returns:

        sm_def_dict (dict) = the state machine definition as a dictionary.

    “””

 

    try:

        with open(f”{sm_def_file}”, “r”) as f:

            return f.read()

    except IOError as e:

        print(“Path does not exist!”)

        print(e)

        sys.exit(1)

 

def template_state_machine(

    sm_def: dict

) -> dict:

    “””

    Templates out the CloudFormation for creating a state machine.

 

    Parameters:

        sm_def (dict) = a dictionary definition of the aws states language state machine.

 

    Returns:

        templated_cf (dict) = a dictionary definition of the state machine.

    “””

   

    templated_cf = {

        “AWSTemplateFormatVersion”: “2010-09-09”,

        “Description”: “Creates the Step Function State Machine and associated IAM roles and policies”,

        “Parameters”: {

            “StateMachineName”: {

                “Description”: “The name of the State Machine”,

                “Type”: “String”

            }

        },

        “Resources”: {

            “StateMachineLambdaRole”: {

                “Type”: “AWS::IAM::Role”,

                “Properties”: {

                    “AssumeRolePolicyDocument”: {

                        “Version”: “2012-10-17”,

                        “Statement”: [

                            {

                                “Effect”: “Allow”,

                                “Principal”: {

                                    “Service”: “states.amazonaws.com”

                                },

                                “Action”: “sts:AssumeRole”

                            }

                        ]

                    },

                    “Policies”: [

                        {

                            “PolicyName”: {

                                “Fn::Sub”: “States-Lambda-Execution-${AWS::StackName}-Policy”

                            },

                            “PolicyDocument”: {

                                “Version”: “2012-10-17”,

                                “Statement”: [

                                    {

                                        “Effect”: “Allow”,

                                        “Action”: [

                                            “logs:CreateLogStream”,

                                            “logs:CreateLogGroup”,

                                            “logs:PutLogEvents”,

                                            “sns:*”            

                                        ],

                                        “Resource”: “*”

                                    },

                                    {

                                        “Effect”: “Allow”,

                                        “Action”: [

                                            “lambda:InvokeFunction”

                                        ],

                                        “Resource”: “*”

                                    }

                                ]

                            }

                        }

                    ]

                }

            },

            “StateMachine”: {

                “Type”: “AWS::StepFunctions::StateMachine”,

                “Properties”: {

                    “DefinitionString”: sm_def,

                    “RoleArn”: {

                        “Fn::GetAtt”: [

                            “StateMachineLambdaRole”,

                            “Arn”

                        ]

                    },

                    “StateMachineName”: {

                        “Ref”: “StateMachineName”

                    }

                }

            }

        }

    }

 

    return templated_cf

 

 

sm_def_dict = read_sm_def(

    sm_def_file=’sm_def.json’

)

 

print(sm_def_dict)

 

cfm_sm_def = template_state_machine(

    sm_def=sm_def_dict

)

 

with open(“sm_cfm.json”, “w”) as f:

    f.write(json.dumps(cfm_sm_def))

 

 

테스트 파이프라인 배포

 

전체 상태 머신의 전체 기능을 확인하려면 적절하게 테스트할 수 있도록 머신을 세워 두어야 합니다. 이는 프로덕션에 배포할 대상의 정확한 복제본으로, 적절한 엔드 투 엔드 테스트 및 승인을 통과한 후 배포되는 실제 프로덕션 스택과는 완전히 분리된 스택입니다. CodePipeline에서 지원하는 AWS CloudFormation 타겟을 활용할 수 있습니다. AWS 콘솔에서 이 단계를 구성하는 방법을 보여주는 다음 스크린 샷의 구성을 참조하세요.

 

 

엔드 투 엔드 테스트

특정 변경 사항이 주어졌을 때 문제없이 전체 상태 머신이 작동하고 실행되는지 확인하려면 샘플 인풋을 입력하고 특정 아웃풋에 대해 어서션을 작성하세요. 특정 어서션이 통과하고 예상되는 아웃풋을 얻는 경우 수동 승인 단계로 진행할 수 있습니다.

 

e2e_tests_buildspec.yaml file:

version: 0.2

env:

  git-credential-helper: yes

phases:

  install:

    runtime-versions:

      python: 3.8

    commands:

      pip3 install r tests/requirements.txt

 

   build:

    commands:

      pytest s vvv tests/e2e/ junitxml=reports/e2e.xml

 

 reports:

  StateMachineReports:

    files:

      “**/*”

    base-directory: “reports”

 

 

수동 승인 (SNS 주제 알림)

CI/CD 파이프라인에서 진행하려면 프로덕션으로 배포를 진행하기 전에 공식 승인 단계가 있어야 합니다. AWS CodePipeline의 수동 승인 단계를 사용하여 계속 진행하기 전에 파이프라인을 중지하고 Amazon SNS 토픽에 메시지를 보내도록 구성할 수 있습니다. SNS 주제는 다양한 가입자를 가질 수 있지만, 이 경우 승인자 이메일 주소를 주제에 가입하여 승인이 요청될 때마다 통지할 수 있습니다. 승인자가 파이프라인을 프로덕션으로 이동하도록 승인하면 파이프라인은 프로덕션 버전의 단계 기능 상태 머신 배치를 진행합니다.

 

이 수동 승인 단계는 다음과 유사한 구성을 사용하여 AWS 콘솔에서 구성할 수 있습니다.

 

 

 

프로덕션에 배포

린트, 장치 테스트, 엔드 투 엔드 테스트 및 수동 승인 단계가 끝나면 단계 기능 상태 머신을 프로덕션에 배치하는 단계로 넘어갈 수 있습니다. 이 단계는 AWS CloudFormation 스택의 이름이 다르다는 점을 제외하고 테스트 단계 배포 단계와 유사합니다. 이 경우 CodeDeploy에 대한 AWS CloudFormation 대상을 활용할 수도 있습니다.

 

이 단계가 성공적으로 완료되면 파이프라인 실행이 완료됩니다.

 

 

정리

테스트 상태 머신 및 Lambda 함수가 작동 여부를 확인한 후 기존 테스트 인프라를 삭제하는 CloudFormation단계를 포함하세요. 아래 구성과 비슷한 새로운 CodePipeline 단계로 구성할 수 있습니다.

 

 

결론

AWS States 언어 정의를 린트하고 검증했으며, Lambda 함수 코드를 단위로 테스트하고, AWS 상태 머신 테스트를 배포했으며, 엔드 투 엔드 테스트를 실행하고, 프로덕션에 배포하기 위한 수동 승인을 받아 프로덕션에 배포했습니다. 이를 통해 상태 머신과 주변 Lambda 함수 코드에 대한 변경 사항이 프로덕션 환경에서 올바르게 실행되는 것을 확인할 수 있습니다.

 

 

 

 

원문 URL: https://aws.amazon.com/ko/blogs/devops/testing-and-creating-ci-cd-pipelines-for-aws-step-functions-using-aws-codepipeline-and-aws-codebuild/

** 메가존클라우드 TechBlog는 AWS BLOG 영문 게재 글 중에서 한국 사용자들에게 유용한 정보 및 콘텐츠를 우선적으로 번역하여 내부 엔지니어 검수를 받아서, 정기적으로 개제하고 있습니다. 추가로 번역 및 게재를 희망하는 글에 대해서 관리자에게 메일 또는 SNS 페이지에 댓글을 남겨주시면, 우선적으로 번역해서 전달 드리도록 하겠습니다.