サーバーレス | 株式会社Altus-Five / 株式会社Altus-Five は、技術力で勝負するシステム開発会社です。 Sun, 01 Jun 2025 09:08:08 +0000 ja hourly 1 https://wordpress.org/?v=6.8.2 /wp-content/uploads/2025/01/cropped-favicon-32x32.png サーバーレス | 株式会社Altus-Five / 32 32 AWSのエラーログ監視の設定 /blog/2020/09/07/aws-errorlog-monitoring/ /blog/2020/09/07/aws-errorlog-monitoring/#respond Mon, 07 Sep 2020 05:14:00 +0000 http://43.207.2.176/?p=268 AWS に サーバーレスなシステムを構築したときのログは、 CloudWatch Logs を使うことになりますが、エラーログの監視設定をするときの手順をご紹介します。 AWS コンソール画面から設定するやり方は、他にも […]

The post AWSのエラーログ監視の設定 first appeared on 株式会社Altus-Five.

]]>
AWS に サーバーレスなシステムを構築したときのログは、 CloudWatch Logs を使うことになりますが、エラーログの監視設定をするときの手順をご紹介します。

AWS コンソール画面から設定するやり方は、他にも、たくさん記事があるので、本記事では、 コピペで再利用できるように aws cli で設定するやり方で説明します。

大まかな、設定手順は、次のとおりです。

1) SNSトピックを作成
2) SNSトピックにサブスクライバーとしてエラー通知先を登録
3) CloudWatch のロググループにメトリクスを作成
4) メトリクスのアラームとして検出する閾値を設定して、アラーム時のアクションをSNSトピックにする

実際にエラーが検出されると、こんな感じで流れます。

a) ログに “ERROR” の文字列で見つかる
b) 対象ロググループのメトリクスフィルタでアラームになる条件が満たされると、
c) メトリクスのアクションが実行されて、SNSトピックに投稿される
d) SNSトピックのサブスクライバーとなっているメールアドレスに配信される

設定

実際の設定内容です。

SNSトピックの作成

CloudWatch Alarm からの投稿先となる SNSトピック を作成します。
また、このトピックのサブスクライバーとして、エラー通知先のメールアドレスを登録します。

export AWS_PROFILE=default
export AWS_ACCOUNT_ID=111111111111
export REGION=ap-northeast-1

# SNSトピック名
## CloudWatch Alarm から投稿されるトピック
SNS_TOPIC_ALARM=MyAppAlarmTopic

# SNS トピックの作成
aws sns create-topic \
    --name $SNS_TOPIC_ALARM

# トピックへのサブスクライブの登録
aws sns subscribe \
    --topic-arn arn:aws:sns:${REGION}:${AWS_ACCOUNT_ID}:${SNS_TOPIC_ALARM} \
    --protocol email \
    --notification-endpoint error@example.com

しばらくすると AWS Notification – Subscription Confirmation という件名のメールが来ます。本文を確認して Confirm を行ってください。

ちなみに、何回か設定を修正することになると思うので、削除するときのコマンドも載せておきます。

※設定修正のための削除コマンド

# トピックの削除
aws sns delete-topic \
    --topic-arn arn:aws:sns:${REGION}:${AWS_ACCOUNT_ID}:${SNS_TOPIC_ALARM}

※設定修正のための解除コマンド
サブスクライブを解除するときは、サブスクライブの一覧で ARN を取得してから削除します。

# 一覧取得
aws sns list-subscriptions

## 対象の ARN を指定して解除する
SUBSCRIPTION_ARN=arn:aws:sns:${REGION}:${AWS_ACCOUNT_ID}:${SNS_TOPIC_ALARM}:xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx
aws sns unsubscribe \
    --subscription-arn $SUBSCRIPTION_ARN

CloudWatch Logs のメトリクスフィルタを作成

ロググループ=/aws/lambda/api-hello-world で “ERROR” という文字列を検出する場合の設定です。

LOG_GROUP_NAME=/aws/lambda/api-hello-world
METRIC_NS=MyAppLog
METRIC_NAME="MyAppLogMetric_${LOG_GROUP_NAME}"
FILTER_NAME=ERROR

