LoginSignup
1
0

More than 1 year has passed since last update.

Lambda (Python) で EC2 のイメージ (AMI) を作成してみる

Last updated at Posted at 2021-06-24

■ はじめに

963f9ca9395c096c234f891ad8877b28.png

ずいぶん昔に作成したものですが…

特定の EC2 インスタンスについて、定期的にイメージを作成したかったのでやってみました。

■ プログラムの流れ

  1. EC2 インスタンスのイメージの一覧情報を取得
  2. 古いイメージを削除
  3. EC2 インスタンスの一覧情報を取得
  4. 一覧情報の内、特定の EC2 インスタンスについて、イメージを作成
  • イメージを作成する EC2 インスタンスの識別としては、今回は、Name タグの先頭文字列が「★」であれば、イメージを作成することに。

■ コード

コードは、以下。

MaintenanceEC2_Image.py
#!/usr/bin/env python
# -*- coding: utf-8-unix; -*-
"""AWS Lambda Function - Maintenance EC2 Images
"""
from __future__ import print_function

import logging
import boto3
import os

# 「Python 3 で少しだけ便利になった datetime の新機能 - Qiita」
# <https://qiita.com/methane/items/d7ac3c9af5a2c659bc51>
from datetime import datetime, timezone, timedelta
TimeZone = timezone(timedelta(hours=+9), 'JST')

# 「Lambdaの本番業務利用を考える① - ログ出力とエラーハンドリング | ナレコムAWSレシピ」
# <https://recipe.kc-cloud.jp/archives/9968>
logger = logging.getLogger()
logLevelTable={'DEBUG':logging.DEBUG,'INFO':logging.INFO,'WARNING':logging.WARNING,'ERROR':logging.ERROR,'CRITICAL':logging.CRITICAL}
if os.environ.get('logging_level') in logLevelTable :
    logger.setLevel(logLevelTable[os.environ['logging_level']])
else:
    logger.setLevel(logging.WARNING)

#
StartDateTime = datetime.now(TimeZone)

def AmazonEC2_DescribeDeregisterImages(response_images, Image_Description_Head):
    try:
        deregister_images = []
        for n, k in enumerate(response_images["Images"]):
            id = response_images["Images"][n].get("ImageId")
            name = response_images["Images"][n].get("Name")
            description = response_images["Images"][n].get("Description")
            state = response_images["Images"][n].get("State")
            creationdate = response_images["Images"][n].get("CreationDate")
            if description.startswith(Image_Description_Head):
                logger.debug("(%04d/%04d) EC2 Image | [My] ID:[%s] Name:[%s] Description:[%s] State:[%s] CreationDate:[%s]",
                n + 1, len(response_images["Images"]), id, name, description, state, creationdate)
                deregister_images.append({
                    "ImageId" : id,
                    "Name" : name,
                    "Description" : description,
                    "State" : state,
                    "CreationDate" : creationdate,
                })
            else:
                logger.debug("(%04d/%04d) EC2 Image | [--] ID:[%s] Name:[%s] Description:[%s] State:[%s] CreationDate:[%s]",
                n + 1, len(response_images["Images"]), id, name, description, state, creationdate)
    except Exception as e:
        logger.exception('Error dosomething: %s', e)
    else:
        pass
    finally:
        pass
    return deregister_images

def AmazonEC2_DescribeCreateImageInstances(response_instances):
    try:
        create_image_instances = []
        for n, k in enumerate(response_instances["Reservations"]):
            id = response_instances["Reservations"][n]["Instances"][0].get("InstanceId")
            state = response_instances["Reservations"][n]["Instances"][0]["State"].get("Name")
            name = ""
            for nn, kk in enumerate(response_instances["Reservations"][n]["Instances"][0]["Tags"]):
                if response_instances["Reservations"][n]["Instances"][0]["Tags"][nn]["Key"] == "Name":
                    name = response_instances["Reservations"][n]["Instances"][0]["Tags"][nn]["Value"]
            if name.startswith("★"):
                logger.debug("(%04d/%04d) EC2 Instance | [Do] ID:[%s] Name:[%s] State:[%s]",
                n + 1, len(response_instances["Reservations"]), id, name, state)
                create_image_instances.append({
                    "InstanceId" : id,
                    "Name" : name,
                    "State" : state,
                })
            else:
                logger.debug("(%04d/%04d) EC2 Instance | [--] ID:[%s] Name:[%s] State:[%s]",
                n + 1, len(response_instances["Reservations"]), id, name, state)
    except Exception as e:
        logger.exception('Error dosomething: %s', e)
    else:
        pass
    finally:
        pass
    return create_image_instances

