AWS

Make a Scheduler With AWS EventBridge

kahnco 2023. 11. 21. 23:41

문제 사항

  1. AWS Lambda의 Cold Start 문제로 인해 최대 15초 정도 시간이 소요되는 API가 존재

요구 사항

  1. 10분마다 해당 Lambda를 호출해주어서 Lambda Container가 Terminating 상태에 빠지지 않게 조치 필요
  2. 호출 비용을 최소화하기 위해서 트래픽이 가장 많이 몰리는 특정 시간에만 Warming 해주는 조치 필요

조치 사항

 AWS EventBridge를 활용해서 Lambda를 정기적으로 호출해주는 Cron 작업을 선언해주었고, 이를 AWS CDK를 활용하여 IaaC 특성을 살려주었다.

 

 활용한 코드 예시는 다음과 같다.

import {Construct} from 'constructs'
import * as cdk from 'aws-cdk-lib'
import * as iam from 'aws-cdk-lib/aws-iam'
import {FunctionUrlAuthType, InvokeMode} from "aws-cdk-lib/aws-lambda";
import {CfnSchedule} from "aws-cdk-lib/aws-scheduler";

export class LambdaSchedulerStack extends cdk.Stack {

    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props)

        const lambdaFunction = new cdk.aws_lambda.Function(this, id, {
            functionName: {WANTED_FUNCTION_NAME},
            runtime: {WANTED_RUNTIME},
            memorySize: {WANTED_MEMORY_SIZE},
            handler: {WANTED_APP_HANDLER},
            code: {WANTED_APP_CODE},
            timeout: cdk.Duration.seconds(300),
            architecture: cdk.aws_lambda.Architecture.ARM_64,
            environment: {WANTED_ENV_VALUES},
        })

        new CfnSchedule(this, `{WANTED_SCHEDULE_ID}`, {
            flexibleTimeWindow: {
            	// 유연 정책을 OFF 해두어서 정시에 작동되도록 설정
                mode: 'OFF',
            },
            // 10분마다, [06~09, 11~13, 16-21] 시간에만 작동되도록 설정
            scheduleExpression: 'cron(*/10 6-9,11-13,16-21 * * ? *)',
            // 스케줄 기준 TZ 설정
            scheduleExpressionTimezone: 'Asia/Seoul',
            // 설정된 스케줄마다 실행할 타겟 설정
            target: {
                arn: lambdaFunction.functionArn,
                roleArn: '{WANTED_ROLE_ARN}',
                input: JSON.stringify({
                    "test": "test",
                }),
                retryPolicy: undefined,
                deadLetterConfig: undefined,
            },
            groupName: '{WANTED_GROUP_NAME}'
        });
    }
}

 

 위의 코드에서 타겟으로 사용된 설정 중에서 정책 관련된 값은 다음과 같다. Production 레벨로 사용하려면 권한을 좀더 타이트하게 설정해야 하지만, 지금은 Lambda가 제대로 트리거 되는지 확인하는 Practice이기 때문에 관련 권한을 Full로 주었다.

// AmazonEventBridgeSchedulerFullAccess
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "scheduler:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::*:role/*",
            "Condition": {
                "StringLike": {
                    "iam:PassedToService": "scheduler.amazonaws.com"
                }
            }
        }
    ]
}

// AWSLAMBDA_Access
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudformation:DescribeStacks",
                "cloudformation:ListStackResources",
                "cloudwatch:ListMetrics",
                "cloudwatch:GetMetricData",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVpcs",
                "kms:ListAliases",
                "iam:GetPolicy",
                "iam:GetPolicyVersion",
                "iam:GetRole",
                "iam:GetRolePolicy",
                "iam:ListAttachedRolePolicies",
                "iam:ListRolePolicies",
                "iam:ListRoles",
                "lambda:*",
                "logs:DescribeLogGroups",
                "states:DescribeStateMachine",
                "states:ListStateMachines",
                "tag:GetResources",
                "xray:GetTraceSummaries",
                "xray:BatchGetTraces"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "iam:PassedToService": "lambda.amazonaws.com"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:DescribeLogStreams",
                "logs:GetLogEvents",
                "logs:FilterLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*"
        }
    ]
}

 

 그리고 해당 정책의 신뢰 관계는 다음과 같다. 다음처럼 scheduler Principal을 추가하지 않으면 해당 scheduler 에서 lambda를 호출해주지 못한다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "scheduler.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

 

 이렇게 설정하고 cdk를 배포해주면, EventBridge의 일정 탭에 새로운 스케줄러가 생성되게 된다. 하지만 다음 사진과 같이 Lambda의 트리거에는 해당 스케줄러가 설정되어 있지는 않다.

함수의 트리거에 스케줄러는 안 붙는다

 

 하지만 CloundWatch 로그를 통해서 Invocation 히스토리를 살펴보면 스케줄러가 제대로 동작하고 있음을 확인할 수 있다.

하지만 제대로 동작은 하고 있다

 


아쉬운 점

  1. 함수당 스케줄러가 하나씩 붙는 구조이기 때문에 여러 함수에서 Warm Status를 유지해야할 때에 관리해야할 스케줄러가 너무 많아진다.
  2. 비록 한 달 유지 비용이 300원 정도이지만, 함수가 많아질수록 비용이 증가하기 때문에 사용량에 따라서 Cron 범위를 조절해야할 필요성은 있어보인다.
반응형

'AWS' 카테고리의 다른 글

[WriterSide] Publishing and Access Control  (0) 2024.04.24
Deploy Selenium to the AWS Machine  (0) 2023.11.18
AWS CDK - API Gateway Fix Note 1  (0) 2023.11.07