前書き
AWS Glueの開発エンドポイントを利用すると、コードを一行ずつお試し実行しながらETLスクリプトを作成することが可能です。
Developing Scripts Using Development Endpoints - AWS Glue
実行環境としては、以下の二種類から選ぶことができます。
- Zeppelin Notebook
- SageMaker Notebook
SageMakerはjupyter Notebookをベースにしていて扱いやすかったです(個人的な感想)。
本記事では開発エンドポイント/SageMaker Notebookの環境構築をAWS CLIを使って行う方法をまとめました(※ SageMaker Notebookの立ち上げはGUIで行います)。
注意: 開発エンドポイントの料金
開発エンドポイントはオプションで、ETL コードをインタラクティブに開発することを選択した場合にのみ課金が適用されます。開発エンドポイントは、開発エンドポイントのプロビジョニングに使用されたデータ処理ユニットの時間に基づいて課金されます。AWS Glue の開発エンドポイントには最低で 2 個の DPU が必要です。デフォルトでは、AWS Glue は各開発エンドポイントに 5 個の DPU を割り当てます。DPU 時間あたり 0.44 ドルが 1 秒単位で課金され、最も近い秒単位に切り上げられます。プロビジョニングされた開発エンドポイントごとに 10 分の最小期間が設定されます。
- 開発エンドポイントは存在している間、常に課金され続けます。
- デフォルトではDPUs(Data processing units)が5に設定されます。(2020年11月28日現在)
「DPU時間当たり0.44ドルが1秒単位で課金され」とは、1時間につき、DPU一つに対して0.44ドルが課金されるということです。 何も考えずにデフォルト(DPUs-> 5個)で開発エンドポイントを作成すると、一時間当たり2.2ドル課金されます。
仮に1ヶ月そのままにしていれば、来月の請求には1580ドルほどになります。
(ちなみにマネジメントコンソール(GUI)から開発エンドポイントのタブを開くと「使わない時も課金されるから、削除してね!(意訳)」というメッセージが表示されます。親切ですね。)
月18万円!AWS Glueの開発エンドポイントで破産しないために - Qiita
↑こちらの記事では、落とし忘れた開発エンドポイントをLambdaによって通知する仕組みを提案されています。
環境
ローカルでAWS CLIコマンドを打っても、同じようにできるはずです(未確認)
開発エンドポイント/SageMaker Notebookの環境構築手順
事前準備
- AWS CLIが使える環境を用意すること(お勧めはCloud9)。
- AWS CLIを実行するロール or ユーザ(グループ)に以下のポリシーをアタッチしておくこと。
-
IAMFullAccess
※ これは最強なので、使用後は外しておくことをお勧めします。 -
AmazonS3FullAccess
-
AWSGlueServiceRole
-
AmazonSageMakerFullAccess
-
IAMロールの作成
必要なIAMリソースを作成していきます。AWS Glueの公式ドキュメントにも詳しく書かれていますが少々記述が不親切なところがあるので、その都度読み替えて適応します。 本手順では、以下のリソースが必要になります。
- 開発エンドポイント用のロール
- SageMaker Notebook用のロール
IAMロール作成手順
1. S3バケットと、バケットへのアクセス権限を定義したIAM ポリシーの作成
デフォルトリージョンを指定しておきます。
export AWS_DEFAULT_REGION=ap-northeast-1
自分のアカウントのAWS_IDを取得し、これをリソース名に含めるようにします。(JAWS-UG CLI専門支部 - connpass様のハンズオンを大いに参考にさせていただきました。)
AWS_ID=`aws sts get-caller-identity --query 'Account' --output text` && echo ${AWS_ID} aws s3api create-bucket --bucket "tmp-test-${AWS_ID}" --create-bucket-configuration "LocationConstraint=ap-northeast-1" cat << EOS > tmp-s3-access-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::tmp-test-${AWS_ID}/*" ] } ] } EOS # jsonが壊れてないかチェック cat tmp-s3-access-policy.json | python3 -m json.tool # ポリシー作成 POLICY_NAME=tmp-s3-access-policy aws iam create-policy --policy-name ${POLICY_NAME} --path "/tmp-test/" --policy-document file://tmp-s3-access-policy.json # 作成したポリシーが存在することを確認する。 aws iam list-policies --scope Local --path-prefix /tmp-test/ --query "Policies[*].PolicyName"
2. 開発エンドポイント用のロールの作成
繰り返しになりますが、基本的に「開発エンドポイント」につけるロールは、Glueで実行するジョブに付加するロールと同じ権限のものにします。 すでにジョブ実行に使用しているIAMロールがあれば、そちらを使うようにしてもOKです。
ロールの作成時には、「このロールはGlueにつかえますよ〜」というtrust relationship(Principalと呼ばれているもの)を定義したpolicy documentを作成する必要があります。これは以下に示す形式で、「Service」のところだけglueとなるように変更します。
(余談)厄介なのですが、このサービス指定部分の一覧は、公式ドキュメントにも整備されていないようです。
一度マネコンから作成したロールを確認するか、非公式の一覧(有志がまとめたもの)を参照するしかないようです。 -> List of AWS Service Principals · GitHub
# ロールの作成 $ cat << EOS > role-for-glue.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "glue.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOS # jsonが壊れていないか確認する。 cat role-for-glue.json | python3 -m json.tool GLUE_ROLE_NAME='AWSGlueServiceRole-Tmp-Test' && echo $GLUE_ROLE_NAME aws iam create-role --role-name $GLUE_ROLE_NAME --assume-role-policy-document file://role-for-glue.json --path /tmp-test/ # ロールができているか確認 aws iam list-roles --path /tmp-test/ --query "Roles[?RoleName=='${GLUE_ROLE_NAME}']"
3. 開発エンドポイント用のロールに、ポリシーをアタッチする
AWSGlueServiceRoleについては公式ドキュメントに詳しく記述されています。(AWSのmanaged policy。名前が「Role」なのでわかりづらいですが、これはポリシーです。)
AWSGlueServiceRole
(AWSのmanaged policy)で基本的なGlueの操作の権限を与え、自作のtmp-s3-access-policy
を使ってs3バケットへのアクセス権限を渡します。
# 先ほど作ったs3アクセス用ポリシーのARN POLICY_ARN=`aws iam list-policies --scope Local --path-prefix /tmp-test/ --query "Policies[?PolicyName=='${POLICY_NAME}'].Arn" --output text` # AWSGlueの基本的な権限を持つ、AWS managed policy も取得しておく。 GLUE_POLICY_ARN=`aws iam list-policies --scope AWS --query "Policies[?PolicyName=='AWSGlueServiceRole'].Arn" --output text`
aws iam attach-role-policy --role-name $GLUE_ROLE_NAME --policy-arn $GLUE_POLICY_ARN aws iam attach-role-policy --role-name $GLUE_ROLE_NAME --policy-arn $POLICY_ARN # アタッチしたポリシーが存在することを確認しておく。 aws iam list-attached-role-policies --role-name $GLUE_ROLE_NAME
4. SageMaker Notebook用のロールを作成する
SageMaker Notebookは、PrincipalでServiceを"sagemaker.amazonaws.com"に設定します。
cat << EOS > role-for-sagemaker.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "sagemaker.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOS cat role-for-sagemaker.json | python3 -m json.tool # 必ず、AWSGlueServiceSageMakerNotebookRole というprefixにします。 SAGEMAKER_ROLE_NAME='AWSGlueServiceSageMakerNotebookRole-Tmp-Test' && echo $SAGEMAKER_ROLE_NAME aws iam create-role --role-name $SAGEMAKER_ROLE_NAME --assume-role-policy-document file://role-for-sagemaker.json --path /tmp-test/ # ロールができているか確認 aws iam list-roles --path /tmp-test/ --query "Roles[?RoleName=='${SAGEMAKER_ROLE_NAME}']"
5. SageMaker Notebook用のロールに、ポリシーを作成 & アタッチする
ポリシーに、かなりクセがあります。 どうしても失敗するようならマネジメントコンソールでSageMaker Notebookのインスタンスをマネジメントコンソールから作成するときに、ロールを新規作成するオプションがあるので、そちらを選んだ方が簡単かもしれません。
Step 6: Create an IAM Policy for SageMaker Notebooks - AWS Glue
こちらのドキュメントに記載されているように作成しますが、bucket-nameに指定するのはaws-glue-jes-prod-ap-northeast-1-assets
となります。
(AWS glueに関係する設定ファイルなどが置かれている、公共のs3バケットのようです)
あとは自分のAWS IDとリージョン(ap-northeast-1など)を適切な場所に置換します。
(余談ですが、AmazonSageMakerFullAccessを与えてもうまく行きません。他のサービスと異なり、Glue関連は"FullAccess"系のポリシーだけでは動かないものが多いです。)
AWS_ID=`aws sts get-caller-identity --query 'Account' --output text` && echo ${AWS_ID} cat << EOS > tmp-sagemaker-policy.json { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:ListBucket" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::aws-glue-jes-prod-${AWS_DEFAULT_REGION}-assets" ] }, { "Action": [ "s3:GetObject" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::aws-glue-jes-prod-${AWS_DEFAULT_REGION}-assets*" ] }, { "Action": [ "logs:CreateLogStream", "logs:DescribeLogStreams", "logs:PutLogEvents", "logs:CreateLogGroup" ], "Effect": "Allow", "Resource": [ "arn:aws:logs:${AWS_DEFAULT_REGION}:${AWS_ID}:log-group:/aws/sagemaker/*", "arn:aws:logs:${AWS_DEFAULT_REGION}:${AWS_ID}:log-group:/aws/sagemaker/*:log-stream:aws-glue-*" ] }, { "Action": [ "glue:UpdateDevEndpoint", "glue:GetDevEndpoint", "glue:GetDevEndpoints" ], "Effect": "Allow", "Resource": [ "arn:aws:glue:${AWS_DEFAULT_REGION}:${AWS_ID}:devEndpoint/*" ] }, { "Action": [ "sagemaker:ListTags" ], "Effect": "Allow", "Resource": [ "arn:aws:sagemaker:${AWS_DEFAULT_REGION}:${AWS_ID}:notebook-instance/*" ] } ] } EOS # jsonが壊れてないかチェック cat tmp-sagemaker-policy.json | python3 -m json.tool # ポリシー作成 SAGE_POLICY_NAME=tmp-sagemaker-policy aws iam create-policy --policy-name ${SAGE_POLICY_NAME} --path "/tmp-test/" --policy-document file://tmp-sagemaker-policy.json # ポリシーのアタッチ SAGE_POLICY_ARN=`aws iam list-policies --scope Local --query "Policies[?PolicyName=='${SAGE_POLICY_NAME}'].Arn" --output text` aws iam attach-role-policy --role-name $SAGEMAKER_ROLE_NAME --policy-arn $SAGE_POLICY_ARN # アタッチされていることを確認 aws iam list-attached-role-policies --role-name $SAGEMAKER_ROLE_NAME
開発エンドポイントの作成
公式ドキュメント(下のリンク)には、マネジメントコンソール(GUI)とAWS CLI、それぞれを用いた開発エンドポイントの作成方法が簡単に記載されています。こちらを元に、手順を作りました。
Adding a Development Endpoint - AWS Glue
考慮すべきパラメータは以下の通りです。
--number-of-nodes
: (e.g.) '2'- 最初に述べたDPUs(Data Processing Units)。時間当たりの課金額を決める要員。
- 最小で2に設定できる。デフォルトは5。 最低の2に設定すれば、一時間に100円以下(正確には0.88ドル)の課金となるので安心。
--glue-version
: (e.g.) '1.0'- 開発エンドポイントでは
0.9
or1.0
しか選択できない。 - 実際には、現在は最新の
2.0
でjobを作ることが望ましい。
- 開発エンドポイントでは
--arguments
: (e.g.) 'GLUE_PYTHON_VERSION=3'
開発エンドポイント作成手順
パラメータが多いので、引数を準備 & 確認してから実行します。
AWS_ID=`aws sts get-caller-identity --query 'Account' --output text` && echo ${AWS_ID} GLUE_ROLE_NAME='AWSGlueServiceRole-Tmp-Test' && echo $GLUE_ROLE_NAME ENDPOINT_NAME="endpoint-tmp-${AWS_ID}" # 先ほど作成した、開発エンドポイント用のロールを選択します。 ROLE_ARN=`aws iam list-roles --query "Roles[?RoleName=='${GLUE_ROLE_NAME}'].Arn" --output text` NUMBER_OF_NODES=2 # Data Processing Units (DPUs) 2以上。 GLUE_VERSION=1.0 # 0.9か1.0 ARGUMENTS="GLUE_PYTHON_VERSION=3" REGION='ap-northeast-1' # きちんと定義できていることを確認しておく。 cat << EOS ENDPOINT_NAME: $ENDPOINT_NAME ROLE_ARN: $ROLE_ARN NUMBER_OF_NODES: $NUMBER_OF_NODES GLUE_VERSION: $GLUE_VERSION ARGUMENTS: $ARGUMENTS REGION: $REGION EOS
開発エンドポイント一覧を確認 & 新規作成。 この瞬間から課金されるので、開発エンドポイントは忘れずに削除する必要があります。
# 現在の開発エンドポイント一覧を確認する。 aws glue list-dev-endpoints # 開発エンドポイントの作成 aws glue create-dev-endpoint --endpoint-name $ENDPOINT_NAME --role-arn $ROLE_ARN --number-of-nodes $NUMBER_OF_NODES --glue-version $GLUE_VERSION --arguments $ARGUMENTS --region $REGION # 完了確認。 aws glue get-dev-endpoint --endpoint-name $ENDPOINT_NAME
エンドポイント作成後、利用可能になるまでに数分〜10分程度かかります。
以下のコマンドの結果が、PROVISIONING
の間は待ちましょう。
READY
になれば、次の手順に進めます(SageMaker Notebook作成)
aws glue get-dev-endpoint --endpoint-name $ENDPOINT_NAME --query "DevEndpoint.Status"
SageMaker Notebookの作成
SageMakerのインスタンスと、開発エンドポイントを紐つける処理を指定するのは少し面倒です。 (SageMakerのLifecycle configurationsから、インスタンス起動時のShellScriptをbase64でエンコードしてから指定する必要があります。)
そのため、この部分はマネジメントコンソール(GUI)から行うことにします(SageMaker自体もマネジメントコンソールから開く必要があります)
インスタンスタイプの指定などはCLIからupdateコマンドで行う必要があるようです(GUIからは指定することができません) (余談)インスタンスの状態がFailになってしまうとCLIからでないとリスタートできなかったり、現状はいろいろと不便なUIです。
SageMaker Notebook作成手順
AWS Glueの画面にアクセスし、作成した開発エンドポイントを選択してSageMaker Notebookのインスタンスを作成します。 この手順で作成すると、自動で開発エンドポイント周りの設定を行うスクリプトを作成してくれます。(インスタンス作成後、SageMakerのマネジメントコンソール-> Lifecycle configurationsからスクリプトを確認可能)
以下の様に設定し、ページ下のCreate Notebook
を押せば完了です。
もし10分ほどしてインスタンスの状態がFailとなってしまう場合は、SageMaker Notebookに付加したIAMロールの権限を間違えている可能性が高いです。その場合、「Create an IAM role」を選択すると自動で正しいロールを作成してくれます。
立ち上がるのには結構時間がかかります。下手すると10分くらいかかっているようです。
なお、IAMなどの設定ミスでインスタンス立ち上げがFailするとGlueのマネジメントコンソールからはstartできなくなります。 Amazon SageMakerのマネコン(もしくはCLI)を使ってstartする必要があります。
ETLスクリプトをSageMaker Notebookで試す
公式ドキュメントに、以下のような手順書が書かれています。こちらにしたがって試してみます。(上記でスクショを記載した、SageMaker Notebookインスタンスの作成手順も下のリンクに書いてあります。) Tutorial: Use an SageMaker Notebook with Your Development Endpoint - AWS Glue
pysparkを選び、ノートを作成します。
spark
と打ち込み、実行すれば準備はOKです。
上記スクショのように、ETLスクリプトと同じようにコードが実行できます。クローラを実行してGlueのデータベースを作っていれば、from_catalogメソッドを使ってS3バケットからの読み込みもできます。 AWSのリソース(S3など)にETLスクリプトと同じようにアクセスできるため、コードのデバッグに集中できます。
なお、sc = SparkContext()
は実行するとエラーになります。最初のspark
コマンドですでにscは作成済みだからです。
作成したリソースの後始末
作成したリソースを削除していきます。
SageMaker Notebook
インスタンスを停止しておきます。(停止すれば課金されません。コードを残しておけるので、stopにしておきます) 開発エンドポイントを削除しても、同じ名前の開発エンドポイントを再度作成すれば、SageMaker Notebookのインスタンスを再度立ち上げることができます。
不要ならデリートしてもOKです。
開発エンドポイント
絶対に消し忘れてはいけません!!課金されてしまいます。
# 一覧確認 aws glue list-dev-endpoints # 開発エンドポイント名を取得する ENDPOINT_NAME=`aws glue list-dev-endpoints --query "DevEndpointNames[0]" --output text` && echo $ENDPOINT_NAME # 削除する aws glue delete-dev-endpoint --endpoint-name $ENDPOINT_NAME # 削除されたことを確認 aws glue list-dev-endpoints
IAMリソース
不要ならば削除しておきます。IAMリソースは残しておくと増えていきがち & 使用目的を忘れると消すことができなくなるので、その場で消すのが良いと思います。
- 今回作成したポリシーとロールの一覧
ポリシーもロールも、/tmp-test/
というパスを指定して作成しました。パスを使えば、作成したIAMリソースの一覧を簡単に取得できます。(そのほかuserやgroupなども含め、IAMリソースはpathをつけておいた方が扱いやすいです。)
削除漏れ防止のために、確認しておきます。
# 作成したロール名一覧の確認方法 aws iam list-roles --path-prefix /tmp-test/ --query "Roles[*].RoleName" # 作成したポリシー名一覧の確認方法 aws iam list-policies --scope Local --path-prefix /tmp-test/ --query "Policies[*].PolicyName"
- SageMaker Notebookのロール(AWSGlueServiceSageMakerNotebookRole-Tmp-Test)からポリシーをデタッチする。
SAGEMAKER_ROLE_NAME=AWSGlueServiceSageMakerNotebookRole-Tmp-Test aws iam list-attached-role-policies --role-name $SAGEMAKER_ROLE_NAME SAGE_POLICY_NAME=tmp-sagemaker-policy SAGE_POLICY_ARN=`aws iam list-policies --scope Local --path-prefix /tmp-test/ --query "Policies[?PolicyName=='${SAGE_POLICY_NAME}'].Arn" --output text` aws iam detach-role-policy --role-name $SAGEMAKER_ROLE_NAME --policy-arn $SAGE_POLICY_ARN
tmp-sagemaker-policy
は自作のポリシーなのでこれも削除しておきます。
# 存在することを確認。 SAGE_POLICY_NAME=tmp-sagemaker-policy aws iam list-policies --scope Local --path-prefix /tmp-test/ --query "Policies[?PolicyName=='${SAGE_POLICY_NAME}'].Arn" --output text # ポリシーの削除 SAGE_POLICY_ARN=`aws iam list-policies --scope Local --path-prefix /tmp-test/ --query "Policies[?PolicyName=='${SAGE_POLICY_NAME}'].Arn" --output text` aws iam delete-policy --policy-arn $SAGE_POLICY_ARN
- SageMaker Notebookのロール(AWSGlueServiceSageMakerNotebookRole-Tmp-Test)を削除
# 削除対象のロールを確認 SAGEMAKER_ROLE_NAME=AWSGlueServiceSageMakerNotebookRole-Tmp-Test aws iam list-roles --path-prefix /tmp-test/ --query "Roles[?RoleName=='${SAGEMAKER_ROLE_NAME}'].RoleName" # 削除。上記手順(list-roles)を再度実行し、削除成功したかを確認しておく。 aws iam delete-role --role-name $SAGEMAKER_ROLE_NAME
- 開発エンドポイントのロールからポリシーをデタッチする
GLUE_ROLE_NAME=AWSGlueServiceRole-Tmp-Test aws iam list-attached-role-policies --role-name $GLUE_ROLE_NAME # アタッチされていたポリシーを、デタッチする。(POLICYのARNは、上記list-attached-role-policiesコマンドの結果からARNを探し、コピペで貼ると楽) aws iam detach-role-policy --role-name $GLUE_ROLE_NAME --policy-arn arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole aws iam detach-role-policy --role-name $GLUE_ROLE_NAME --policy-arn arn:aws:iam::${AWS_ID}:policy/tmp-test/tmp-s3-access-policy # ロールを削除する。 aws iam delete-role --role-name $GLUE_ROLE_NAME # 削除できたことを確認。 aws iam list-roles --path-prefix /tmp-test/ --query "Roles[?RoleName=='${GLUE_ROLE_NAME}'].RoleName"
デタッチしたポリシーのうち、tmp-s3-access-policy
は自作のポリシーなのでこれも削除しておく。
# 存在することを確認。 POLICY_NAME=tmp-s3-access-policy aws iam list-policies --scope Local --path-prefix /tmp-test/ --query "Policies[?PolicyName=='${POLICY_NAME}'].Arn" --output text # ポリシーの削除 POLICY_ARN=`aws iam list-policies --scope Local --path-prefix /tmp-test/ --query "Policies[?PolicyName=='${POLICY_NAME}'].Arn" --output text` aws iam delete-policy --policy-arn $POLICY_ARN
- s3バケットの削除
# 中身を空にしておきます。 aws s3 rm --recursive "s3://tmp-test-${AWS_ID}" # バケットを削除します。 aws s3 rb "s3://tmp-test-${AWS_ID}"
参考にさせていただいたリソースへのリンク
- JAWS-UG CLI専門支部 - connpass
- CLIコマンドについて、大いに参考にさせていただきました:pray:
- 月18万円!AWS Glueの開発エンドポイントで破産しないために - Qiita
- 開発エンドポイントのコストについて、わかりやすく解説されています。