【AWS CDK】 Glue L2 Construct alphaがリリースされたのでPython Shell Jobsで試してみる
はじめに
データ事業本部ビッグデータチームのkasamaです。
今回は新しくリリースされたGlue CDK L2 construct alphaでGlue Python Shell Jobsを実装したいと思います。
概要
AWSの公式ブログで、Glue CDK L2 construct alphaのリリースがありました。L1 constructと比べて、必須パラメータが少なくデプロイ可能となります。指定していないパラメータはデフォルトが設定されます。L2のインターフェースを介した値の適用によりコンパイル時に不正な値や設定をチェックが可能になります。
The AWS CDK Glue L2 construct will migrate from its current alpha state to the AWS CDK core library after it completes the stabilization phase, which usually takes 3 months. For more details on the new Glue L2 construct and examples of its use, see the Glue CDK documentation. As always, if you have any feedback on the new construct or the CDK in general, you may create a GitHub issue on AWS CDK GitHub repository.
ブログにも記載がある通り、現在のアルファ版から、安定化フェーズを経て、AWS CDK コアライブラリに移行します。
The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the Semantic Versioning model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.
アルファ版の場合は、将来のバージョンで、後方互換性のない変更や削除が行われる可能性がありますので、本番環境などで運用する際は注意が必要です。
実装
実装したソースコードは以下になります。
48_glue_python_shell_with_cdk_l2 % tree
.
├── README.md
├── cdk
│ ├── bin
│ │ └── app.ts
│ ├── cdk.json
│ ├── jest.config.js
│ ├── lib
│ │ └── glue.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── test
│ │ └── app.test.ts
│ └── tsconfig.json
└── resources
└── glue
└── etl_script.py
7 directories, 10 files
app.ts
#!/usr/bin/env node
import * as cdk from "aws-cdk-lib";
import { GlueStack } from "../lib/glue";
const app = new cdk.App();
new GlueStack(app, "CMKasamaETL", {
description: "ETL (tag:kasama-test-tag)",
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
tags: {
Repository: "kasama-test-tag",
},
projectName: "cm-kasama-dev",
envName: "dev",
});
Glue stackをシンプルに呼び出すapp.tsです。
glue.ts
import * as path from "node:path";
import * as gluel2 from "@aws-cdk/aws-glue-alpha";
import * as cdk from "aws-cdk-lib";
import * as glueL1 from "aws-cdk-lib/aws-glue";
import * as iam from "aws-cdk-lib/aws-iam";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as s3deploy from "aws-cdk-lib/aws-s3-deployment";
import type { Construct } from "constructs";
export interface GlueStackProps extends cdk.StackProps {
envName: string;
projectName: string;
}
export class GlueStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: GlueStackProps) {
super(scope, id, props);
// ===== 共通のS3バケット設定 =====
const scriptBucket = new s3.Bucket(this, "ScriptBucket", {
bucketName: `${props.projectName}-${props.envName}-scripts`,
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});
// スクリプトをS3にアップロード
new s3deploy.BucketDeployment(this, "DeployScript", {
sources: [
s3deploy.Source.asset(path.join(__dirname, "../../resources/glue")),
],
destinationBucket: scriptBucket,
destinationKeyPrefix: "scripts",
});
// ===== L1 Construct による実装 =====
// L1用のIAMロール作成 - より詳細な権限設定
const glueJobRoleL1 = new iam.Role(this, "GlueJobRoleL1", {
roleName: `${props.projectName}-${props.envName}-etl-glue-l1-execution-role`,
assumedBy: new iam.ServicePrincipal("glue.amazonaws.com"),
});
// CloudWatchLogsへのアクセス権限を明示的に設定
glueJobRoleL1.addToPolicy(
new iam.PolicyStatement({
resources: ["arn:aws:logs:*:*:*:/aws-glue/*"],
actions: [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
}),
);
// S3バケットへの読み取り権限を付与
scriptBucket.grantRead(glueJobRoleL1);
// L1 Construct: CfnJobの作成
// より低レベルな実装で、AWS CloudFormationに近い形式
new glueL1.CfnJob(this, "SamplePythonShellJobL1", {
name: `${props.projectName}-${props.envName}-sample-job-l1`,
role: glueJobRoleL1.roleArn,
description: "Sample Python Shell Job (L1)",
glueVersion: "3.0",
// コマンド設定
command: {
name: "pythonshell",
pythonVersion: "3.9",
scriptLocation: `s3://${scriptBucket.bucketName}/scripts/etl_script.py`,
},
maxCapacity: 0.0625,
// 同時実行数の設定
executionProperty: {
maxConcurrentRuns: 1,
},
// ジョブ実行時の引数設定
defaultArguments: {
"--env": props.envName,
"--project": props.projectName,
// L2と同様にcontinuousLoggingを有効化
"--enable-continuous-cloudwatch-log": "true",
"--enable-metrics": "",
},
timeout: 30,
maxRetries: 2,
});
// ===== L2 Construct による実装 =====
// L2用のIAMロール作成
const glueJobRoleL2 = new iam.Role(this, "GlueJobRoleL2", {
roleName: `${props.projectName}-${props.envName}-etl-glue-l2-execution-role`,
assumedBy: new iam.ServicePrincipal("glue.amazonaws.com"),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName(
"service-role/AWSGlueServiceRole",
),
],
});
// CloudWatchLogsへのアクセス権限を明示的に設定
glueJobRoleL2.addToPolicy(
new iam.PolicyStatement({
resources: ["arn:aws:logs:*:*:*:/aws-glue/*"],
actions: [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
}),
);
// S3バケットへの読み取り権限を付与
scriptBucket.grantRead(glueJobRoleL2);
// L2 Construct: PythonShellJobの作成
// より高レベルな抽象化を提供し、設定がシンプル
new gluel2.PythonShellJob(this, "SamplePythonShellJobL2", {
jobName: `${props.projectName}-${props.envName}-sample-job-l2`,
role: glueJobRoleL2,
description: "Sample Python Shell Job (L2)",
glueVersion: gluel2.GlueVersion.V3_0,
pythonVersion: gluel2.PythonVersion.THREE_NINE,
script: gluel2.Code.fromBucket(scriptBucket, "scripts/etl_script.py"),
maxCapacity: gluel2.MaxCapacity.DPU_1_16TH,
defaultArguments: {
"--env": props.envName,
"--project": props.projectName,
},
timeout: cdk.Duration.minutes(30),
continuousLogging: { enabled: true },
maxRetries: 2,
});
}
}
Python scriptのデプロイ先となるS3 Bucketと、比較のためにGlueをL1 construct、L2 constructで実装しています。
// L2 実装
glueVersion: gluel2.GlueVersion.V3_0,
pythonVersion: gluel2.PythonVersion.THREE_NINE,
script: gluel2.Code.fromBucket(scriptBucket, "scripts/etl_script.py"),
L2ではenum型を使用して、有効な値のみを指定可能です。また、scriptの指定方法がより直感的になっています。今回はS3の格納先を指定していますが、以下の様にfromAsset
でlocalファイルを指定することでCDK側で自動作成されるS3 Bucketにファイルを格納してくれることもできます。
script: gluel2.Code.fromAsset("../resources/glue/etl_script.py"),
MaxCapacityについてもenumで明確な定数を使用して容量を指定可能です。
// L2 実装
maxCapacity: gluel2.MaxCapacity.DPU_1_16TH,
今回はオプションをある程度指定しましたが、以下の様に最小限の設定でも作ることが可能です。
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
declare const stack: cdk.Stack;
declare const role: iam.IRole;
declare const script: glue.Code;
new glue.PythonShellJob(stack, 'ImportedJob', { role, script });
詳しくは公式ページやaws-cdkライブラリのGithubを参照することでより理解が深まります。
etl_script.py
import sys
from awsglue.utils import getResolvedOptions
# 引数を取得
args = getResolvedOptions(sys.argv, ["env", "project"])
# 単純な出力
print(f"Environment: {args['env']}")
print(f"Project: {args['project']}")
print("Hello from Glue Python Shell Job!")
今回はGlue resourceの実装がmainのため、python処理自体は単純な出力のみです。
デプロイ
package.jsonがあるディレクトリで依存関係をインストールします。
npm install
次にcdk.jsonがあるディレクトリで、CDKで定義されたリソースのコードをAWS CloudFormationテンプレートに合成(変換)するプロセスを実行します。
npx cdk synth --profile <YOUR_AWS_PROFILE>
同じくcdk.jsonがあるディレクトリでデプロイコマンドを実行します。--all
はCDKアプリケーションに含まれる全てのスタックをデプロイするためのオプション、--require-approval never
はセキュリティ的に敏感な変更やIAMリソースの変更を含むデプロイメント時の承認を求めるダイアログ表示を完全にスキップします。never
は、どんな変更でも事前確認なしにデプロイすることを意味します。今回は検証用なので指定していますが、慎重にデプロイする場合は必要のないオプションになるかもしれません。
npx cdk deploy --all --require-approval never --profile <YOUR_AWS_PROFILE>
デプロイ後の確認
Cloudformationで問題なくスタックがデプロイできていることを確認。
Glue jobも正常に作成されています。
ジョブも成功しました。
最後に
L2の方が設定がよりシンプルで可読性高くなったので、安定化したら本番環境でも活用していきたいと思います。