■ はじめに
ずいぶん昔に作成したものですが…
特定の EC2 インスタンスについて、定期的にイメージを作成したかったのでやってみました。
■ プログラムの流れ
- EC2 インスタンスのイメージの一覧情報を取得
- 古いイメージを削除
- EC2 インスタンスの一覧情報を取得
- 一覧情報の内、特定の 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 :
[ログレベル]
■ ハマりポイント
- 昔のことなので、覚えていません…
🤔🤔🤔
■ まとめ
参考になれば ♪♪♪
👋👋👋