デジタルオーシャンの12日間(第4日) – デジタルオーシャンファンクションを使った誕生日通知のデプロイ

ようこそ 第4日デジタルオーシャンの12日間 へ!昨日、私たちは誕生日リマインダーサービスにTwilio SMS通知を追加しました。これにより、今日の誕生日のためにテキストメッセージを送信できるようになりました。🎂

今日は、デジタルオーシャンファンクションズ にスクリプトをデプロイすることで次のステップに進みます。これにより、専用サーバーを必要とせずにサービスがクラウドで動作するようになり、アプリが軽量でスケーラブルになり、自動化の準備が整います。

この設定により、コンピューターがオフになっているか、インターネットに接続されていない場合でも誕生日リマインダーを受け取ることができます。もはやマシンでスクリプトを手動で実行する必要はありません。🎉

なぜデジタルオーシャンファンクションズなのか?

時には、定期的に実行されるシンプルなスクリプトだけで十分なこともあります。そのためのインフラ管理は過剰になることがあります。そこで Functions が登場します。これはサーバーレスプラットフォームで、必要なときだけ実行されるコードをデプロイでき、使用した分だけ支払えば済みます。誕生日を確認し、毎日リマインダーを送るという私たちのユースケースに最適です。

🚀 学べること

今日の終わりまでに、以下のことができるようになります:

  1. DigitalOceanの doctl CLIツール を設定する。
  2. サーバーレス ネームスペース(DigitalOceanが関数を整理する方法)を作成し、接続する。
  3. あなたの誕生日リマインダーサービスを DigitalOcean Functions にパッケージしてデプロイする。
  4. クラウドでデプロイした関数をテストする。

🛠 必要なもの

開始する前に、次のものを準備してください:

🧑‍🍳 4日目のレシピ: DigitalOcean Functionsへのデプロイ

ステップ1: doctl CLIの設定

もしすでにあなたのマシンにdoctlを設定しているなら、このステップはスキップできます。設定が必要な方は、以下の手順に従ってください:

始める前に、doctlについて簡単に説明しましょう。これはDigitalOceanの公式コマンドラインインターフェースツールで、ターミナルから直接クラウドリソースを管理することができます。私たちはこれを使って、名前空間(サーバーレス関数用のフォルダー)を作成し、Pythonスクリプトをデプロイし、関数をテストします。

設定は簡単です:

  1. doctlをインストールします: お使いのオペレーティングシステムのためのインストールガイドに従ってください。

  2. doctlを認証します: 以下を実行してDigitalOceanアカウントに接続します:

    doctl auth init
    
  3. インストールを確認します: 以下を実行してすべてが正常に動作しているか確認します:

    doctl account get
    

成功した場合、このコマンドはあなたのDigitalOceanアカウントに関する詳細情報、例えばメールアドレスやアカウントIDを返します。

ステップ2: サーバーレスソフトウェアのインストール

DigitalOcean Functions はサーバーレスサポートソフトウェアを必要とし、これをインストールする必要があります。これは一度だけの設定ですので、インストールが完了すれば、今後のプロジェクトでは再度行う必要はありません。

以下のコマンドを実行してください:

doctl serverless install

インストール状況を確認するには:

doctl serverless status

以下のようなエラーが表示された場合:

Error: serverless support is installed but not connected to a functions namespace

心配しないでください—それはまだネームスペースを作成または接続していないだけです。それは次のステップで対処します。

ステップ3: ネームスペースの作成と接続

ネームスペースは、サーバーレス関数を整理するためのフォルダーのようなものです。私たちの誕生日リマインダーサービスのために、1つ作成しましょう:

  1. 新しいネームスペースを作成:

    doctl serverless namespaces create --label "my-birthday-reminder-namespace" --region "nyc1"
    

  2. ネームスペースに接続:

    doctl serverless connect my-birthday-reminder-namespace
    

  3. 接続を確認:

    doctl serverless status
    

ネームスペースに接続されていることを確認するメッセージが表示されるはずです。