def lambda_handler(event, context):
    try:
        global StartDateTime
        StartDateTime = datetime.now(TimeZone)
        logger.info("StartDateTime:[%s]", StartDateTime)
        #
        logger.info("event:[%s]", event)
        logger.info("context:[%s]", context)
        #
        ImageCountMax_All = int(event["ImageCountMax_All"])
        logger.info("ImageCountMax_All:[%d]", ImageCountMax_All)
        #
        Image_Description_Head_Name = event["Image_Description_Head_Name"]
        Image_Description_Head = "Created by " + Image_Description_Head_Name + " from "
        logger.info("Image_Description_Head:[%s]", Image_Description_Head)
        #
        DeregisterImages_DryRun = event.get("DeregisterImages_DryRun")
        if not DeregisterImages_DryRun:
            DeregisterImages_DryRun = False
        logger.info("DeregisterImages_DryRun:[%s]", DeregisterImages_DryRun)
        if not isinstance(DeregisterImages_DryRun, bool):
            return "[ERROR] Parameter DeregisterImages_DryRun Format is Bool"
        CreateImages_DryRun = event.get("CreateImages_DryRun")
        if not CreateImages_DryRun:
            CreateImages_DryRun = False
        logger.info("CreateImages_DryRun:[%s]", CreateImages_DryRun)
        if not isinstance(CreateImages_DryRun, bool):
            return "[ERROR] Parameter CreateImages_DryRun Format is Bool"
        #
        ec2_client = boto3.client("ec2")
        response_images = ec2_client.describe_images(Owners=['self'])
        response_instances = ec2_client.describe_instances()
        # Amazon EC2 - Deregister Images
        deregister_images = AmazonEC2_DescribeDeregisterImages(response_images, Image_Description_Head)
        for n, k in enumerate(sorted(deregister_images, key=lambda image: image['CreationDate'], reverse=True)):
            if n < ImageCountMax_All:
                logger.info(("(%04d/%04d) Deregister Image | [--] "
                    "ID:[%s] Name:[%s] Description:[%s] State:[%s] CreationDate:[%s]"),
                    n + 1, len(deregister_images),
                    k["ImageId"], k["Name"], k["Description"], k["State"], k["CreationDate"])
                continue
            logger.info(("(%04d/%04d) Deregister Image | [Do] "
                "ID:[%s] Name:[%s] Description:[%s] State:[%s] CreationDate:[%s]"),
                n + 1, len(deregister_images),
                k["ImageId"], k["Name"], k["Description"], k["State"], k["CreationDate"])
            if DeregisterImages_DryRun:
                logger.info("Deregister Image - Dry Run. (DeregisterImages_DryRun is %s)", DeregisterImages_DryRun)
                continue
            response_deregister_image = ec2_client.deregister_image(
                ImageId=k["ImageId"])
            logger.info("Deregister Image Response | [%s] | %s", k["ImageId"], response_deregister_image)
        # Amazon EC2 - Create Images
        create_image_instances = AmazonEC2_DescribeCreateImageInstances(response_instances)
        for n, k in enumerate(create_image_instances):
            instanceid = k["InstanceId"]
            name = instanceid + StartDateTime.strftime("_%Y%m%d%H%M%S")
            description = Image_Description_Head + instanceid + " (" + StartDateTime.strftime("%Y/%m/%d %H:%M:%S") + ")"
            logger.info(("(%04d/%04d) Create Image [Do] "
                "ID:[%s] Name:[%s] Description:[%s] NoReboot:[%d]"),
                n + 1, len(create_image_instances),
                instanceid, name, description, True)
            if CreateImages_DryRun:
                logger.info("Create Image - Dry Run. (CreateImages_DryRun is %s)", CreateImages_DryRun)
                continue
            response_create_image = ec2_client.create_image(
                InstanceId=instanceid, Name=name, Description=description, NoReboot=True)
            logger.info("Create Image Response | [%s] | %s", instanceid, response_create_image)
    except Exception as e:
        logger.exception('Error dosomething: %s', e)
    else:
        pass
    finally:
        pass
    #
    return "normal end"

■ Lambda への引数

  • ImageCountMax_All
    • イメージの最大保持数。新しい方から数えて、この値を超えたイメージは削除。
  • DeregisterImages_DryRun
    • イメージ削除処理のドライランの設定。これが、false になっていないと、削除しない。
  • CreateImages_DryRun
    • イメージ作成処理のドライランの設定。これが、false になっていないと、作成しない。
  • Image_Description_Head_Name
    • 作成したイメージの description の先頭に付与する特定の文字列。当該プログラムで作成したイメージかどうかの判別に使用。
{
    "ImageCountMax_All": 360,
    "DeregisterImages_DryRun": false,
    "CreateImages_DryRun": false,
    "Image_Description_Head_Name": "Lambda (MaintenanceEC2_Image)"
}

■ Lambda に設定する環境変数

  • logging_level : [ログレベル]

■ ハマりポイント

  • 昔のことなので、覚えていません…

🤔🤔🤔

■ まとめ

参考になれば ♪♪♪

👋👋👋

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0