# メトリクスフィルタを作成
aws logs put-metric-filter \
    --log-group-name ${LOG_GROUP_NAME} \
    --filter-name ${FILTER_NAME} \
    --filter-pattern 'ERROR' \
    --metric-transformations \
    metricNamespace=${METRIC_NS},metricName=${METRIC_NAME},metricValue=1,defaultValue=0

※設定修正のための削除コマンド

aws logs delete-metric-filter \
    --log-group-name ${LOG_GROUP_NAME} \
    --filter-name ${FILTER_NAME}

メトリクスフィルタにアラームを作成

このアラームで、エラー検出の閾値の設定と、エラー検出時のアクションとして、SNSトピックへの投稿を登録します。

ALARM_NAME="Alarm-$LOG_GROUP_NAME"

# メトリクスフィルタにアラームを作成
aws cloudwatch put-metric-alarm \
    --alarm-name ${ALARM_NAME} \
    --alarm-description "${LOG_GROUP_NAME} のログにエラー" \
    --alarm-actions arn:aws:sns:${REGION}:${AWS_ACCOUNT_ID}:${SNS_TOPIC_ALARM} \
    --metric-name ${METRIC_NAME} \
    --namespace ${METRIC_NS} \
    --statistic Sum \
    --period 60 \
    --evaluation-periods 1 \
    --datapoints-to-alarm 1 \
    --threshold 1 \
    --comparison-operator GreaterThanOrEqualToThreshold \
    --treat-missing-data notBreaching

※設定修正のための削除コマンド

aws cloudwatch delete-alarms \
    --alarm-names ${ALARM_NAME}

確認

ログを書き込んでメール通知されることを確認します。
ログストリームの作成が必要ですが、なんでもよいので、 “test” というログストリームを作ります。

# テスト用のログストリームを作成する
LOG_STREMA_NAME=test
aws logs create-log-stream \
    --log-group-name ${LOG_GROUP_NAME} \
    --log-stream-name ${LOG_STREMA_NAME}

# エラーログを書き込む
CURRENT_TIME_MS=$(($(date +%s%N)/1000000))
aws logs put-log-events \
    --log-group-name ${LOG_GROUP_NAME} \
    --log-stream-name ${LOG_STREMA_NAME} \
    --log-events timestamp=$CURRENT_TIME_MS,message='ERROR test'

監視の間隔を60秒に設定しているので、60秒くらい待って、メールを確認します。
メール通知が確認できたら、 “test” ログストリームを削除します

aws logs delete-log-stream \
    --log-group-name ${LOG_GROUP_NAME} \
    --log-stream-name ${LOG_STREMA_NAME}

実際に、メール配信される通知内容の例です。

You are receiving this email because your Amazon CloudWatch Alarm "Alarm-/aws/lambda/hello-world" in the Asia Pacific (Tokyo) region has entered the ALARM state, because "Threshold Crossed: 1 out of the last 1 datapoints [1.0 (07/09/20 04:00:00)] was greater than or equal to the threshold (1.0) (minimum 1 datapoint for OK -> ALARM transition)." at "Monday 07 September, 2020 04:01:21 UTC".

View this alarm in the AWS Management Console:
https://ap-northeast-1.console.aws.amazon.com/cloudwatch/xxxxxxxxxxxxxxxx

Alarm Details:
- Name:                       Alarm-/aws/lambda/hello-world
- Description:                /aws/lambda/hello-world のログにエラー
- State Change:               OK -> ALARM
- Reason for State Change:    Threshold Crossed: 1 out of the last 1 datapoints [1.0 (07/09/20 04:00:00)] was greater than or equal to the threshold (1.0) (minimum 1 datapoint for OK -> ALARM transition).
- Timestamp:                  Monday 07 September, 2020 04:01:21 UTC
- AWS Account:                11111111111111111
- Alarm Arn:                  arn:aws:cloudwatch:ap-northeast-1:11111111111111111:alarm:Alarm-/aws/lambda/hello-world

Threshold:
- The alarm is in the ALARM state when the metric is GreaterThanOrEqualToThreshold 1.0 for 60 seconds.

Monitored Metric:
- MetricNamespace:                     MyAppLog
- MetricName:                          MyAppLogMetric_/aws/lambda/hello-world
- Dimensions:                         
- Period:                              60 seconds
- Statistic:                           Sum
- Unit:                                not specified
- TreatMissingData:                    notBreaching


