Notebook上からEmail通知を送る (AWS SNS編)

この記事の執筆現在(2022/02)、Databricksが提供する通知機能は以下の通りです。

* [Databricks SQLのダッシュボード アラート/通知](https://docs.databricks.com/sql/admin/alert-destinations.html)
    * Email
    * Slack, Mattermost, Mattermost, ChatWork, PagerDuty, Google Hangouts Chat
    * Webhook    
* [Jobsでのジョブ実行の開始・終了・エラー時の通知](https://docs.databricks.com/jobs.html#alerts-job)
    * Email
* [MLflow モデルレジストリのイベント通知](https://docs.databricks.com/applications/mlflow/model-registry-webhooks.html)
    * Webhook


上記以外での通知に関しては、Notebook上で実装できます。DatabricksのNotebookは通常のPythonやScalaの実行環境として機能するので、コードとして通知機能を実装できます。この記事では、以下のドキュメントの内容に基づいて、AWS Simple Notification Service(SNS)を使用してEmail通知をNotebook上で実装する方法を見ていきます。なお、Simple Email Service(SES)を使ったEmailの通知方法は別途記事で説明します。

* [How to send email or SMS messages from Databricks notebooks](https://kb.databricks.com/notebooks/send-email.html)

(AWS SNSではEmailに限らず、SMSやWebhookなども同様に連携可能になります。ただし、この記事ではEmail連携を見ていきます。)

# 概要

AWS SNSとAWS SESはどちらともEmail送信サービスという同じ機能を提供していますが、以下のような違いがあります。

>Q: Amazon SES と Amazon SNS の違いは何ですか?
>
>Amazon SES は、E メールで通信を送信する必要があるアプリケーション向けのサービスです。Amazon SES は、E メールのカスタムヘッダーフィールドやさまざまな MIME タイプをサポートしています。
>
>これとは対照的に、Amazon Simple Notification Service（Amazon SNS）はメッセージ指向のアプリケーションで、HTTP や Amazon SQS、またはEメールなどの選択したトランスポートプロトコルを介して、複数の加入者が時間制約が厳しいメッセージの「プッシュ」通知をリクエスト/受信するものです。Amazon SNS の通知の本文は、UTF-8 文字列の 8,192 文字に制限されており、マルチメディアのコンテンツをサポートするためのものではありません。

[Amazon SES に関してよくある質問](https://aws.amazon.com/jp/ses/faqs/)より抜粋

Databricksの文脈では、Notebookのグラフなどを添付してレポートとしてEmailを送信したい場合はSESを、簡単な通知をEmailで送信もしくはWebhookなどを使いたい場合はSNSを、それぞれ使うことになると思います。

# AWS SNSを用いた通知

AWS SNSの通知の仕組みはPublish/Subscribe型のメッセージサービスになっています。つまり、あるトピックを作成し、受信者がトピックをSubscribe(購読)登録した後、通知をそのトピックにPublishすると、Subscribeしている受信者に一斉に通知されます。受信者はEmail以外にも、携帯電話のSMS、Webhook、モバイルpush通知が利用可能になっているます。

そのため、SNSを使用することで、通知を

* AdminのEmailアドレス
* 担当者のEmailアドレス
* 担当者のSMS
* SlackへのWebhook通知

の複数チャネルに一斉に送信できます。また、通知の発信者と受信者が独立に管理できるため、疎結合な運用が可能です。

## トピックの作成

まずは、通知を送る先のトピックを作成します。CLIやboto3からも可能ですが、シンプルのため、AWSコンソールから設定する方法を見ていきます。

1. AWSコンソールのメニューから"Amazon Simple Notification Service"のページを開く。
1. 使用するリージョンが正しいかを確認する。
1. 左側のメニューから"トピック"のページを開く。
1. "トピックの作成"をクリックして、適宜名前をつけてトピックを作成する。
1. 作成されたトピックのARNをメモする

この例では以下のトピックを使います。

* トピック名: `testtopic`
* トピックのARN: `arn:aws:sns:ap-northeast-1:1234567890:testtopic`

![Screen Shot 2022-02-21 at 20.45.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62653/24a327b9-01e6-5dbe-2da2-dae3e55320c5.png)

## サブスクリプションの登録

通知を受信したいEmailアドレスなどを登録します。

* 左のメニューから"サブスクリプション"を開く
* "サブスクリプションの作成"をクリック
* Emailアドレスなどを登録する

Emailアドレスの場合は、登録後にEmailアドレス宛に確認メールが送付されます。このメール内にあるリンクを開くことで、Emailアドレスが有効化されます。有効化されないEmailアドレスには通知が送付されません。一つのトピックに複数のサブスクリプション(受信者)を登録できます。

![Screen Shot 2022-02-21 at 20.57.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62653/aea6e834-3a7a-32a0-abc8-20c393271eab.png)



## ポリシー設定とNotebookからの通知送信

Notebook上からSNSを使うには、AWSのPythonライブラリである`boto3`を使用します。SNSへのメッセージ送信(Publish)のためには`sns:Publish`の許可ポリシーが必要です。このポリシーを`boto3`クライアントに渡す方法は2通りあります。

* オプション1) 送信専用のAWS IAMユーザーを作成し、上記のポリシーを追加して、Access Key, Secret Keyを使って認証する。Secretsを使用してコード上ではKey値を隠蔽する。
* オプション2) Databricksクラスタに紐付けるInstance Profileに上記のポリシーを追加して、認証する(結果としてSTSが使われる)。

前者の方式は、クラウド環境を選ばず、AzureやGCP上でDatabricsを使用している場合でも適用可能です。一方の、後者の方式は、AWS上でDatabricksを使用する場合に限ります。

両者の方法を見ていきます。

### オプション1) IAMユーザーを使用する方法

SNSを送信する`boto3`クライアント用のユーザーを作成します。

1. AWS IAMページから"アクセスキー - プログラムによるアクセス"を有効にしたユーザーを作成する。
1. ユーザー作成後に、"認証情報"タブからAccess Key, Secret Keyを取得する。
2. ユーザーのアクセス権の設定で、"インラインポリシーの追加"をクリック。
2. 以下のポリシー(JSON)を追加する。 `Resource`は上記で作成したトピックのARN。

```JSON
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sns:Publish"
            ],
            "Resource": "arn:aws:sns:ap-northeast-1:1234567890:testtopic"
        }
    ]
}
```

以上で、認証関連の設定は完了しました。それでは、Databricsk Notebook上から通知を送信してみましょう。`boto3`はDatabricks環境にはプリインストールされていないので、`pip`でインストールします。その後、`boto3`を使用して通知を送信します。

```python
## Databricks Notebook上のセル
% pip install boto3


import boto3
client = boto3.client('sns', region_name='ap-northeast-1'                      　　　　               
                     aws_access_key_id='<発行したアクセスキー>', # 実際にはSecretsを使って隠蔽します
                     aws_secret_access_key='<発行したシークレットキー>'  # 実際にはSecretsを使って隠蔽します
                     )

topic_arn='arn:aws:sns:ap-northeast-1:1234567890:testtopic'

client.publish(
    TopicArn = topic_arn,
    Subject = 'notification_test123',
    Message = 'Hello World via SNS'
)
```

上記のコメントで書いている通り、Access Key, Secret KeyはDatabricks Secretsを使用して隠蔽してください。
Databricks Secretsに関しては以下のページで解説しています。

https://qiita.com/ktmrmshk/items/579349f41494e27e1b39

あとは、サブスクリプションしているEmailで受信できているかを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62653/0ba338d2-8ddd-568e-676d-0d076569b4a2.png)

上記の通り、コードに書いたメッセージが`no-reply@sns.amazonaws.com`から届きます。


### オプション2) Instance Profileを使用する方法

Databricksのクラスターに紐づけるInstance Profileの作成・登録方法は下記で解説されています。

https://qiita.com/taka_yayoi/items/446c7971be354f88c679

上記の記事では、S3バケツにアクセス許可をするポリシーについての例になっています。SNSについても全く同様に可能です。作成済みのInstance Profileに以下のポリシーを追加するだけです。

```JSON
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sns:Publish"
            ],
            "Resource": "arn:aws:sns:ap-northeast-1:1234567890:testtopic"
        }
    ]
}
```

あとは、このInstance Profileを紐付けたクラスタを立ち上げて、Notebook上からアタッチします。そのNotebook上で以下のコードで通知が可能です。

```python
## Instance ProfileのクラスタにアタッチされたNotebook上
% pip install boto3


import boto3
client = boto3.client('sns', region_name='ap-northeast-1')

topic_arn='arn:aws:sns:ap-northeast-1:1234567890:testtopic'

client.publish(
    TopicArn = topic_arn,
    Subject = 'notification_test123',
    Message = 'Hello World via SNS'
)

```

先ほどと同様に、メールが受信できているかを確認します。

# まとめ

今回は、AWS SNSを使用して、Databricks NotebookからEmail通知を送信する方法についてみてきました。AWS SNSはシンプルなメッセージを前提とした通知になります。一方で、別の記事で取り上げるAWS SESを用いると、Databricks Notebook上でプロットしたグラフなどのレポートを送信することも可能になります。