문제 상황
항상 24시간 띄워져야하는 서버를 제외하고, 특정 시간만 필요있는 서버가 있다. 우리 회사의 경우 세무사님들만 사용하는 서버가 하나 있는데,
세무사님들이 퇴근한 이후, 홈텍스의 서버가 닫히는 시간 이후로는 굳이 열어둘 필요가없었다. 따라서 불필요한 비용을 절감하기 위해서 밤 10시 이후로는 인스턴스를 자동 종료하고 아침 7시에 다시 서버를 자동 구동하는 환경을 만들어보았다.
사용되는 서비스

간단한 작동원리를 설명하자면
EventBridge 에서 매일 특정 시간 (22시, 07시) 이 되면 lambda 에게 event를 뿌린다. Lambda는 해당 이벤트의 종류를 확인한 후 (서버 구동인지, 종료인지) Ec2에 원격접속하여서 (boto3 사용) 서버 및 인스턴스를 종료하거나 인스턴스를 켜고 서버를 구동시킨다.
비용
이게 가장 중요한 포인트중 하나였다.
결국 CPU 사용시간을 줄여서 비용을 절약하는게 포인트였기때문에 다른 곳에서 돈이 더들면 안됐다.
EventBridge 의 경우 월 100만회 트리거까지 무료다. 하루에 총 3번 쓰이니깐 90번. 무조건 무료다. 왜 2번이아니라 3번이냐? 밑에서 설명하겠다.
Lambda 는 호출 횟수랑, 함수 시간에 비례해서 비용이 책정되는데, 프리티어의 경우 월 100만번, 400,000GB-초까지 무료이다.
Q: 왜 람다가 하루에 2번이 아닌 3번 호출되냐?
우선 이 질문에 답하기위해서는 람다가 어떤일을 해야할지를 설명해야한다,
부터 생각해보자
먼저 인스턴스를 종료하기전에 인스턴스에 돌아가고있는 서버들을 차례로 종료해야한다.
우리 서버의 경우 ws로 nginx, was 로 python 개발서버를 띄우고 있었기 때문에
순서대로 종료해준다음 인스턴스를 꺼주면된다.
즉 종료 프로세스는 다음과같다.
django 서버 닫기 -> nginx 종료 -> ec2 인스턴스 종료
는 다음과같다. 조립은 분해의 역순이므로 생각을 해보자.
먼저 인스턴스를 켜야할것이다.
켠 다음에는 nginx를 구동시키고 django 서버를 켜면된다.
여기서 문제는 아까말한것처럼 람다는 함수 실행 시간에 비례해서 비용이 든다.
근데 인스턴스가 켜질때까지 시간이 좀 걸리는데, 인스턴스를 켜는거부터 django 서버를 켜는거까지 하나의 람다 함수에서 실행하려면 인스턴스 다 켜질때까지 계속 기다려야한다. 즉 쓸데없는 비용이 나간다.
인스턴스를 켜는 람다를 만든다음 비동기로 완전히 켜졌을때 다른 람다를 호출한다.
그냥 인스턴스 켜는시간이 거의 정해져있으니, 시간이 촉박하지않다면 이벤트브릿지에서 인스턴스 켜는 이벤트, 서버켜는 이벤트 , 총 2개의 이벤트를 만들면된다.
나는 그냥 2번으로 했다. 이유는 복잡하게 하기싫어서이다.
어짜피 켜지는데 10분도 안걸린다. 따라서 많이잡아봤자 10분으로 잡고
6시 50분에 인스턴스 켜는 람다를 이벤트 브릿지에서 호출하고
7시에 서버를 켜는 람다를 이벤트 브릿지에서 호출하면된다.
Q: 람다가 ec2에 어떻게 접근하나요?
나는 python 개발자이므로 python 기준으로 설명하자면
boto3이라는 aws 전용 sdk가 있다.
이를 통해서 Ec2의 api 엔드포인트에 쉽게 접근가능하고,
만약 Ec2 내부에 커맨드를 날리고싶으면
SSM 서비스의 엔드포인트를 이용해서 EC2에 명령을 내리는 방법을 사용하면된다.
즉 ec2를 단순히 켜거나 끄려면 boto3이 EC2에게 명령을 내리는거고
ec2의 bash 쉘에 명령어를 입력하고 싶으면 boto3이 SSM에게 ec2로 명령어를 쏴주라고 명령을 내리는거라고 이해하면된다.
권한 설정
당연히, ssm command 를 할 수있는 권한이 람다에게 주어져야하고, ec2의 상태를 확인 하는 권한, 실행 및 종료할수 있는 권한 등등이 모두 주어져야한다.
다음과 같이 설정하면된다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ssm:SendCommand",
"ec2:DescribeInstances",
"ssm:ListCommands",
"ec2:StartInstances",
"ec2:StopInstances",
"ssm:ListCommandInvocations",
"ssm:GetCommandInvocation",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "{타겟 인스턴스}"
}
]
}