State Change Actions:
- OK:
- ALARM: [arn:aws:sns:ap-northeast-1:11111111111111111:MyAppAlarmTopic]
- INSUFFICIENT_DATA:

エラーログの詳細を再通知する仕組みの追加

通知内容は、上記のような感じです。

確かにアラーム状態になったことは分かるのだけど、エラーログそのものが提示されてません。 そのため、 CloudWatch の Logを見に行かないといけないです。
それが面倒だという場合は、アラーム通知をLambda関数でサブスクライブして、CloudWatch の Log を抽出して、別のSNSトピックで再通知するという方法が、以下の記事で紹介されているので、参考にするとよいとい思います。
https://dev.classmethod.jp/articles/notification_cloudwatchlogs_from_sns/

以下は、この参考記事の内容を、aws cli でやっています。

ログ詳細通知用のトピックを作成

# トピックを作成
SNS_TOPIC_LOG_DETAIL=MyAppLogDetailTopic
aws sns create-topic \
    --name $SNS_TOPIC_LOG_DETAIL

# トピックへのサブスクライブの登録
aws sns subscribe \
    --topic-arn arn:aws:sns:${REGION}:${AWS_ACCOUNT_ID}:${SNS_TOPIC_LOG_DETAIL} \
    --protocol email \
    --notification-endpoint error@example.com

しばらくすると AWS Notification – Subscription Confirmation という件名のメールが来ます。本文を確認して Confirm を行ってください。

サブスクライブする Lambda 関数用の Role の作成

ROLE_NAME=MyAppLambdaSnsRole
aws iam create-role --role-name ${ROLE_NAME} \
    --assume-role-policy-document file://trust-policy.json

# ロールに基本的な Lambda 実行権限と CloudWatchLogsReadOnlyAccess と AmazonSNSFullAccess を付与
POLICY1=arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
POLICY2=arn:aws:iam::aws:policy/CloudWatchLogsReadOnlyAccess
POLICY3=arn:aws:iam::aws:policy/AmazonSNSFullAccess
aws iam attach-role-policy --role-name ${ROLE_NAME} --policy-arn $POLICY1
aws iam attach-role-policy --role-name ${ROLE_NAME} --policy-arn $POLICY2
aws iam attach-role-policy --role-name ${ROLE_NAME} --policy-arn $POLICY3

※もし作り直すなら

aws iam detach-role-policy --role-name ${ROLE_NAME} --policy-arn $POLICY1
aws iam detach-role-policy --role-name ${ROLE_NAME} --policy-arn $POLICY2
aws iam detach-role-policy --role-name ${ROLE_NAME} --policy-arn $POLICY3
aws iam delete-role --role-name ${ROLE_NAME}

lambda関数を作成

こちらの記事にあるlambda関数をそのまま sns_handler.py として保存してください。

lambda関数の処理は、アラートの通知トピックがパラメータで入ってくるので、それを条件に CloudWatch Logs から該当するログを検索して、ログ詳細通知トピックに投稿する処理です。

# lambdaのコードをzipにする
zip -r sns_handler.zip sns_handler.py

# 作成
LAMBDA_FUNCNAME=sns_handler
SNS_TOPIC_LOG_DETAIL_ARN=arn:aws:sns:${REGION}:${AWS_ACCOUNT_ID}:${SNS_TOPIC_LOG_DETAIL}
ENVIROMENT="Variables={SNS_TOPIC_ARN=$SNS_TOPIC_LOG_DETAIL_ARN}"
aws lambda create-function \
    --function-name ${LAMBDA_FUNCNAME} \
    --runtime python3.6 \
    --zip-file fileb://sns_handler.zip \
    --handler ${LAMBDA_FUNCNAME}.lambda_handler \
    --environment $ENVIROMENT \
    --timeout 60 \
    --role arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME}

# zipはもう不要なので削除
rm -f sns_handler.zip

※もし作り直すなら

aws lambda delete-function \
    --function-name ${LAMBDA_FUNCNAME}

アラーム通知トピックへの lambda のサブスクライブの登録

