1. 목적

  • AutoScaling을 사용하는 중 매일 AMI 갱신 자동화가 필요한 고객사에 적용하기 위함

2. 내용

  • 구성 정보
    • 참고
      - Lambda는 AMI 생성ASG AMI 갱신 두개로 나뉘어 동작 한다.
      - 이유는 AMI 생성 시 State : Available로 되기까지 길게는 수십분 소요되는데, Lambda 동작 제한시간은 최대 15분이기 때문에 한 코드로 진행이 불가하다.
      - Pending 상태에서도 AMI 갱신까지 처리는 가능하지만, 만약 Available이 되지 않았는데 AutoScaling이 발생할 경우 정상적으로 Scale-Out이 될 수 없어 장애로 이어질 수 있다.
      - 위 내용을 해소하기 위해 두가지 작업을 분리하였다.

    • 코드 설명

      1. AMI 생성

      - 필수 수정 항목
        : c2id 에 원본 AMI 대상이 될 인스턴스 ID를 입력한다.

      - 코드

      import boto3


      ec2id = 'i-xxxxxxxxx'


      def AMI(ec2client):

          AMIName = ['[ASG]Auto_AMI_1','[ASG]Auto_AMI_2']

          try:

              if ec2client.describe_images(

                  Filters=[

                      {

                          'Name': 'name',

                          'Values': [AMIName[0]]

                      }])['Images'][0]['Name'] == AMIName[0]:

                  Ceimg = str(AMIName[1])

          except IndexError:

              Ceimg = str(AMIName[0])

              

          ec2client.create_image(

                  InstanceId = ec2id,

                  Name = Ceimg,

                  NoReboot=True

                  )


      def lambda_handler(event, context):

          ec2client = boto3.client('ec2')

          AMI(ec2client)



      2. ASG AMI 갱신

      - 필수 수정 항목
        : ASGName 에 대상이 될 오토스케일링그룹 이름을 입력한다.

      : userdata 에 기존 시작구성 > 사용자 데이터란에 있는 명령어 정보를 입력한다. 줄바꿈의 경우 \n을 입력한다.

        : 원본 시작구성 정보에 따라 주석을 해제 또는 설정한다(코드 내용 개별 수정 필요).
       


      -코드

      import boto3


      ASGName = 'TEST_AutoChangeLC'

      userdata = "systemctl stop nimbus\nrpm -e nimsoft-robot-7.90.beta3-1.x86_64"


      def AMI(ec2client):

          global AMIID

          AMIName = ['[ASG]Auto_AMI_1','[ASG]Auto_AMI_2']

          img1 = ec2client.describe_images(Filters=[{'Name': 'name','Values': [AMIName[0]]}])['Images'][0]

          img2 = ec2client.describe_images(Filters=[{'Name': 'name','Values': [AMIName[1]]}])['Images'][0]

          

          Ceimg = str(AMIName[1]) if img1['CreationDate'] < img2['CreationDate'] else str(AMIName[0])

          Deimg = str(AMIName[0]) if img1['CreationDate'] < img2['CreationDate'] else str(AMIName[1])

              

          ec2client.deregister_image(

              ImageId=ec2client.describe_images(

                  Filters=[

                      {

                          'Name': 'name',

                          'Values': [Deimg]

                      }])['Images'][0]['ImageId'])

              

          AMIID = ec2client.describe_images(

              Filters=[

                  {

                      'Name': 'name',

                      'Values': [Ceimg]

                  }])['Images'][0]['ImageId']

                  

      def ASG(asgclient):

          LCName = ['AutoLC_#1','AutoLC_#2']

          try:

              if asgclient.describe_launch_configurations(LaunchConfigurationNames=[LCName[0]])['LaunchConfigurations'][0]['LaunchConfigurationName'] == LCName[0]:

                  CeName = str(LCName[1])

                  DeName = str(LCName[0])

          except IndexError:

              CeName = str(LCName[0])

              DeName = str(LCName[1])

              

          Deslc = asgclient.describe_launch_configurations(LaunchConfigurationNames=[DeName])


          SGList = []

          for i in range(len(Deslc['LaunchConfigurations'][0]['SecurityGroups'])):

              SGList.append(Deslc['LaunchConfigurations'][0]['SecurityGroups'][i])

          

          asgclient.create_launch_configuration(

              LaunchConfigurationName=CeName,

              ImageId=AMIID,

              KeyName=Deslc['LaunchConfigurations'][0]['KeyName'],

              SecurityGroups=SGList,

              # ClassicLinkVPCId=Deslc['LaunchConfigurations'][0]['ClassicLinkVPCId'],

              ClassicLinkVPCSecurityGroups=Deslc['LaunchConfigurations'][0]['ClassicLinkVPCSecurityGroups'],

              UserData=userdata,

              # InstanceId=Deslc['LaunchConfigurations'][0]['InstanceId'],

              InstanceType=Deslc['LaunchConfigurations'][0]['InstanceType'],

              # KernelId=Deslc['LaunchConfigurations'][0]['KernelId'],

              # RamdiskId=Deslc['LaunchConfigurations'][0]['RamdiskId'],

              InstanceMonitoring={'Enabled': Deslc['LaunchConfigurations'][0]['InstanceMonitoring']['Enabled']},

              # SpotPrice=Deslc['LaunchConfigurations'][0]['SpotPrice'],

              IamInstanceProfile=Deslc['LaunchConfigurations'][0]['IamInstanceProfile'],

              EbsOptimized=Deslc['LaunchConfigurations'][0]['EbsOptimized']

              # AssociatePublicIpAddress=Deslc['LaunchConfigurations'][0]['AssociatePublicIpAddress'],

              # PlacementTenancy=Deslc['LaunchConfigurations'][0]['PlacementTenancy']

          )

          

          asgclient.update_auto_scaling_group(AutoScalingGroupName = ASGName,LaunchConfigurationName = CeName)

          

          asgclient.delete_launch_configuration(

              LaunchConfigurationName=DeName

              )


      def lambda_handler(event, context):

          ec2client = boto3.client('ec2')

          AMI(ec2client)

          asgclient = boto3.client('autoscaling')

          ASG(asgclient)




    • 사용 방법

      1. Lambda 생성

      - Python 버전은 3.8로 생성한다.
        : AMI 생성과 ASG갱신 두가지 Lambda로 생성해야 한다.

      - Rule은 Policies:AmazonEC2FullAccessAutoScalingFullAccess로 생성한다.
        : 위 정책만 있으면 기본 동작은 가능하나 시작구성 내용 중 IAMInstanceProfile에 등록되어있는 역할에 따라 정책 정보가 추가 된다. 내용 확인하여 필요한 정책을 추가한다. 
        : 크게 이슈사항이 없다면 AdministratorAccess 추가한다.(권장하지 않음)



      2. 함수 업로드

      - 첨부된 파일을 이용하여 각각에 파일 업로드 진행한다.



      - Lambda 구성 > 일반구성 > 제한시간 값을 3초에서 10초로 변경한다.



      3. 시작구성 복사

      - 기존 사용하던 시작구성을 복사하여 AutoLC_#1 또는 AutoLC_#2로 생성한다.




      4ASG_AMI_AutoUpdate_Step1 두번 실행하여 AMI를 2개 생성(필수!) 

      - [ASG]Auto_AMI_1, [ASG]Auto_AMI_2 두개 생성 확인(상태 available 체크)




      5ASG_AMI_AutoUpdate_Step2 실행 

      - Lambda로 생성된 AMI 중 먼저 생성된 AMI는 제거되며, 최신 AMI ID를 이용해 시작구성 복사 및 ASG 정보갱신


      - 기 생성된 AutoLC_#1를 기반으로하여 신규 AMI정보 i-~1f6b 정보로 업데이트 된 시작구성 AutoLC_#2가 생성

      - 등록된 AutoScaling그룹의 시작구성 변경 확인

      - ASG 수량 증가시 해당 시작구성으로 인스턴스 올라오는 것 확인
       




    • [참고] 생성된 람다 리스트


3. [추가]CloudWatch Event 적용

  • 트리거 추가
    • 각 람다마다 개별로 트리거 추가하여 cron 동작하도록 설정
      - AMI 생성 소요시간에 따라 cron 개별 조정