출퇴근에 맞추어 AWS 리소스 시작/정지 자동화하기

AWS를 사용하다 보면 가장 많이 하게되는 고민 중 하나는 요금 관련 고민입니다. 어떻게든 고정 지출 비용을 줄이는 것 만큼 확실한 효율 상승 효과는 없으니까요. 이런 수요를 알기 때문에 AWS에서도 매우 다양한 비용 절감 서비스(AWS Billing, Reserved Instance, Saving Plan 등)들을 제공하고 있습니다.

제공되는 서비스를 이용하는 것 이외에도 근본적으로 “사용하지 않으면 꺼둔다” 라는 단순한 방법으로도 많은 비용을 절감할 수 있습니다. 이번 포스트에서는 CloudWatch에서 제공하는 일정 규칙을 통해 퇴근후 자동으로 개발 리소스를 정지시키고 출근전 시작되도록 해서 비용을 절감하는 방법을 공유하려 합니다.

아키텍쳐 다이어그램


개요

제가 근무하는 트위니는 자율 출퇴근 기업입니다. 즉 출/퇴근 시간이 정해져있지 않습니다. 개발자의 창의성을 배려한 대표님의 정책인데 저는 매우 맘에 드는 회사의 대표적인 장점이라 생각합니다. 다만 저희 RND팀의 경우 팀원들 거의가 아이가 있다보니 출퇴근 시간이 어느정도 맞춰져 있습니다.(아이는 항상 아침에 일어나니까요…ㅠㅠ) 이에 따라 퇴근후 리소스를 정지시키고 출근전에 리소스를 켜두기만 해도 하루에 거의 2/3 이상의 시간동안 리소스를 정지시켜둘 수 있고(주말에는 항상 정지되어 있으니 실제로는 더) 이를 통해 대략 2/3의 비용 절감 효과를 누릴 수 있게 되었습니다.

이번 포스팅에서는 사용했던 솔루션의 예시로 EC2와 RDS를 시작/정지 하는 솔루션을 만들고 포스팅 끝에 간단히 재사용할 수 있는 CloudFormation 템플릿을 공유하겠습니다.

아래 내용은 마지막에 첨부한 CloudFormation 템플릿이 모든 과정을 포함하고 있으니 가볍게 보시기만 해도 됩니다.

다음 순서로 진행하겠습니다.

  1. IAM 역할 생성
  2. 람다 작성
  3. CloudWatch 스케쥴 규칙 생성후 람다 함수 연동

IAM 역할 생성

우선 람다를 통해 리소스를 시작/정지하려면 람다를 위한 역할을 생성해서 람다에 붙여주어야 합니다. 역할은 람다의 로깅을 위한 log 퍼미션과 각 리소스(이 경우에는 EC2,RDS)를 start/stop하기 위한 퍼미션을 부여합니다.

  • 우선 IAM 서비스로 이동하여 좌측 메뉴에서 역할을 선택하여 역할 만들기를 클릭합니다.
    IAM에서 역할을 선택합니다.

  • 사용 사례는 Lambda에 붙여 사용할 역할이기 때문에 Lambda를 선택합니다.
    사용 사례에서 Lambda를 선택합니다.

  • 우선은 아무런정책도 연결하지 않고 진행합니다. 그리고 적당한 이름(예시에서는 LambdaStartStop)을 부여하고 생성합니다.
  • 역할 목록에서 방금 생성한 역할을 선택합니다.
    아까 생성한 이름으로 된 역할을 누릅니다.

  • 권한에서 인라인 정책 추가를 눌러 인라인 정책을 부여하겠습니다.
    인라인 정책 추가를 누릅니다.

  • 탭에서 JSON을 눌러 정책을 직접 입력하겠습니다.
    시각 편집기 대신 JSON을 사용하겠습니다.


  • 아래 정책을 복사하여 붙여넣습니다.

policy.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:CreateLogGroup"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"ec2:Start*",
"ec2:Stop*",
"rds:StopDBInstance",
"rds:StartDBInstance"
],
"Resource": "*"
}
]
}
  • 이후 정책 검토를 눌러 진행하여 적절한 이름을 넣습니다. 정책까지 넣었으면 역할 생성이 끝났습니다.

AWS Lambda 생성

AWS Lambda는 서버 없이 코드를 실행시킬 수 있는 AWS 서비스입니다. 이 서비스를 통해 리소스를 정지/시작하는 함수를 만들어 스케쥴마다 호출할 예정입니다. 이 솔루션에서는 시작/정지를 위한 함수를 두 개 만드는 대신에 입력에 따라 시작/정지 시킬 수 있도록 분기처리를 해서 조금더 쉽게 구성해보았습니다.

  • 람다 서비스로 이동하여 람다를 생성합니다. 런타임은 Node.js 12.x로 선택합니다. 이름은 적절한 이름을 지어줍시다. (예시로 lambdaStartStop)
    많은 런타임이 있지만 Node.js 12.x를 선택합니다.


  • 실행 역할의 라디오 박스중 기존 역할 사용을 선택합니다. 이후 기존 역할중 아까 생성한 역할(예시로 LambdaStartStop)을 선택합니다.
    기존 역할은 아까 생성한 역할을 부여합니다.


  • 람다 소스 코드에 EC2 혹은 RDS를 시작/정지 시키는 함수 코드를 복사합니다.

  • EC2 start/stop

    startOrStopEC2.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    var AWS = require("aws-sdk");
    AWS.config.update({
    region: "ap-northeast-2"
    });
    exports.handler = async function (event, context) {
    console.log("event:",event);
    var ec2 = new AWS.EC2();
    var params = {
    InstanceIds: [
    event.ec2_id
    ]
    };
    if(event.action=="start")
    {
    await ec2.startInstances(params).promise();
    }
    else
    {
    await ec2.stopInstances(params).promise();
    }
    };
  • RDS start/stop

    stopRDS.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var AWS = require("aws-sdk");
    var rds = new AWS.RDS({ apiVersion: '2014-10-31' });
    exports.handler = async (event) => {
    var params = {
    DBInstanceIdentifier: event.rds_id
    };
    if(event.action=="start")
    {
    await rds.startDBInstance(params).promise();
    }
    else
    {
    await rds.stopDBInstance(params).promise();
    }
    };
  • Deploy를 눌러 저장합니다.
    Deploy를 눌러야 저장됩니다.