作成した lambda 関数と、もともとのアラーム通知トピックと紐づけます。 これによって、アラームのトピックに通知があると、 lambda関数 がトリガーされて、そのときの event パラメータにトピックの内容が渡されるようになります。

尚、lambda 関数を作り直したら、以下のコマンドも毎回再設定してください。

# トピックのサブスクライバーとしてlambda関数を登録
aws sns subscribe \
    --topic-arn arn:aws:sns:${REGION}:${AWS_ACCOUNT_ID}:${SNS_TOPIC_ALARM} \
    --protocol lambda \
    --notification-endpoint arn:aws:lambda:${REGION}:${AWS_ACCOUNT_ID}:function:${LAMBDA_FUNCNAME}

# SNSトピックからトリガー起動されるための設定
aws lambda add-permission \
    --function-name ${LAMBDA_FUNCNAME} \
    --source-arn arn:aws:sns:${REGION}:${AWS_ACCOUNT_ID}:${SNS_TOPIC_ALARM} \
    --statement-id ${LAMBDA_FUNCNAME} \
    --action "lambda:InvokeFunction" \
    --principal sns.amazonaws.com

まとめ

エラーログ監視の設定が、開発の本質的な作業かというと、ちょっと違うと思います。
でも、こういうものの設定にも、その設定方法を調べることに、多くの時間が割かれます。
だから、ついつい、先送りにしてしまうんですが、上記のようなコピペの元があると、最初の開発環境の一部として設定して、開発時のデバッグのために活用できるのではないでしょうか?

The post AWSのエラーログ監視の設定 first appeared on 株式会社Altus-Five.

]]>
/blog/2020/09/07/aws-errorlog-monitoring/feed/ 0
PySparkの分散される処理単位であるクロージャと共有変数の仕組み /blog/2020/06/15/pyspark/ /blog/2020/06/15/pyspark/#respond Mon, 15 Jun 2020 05:32:00 +0000 http://43.207.2.176/?p=270 Spark では、処理が分散されて、複数のノードやスレッドで実行されますが、 分散先でのデータの共有方法と、分散先で実行される処理単位のクロージャについて説明します。 共有変数 Spark には、データの共有とか、集約す […]

The post PySparkの分散される処理単位であるクロージャと共有変数の仕組み first appeared on 株式会社Altus-Five.

]]>
Spark では、処理が分散されて、複数のノードやスレッドで実行されますが、 分散先でのデータの共有方法と、分散先で実行される処理単位のクロージャについて説明します。

共有変数

Spark には、データの共有とか、集約するための仕組みが備わっています。
それが、ブロードキャスト(Broadcast)と集約器(Accumulator) です。
http://mogile.web.fc2.com/spark/spark220/rdd-programming-guide.html#shared-variables

ブロードキャスト
参照専用の共有メモリのようなもので、ドライバーで共有したい変数をブロードキャスト変数として登録すると、その変数のラッパーオブジェクトが作成されて、ラッパーオブジェクトが、各エグゼキューターに配信されます。
エグゼキューターからは、ラッパーを通じて、共有変数の実態にアクセスすることができます。 このあたりは、通信のコストを下げるために効果的なブロードキャストアルゴリズムで実装されていると、マニュアルに記載されています。

集約器(Accumulator)
accumulatorは、ドライバーで初期化されて、エグゼキューターからは、加算処理だけが可能で読み取りは出来きません。 読み取りはドライバーからのみ行うことができます。
何かの件数をカウントしたり合計値を求めるような処理に使われます。

クロージャ

PySpark では、ドライバがRDDのメソッドをクロージャとしてシリアライズして、エグゼキューターに配信して、エグゼキューターでデシリアライズされて、処理が実行されます。

Python は “全てがオブジェクト” ということで、関数もオブジェクトなので、関数自体が様々な属性を持っています。
例えば、func()という関数は、func.__code__でコード自体を参照可能だし、func.__globals__で関数内で参照している global な変数がリストされます。他にも、func.__defaults__func.__dict__などもあります。 https://docs.python.org/ja/3/reference/datamodel.html

クロージャとは、関数と関数内で使用される変数などがセットになったものを指します。 ときどき、クロージャとlambda関数が同じものとして説明されている記事を見ることがありますが、違うものです。lambda関数は、無名関数がクロージャを構成するときの要素の1つというのが、正しいです。

繰り返しになりますが、PySparkは、クロージャをシリアライズして、各エグゼキューターに配信します。

Python のシリアライズモジュールには pickle, marshal, dill, cloudpickle などがあります。(※この記事もあわせて読んでください。https://qiita.com/kojisuganuma/items/e9b29e8e5ef5f5f289b2
PySpark のシリアライズモジュールは、Python標準の pickle を元にした、独自のものでした。 実際のソースコードは、こちらです。 https://github.com/apache/spark/blob/d48935400ca47275f677b527c636976af09332c8/python/pyspark/cloudpickle.py#L222
A func comprises: code, globals, defaults, closure, and dict.とコメントがあるとおり、 PySparkがエグゼキューターに配信するクロージャは、以下を要素としています。

  • コード
  • 関数内で使っているグローバル変数
  • 引数のデフォルト値
  • 引数以外の変数に対してのセル (cell) 群
  • 関数属性の名前空間

シリアライズされる変数(グローバル変数など)が持つ値は、”宣言時のスコープ”によって設定された値です。 間違いやすいのは、global変数が宣言されていて、その変数の設定を init() などで、別の関数で初期化してたりすると、たとえ、それが RDD を実行する前だとしても、配信先では、復元されないということです。あくまで、変数宣言時のスコープで行われる処理だけがシリアライズされます。

参考記事

https://qiita.com/Hiroki11x/items/4f5129094da4c91955bc#%E2%85%B3-rdd%E3%81%AE%E6%93%8D%E4%BD%9C
https://www.lifewithpython.com/2014/09/python-use-closures.html

stackoverflow にも興味深い記事がありました。
「PySparkがブロードキャストされなかった変数を参照できるのはなぜですか?」
https://stackoverflow.com/questions/33337446/why-is-pyspark-picking-up-a-variable-that-was-not-broadcast
以下、回答の訳です。

基本的には、クロージャはシリアライズされて各executorとtaskに送られ、 task実行中にアクセスできるようになるので、 RDDのスコープ外のすべての変数をブロードキャストする必要はありません。
ブロードキャスト変数は、ブロードキャストされるデータセットが大きい場合に便利です。

まとめ

ブロードキャストとクロージャの使い分け

グローバル変数の初期値としてセットして、それを RDD や UDF の関数内から、参照するように実装しておけば、クロージャとして、データも一緒にエグゼキューターに配信されるので、小さいデータは、クロージャとして実装して、大きいデータはブロードキャストの機能を使うという使い分けができます。

分散実行されるコードの境界

Javaと違って、Pythonのコードだと、分散先で実行されるコードの境界がわかりづらいのだけど、その答えは、クロージャが実行されるということでした。

ご参考までに。

The post PySparkの分散される処理単位であるクロージャと共有変数の仕組み first appeared on 株式会社Altus-Five.

]]>
/blog/2020/06/15/pyspark/feed/ 0
私たちの AWS Glue を使った開発のやり方 /blog/2020/05/17/aws-glue-development/ /blog/2020/05/17/aws-glue-development/#respond Sun, 17 May 2020 08:59:00 +0000 http://43.207.2.176/?p=273 弊社での AWS Glue (Spark) を使ったシステムの開発のやり方についてご紹介します。ポイントはできるだけローカルの環境を使うことです。 はじめに AWS Glue は、ジョブの実行に Spark が使われてい […]

The post 私たちの AWS Glue を使った開発のやり方 first appeared on 株式会社Altus-Five.

]]>
弊社での AWS Glue (Spark) を使ったシステムの開発のやり方についてご紹介します。
ポイントはできるだけローカルの環境を使うことです。

はじめに

AWS Glue は、ジョブの実行に Spark が使われています。

Glueの動作イメージ
Glue は、 たぶん 、こんな動作イメージです。

  • ジョブの実行要求があると、Sparkのクラスターを起動する
  • Spark に対して、スクリプトを送信する
  • Spark 内でそのスクリプトが実行される
  • 実行結果が出力される

Glue 用のスクリプトをローカルの Python インタープリターで実行するというのとはちょっと違います。

