📁 完全なコードはGitHubで公開:GitHub: pipeiac02
シリーズ一覧(全12回)
Phase 1: 基盤構築
Phase 2: ワークフロー
Phase 3: セキュリティ・運用
Phase 4: 開発効率化
1. はじめに
1-1. 今回のゴール
Glue Crawlerを作成して、Parquetファイルのスキーマを自動検出できるようにします。
| ゴール | 内容 |
|---|---|
| 設計 | Crawlerの役割を理解する |
| 実装 | TerraformでCrawlerを作成する |
| 確認 | スキーマが検出されることを確認する |
1-2. 前回の振り返り
前回(第3回)では、Lambda ETLを作成しました。
| 作成済み | 今回作る |
|---|---|
| S3 Raw | Glue Database |
| Lambda ETL | Glue Crawler |
| S3 Processed | - |
1-3. この記事で作るもの
2. 比喩で理解する
2-1. Glue Crawlerを「メニュー係」で考える
料理が完成しても、メニュー表がないとお客さんは注文できません。
2-2. 比喩の図解(レストラン)
2-3. メニュー係の仕事
| 手順 | 内容 |
|---|---|
| 1. 料理を確認 | 盛り付け台にある料理を見る |
| 2. 情報を整理 | 料理名、価格、材料を把握 |
| 3. メニュー表に記載 | お客さんが注文できる形にする |
2-4. なぜメニュー係が必要?
2-5. 技術の図解(AWS)
2-6. 対応関係
| レストラン | AWS | 役割 |
|---|---|---|
| 盛り付け台 | S3 Processed | 完成した料理(データ) |
| メニュー係 | Glue Crawler | 料理を確認して記録 |
| メニュー表 | Glue Database | 料理の一覧と詳細 |
| お客さんの注文 | Athena | メニューを見てSQLで分析 |
2-7. スキーマとは?
| 用語 | 料理で例えると |
|---|---|
| スキーマ | メニュー表の項目(料理名、価格、説明) |
| カラム | 各項目(料理名 = 文字列、価格 = 数値) |
| テーブル | 1つのメニューカテゴリ(前菜、メイン、デザート) |
3. Glue Crawlerの役割
3-1. なぜCrawlerが必要か
S3にParquetファイルがあっても、Athenaは直接読めません。
3-2. Crawlerがやること
| 手順 | 内容 | 料理で例えると |
|---|---|---|
| 1 | S3をスキャン | 盛り付け台を見に行く |
| 2 | ファイル形式を判定 | 料理の種類を確認 |
| 3 | スキーマを検出 | 材料・価格を把握 |
| 4 | テーブルを作成/更新 | メニュー表に記載 |
3-3. Glueの構成要素
| 要素 | 役割 | 料理で例えると |
|---|---|---|
| Database | テーブルをまとめる入れ物 | メニューブック全体 |
| Table | 1つのデータセットのスキーマ | 1ページのメニュー |
| Crawler | スキーマを自動検出 | メニュー係 |
3-4. Athenaとの連携
3-5. パーティションの認識
Crawlerはパーティション構造も自動認識します。
processed/
├── year=2026/
│ ├── month=01/
│ │ └── data_001.parquet
│ └── month=02/
│ └── data_002.parquet
↓ Crawlerが検出
| カラム | 型 | 説明 |
|---|---|---|
| order_id | string | 注文ID |
| product | string | 商品名 |
| price | bigint | 価格 |
| year | string | パーティション(年) |
| month | string | パーティション(月) |
3-6. パーティションのメリット
-- パーティションなし:全データスキャン
SELECT * FROM sales WHERE year = '2026'
-- パーティションあり:2026年のみスキャン
SELECT * FROM sales WHERE year = '2026'
| 方式 | スキャン量 | コスト |
|---|---|---|
| パーティションなし | 全データ | 高い |
| パーティションあり | 該当年のみ | 低い |
4. 実装
4-1. ファイル構成
今回作成するファイル:
pipeiac02/
└── tf/
├── glue.tf # ★今回作成
└── iam.tf # ★Crawler用ロール追加
4-2. Glue Crawler用IAM(iam.tf に追加)
CrawlerがS3にアクセスするための権限を設定します。
コードの骨格:
# IAMロール(信頼ポリシー)
resource "aws_iam_role" "glue_crawler" {
name = "${var.project}-glue-crawler-role"
assume_role_policy = jsonencode({...}) # glue.amazonaws.com を許可
}
# IAMポリシー(S3アクセス権限)
resource "aws_iam_policy" "glue_s3" {
name = "${var.project}-glue-s3-policy"
policy = jsonencode({
Statement = [
{ Action = ["s3:GetObject"], Resource = ["...processed/*"] }, # ファイル読み取り
{ Action = ["s3:ListBucket"], Resource = ["...processed"] } # 一覧取得
]
})
}
# ポリシーをロールにアタッチ
resource "aws_iam_role_policy_attachment" "glue_s3" {...}
resource "aws_iam_role_policy_attachment" "glue_service" {...} # AWSGlueServiceRole
フルコード:
# iam.tf に追加
# ================================
# Glue Crawler用IAM
# ================================
resource "aws_iam_role" "glue_crawler" {
name = "${var.project}-glue-crawler-role"
# Glue用の信頼ポリシー
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "glue.amazonaws.com" }
}]
})
tags = merge(local.common_tags, {
Name = "${var.project}-glue-crawler-role"
})
}
# Glue Crawler用S3アクセスポリシー(最小権限)
resource "aws_iam_policy" "glue_s3" {
name = "${var.project}-glue-s3-policy"
description = "Glue Crawler用S3アクセスポリシー"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "ReadProcessedBucket"
Effect = "Allow"
Action = ["s3:GetObject"]
Resource = ["${aws_s3_bucket.processed.arn}/*"]
},
{
Sid = "ListProcessedBucket"
Effect = "Allow"
Action = ["s3:ListBucket"]
Resource = [aws_s3_bucket.processed.arn]
}
]
})
tags = local.common_tags
}
# Glue S3ポリシーをアタッチ
resource "aws_iam_role_policy_attachment" "glue_s3" {
role = aws_iam_role.glue_crawler.name
policy_arn = aws_iam_policy.glue_s3.arn
}
# Glue基本権限マネージドポリシーをアタッチ
resource "aws_iam_role_policy_attachment" "glue_service" {
role = aws_iam_role.glue_crawler.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole"
}
4-3. 権限の考え方
| 権限 | 理由 |
|---|---|
| GetObject | ファイルの中身を読む |
| ListBucket | ファイル一覧を取得 |
4-4. Glue Database / Crawler(glue.tf)
# glue.tf
# ================================
# Glue Data Catalog
# ================================
# Glue Database
# Athenaからクエリするためのメタデータを格納
resource "aws_glue_catalog_database" "main" {
name = "${var.project}_db"
description = "Data Pipeline用データベース"
}
# Glue Crawler
# S3のParquetファイルをクロールしてスキーマを自動検出
resource "aws_glue_crawler" "main" {
name = "${var.project}-crawler"
database_name = aws_glue_catalog_database.main.name
role = aws_iam_role.glue_crawler.arn
# クロール対象のS3パス
s3_target {
path = "s3://${aws_s3_bucket.processed.id}/processed/"
}
# スキーマ変更時の動作
schema_change_policy {
delete_behavior = "LOG" # 削除はログのみ
update_behavior = "UPDATE_IN_DATABASE" # 更新は反映
}
tags = merge(local.common_tags, {
Name = "${var.project}-crawler"
})
}
4-5. コード解説
| 設定 | 意味 | 料理で例えると |
|---|---|---|
database_name |
テーブルの格納先 | メニューブック |
s3_target.path |
スキャン対象 | 確認する盛り付け台 |
delete_behavior |
削除時の動作 | 料理がなくなったら記録だけ残す |
update_behavior |
更新時の動作 | 新しい料理はメニューに追加 |
4-6. schema_change_policy の解説
| 設定 | 値 | 理由 |
|---|---|---|
| delete_behavior | LOG | データ削除時もテーブル定義は残す |
| update_behavior | UPDATE_IN_DATABASE | 新しいカラムは自動追加 |
5. 動作確認
5-1. 計画(プレビュー)
cd pipeiac02/tf
terraform plan
期待される出力:
Plan: 4 to add, 0 to change, 0 to destroy.
追加されるリソース:
- IAMロール(Crawler用)
- IAMポリシーアタッチメント
- IAMロールポリシー
- Glue Database
- Glue Crawler
5-2. 適用(作成)
terraform apply
Enter a value: が出たら yes を入力。
期待される出力:
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
5-3. Crawlerの確認
aws glue get-crawler --name dp-crawler --query 'Crawler.{Name:Name,Database:DatabaseName,State:State}'
期待される出力:
{
"Name": "dp-crawler",
"Database": "dp_db",
"State": "READY"
}
5-4. Crawler実行(手動)
📝前提: Crawler実行前に、S3 Processedバケットにデータが必要、
第3回記事の「6-5.〜6-7.」でLambda ETLを実行していない場合は、先に実行が必要
aws glue start-crawler --name dp-crawler
5-5. 実行状態の確認
# 数秒待ってから実行
aws glue get-crawler --name dp-crawler --query 'Crawler.State'
| 状態 | 意味 |
|---|---|
| RUNNING | 実行中 |
| STOPPING | 停止中 |
| READY | 完了(待機中) |
5-6. 作成されたテーブルの確認
aws glue get-tables --database-name dp_db --query 'TableList[].{Name:Name,Columns:StorageDescriptor.Columns[].Name}'
期待される出力:
[
{
"Name": "processed",
"Columns": [
"order_id",
"product",
"price",
"quantity"
]
}
]
5-7. Athenaでクエリ実行
AWSコンソールで確認:
- Athena → クエリエディタ
- データベース:
dp_dbを選択 - 以下のクエリを実行:
SELECT * FROM processed LIMIT 10;
期待される出力:
| order_id | product | price | quantity | year | month |
|---|---|---|---|---|---|
| A001 | ノートPC | 89000 | 1 | 2026 | 01 |
| A002 | マウス | 2500 | 2 | 2026 | 01 |
| A003 | キーボード | 8000 | 1 | 2026 | 01 |
5-8. 確認チェックリスト
| 確認項目 | 期待値 |
|---|---|
| Crawler |
dp-crawler が存在 |
| Database |
dp_db が存在 |
| Table |
processed が作成されている |
| カラム | order_id, product, price, quantity |
| パーティション | year, month |
| Athenaクエリ | データが表示される |
6. まとめ
6-1. この記事でやったこと
| 項目 | 内容 |
|---|---|
| 設計 | Crawlerの役割を理解 |
| 実装 | TerraformでCrawlerを作成 |
| 確認 | スキーマ検出・Athenaクエリ |
6-2. 比喩の振り返り
| レストラン | AWS | 今回やったこと |
|---|---|---|
| 冷蔵庫 | S3 Raw | ✅ 作成済み |
| 料理人 | Lambda | ✅ 作成済み |
| 盛り付け台 | S3 Processed | ✅ 作成済み |
| メニュー係 | Glue Crawler | ✅ 今回作成 |
| メニュー表 | Glue Database | ✅ 今回作成 |
| お客さんの注文 | Athena | ✅ 動作確認 |
6-3. 作成したリソース
6-4. 現在のパイプライン
6-5. 次回予告
第5回: Step Functions では、ワークフロー管理を実装していきます。
- Lambda → Crawler の順序制御
- エラー時のリトライ設定
- ステートマシンの設計
レストランで言うと「調理長の工程管理」を作っていきます。