プロのヒント: 利用可能なすべての名前空間のリストを表示するには、次のコマンドを使用します:

doctl serverless namespaces list

これは、複数のプロジェクトを管理している場合や、作成したばかりの名前空間を確認したい場合に便利です。

ステップ4:プロジェクト構造の初期化と設定

DigitalOcean Functionsは、サーバーレスデプロイメントのために特定のプロジェクト構造を期待しています。この構造はdoctl serverless initを使って開始するか、手動で作成するか、またはスターターレポをクローンすることができます。シンプルに保つために、doctl serverless initを使用して設定します:

  1. プロジェクトを初期化するには、次のコマンドを実行してください:

    doctl serverless init --language python birthday-reminder-service
    

    これにより、次のデフォルト構造を持つ my-birthday-reminder-service という名前のローカルプロジェクトディレクトリが作成されます:

    my-birthday-reminder-service/
    ├── packages
    │   └── sample
    │       └── hello
    │           └── hello.py
    └── project.yml
    

  2. プロジェクトディレクトリに移動します:

    cd my-birthday-reminder-service
    
  3. フォルダの名前をユースケースに合わせて変更します:

    mv packages/sample packages/reminders
    mv packages/reminders/hello packages/reminders/birthdays
    mv packages/reminders/birthdays/hello.py packages/reminders/birthdays/__main__.py
    
  4. 必要なファイルを作成する:

    • プロジェクトのルートに空の.envファイルを作成します:
    touch .env
    

    このファイルには、データベースとTwilioの認証情報が含まれます。このファイルはmy-birthday-reminder-serviceフォルダのルートに配置されます。

    • requirements.txtファイルをbirthdaysフォルダに作成します:
    touch packages/reminders/birthdays/requirements.txt
    

    このファイルには、関数に必要なPythonの依存関係がリストされます。packages/reminders/birthdaysの下に配置されます。

    • build.shファイルをbirthdaysフォルダに作成します:
    touch packages/reminders/birthdays/build.sh
    chmod +x packages/reminders/birthdays/build.sh
    

    build.shスクリプトは、外部依存関係を持つ関数をデプロイするために必要です。chmodコマンドは、スクリプトがMac/Linuxシステムで実行可能であることを確認します。

更新された構造:これらの手順を完了した後、プロジェクトの構造は次のようになります:

my-birthday-reminder-service/
├── project.yml
├── .env
├── packages
│   └── reminders
│       └── birthdays
│           ├── __main__.py
│           ├── requirements.txt
│           ├── build.sh
├── .gitignore

プロのヒント:フォルダーの名前を誤って付けた場合は、再度コマンドを実行するか、ファイルエクスプローラーで手動で名前を変更できます。

ステップ 5: ファイルを更新する

構造が整ったので、必要なファイルで埋めていきましょう。お気に入りのコードエディタで my-birthday-reminder-service ディレクトリを開きます。

1. project.ymlを更新する

project.ymlファイルは、サーバーレスプロジェクトの構造、環境変数、および関数を定義する設定ファイルです。内容を以下に置き換えてください:

packages:
  - name: reminders
    shared: false
    environment:
      DO_DB_NAME: "${DB_NAME}"
      DO_DB_USER: "${DB_USER}"
      DO_DB_PASSWORD: "${DB_PASSWORD}"
      DO_DB_HOST: "${DB_HOST}"
      DO_DB_PORT: "${DB_PORT}"
      TWILIO_ACCOUNT_SID: "${TWILIO_ACCOUNT_SID}"
      TWILIO_AUTH_TOKEN: "${TWILIO_AUTH_TOKEN}"
      TWILIO_PHONE_FROM: "${TWILIO_PHONE_FROM}"
      TWILIO_PHONE_TO: "${TWILIO_PHONE_TO}"
    functions:
      - name: birthdays
        runtime: python:default

このファイルはリマインダー用パッケージを設定し、環境変数をDigitalOcean Functionsにマッピングします。各変数は、データベースとTwilio統合に必要な認証情報に対応します。

2. .envファイルを更新する

1日目:誕生日リマインダーのためのPostgreSQLデータベースの設定を参照してデータベースの認証情報を取得し、3日目:誕生日の確認とSMS通知の送信を参照してTwilioの認証情報を取得し、以下の値を入力してください:

# データベースの認証情報(1日目から)
DB_HOST=<your-database-hostname>
DB_NAME=<your-database-name>
DB_USER=<your-database-username>
DB_PASSWORD=<your-database-password>
DB_PORT=5432  # デフォルトのPostgreSQLポート

# Twilioの認証情報(3日目から)
TWILIO_ACCOUNT_SID=<your-twilio-account-sid>
TWILIO_AUTH_TOKEN=<your-twilio-auth-token>
TWILIO_PHONE_FROM=<your-twilio-phone-number>
TWILIO_PHONE_TO=<your-personal-phone-number>

注意: .env ファイルは、機密情報を安全に保存するために使用されます。これらの値は、project.yml ファイルによって読み込まれ、デプロイメント中にサーバーレス環境にマッピングされ、クラウド内の関数でアクセス可能になります。

3. 依存関係を追加

次の依存関係を requirements.txt ファイルに更新してください:

pg8000  
python-dotenv  
twilio  

pg8000: ピュアPythonのPostgreSQLクライアントライブラリ。

python-dotenv: .env ファイルから環境変数を読み込むために使用されます。

twilio: SMSメッセージを送信するためのTwilioのPythonライブラリ。

4. build.sh

build.sh ファイルに次のスクリプトを追加してください:

#!/bin/bash
set -e

# デバッグ用に現在の作業ディレクトリを表示
echo "Current working directory: $(pwd)"

# requirements.txt が存在するか確認
if [[ -f "requirements.txt" ]]; then
  echo "Found requirements.txt in $(pwd)"
else
  echo "Error: requirements.txt not found in $(pwd)"
  exit 1
fi

# 仮想環境を作成
virtualenv --without-pip virtualenv

# requirements.txt から依存関係をインストール
pip install -r requirements.txt --target virtualenv/lib/python3.9/site-packages

このスクリプトは、すべての依存関係が関数と一緒に正しくパッケージ化されることを保証します。ステップ4の chmod +x コマンドは、実行可能であることを確認します。

5. __main__.py を更新

これは、あなたの誕生日リマインダーサービスの主要なスクリプトです。基本的に、誕生日通知を送信するためにDay 3で構築したスクリプトを使用しています。しかし、DigitalOcean Functionsと互換性を持たせるために、いくつかの小さな調整が必要です。

次の内容で __main__.py ファイルを更新してください:

# birthday_reminder_service/__main__.py

from datetime import datetime
import pg8000
from dotenv import load_dotenv
from twilio.rest import Client
import os

# 環境変数を読み込む
load_dotenv()

def main(params):
    """DigitalOcean Functions entry point."""
    try:
        # データベースに接続する
        connection = pg8000.connect(
            host=os.getenv("DO_DB_HOST"),
            database=os.getenv("DO_DB_NAME"),
            user=os.getenv("DO_DB_USER"),
            password=os.getenv("DO_DB_PASSWORD"),
            port=int(os.getenv("DO_DB_PORT"))
        )
        cursor = connection.cursor()

        # 今日の日付と月を取得する
        today = datetime.now()
        today_month = today.month
        today_day = today.day

        # 今日の日付に一致する誕生日の連絡先を取得するクエリ
        cursor.execute(
            """
            SELECT first_name, last_name, birthday
            FROM contacts
            WHERE EXTRACT(MONTH FROM birthday) = %s
              AND EXTRACT(DAY FROM birthday) = %s;
            """,
            (today_month, today_day)
        )
        rows = cursor.fetchall()

        # 一致する連絡先ごとに通知する
        if rows:
            account_sid = os.getenv("TWILIO_ACCOUNT_SID")
            auth_token = os.getenv("TWILIO_AUTH_TOKEN")
            client = Client(account_sid, auth_token)

            for row in rows:
                first_name, last_name, _ = row
                message = client.messages.create(
                    body=f"🎉 It's {first_name} {last_name or ''}'s birthday today! 🎂",
                    from_=os.getenv("TWILIO_PHONE_FROM"),
                    to=os.getenv("TWILIO_PHONE_TO")
                )
                print(f"Message sent for {first_name} {last_name}. Message SID: {message.sid}")
        else:
            print("No birthdays today.")

        # カーソルと接続を閉じる
        cursor.close()
        connection.close()

    except Exception as e:
        print(f"An error occurred: {e}")