Glue のスクリプトは S3 に配置される
GlueのAWSコンソールでスクリプトを編集して、登録することもできるのだけど、編集したスクリプトは、S3に保存されます。
そのため、そのS3のパスのスクリプトを上書きしたらOKなので、デプロイは、コピーで行う感じになります。

最初のglueのジョブ登録だけはコンソールから行う方法しか、わかっていませんが、その登録時に、保存先のS3パスを設定しているので、そのパスにコピーします。

ジョブの作成

ジョブの作成手順です。

  • AWSコンソールのGlueを開く
  • サイドメニューの「ジョブ」 を選択
  • 「ジョブの追加」ボタン押下
    • 名前 任意に名前をつける。ジョブ実行時の指定の使われる。
    • IAMロール 選択する(無ければ作成)
    • Type Spark
    • Glue Version Spark 2.4 (Glue Version 1.0)
    • このジョブ実行 ユーザーが作成する新しいスクリプト
    • スクリプトファイル名 e.g.) example.py
    • スクリプトが保存されている S3 パス
      • s3://my-data/glue-scripts
    • 一時ディレクトリ
      • s3://my-data/glue-temporary
      以上を設定して、「次へ」
  • 「接続」の画面では、何も追加しないで、「ジョブを保存してスクリプトを編集する」ボタン押下
  • 「スクリプト編集」の画面では、何も書かずに、「保存」ボタンを押下

これで、ジョブだけ登録されたので、実際のスクリプトは、ローカルでデバッグしたものを、上記の S3パスとスクリプトファイル名 に上書きコピーしてデプロイします。

Glue をローカルでデバッグする

Glue を docker で実行できるようにしました。詳細は、こちらの記事「AWS Glueのローカル環境を作成する」に書いているので、読んでみてください。

作成した dockerイメージは、altus5/devcontainer-glue で公開してあります。(buildしなくて大丈夫)

docker-compose に glue というサービスで起動するようになっているので、次のようにコンテナに入って実行できます。

docker-compose exec glue bash

以降の説明は、特に指定がないものは、この glue コンテナの中で実行するものとします。

小さいテストデータの作り方

ローカルの開発では、小さいデータを用意して、サクサク開発した方がよいでしょう。

例えば、変換元の hogelog という CSV があって、それを開発時のデータとして小さく編集して使おうという場合

## s3にある変換元データを持ってくる
aws s3 cp s3://my-data/hogelog/dt=2019-09-01/hogelog_v2_20190901-000.csv.gz .
## デバッグ用なので100行に小さくする
gzip -dc hogelog_v2_20190901-000.csv.gz | head -100 > hogelog_v2_20190901-000.csv
## ./fixture/srcdata/hogelog に移動
mkdir -p ./fixture/srcdata/hogelog
mv hogelog_v2_20190901-000.csv ./fixture/srcdata/hogelog
gzip ./fixture/srcdata/hogelog/hogelog_v2_20190901-000.csv

ローカルで AWS 無しで開発する

ローカルで実行できる前提があると、最初の1歩の踏み出しが楽になります。

ローカルのデータは、 file:// スキーマで URL を指定すると Spark はローカルから読み出します。
出力も同じくローカルに出力できます。そして、最初は、 Parquet 形式ではなくて csv で出力して、出力された値を目で確認できた方がよいでしょう。
いろいろ使い分けてデバッグしてみてください。

実装例

import io
import sys
import csv
import logging
import datetime
from awsglue.job import Job
from awsglue.transforms import *
from awsglue.context import GlueContext
from awsglue.utils import getResolvedOptions
from pyspark.sql import SQLContext
from pyspark.context import SparkContext
from pyspark.sql.types import StructType
from pyspark.sql.types import StructField
from pyspark.sql.types import StringType
from pyspark.sql.types import IntegerType
from pyspark.sql.types import BooleanType
from pyspark.sql.functions import *

# @params: [JOB_NAME]
args = getResolvedOptions(sys.argv, ['JOB_NAME'])

# ジョブ初期化
sc = SparkContext()
sqlContext = SQLContext(sc)
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)
logger = glueContext.get_logger()

# ローカルのテストデータを読み込む
df = spark.read.csv(
    "file:///workspace/fixture/srcdata/hogelog/*.gz", header=True, sep=",")

