EC2 Image BuilderでAMIの自動作成を試してみる
Posted On 2025-01-18
TerraformとYAMLファイルで定義したイメージレシピを用い、Amazon Linux 2023にPyTorchをインストールしたカスタムAMIを自動的にビルドする手順を解説。IAMロールやセキュリティグループの設定を整えれば、定期実行や最新AMIへの追従なども効率的に行える。
目次
はじめに
- SCS勉強してたときに出てきた、AMIを自動でビルドしてくれる「EC2 Image Builder」を使ってみた
- ビルドのスクリプトをYAMLで書いて、いい感じにビルドしてくれるもの
- Dockerイメージのビルドにも使えるが今回はAMIのビルドを例に挙げる
- この例では、Amazon Linux 2023のAMIにPyTorchをインストールしたカスタムAMIを作成してみる
YAMLの書き方(component.yaml)
YAMLで書かれたイメージレシピを定義する。ステップ以下にいろいろ書いていくスタイル
name: InstallPackages
description: Install additional packages
schemaVersion: 1.0
phases:
- name: build # フェーズ名
steps:
- name: installPackages # ステップ名(任意)
action: ExecuteBash
inputs:
commands:
- dnf update -y
- dnf install -y python3-pip
- pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
EC2 Image BuilderのTerraform
EC2 Image Builderを使うためのTerraformは以下の通り
- ビルドするときに使うロール(この例では
EC2ImageBuilderRole
)が必要- ポリシーとして
AWSImageBuilderFullAccess
を付与する必要がある(絞る気ならもう少し絞れるが、これがないとPermission Deniedでビルドが失敗する)。 AmazonSSMManagedInstanceCore
もほぼ必須とされているようだ。
- ポリシーとして
- イメージレシピの部分がImage Builderで何をビルドするかで、ベースのAMI(この例ではAmazon Linux 2023)を指定し、レシピを外部のyamlファイル(
component.yaml
)で定義している - セキュリティグループ定義したり、インスタンスタイプを指定したりあたかもEC2を立ち上げるような設定をしているが、これはイメージをビルドしたりテストしたりするときに使うEC2インスタンスの設定。ビルド時に使われるもので、終わったら勝手に終了される。
- 定期実行も可能だが、今回は手動実行の例で示す
# IAM ロールの作成
resource "aws_iam_role" "image_builder_role" {
name = "EC2ImageBuilderRole"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [{
Effect = "Allow",
Principal = {
Service = "ec2.amazonaws.com"
},
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "image_builder_role_attach" {
role = aws_iam_role.image_builder_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" # 必要に応じて他のポリシーを添付
}
# すでに作成済みの ImageBuilderFullAccess に追加アタッチする例
resource "aws_iam_role_policy_attachment" "image_builder_role_attach_ec2img_full" {
role = aws_iam_role.image_builder_role.name
policy_arn = "arn:aws:iam::aws:policy/AWSImageBuilderFullAccess"
}
resource "aws_iam_instance_profile" "image_builder_profile" {
name = "EC2ImageBuilderInstanceProfile"
role = aws_iam_role.image_builder_role.name
}
# コンポーネントの作成(外部 YAML を参照)
resource "aws_imagebuilder_component" "example_component" {
name = "example-component"
version = "1.0.0"
platform = "Linux"
description = "Install packages example"
data = file("${path.module}/component.yaml")
}
# Amazon Linux 2023 AMI lookup
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["137112412989"]
filter {
name = "name"
values = ["al2023-ami-2023*-kernel-*-x86_64"]
}
filter {
name = "architecture"
values = ["x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Security Groups for ec2
resource "aws_security_group" "ec2_sg" {
name = "imagebuilder_ec2_sg"
description = "A security group for EC2 Image Builder"
vpc_id = var.vpc_id
egress {
description = "Allow all outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
}
# イメージレシピの作成
resource "aws_imagebuilder_image_recipe" "example_recipe" {
name = "example-recipe"
version = "1.0.0"
# 最新の Amazon Linux 2023 の AMI ID
parent_image = data.aws_ami.amazon_linux.id
component {
component_arn = aws_imagebuilder_component.example_component.arn
}
}
# インフラ構成の作成
resource "aws_imagebuilder_infrastructure_configuration" "example_infra_config" {
name = "example-infra-config"
instance_profile_name = aws_iam_instance_profile.image_builder_profile.name
security_group_ids = [aws_security_group.ec2_sg.id]
subnet_id = var.subnet_id # 適切なサブネットIDを指定
instance_types = ["t3.medium"]
terminate_instance_on_failure = true
}
# イメージパイプラインの作成(スケジュールなしで手動実行)
resource "aws_imagebuilder_image_pipeline" "example_pipeline" {
name = "example-image-pipeline"
image_recipe_arn = aws_imagebuilder_image_recipe.example_recipe.arn
infrastructure_configuration_arn = aws_imagebuilder_infrastructure_configuration.example_infra_config.arn
status = "ENABLED"
# schedule ブロックを省略 → 自動スケジュールなし
# schedule {
# schedule_expression = "cron(0 0 * * ? *)"
# }
}
ビルドしてみる
EC2 Image Builderの「イメージパイプライン」から、「アクション」→「パイプラインを実行」
裏でEC2が立ち上がってビルドが始まる
イメージを見ると途中経過が表示される。まあまあ時間がかかる(15分ぐらい)。特にエラーなければ全部完了済みになっているはず。ロールにポリシーが付与されていないとハマるとここが失敗することがある。
使ってみる
作ったAMIを使いたいときは、EC2の起動画面から「自分のAMI」で表示される。
Session Managerなどで適当にSSHして、Pythonに入りPyTorchのバージョンを表示してみると、最新のがインストールされている。
sh-5.2$ python3
Python 3.9.20 (main, Dec 11 2024, 00:00:00)
[GCC 11.4.1 20230605 (Red Hat 11.4.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.__version__
'2.5.1+cpu'
削除時
- terraform destroyしても、手動実行したジョブによって作られたイメージは自動削除されない。別途手動で削除する必要がある
- 作ったAMIを消すときは、ロールライフサイクル移行するのに必要な権限を持ったロール(ポリシー:
EC2ImageBuilderLifecycleExecutionPolicy
)が必要で、これは最初に作ったフルアクセスとは別のロールが必要。 - イメージを削除しようとすると、自動的に作成されたサービスロール(
AWSServiceRoleForImageBuilder
)があるが、これだと権限不足で消せない - 削除用のロールを作成するときは、信頼関係を「EC2 Image Builder」にして、
EC2ImageBuilderLifecycleExecutionPolicy
をアタッチすればOK。ここが若干面倒くさい
所感
- まあなるほどねという感じ
- GPUのAMIを作るときは結構便利そう
- AMI作るときは便利そうだけど、Terraformでこう作ってしまうと最新といつつベースのAMIのIDが固定されてしまうので、CI/CDとの棲み分けが詰める必要あるかなと思った
- ただAMIのセキュリティ対策で、どんどん最新のを使いましょうというのには結構使えそう。実際は定期実行でやるのだと思われる
Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー