Cloud Run Functions入門 ～なんでもメール通知を作る～

# Cloud Run Functionsにリニューアル

前回Cloud FunctionsがCloud Runに統合されCloud Run functionsになったことを紹介しました。

https://qiita.com/nekoniii3/items/d443f49de6480a6309b2

今回は実際にCloud Run functionsを利用してみたいと思います。

# URL叩く→メールを送信

個人で開発したサイトやシステムのエラー時にメールで通知が行くと便利ですが、いちいちメール送信のためのコードを書くのは面倒です。

そんなときにメール通知用のCloud Run functionsを作っておくとURLを叩く(httpでPost)だけで、メールの送信をすることができます。

![Snapshot_294.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/cc672447-3465-4510-9cb7-dff385e9b509.png)

<br>

↓このURLからPostする（件名、内容は送信時に指定できます）
![スクリーンショット 2025-04-13 085637.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/842c9e85-b8c1-4187-9c4a-afa85c0d8ffe.png)

<br>

今回はこちらの作成例を紹介します。（Google Cloudのアカウント、アプリパスワードが必要です。）

アプリパスワードはこちらから（[セキュリティ設定](https://myaccount.google.com/security)から2段階認証が必要です）

https://myaccount.google.com/apppasswords

※こちらを作成したアカウントからメールが送信されます

# Cloud Run Functions作成

Cloud Runのページを開きます（前回の通りCloud Functionsのページはなくなりました）。[関数を作成]をクリック。

![Snapshot_273.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/644d9a5d-1e85-4fda-84f5-62236be0b946.png)

<br>

今回はソースを直接入力するので[インライン エディタで関数を作成する]を選択します。サービス名を適当に付けてリージョンを選んで下さい。あまり変わらないですがとりあえずリージョンは東京にしておきます。

![Snapshot_274.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/037e9197-3f96-4ced-ae1c-e97375325dfc.png)

<br>

ランタイムは今回はPythonにします。トリガーは今回設定しませんがスケジューラーで利用するときは必要です。どこでも呼べるように**未認証の呼び出しを許可**しますが、緊急の通達など万が一でも呼び出されたらまずいものであれば認証を設定して下さい。

![Snapshot_275.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/34122a0c-f6d5-4e58-bf69-372fc07a9ceb.png)

<br>

今回はリクエストベースにします。そうすると**呼び出された時に起動**するので動作が遅くなりますが費用は安く抑えられます。

![Snapshot_276.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/970053d1-c4f8-4958-bcf7-009183346a72.png)

<br>

次にコンテナの設定から[変数とシークレット]タブに移ります。

![Snapshot_277.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/895fc6e3-eab5-48f1-815b-a8a2ecfc160b.png)

<br>

ここに環境変数を設定します。今回は送り元のアドレス（上のアプリパスワードを発行したGoogleアカウントのアドレス）、送り先のアドレス（こちらは呼び出し時に指定することもできますが、固定としました）、アプリパスワードを設定しておきます。

![Snapshot_290.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/9da8c895-04c2-427b-8184-92a1c7aa3ded.png)

<br>

[完了]を押して保存し、特に設定することはないので **[作成]** を押します。

![Snapshot_279.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/b3fc6240-a432-4988-a141-671fd0436c7d.png)


<br>

ソースの編集画面になります。今回は次のようにしました。

![Snapshot_280.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/99b737e8-bda7-46a8-b0c3-c2d684f9ed22.png)

<br>

```Python:main.py
import functions_framework
import os
from smtplib import SMTP
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

GMAIL_HOST = 'smtp.gmail.com'
GMAIL_PORT = 587

def send_email(account, password, msg):

    # Gmailのサーバ接続
    server = SMTP(GMAIL_HOST, GMAIL_PORT)

    # STARTTLSで暗号化
    server.starttls()

    # ログイン処理
    server.login(account, password)

    # メールを送信する
    server.send_message(msg)

    # 閉じる
    server.quit()

def make_msg(from_email, to_email, title, content):

    # MIMETextでメールの内容を作成作成
    msg = MIMEMultipart()

    msg['Subject'] = title
    msg['From'] = from_email
    msg['To'] = to_email
    message = content
    msg.attach(MIMEText(message, "plain"))

    return msg

@functions_framework.http
def hello_http(request):

    request_json = request.get_json(silent=True)

    if not request_json:
        raise RuntimeError("get json error")


    if 'title' not in request_json or 'content' not in request_json:
        raise RuntimeError("json key error")

    try:

        title = request_json['title']
        content = request_json['content']

        # アカウント設定
        account = os.environ['MAIL_FROM']
        password = os.environ['PASSWORD']

        # メールの送信元・送信先設定
        from_email = os.environ['MAIL_FROM']
        to_email = os.environ['MAIL_TO']

        # メッセージ作成
        msg = make_msg(from_email, to_email, title, content)

        # # メールの送信
        send_email(account, password, msg)

        return 'send success'

    except Exception as e:
        print(e)
        raise RuntimeError("mail send error")
```

<br>

Smtplibによるメールの送信です。Smtplibについて詳しくは[こちらを参考にして下さい。](https://qiita.com/Nats72/items/b5ca98c5fe1c41551a8e)

デフォルトのエントリーポイントが"hello_http"なのでそのままこちらの名前で関数を作りました。Functionが起動するとこちらの関数が呼び出される形になります。変えても問題ありません。

request.get_jsonでPost時のJSONを受け取れます。今回はタイトルと内容だけを受け取りましたが、送信先アドレスを受け取って使うこともできます。

<br>

今回はrequirements.txtを追加しませんでしたが、必要なパッケージがあるなら追記します。また「＋」マークをクリックで別のファイルも追加できます。

![Snapshot_295.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/e5bd3061-17c4-4520-a4e1-218cf4e6f1c9.png)

<br>

では **[保存して再デプロイ]** でデプロイします。

![Snapshot_281.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/38a2b317-16cf-4c14-a2b6-dc38ceb62faf.png)

<br>

少し時間がかかります。

![Snapshot_282.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/c5640c22-e3df-40f7-a2ba-225641b5b1b2.png)

<br>

エラーにならなければOKです。エラーの場合はログを確認してソースを再度修正して下さい。

[テスト]ボタンからテストしてみます。curlするだけなのでそんなに意味はないですが認証の問題かどうか把握するためにやっておきます。

![Snapshot_283.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/f83e53b7-2ed1-4511-a531-1827decf8eb2.png)

<br>

POST時のテスト用JSONを設定します。今回は"title"と"content"です。

![Snapshot_284.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/e3ec2cdb-d771-463c-9ff7-983385364560.png)

<br>

[Cloud Shellでテストする]をクリックします。

![Snapshot_285.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/f0c609c5-226a-4e45-b6d9-4544dda30caa.png)

<br>

テスト用スクリプトが打ち込まれるのでEnterだけ押して下さい。

![Snapshot_289.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/4f910776-222f-4bec-824f-2a37db6be528.png)

<br>

エラーにならず、メールが送れていたら成功です。（今回はコードで"send success"を返すようにしていますが、何も設定していなければ表示されません。）

![Snapshot_286.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/2dfe1595-52ef-40ce-bcfd-2ba58a42ccdc.png)

![スクリーンショット 2025-04-16 214207.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/9e1ab55d-1d2a-4acb-942c-d5823c8c4939.png)

<br>

後は発行されたURLを使ってどっからでも送信してみて下さい。

![Snapshot_287.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/690fb409-39ea-49f2-95bc-fc629fc70ab2.png)

<br>

WindowsのPowerShellから呼び出す例です。

```PowerShell:
curl.exe -X POST https://func-send-mail-1043149056745.asia-northeast1.run.app -H "Content-Type: application/json" -d '{ \"title\": \"title_test\" , \"content\": \"content_test\" }'
```

※ここでエラーとなる場合はこのスクリプトか未認証の呼び出しが許可されていないと思われます

Pythonから呼び出す例です。

```python:
import requests
import json

MAILHOOK = "https://func-send-mail-175332392539.asia-northeast1.run.app"

json_data = {
    "title": "title_test",
    "content": "content_test"
}

response = requests.post(
    MAILHOOK,
    data=json.dumps(json_data),
    headers={"Content-Type": "application/json"}
)
```

## おわりに

何かの役に立てば幸いです！
見る方多ければトリガーを作ってスケジューラーから呼び出す方法を紹介したいと思います！