[アップデート] EC2 Image Builderにライフサイクル機能が追加され、イメージの非推奨化や削除が自動化できるようになりました

2023.11.24

いわさです。

EC2 Image Builder を使うと AMI やコンテナイメージなど、イメージを作成するレシピを使ったパイプラインを構築することが出来ます。

EC2 Image Builder は自動作成には便利なのですが、古いイメージなどを削除する機能がありませんでした。
そのため、削除が必要な場合は次のようなカスタム実装が必要でした。

しかし、先日のアップデートで EC2 Image Builder にライフサイクル機能が実装されました。
これによって、古いイメージを削除したり非推奨にしたりといった部分も、組み込み機能のみで自動化することが出来るようになりました。

アナウンス直後に試したみたのですが、なかなかライフサイクルが動かずでようやく動作を確認することが出来ましたので、本記事で紹介したいと思います。

ライフサイクルポリシーでルールとアクションを定義する

まず、ライフサイクルを実装するためにライフサイクルポリシーを作成する必要があります。

EC2 Image Builder で作成出来るイメージには AMI とコンテナイメージがありますが、ライフサイクルはそのどちらのタイプもサポートされています。
ただし、ライフサイクルポリシーごとにどちらのポリシータイプにするのか選択する必要があります。
さらに、ポリシーにはアクティブかどうかの概念があり、ポリシーの作成だけしておいて実行はしないでおくことも出来ます。

IAM ロールについてはライフサイクルが必要な権限を付与したものが必要なのですが、作成方法は後述します。

ライフサイクルポリシーはルールの範囲、つまり適用対象をレシピかタグかで指定する形です。
今回は Amazon Linux に CloudWatch エージェントをインストールする標準コンポーネントを使ったレシピを作成しましたので、そちらを対象にしています。
なおレシピに複数バージョンある場合は、バージョンごとの指定が必要です。

ライフサイクルルールについてはポリシータイプによって選択可能なアクションが異なっています。 す。
AMI の場合は非推奨・無効化・削除をアクションとして組み合わせることが可能で、ルールとしては保存期間あるいはイメージの数をルールにアクションを実行することが可能です。

例えば次の場合だと、対象レシピから作成されたイメージを 2 つまで保持し、3 つ目以上はライフサイクルが実行されたタイミングで削除されます。

以下の場合だと、イメージの数に関わらずイメージが生成されてから一定期間が経過したものを削除するルールとなります。

ポリシータイプがコンテナイメージの場合だと、イメージ削除のみが可能となっています。

IAM ロール

ライフサイクルポリシーでは、ライフサイクル実行時に Image Builder へルール実行のためのアクセス許可を付与するために IAM ロールを関連付けする必要があります。

作成手順については公式ドキュメントをご確認頂くのが確実です。

信頼されたエンティティタイプで EC2 Image Builder を選択するとデフォルトのサービスロールが作成されるので、カスタム信頼ポリシーを選択します。

今回新たにマネージドポリシーEC2ImageBuilderLifecycleExecutionPolicyが追加されていまして、このマネージドポリシーをアタッチした IAM ロールが作成出来れば良いです。

マネージドポリシーの内容は以下となっています。
確認してみると、ライフサイクルポリシーのアクションに必要なアクセス許可が一式含まれています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Ec2ImagePermission",
            "Effect": "Allow",
            "Action": [
                "ec2:EnableImage",
                "ec2:DeregisterImage",
                "ec2:EnableImageDeprecation",
                "ec2:DescribeImageAttribute",
                "ec2:DisableImage",
                "ec2:DisableImageDeprecation"
            ],
            "Resource": "arn:aws:ec2:*::image/*",
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/CreatedBy": "EC2 Image Builder"
                }
            }
        },
        {
            "Sid": "EC2DeleteSnapshotPermission",
            "Effect": "Allow",
            "Action": "ec2:DeleteSnapshot",
            "Resource": "arn:aws:ec2:*::snapshot/*",
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/CreatedBy": "EC2 Image Builder"
                }
            }
        },
        {
            "Sid": "EC2TagsPermission",
            "Effect": "Allow",
            "Action": [
                "ec2:DeleteTags",
                "ec2:CreateTags"
            ],
            "Resource": [
                "arn:aws:ec2:*::snapshot/*",
                "arn:aws:ec2:*::image/*"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:RequestTag/DeprecatedBy": "EC2 Image Builder",
                    "aws:ResourceTag/CreatedBy": "EC2 Image Builder"
                },
                "ForAllValues:StringEquals": {
                    "aws:TagKeys": "DeprecatedBy"
                }
            }
        },
        {
            "Sid": "ECRImagePermission",
            "Effect": "Allow",
            "Action": [
                "ecr:BatchGetImage",
                "ecr:BatchDeleteImage"
            ],
            "Resource": "arn:aws:ecr:*:*:repository/*",
            "Condition": {
                "StringEquals": {
                    "ecr:ResourceTag/LifecycleExecutionAccess": "EC2 Image Builder"
                }
            }
        },
        {
            "Sid": "ImageBuilderEC2TagServicePermission",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeImages",
                "tag:GetResources",
                "imagebuilder:DeleteImage"
            ],
            "Resource": "*"
        }
    ]
}