変更点は以下の通りです:

  1. 追加された main(params) 関数: DigitalOcean Functions では、params 引数を受け取る main という名前のエントリポイント関数を期待しています。ここが関数の実行が開始される場所です。

  2. スクリプトのロジックを main 関数内に移動:
    Day 3 のコードは、この要件に合わせて main 関数内にラップされました。

  3. その他はすべて同じです:
    データベース接続ロジック、誕生日チェック、SMS通知ロジックは変更されていません。

ステップ5:パッケージ化とデプロイ

すべてが整ったので、プロジェクトをDigitalOcean Functionsにデプロイします:

  1. プロジェクトをデプロイする:
doctl serverless deploy my-birthday-reminder-service

関数がネームスペースに正常にデプロイされたことを確認するには:

  1. DigitalOceanコントロールパネルにアクセスし、左側のバーからFunctionsに移動します。
  2. ネームスペースを見つけます(例:my-birthday-reminder-namespace)。
  3. 関数がネームスペースの下に表示されていることを確認します。通常はreminders/birthdaysとしてリストされています。
  4. 関数名をクリックして、ログ、設定、呼び出し履歴などの詳細を表示します。

ステップ6:デプロイした関数をテストする

関数がデプロイされたら、テストする時です。関数を手動で呼び出して、期待通りに動作することを確認できます。これを行う方法は2つあります:

オプション 1: DigitalOcean CLIを使用

doctl serverless functions invoke reminders/birthdays

すべてが正しく設定されていれば、あなたの関数はクラウドで実行され、今日の誕生日を確認してSMS通知を送信します。

![https://doimages.nyc3.cdn.digitaloceanspaces.com/006Community/12-Days-of-DO/Postgressql-birthday/birthday_reminder_service_text_message.jpeg]

オプション 2: DigitalOceanダッシュボードを使用

  1. DigitalOceanコントロールパネルに移動します。
  2. Functionsに移動し、リマインダー/誕生日関数を見つけます。
  3. 手動で実行するにはRunをクリックします。
  4. 出力とログをコンソールで直接表示します。

この方法は、視覚的なインターフェースを好む場合や、クリーンで読みやすい形式でログを確認したい場合に特に役立ちます。

テストのヒント

関数を呼び出すと、今日の日付に一致する誕生日をチェックします。一致するものがあれば、詳細を含むテキストメッセージが届きます。関数を効果的にテストするには:

  • 現在の日付に一致する誕生日をデータベースに1つ以上追加してください。
  • コンソールまたはCLIログを確認して、関数が正常に実行されたことを確認してください。

🎁 まとめ

今日達成したことは次のとおりです:

doctlを設定し、プロジェクトのための名前空間を作成しました。
✅ デプロイのためにPythonスクリプトをリファクタリングしました。
✅ 誕生日リマインダーサービスをDigitalOcean Functionsにパッケージ化してデプロイしました。
✅ CLIとDigitalOceanダッシュボードを使用して、クラウドで関数をテストしました。

次のステップ:これは大きな前進ですが、まだ関数を手動で実行しています。次の投稿では、誕生日リマインダーサービスが特定の時間に自動的に毎日実行されるようにこのプロセスを自動化します。指一本動かさずにテキストリマインダーで目覚めることを想像してみてください—それを明日実現しましょう!🚀

Source:
https://www.digitalocean.com/community/tutorials/deploying-birthday-notifications-with-digitalocean-functions