数字海洋的12天(第4天) – 使用数字海洋函数部署生日通知

欢迎来到数字海洋十二日第4天!昨天,我们为生日提醒服务添加了 Twilio 短信通知,使其能够发送今天生日的短信。🎂

今天,我们将把事情推向新的高度,将我们的脚本部署到数字海洋函数。这样我们的服务可以在云中运行,无需专用服务器,使我们的应用程序轻巧、可扩展,并且可以进行自动化。

有了这个设置,即使您的计算机关闭或未连接互联网,您也会收到生日提醒,不再需要手动在您的设备上运行脚本。🎉

为什么选择数字海洋函数?

有时候,你所需要的只是一个偶尔运行的简单脚本。管理这样的基础设施可能会显得过于繁琐。这就是 Functions 的用武之地。它是一个无服务器平台,这意味着你可以在需要时部署代码,并且只为你使用的部分付费。非常适合我们的用例——每天检查生日并发送提醒。

🚀 你将学到什么

到今天结束时,你将知道如何:

  1. 设置 DigitalOcean 的 doctl CLI 工具
  2. 创建并连接到一个无服务器 命名空间(DigitalOcean 用于组织函数的方式)。
  3. 将你的生日提醒服务打包并部署到 DigitalOcean Functions
  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. 创建一个新命名空间:

    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 文件夹的根目录下。

    • birthdays 文件夹中创建一个 requirements.txt 文件:
    touch packages/reminders/birthdays/requirements.txt
    

    该文件将列出你的函数所需的 Python 依赖项。它将位于 packages/reminders/birthdays 下。

    • 在 birthdays 文件夹中创建一个 build.sh 文件:
    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 函数。每个变量对应于您的数据库和 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

这是您的生日提醒服务的主脚本。我们基本上使用的是第3天构建的发送生日通知的脚本。不过,为了使其与 DigitalOcean Functions 兼容,我们需要进行一些小调整。

用以下内容更新 __main__.py 文件:

# 生日提醒服务/__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 预期有一个名为main的入口函数,接受一个params参数。这是函数开始执行的地方。

  2. 将脚本逻辑移到main函数内:
    将第3天的代码移到main函数内,以符合这个要求。

  3. 其他内容保持不变:
    数据库连接逻辑、生日检查和短信通知逻辑保持不变。

步骤 5:打包和部署

一切就绪后,将项目部署到 DigitalOcean Functions:

  1. 部署项目:
doctl serverless deploy my-birthday-reminder-service

要验证您的函数是否成功部署到命名空间:

  1. 访问 DigitalOcean 控制面板,然后在左侧栏中导航到 Functions。
  2. 找到您的命名空间(例如:my-birthday-reminder-namespace)。
  3. 检查您的函数是否出现在命名空间下,通常列为 reminders/birthdays
  4. 单击函数名称以查看详细信息,包括日志、配置和调用历史。

步骤 6:测试您的已部署函数

一旦您的函数被部署,就可以进行测试。您可以手动调用该函数以确保其按预期工作。有两种方法可以做到这一点:

选项 1:使用 DigitalOcean CLI

doctl serverless functions invoke reminders/birthdays

如果一切设置正确,您的函数将在云中运行,检查今天的生日并发送短信通知。

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

选项 2:使用 DigitalOcean 控制面板

  1. 前往 DigitalOcean 控制面板。
  2. 导航到 函数,找到您的提醒/生日函数。
  3. 点击 运行 以手动运行它。
  4. 直接在控制台查看输出和日志。

如果您更喜欢可视化界面或想以干净、易读的格式查看日志,此方法尤其有用。

测试提示

当您调用此功能时,它将检查与今天日期匹配的生日。如果有匹配,您将收到一条包含详细信息的文本消息。要有效测试该功能:

  • 在您的数据库中添加一个或多个与当前日期匹配的生日。
  • 检查控制台或CLI日志以确认功能成功执行。

🎁 总结

今天我们完成了以下内容:

✅ 设置了 doctl 并为我们的项目创建了命名空间。
✅ 重构了用于部署的Python脚本。
✅ 将生日提醒服务打包并部署到DigitalOcean Functions。
✅ 使用CLI和DigitalOcean仪表板在云中测试了该功能。

接下来:虽然这向前迈出了重要的一步,但我们仍在手动运行该功能。在下一篇文章中,我们将自动化此过程,使生日提醒服务每天在特定时间自动运行。想象一下,您醒来时收到一条文本提醒,无需动手——让我们明天实现这个目标吧! 🚀

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