CloudWatch 이벤트 규칙(Event Rule)

AWS의 Event은 두 가지 트리거가 있는데 특정 이벤트의 발생 혹은 일정입니다. 솔루션에서는 일정을 사용해서 특정 요일(월~금)의 특정 시간대(9시 시작, 오후 10시 종료)에 람다를 호출합니다. 호출시에는 시작/정지시킬 리소스의 아이디와 시작인지 정지인지를 명시하기 위한 파라메터를 같이 보내도록 하겠습니다.

일정을 지정하기 위해서는 Cron 표현식(링크)을 사용합니다.

Cron Expression은 GMT 기준이기 때문에 우리나라 시간으로 맞출 경우에는 -9시간을 해서 Cron Expression을 구성해야 합니다.

예시로 아침 6시에 시작하고 저녁 10시에 정지를 기준으로 한다면 다음으로 구성할 수 있습니다.

  • 시작 (0 21 ? * 1-5 *) : 일요일-목요일 오후 21시 -> 한국시간 월요일-금요일 06시
  • 종료 (0 13 ? * 2-6 *) : 월요일-금요일 오후 1시 -> 한국시간 월요일-금요일 10시
  • 규칙을 2가지(시작, 정지) 생성해야 합니다. 우선 CloudWatch 서비스로 가서 좌측에 규칙을 누릅니다. 그리고 규칙 생성을 누릅니다.
    좌측 메뉴에서 규칙을 누릅니다.


  • 이벤트 소스에 이벤트 패턴은 일정을 선택하고 Cron 표현식을 생성해 위의 크론 표현식을 넣습니다. 시작 규칙은 시작 크론식을, 종료 규칙은 종료 크론식을 넣습니다. 우측의 대상은 Lambda 함수를 넣고 위에서 만든 Lambda 함수를 넣습니다.

    중요한 부분은 입력 구성입니다. 입력 구성은 상수(JSON 텍스트)를 넣는데 인스턴스 아이디와(RDS의 경우 RDS이름) 시작을 위한 규칙은 action:start를, 정지의 경우 action:stop을 넣어줍니다.

    예시로 ec2_id i-0beb860bcb51a7443 에 대한 규칙이라면

    stopRDS.js
    1
    2
    3
    4
    5
    6
    7
    8
    //ec2시작일 경우
    {"ec2_id":"i-0beb860bcb51a7443","action":"start"}
    //ec2종료일 경우
    {"ec2_id":"i-0beb860bcb51a7443","action":"stop"}
    //rds 시작일 경우
    {"rds_id":"rds-name","action":"start"}
    //rds 종료일 경우
    {"rds_id":"rds-name","action":"stop"}

    처럼 입력하면 됩니다.


  • 규칙을 생성하면 완료됩니다.

정지를 위한 규칙 하나, 시작을 위한 규칙 하나 총 두 개의 규칙이 필요합니다.

화면처럼 구성합니다.


CloudFormation 템플릿

위의 모든 내용을 담아 Cloudformation 템플릿을 구성하였습니다. 아래 템플릿은

  1. EC2 아이디 혹은 RDS DB이름을 받아서
  2. 월~금 지정시간(기본 새벽 6시)에 시작시키고
  3. 월~금 지정시간(기본 오후 10시)에 정지시킵니다.

EC2 템플릿 다운로드
RDS 템플릿 다운로드

파라메터 값
- EC2Id: EC2아이디 (예:i-0beb860bcb51a7441)
- RDSName: RDS Name(예:tw-rnd-db)
- StartTimeInGMT: GMT 타임존으로 설정된 리소스 시작 시간
- StopTimeInGMT: GMT 타임존으로 설정된 리소스 정지 시간

시간은 GMT 기준이기 때문에 우리나라 시간으로 맞출 경우에는 -9시간을 해서 넣어주어야 합니다.

템플릿을 사용하면 간편하게 위의 솔루션을 배포하여 여러 리소스에 반복해서 사용할 수 있습니다.

파라메터만 입력하면 간단하게 설정 가능합니다.



여담

AWS에서 공식적으로 제공하는 솔루션(링크) 를 사용할 수 도 있습니다. 이번에 소개한 솔루션과 비슷하며 이 경우에는 조금 복잡한 대신 다수의 인스턴스를 관리할 수 있고 DynamoDB에 지정한 대로 스케쥴을 정의할 수 있습니다.

EC2의 경우 Autoscaling의 scheduled action(링크)를 사용하면 정해진 시간에 인스턴스 숫자를 조절할 수 있습니다.


마치며

24시간 운영되어야 하는 프로덕션 레벨의 리소스가 아니라면 단순히 정지시켜 두기만 해도 많은 비용을 절감할 수 있습니다. 또한 예제의 람다를 조금 바꾼다면 EC2/RDS가 아니라 다른 On-Demand 기반 서비스(AWS ES, ElasticCache 등)에도 적용할 수 있으니 적용해보시면 어떨까 싶습니다.

WRITTEN BY
Dev Lead | Certified Professional AWS Solutions Architect/Devops Engineer

댓글