ライフサイクルの実行タイミングを確認してみた

確認したところ、公式ドキュメントにライフサイクルポリシー実行の明確な記述は見当たりませんでした。
私が今回確認した限りでは、ライフサイクルポリシー作成後に一定期間(24 時間以上)経過後に、1 日 1 回実行されるものと思われます。

検証環境では先程のライフサイクルポリシーを有効化しており、ルールとしては 2 つまでイメージを保持するルールとなっています。
そして、対象レシピを使って次のようにパイプラインを構築しています。

手動でパイプラインを複数回実行し、次のように複数の出力イメージが作成されています。
この時点で「2 つまでイメージを保持して残りを削除」という条件を満たしている状態です。

しかし、10 時間以上待機してもライフサイクルポリシーは実行されず、アクションログタブにも何も出力されませんでした。
イメージの状態がルールに該当する場合に即座にポリシーアクションが実行されるわけではないことがわかりました。

その後しばらく放置しているとようやくライフサイクルポリシーが実行されました。

11 月 22 日 6 時 49 分に作成したポリシーが、11 月 23 日 18 時 44 分に実行されていることがマネジメントコンソール上の情報から確認出来ました。
36 時間後に実行されていますね。
また、11 月 23 日 8 時 7 分に作成したポリシーは 11 月 24 日 12 時 18 分に実行と、28 時間後に実行されていました。

明記されていないので何とも断言出来ないのですが、私の観測範囲としてはポリシー作成後に 24 ~ 48 時間で実行されるような印象です。

実行されたライフサイクルの詳細は、次のようにアクションログタブから実行詳細を確認することが出来ます。

また、実行 ID から詳細を確認してみると影響を受けるイメージ、つまりこのポリシーの場合だと削除されるイメージの一覧を確認することが出来ます。

対象レシピのイメージを確認してみると、最新の 2 つのみが保持されており、残りの 3 つは削除されていました。

また、その実体である AMI や EBS スナップショットについても削除されていることを確認しました。

ちなみにこちらのライフサイクルポリシーによるイメージ削除操作の履歴はCloudTrailでも確認できます。
次のようなイベントレコードを確認することが出来ました。

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "******:ImageBuilder",
        "arn": "arn:aws:sts::123456789012:assumed-role/hoge1122imagebuilderlifecycle/ImageBuilder",
        "accountId": "123456789012",
        "accessKeyId": ""******:",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": ""******:",
                "arn": "arn:aws:iam::123456789012:role/hoge1122imagebuilderlifecycle",
                "accountId": "123456789012",
                "userName": "hoge1122imagebuilderlifecycle"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2023-11-24T03:18:41Z",
                "mfaAuthenticated": "false"
            }
        },
        "invokedBy": "imagebuilder.amazonaws.com"
    },
    "eventTime": "2023-11-24T03:18:41Z",
    "eventSource": "imagebuilder.amazonaws.com",
    "eventName": "DeleteImage",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "imagebuilder.amazonaws.com",
    "userAgent": "imagebuilder.amazonaws.com",
    "requestParameters": {
        "imageBuildVersionArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:image/hoge1122recipe/1.0.0/4"
    },
    "responseElements": {
        "imageBuildVersionArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:image/hoge1122recipe/1.0.0/4",
        "requestId": "9abd2d49-c2da-4327-aba0-c2a4050405fd"
    },
    "requestID": "9abd2d49-c2da-4327-aba0-c2a4050405fd",
    "eventID": "d23fc2d5-5600-438d-9107-6f204f8710a5",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "123456789012",
    "eventCategory": "Management"
}

単純にDeleteImageされているだけですね。
そのため、AMI やスナップショットのごみ箱機能が有効な場合は、ごみ箱に残りますのでコスト削減目的で削除を行う際にはそのあたりも知っておいてください。

さいごに

本日は EC2 Image Builder でライフサイクル機能がサポートされ、自動でイメージの非推奨化や削除が出来るようになりました。

冒頭の記事のようにカスタム処理で世代管理を行っていた方はマネージド機能に乗り換えれるか検討しても良さそうですね。
ライフサイクルポリシーの実行頻度などについては少しタイムラグがあるのでそのあたりは気をつけましょう。

また、起動テンプレートなどで AMI を使っている場合などのチェックはこの機能でも行われないので、削除対象として良いのかどうかはユーザー側で気をつける必要があります。