# ・・・・
# ロジック
# ・・・・

# ローカルに保存する
(df
    .write
    .mode("overwrite")
    #.format("parquet")
    .format("com.databricks.spark.csv")
    .save("file:///workspace/.dstdata/hogelog/"))

# ジョブコミット
job.commit()

Glue にスクリプトを実行させる。
上記の実装例を ./glue-scripts/example.py に保存してあるものとします。

gluesparksubmit \
    ./glue-scripts/example.py \
    --JOB_NAME='dummy'

AWS を使って開発する

データの入力元、出力先を、実際の AWS 環境を使って、テストする方法です。
各自の credentials を環境変数に設定して、 Glue を実行します。

上記の example.py の file:// で指定したデータのURLは、 s3:// などの実際の環境にあわせて変えます。

export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=YYYYYYYYYYY
export AWS_REGION=ap-northeast-1

# Glue 実行
gluesparksubmit \
    ./glue-scripts/example.py \
    --JOB_NAME='dummy' \

これで、 S3 の 出力先に出力されたことを確認できると思います。

The post 私たちの AWS Glue を使った開発のやり方 first appeared on 株式会社Altus-Five.

]]>
/blog/2020/05/17/aws-glue-development/feed/ 0
AWS Glueのローカル環境を作成する /blog/2020/05/07/aws-glue/ /blog/2020/05/07/aws-glue/#respond Thu, 07 May 2020 09:04:00 +0000 http://43.207.2.176/?p=275 AWS 上に構築されたシステム間で、データ変換と連携処理を行う開発を行いました。 連携データは S3 上に csv と json 形式で、何種類かのデータを使います。取り込んだデータは、最終的には、 Athena で検索 […]

The post AWS Glueのローカル環境を作成する first appeared on 株式会社Altus-Five.

]]>
AWS 上に構築されたシステム間で、データ変換と連携処理を行う開発を行いました。

連携データは S3 上に csv と json 形式で、何種類かのデータを使います。
取り込んだデータは、最終的には、 Athena で検索することに使われるんですが、元データの量が大きいため、効率よく短時間で取り込むために Spark が使いたいなぁという状況でした。
ただ、24時間常時実行される仕組みではないので Spark のためだけに複数台の EC2 を使うのはモッタイないです。
夜間バッチの1~2時間くらい使えればよいので Spark そのものが、サービス化されたものってないのかしら・・・と、AWSを探したところ AWS Glue がズバリそのものでした。

AWS Glue のお試し

さっそく、 Glue をシステムに組み込めそうか、お試しすることにしました。
CSV を Parquet 形式に変換するところに Glue を使ってみます。そして、おぉ、やっぱり速い!
ついでに Parquet 形式になったデータを Athena で検索してみても、おぉ、これも速い!
・・・ということを体感して、イケそうな感触が得られたので、本格的な開発の準備に取り掛かかることにしました。

料金を気にせず開発したい

Spark の開発をやったことのある方ならわかると思うのですが Spark は 大量のメモリを使うので、そのクラスター構成を動かすサービスとなると、利用料も安いハズがないです。
バグで暴走して、ものすごい請求が来た!なんてことになると困ります。ジョブを停止する手順をあらかじめ調べておいて、いざというときには、強制停止できるように準備しておく必要があります。
でも、それでも、”ジョブを実行”する段階では、ビビります。特に開発チームに参加したばかりの人は、実行ボタンを推すときの躊躇いの時間が、初期の立ちあがりの時間を遅くします。

プログラム作りは、ちょっと実装して動かしてみて軌道修正して・・・という作業を繰り返すので、”ためらう”ことなく実行できるローカルの環境があると、安心して試行に取り組めます。

AWS Glue をローカルで動かす

そんなわけで、dockerで実行できるようにしました。

参考にしたのは、この2つの記事です。
https://dev.classmethod.jp/articles/aws-glue-local/
https://future-architect.github.io/articles/20191101/

作成した dockerイメージは、Docker Hub で公開してあります。

Dockerfile は以下のようになりました。

  • Glue 1.0 は Python 3.6
  • PySparkを使うために、python:3.6 のイメージを元にしている
  • Spark は JDK8 じゃないとダメ(デフォルトのJDK10ではエラーが出る)
  • gluepyspark を1回動かしてmavenの依存モジュールをイメージに入れておく
  • jarの参照先をglueのものではなくsparkのものに入れ替える
