ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • lambda + cloudwatch 를 이용한 EC2 스케줄링
    AWS 2020. 3. 22. 21:50

    하나의 서비스를 위해서 EC2 인스턴스를 생각보다 많이 만들게 되는 것을 경험하였습니다.

    개발 스테이지, 테스트 스테이지, 프로덕션 스테이지를 나누게 되며 예를 들어 redis인스턴스와 웹서버 인스턴스만 구성하여도 총 6개의 인스턴스가 생기됩니다.

    또한 고가용성을 위해 redis를 cluster로 구축한다면 더 늘어나겠죠.

     

    프로덕션이 아닌 개발 및 테스트 스테이지의 모든 인스턴스가 24시간 돌아간다면 한 달 뒤 요금 청구서를 보면 가슴이 아플 수도 있습니다.

     

    적용을 안 하셨더라도 괜찮습니다. 저 또한 기능 개발이 급하다는 핑계(?)로 미루다가 청구서를 보고 도입을 하고 그 과정을 공유합니다.

    여러분들도 이 글을 보시고 적용하셔서 도움이 되신다면 좋겠습니다.

     

     

    EC2 scheduling을 위한 구성도

    이 글은 아래와 같은 스텝으로 진행이 됩니다.

     

    1. 테스트 인스턴스 생성 및 앱 구동

        ( 주의 ) Spot instance로 생성하시면 이 포스팅의 방법으로는 스케줄링을 하실 수가 없습니다.

     

    2. Policy 생성 및 Role 생성

     

    3. lambda를 이용한 EC2 instance를 start 하고 stop 하는 function 생성

     

    4.EIP (Elastic IP) 할당 및 연결

     

    5. cloud watch의 event를 이용하여 특정 시간에 lambda 함수 호출

     

    Option. EC2 instance 부팅 시 자동으로 서비스 시작을 위한 PM2 조작법

     

    1. 테스트를 위한 인스턴스 생성 및 앱 구동

    저는 ubuntu 16.04 이미지를 사용하여 인스턴스를 생성하며 node + socket.io sample chat 프로젝트를 이용하여 서비스를 구성하겠습니다.

     

    1-1. 인스턴스 생성

     

    ( 주의 ) Spot instance로 생성하시면 이 포스팅의 방법으로는 스케줄링을 하실 수가 없습니다.

     

     

    인스턴스가 생성이 완료되었다면 상단의 connect 버튼을 클릭하여 접속 방법을 확인한 후 접속하여 줍니다.

    EC2 접속방법

    1-2. 서비스 앱 구성

     

    node + git + sample project 을 설치해 주도록 하겠습니다.

     

    1-2-1. git 설치

    sudo add-apt-repository ppa:git-core/ppa
    sudo apt-get update
    sudo apt-get install git-core
    git version

     

    1-2-2. node 설치

    curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
    sudo apt-get install -y nodejs
    

     

    1-2-3. sample 코드 다운로드 및 실행

    git clone https://github.com/dontbesatisfied/EC2_scheduler_sample_app.git
    cd EC2_scheduler_sample_app
    npm i
    npm start

     

    1-3. 확인

     

    instance의 Public DNS 혹은 Public IP의 3000 포트로 접속하시면 해당화면이 뜬다면 성공입니다.

    접속이 안되시면 인스턴스의 보안그룹에서 3000 포트를 열어주세요.

     

    2. Policy 생성 및 Role 생성

    2-1. Policy 생성

     

    [IAM] -> [Policies] -> [Create Policy] -> [JSON] -> [이름 및 설명] -> [완료]

    {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Action": [
              "logs:CreateLogGroup",
              "logs:CreateLogStream",
              "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
          },
          { "Effect": "Allow", "Action": ["ec2:Start*", "ec2:Stop*"], "Resource": "*" }
        ]
    }

     

     

    2-2. Role 생성

     

    [IAM] -> [Role] -> [Create role] -> [use case에 Lambda 선택] -> [방금 생성한 정책 연결] -> [이름 및 설명] -> [생성]

    위에서 생성한 정책을 연결 후 적당한 이름과 설명을 적어 생성을 완료합니다.

     

    3. lambda를 이용한 EC2 instance 를 start 하고 stop 하는 function 생성

    이 과정은 과금이 발생합니다. 하지만 기본 제공량이 있어서 초과하지 않는다면 과금되지 않습니다.

    3-1. stop 시키는 함수 생성

    3-1-1. [Lambda] => [Functions] => [Create function]

    적당한 함수명을 설정하시고 실행환경을 Python2.7 과 실행 역할을 2-2에서 설정한 역할을 연결해주세요.

     

    3-1-2. 하단의 Create function 버튼을 눌러 함수를 생성하신 후 함수의 스크립트를 입력해주세요.

    import boto3
    region = 'ap-northeast-2' # Region with target EC2 instance
    instances = ['i-09ad48a8834da6e50'] # Enter the instance IDs of the target EC2 instance.
    
    def lambda_handler(event, context):
        ec2 = boto3.client('ec2', region_name=region)
        ec2.stop_instances(InstanceIds=instances)
        print 'stopped your instances: ' + str(instances)

     

    3-1-3. Basic settings를 조정해줍니다. 인스턴스 갯수에 따라서 필요 메모리와 함수의 실행 Timeout이 달라질 수 있습니다.

    3-1-4. Save 후 Test를 하여 인스턴스가 제대로 정지되는지 확인

     

    처음이라면 Test버튼을 눌렀을 때 실행시킬 테스트 이벤트가 없기 때문에 아래와 같은 화면이 나타나며 이벤트를 만들어주셔야 합니다.

    적당한 이름만 부여하시고 키 밸류는 여기서 중요하지 않습니다.

     

    이벤트를 생성 후 테스트를 동작시키셨다면 아래와 같은 로그를 확인하실 수 있으며 실제로 EC2 인스턴스가 정지된 것을 확인하실 수 있습니다.

    성공적으로 동작한 것을 확인하실 수 있으며 이 로그의 Duration과 max memory used를 바탕으로 3-1-3의 setting을 튜닝하시면 좋습니다.

     

    3-2. start 시키는 함수 생성

    3-1의 과정을 반복하시되 함수의 스크립트만 아래와 같이 변경하고 테스트를 하여 인스턴스를 시작시켜주세요.

    import boto3
    region = 'ap-northeast-2' # Region with target EC2 instance
    instances = ['i-09ad48a8834da6e50'] # Enter the instance IDs of the target EC2 instance.
    
    def lambda_handler(event, context):
        ec2 = boto3.client('ec2', region_name=region)
        ec2.start_instances(InstanceIds=instances)
        print 'started your instances: ' + str(instances)

    인스턴스를 시작시키고 다시 3000 포트로 접속하여 서비스가 원활하게 작동하는지 살펴보십시오. 

    사이트에 연결할수 없다는 표시가 뜰 것입니다.

    당연히 인스턴스와 그 안의 내부 서비스는 별개이기 때문에 추가적인 작업을 통해 인스턴스가 시작 시 자동으로 서비스 또한 시작되게 해주어야 합니다.

    그 부분에 대해선 Option. EC2 instance 부팅 시 자동으로 서비스 시작을 위한 PM2 조작법에서 다루겠습니다.

     

     

    4.EIP (Elastic IP) 할당 및 연결

    위의 과정을 문제없이 따라오시며 테스트를 잘하셨을 거라고 생각됩니다.

    눈치채셨을 분도 계시겠지만 인스턴스를 정지 후 시작을 하게 되면 인스턴스의 Public IP 및 DNS가 변경되게 됩니다.

     

    종료전
    종료 후 시작

    혹여나 대상 인스턴스가 Route53과 같은 ip를 참조하여 다른 서비스와 연동이 되는 부분이 있다면 문제가 생기게 됩니다. 문제가 되지 않으신 분은 이 부분은 생략하셔도 상관이 없습니다.

     

    그럼 정지 후 시작하여도 IP가 변경되지 않도록 고정아이피 (EIP)를 할당받아보겠습니다.

     

    ( 주의 ) 이 과정은 시간당 소액의 과금이 발생하니 염두하시고 따라 해 주세요.

    https://aws.amazon.com/ec2/pricing/on-demand/#Elastic_IP_Addresses

     

    EC2 인스턴스 요금 – Amazon Web Services(AWS)

    요금은 각 인스턴스에 사용된 인스턴스 시간, 즉 인스턴스가 시작된 시점부터 종료 또는 중단될 때까지의 시간을 기준으로 책정됩니다. 각 부분 인스턴스에 사용된 시간은 Linux 인스턴스의 경우 초당 요금이 책정되고 다른 모든 인스턴스 유형의 경우 1시간 당 요금이 책정됩니다. 이 vCPU 개수는 지정된 EC2 인스턴스 유형에서 사용할 수 있는 기본 및 최대 vCPU 개수입니다. 이 인스턴스 유형을 시작할 때 사용자가 vCPU 개수를 지정할 수 있습니다. 인

    aws.amazon.com

    4-1. 고정 아이피 할당

    [Elastic IPs] => [Elastic IP 주소 할당 버튼 클릭] => [할당 버튼 클릭]

     

     

    4-2. 인스턴스와 고정아이피 연결

     

    Actions 탭을 눌러 인스턴스 ID와 방금 할당받은 고정아이피를 연결하시면 됩니다.

     

    4-3. 확인

    인스턴스를 확인해보시면 아이피가 위에서 할당받은 아이피이며 파란색으로 바뀐 것을 확인하실 수 있습니다.

    또한 인스턴스를 정지 및 시작하여도 아이피가 바뀌지 않는 것을 확인하실 수 있습니다.

     

    5. cloud watch의 event를 이용하여 특정 시간에 lambda 함수 호출

    이 과정은 과금이 발생합니다. 백만 번 호출당 1달러의 요금 부과가 됩니다.

    5-1. 특정 시간에 EC2 인스턴스 stop 시키기

     

    [CloudWatch] => [Events] => [Rules] 탭에 들어가셔서 rule을 생성해주셔야 합니다.

    저는 23시 59분에 람다 함수를 호출하여 EC2 인스턴스가 정지되게 하겠습니다.

     

    Cron 표현식은 UTC 시간대로 동작합니다.

    때문에 9시간의 차이를 두어 제가 의도한 23시 59분에 이벤트가 발생하도록 조정하였습니다.

     

    적당한 이름과 설명을 입력 후 Enabled 시켜주시면 동작하게 됩니다.

     

    5-2. 특정 시간에 EC2 인스턴스 start 시키기

     

    5-1 과정을 반복하시되 Target function을 start 시키는 함수와 Cron 표현식을 조정해주시면 됩니다. 

     

     

    Option. EC2 instance 부팅 시 자동으로 서비스 시작을 위한 PM2 조작법

    저는 PM2라는 것을 이용하여 서비스를 동작시킵니다.

    PM2는 JavaScript 런타임 Node.js의 프로세스 관리자입니다. 더 궁금하신 분들은 하단의 링크를 확인하시면 도움이 될 것 같습니다.

    https://engineering.linecorp.com/ko/blog/pm2-nodejs/

     

    먼저 pm2를 설치하겠습니다.

    sudo npm i -g pm2

    설치가 잘 됐는지 확인해보겠습니다.

    $ pm2 info
    
                            -------------
    
    __/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____
     _\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___
      _\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__
       _\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___
        _\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____
         _\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________
          _\/\\\_____________\/\\\_____________\/\\\___/\\\/___________
           _\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_
            _\///______________\///______________\///__\///////////////__
    
    
                              Runtime Edition
    
            PM2 is a Production Process Manager for Node.js applications
                         with a built-in Load Balancer.
    
                    Start and Daemonize any application:
                    $ pm2 start app.js
    
                    Load Balance 4 instances of api.js:
                    $ pm2 start api.js -i 4
    
                    Monitor in production:
                    $ pm2 monitor
    
                    Make pm2 auto-boot at server restart:
                    $ pm2 startup
    
                    To go further checkout:
                    http://pm2.io/
    
    
                            -------------
    
    [PM2] Spawning PM2 daemon with pm2_home=/home/ubuntu/.pm2
    [PM2] PM2 Successfully daemonized
    
      error: missing required argument `name|id'

     

    이제 pm2를 이용하여 서비스를 동작시키겠습니다.

    $ cd EC2_scheduler_sample_app/
    $ pm2 start --name server npm -- run start

     

    잘 동작하는 것을 확인하실 수 있습니다.

     

    이제 인스턴스가 시작될 때 서비스도 같이 시작할 수 있도록 pm2를 service에 등록해줄 것입니다.

    service 파일을 만들 줄 몰라도 걱정 안 하셔도 됩니다. pm2의 명령어를 이용하면 자동으로 pm2가 해결해줍니다!

     

    ubuntu@ip-172-31-33-34:~$ pm2 startup
    [PM2] Init System found: systemd
    [PM2] To setup the Startup Script, copy/paste the following command:
    sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
    
    ubuntu@ip-172-31-33-34:~$ sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
    [PM2] Init System found: systemd
    Platform systemd
    Template
    [Unit]
    Description=PM2 process manager
    Documentation=https://pm2.keymetrics.io/
    After=network.target
    
    [Service]
    Type=forking
    User=ubuntu
    LimitNOFILE=infinity
    LimitNPROC=infinity
    LimitCORE=infinity
    Environment=PATH=/home/ubuntu/bin:/home/ubuntu/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
    Environment=PM2_HOME=/home/ubuntu/.pm2
    PIDFile=/home/ubuntu/.pm2/pm2.pid
    Restart=on-failure
    
    ExecStart=/usr/lib/node_modules/pm2/bin/pm2 resurrect
    ExecReload=/usr/lib/node_modules/pm2/bin/pm2 reload all
    ExecStop=/usr/lib/node_modules/pm2/bin/pm2 kill
    
    [Install]
    WantedBy=multi-user.target
    
    Target path
    /etc/systemd/system/pm2-ubuntu.service
    Command list
    [ 'systemctl enable pm2-ubuntu' ]
    [PM2] Writing init configuration in /etc/systemd/system/pm2-ubuntu.service
    [PM2] Making script booting at startup...
    [PM2] [-] Executing: systemctl enable pm2-ubuntu...
    Created symlink from /etc/systemd/system/multi-user.target.wants/pm2-ubuntu.service to /etc/systemd/system/pm2-ubuntu.service.
    [PM2] [v] Command successfully executed.
    +---------------------------------------+
    [PM2] Freeze a process list on reboot via:
    $ pm2 save
    
    [PM2] Remove init script via:
    $ pm2 unstartup systemd
    
    ubuntu@ip-172-31-33-34:~$ pm2 save
    [PM2] Saving current process list...
    [PM2] Successfully saved in /home/ubuntu/.pm2/dump.pm2

     

    이제 정지 후 시작을 해보셔도 인스턴스와 내부의 서비스가 같이 시작되는 것을 확인하실 수 있습니다.

     

    이로써 원하시는 시간대에 EC2 instace를 가동으로 조절하여 요금을 절약하실 수 있습니다.

     

    마지막으로 정리를 해보겠습니다.

     

    Lambda를 이용하여 인스턴스를 조작하는 함수를 생성하고 Cloud Watch의 이벤트를 통해 해당 함수를 실행시켜 인스턴스를 조작한다. 인스턴스가 정지 후 시작될 때 IP가 변경되니 EIP를 이용하여 변경을 방지하고 인스턴스 시작될 때 내부 서비스가 같이 실행될 수 있도록 pm2 같은 서비스로 설정이 가능하다.

     

     

     

     

    언제나 피드백은 환영입니다 :) 

    감사합니다

     

    참고자료

    https://aws.amazon.com/ko/premiumsupport/knowledge-center/start-stop-lambda-cloudwatch/

     

    Lambda 및 CloudWatch를 사용하여 예약된 간격으로 EC2 인스턴스 시작 및 중지

    자동으로 EC2 인스턴스를 중지하고 시작하여 Amazon EC2(Amazon Elastic Compute Cloud) 사용량을 줄이고 싶습니다. 이를 위해 AWS Lambda 및 Amazon CloudWatch Events를 사용하려면 어떻게 해야 합니까?

    aws.amazon.com

     

    'AWS' 카테고리의 다른 글

    VPC와 Public/Private subnet 간단 개념 정리  (0) 2021.07.03
    CodePipeline 을 이용한 Nodejs 서버 앱 배포  (0) 2020.05.26
Designed by Tistory.