FROM python:3.6

# Avoid warnings by switching to noninteractive
ENV DEBIAN_FRONTEND=noninteractive

ENV PYTHONUNBUFFERED 1

# Configure apt and install packages
RUN echo 'deb http://ftp.jp.debian.org/debian sid main' >> /etc/apt/sources.list \
    && apt-get update \
    && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \
    #
    # Verify git, process tools, lsb-release (common in install instructions for CLIs) installed
    && apt-get -y install git iproute2 procps lsb-release \
    #
    # Install pylint/awscli
    && pip --disable-pip-version-check --no-cache-dir install pylint awscli \
    #
    # for glue (spark=jdk8)
    && apt-get install -y openjdk-8-jdk zip unzip less \
    #
    # Clean up
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*

# awscli
RUN curl -sSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
RUN unzip awscliv2.zip
RUN ./aws/install

# maven (for glue)
RUN curl -sSOL https://aws-glue-etl-artifacts.s3.amazonaws.com/glue-common/apache-maven-3.6.0-bin.tar.gz
RUN tar -xzvf apache-maven-3.6.0-bin.tar.gz
RUN mv apache-maven-3.6.0 /opt/
RUN ln -s /opt/apache-maven-3.6.0 /opt/apache-maven
ENV PATH $PATH:/opt/apache-maven/bin

# glue 1.0
RUN git clone -b glue-1.0 --depth 1 https://github.com/awslabs/aws-glue-libs
RUN mv aws-glue-libs /opt/
ENV chmod -R ao+wr /opt/aws-glue-libs
ENV PATH $PATH:/opt/aws-glue-libs/bin

# spark
RUN curl -OL https://aws-glue-etl-artifacts.s3.amazonaws.com/glue-1.0/spark-2.4.3-bin-hadoop2.8.tgz
RUN tar -xzvf spark-2.4.3-bin-hadoop2.8.tgz 
RUN mv spark-2.4.3-bin-spark-2.4.3-bin-hadoop2.8 /opt/
RUN ln -s /opt/spark-2.4.3-bin-spark-2.4.3-bin-hadoop2.8 /opt/spark
ENV SPARK_HOME /opt/spark

# glue setup
RUN gluepyspark

# 2020/6/30にbuildし直してみたら、jarのバージョン違いが解消されて、
# 以下のオマジナイをしなくても大丈夫になったようです。
# RUN mv /opt/aws-glue-libs/jarsv1 /opt/aws-glue-libs/jarsv1.bk
# RUN ln -s ${SPARK_HOME}/jars /opt/aws-glue-libs/jarsv

# AWSのGlueの実行環境には、以下の外部ライブラリがインストール済のようで、
# 外部ライブラリとして、zip化しなくても、使えるようです。
# https://docs.aws.amazon.com/ja_jp/glue/latest/dg/add-job-python.html#python-shell-supported-library
# 少なくとも、boto3 は使えたので、コンテナにも、インストール済にしておく。
# 他、ユニットテストに必要なライブラリも追加
RUN pip install -U boto3 pytest pandas

docker-compose.yml も作成します。 のちのち、LocalStackも入れたくなるでしょう。

version: '3'
services:
  glue:
    image: altus5/devcontainer-glue:latest
    volumes:
      - '.:/workspace'
    working_dir: '/workspace'
    environment:
      TZ: 'Asia/Tokyo'
    command: 'sleep infinity'

docker-compose up -d で起動させたら glue のコンテナ内に入ります。

docker-compose exec glue bash

実行するには、それぞれのアカウントを設定して、テストデータを配置して、 glue にスクリプトを送信します。

export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=YYYYYYYYYYY
export AWS_REGION=ap-northeast-1

# S3にテストデータ配置(コピーするデータは適宜自分で用意する)
aws s3 cp testdata.gz s3://glue-test/

# 実行
gluesparksubmit ./src/sample.py --JOB_NAME='dummy'

The post AWS Glueのローカル環境を作成する first appeared on 株式会社Altus-Five.

]]>
/blog/2020/05/07/aws-glue/feed/ 0