12 дней DigitalOcean (день 4) – Развертывание уведомлений о днях рождения с помощью функций DigitalOcean

Добро пожаловать на День 4 12 дней DigitalOcean! Вчера мы добавили SMS-уведомления Twilio в наш сервис напоминаний о днях рождения, что сделало его способным отправлять текстовые сообщения о днях рождения, которые отмечаются сегодня. 🎂

Сегодня мы поднимем уровень и развернем наш скрипт на DigitalOcean Functions. Это позволит нашему сервису работать в облаке без необходимости в выделенном сервере, сделав наше приложение легковесным, масштабируемым и готовым к автоматизации.

С этой настройкой вы будете получать напоминания о днях рождения даже тогда, когда ваш компьютер выключен или не подключен к интернету — больше не нужно вручную запускать скрипт на вашем компьютере. 🎉

Почему DigitalOcean Functions?

Иногда все, что вам нужно, это простой скрипт, который выполняется время от времени. Управление инфраструктурой для чего-то подобного может быть излишним. Здесь на помощь приходят Functions. Это серверная платформа, что означает, что вы можете развернуть код, который выполняется только по мере необходимости, и вы платите только за то, что используете. Идеально подходит для нашего случая — проверки дней рождения и отправки напоминаний ежедневно.

🚀 Что вы узнаете

К концу сегодняшнего дня вы будете знать, как:

  1. Настроить doctl CLI инструмент от DigitalOcean.
  2. Создать и подключиться к серверному пространству имен (способ DigitalOcean организовать функции).
  3. Упаковать и развернуть ваш Сервис Напоминания о Днях Рождения в DigitalOcean Functions.
  4. Проверить вашу развернутую функцию в облаке.

🛠 Что вам понадобится

Перед началом убедитесь, что у вас есть:

🧑‍🍳 Рецепт на День 4: Развертывание в DigitalOcean Functions

Шаг 1: Настройка CLI doctl

Если вы уже настроили doctl на своем компьютере, можете пропустить этот шаг. Для тех, кто нуждается в настройке, следуйте этим инструкциям:

Прежде чем мы начнем, давайте быстро поговорим о doctl. Это официальный инструмент командной строки DigitalOcean, который позволяет управлять вашими облачными ресурсами прямо из вашего терминала. Мы будем использовать его для создания пространства имен (папки для наших безсерверных функций), развертывания нашего Python-скрипта и тестирования функции.

Настройка является простой:

  1. Установите doctl: Следуйте руководству по установке для вашей операционной системы.

  2. Аутентификация doctl: Подключите его к вашему аккаунту DigitalOcean, выполнив:

    doctl auth init
    
  3. Проверьте установку: Убедитесь, что все работает, выполнив:

    doctl account get
    

Если команда выполнена успешно, вы получите информацию о своей учетной записи DigitalOcean, такую как ваш email и 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.

    • Создайте файл 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. Каждая переменная соответствует учетным данным, необходимым для вашей базы данных и интеграции с 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: Чистая библиотека клиента PostgreSQL на Python.

python-dotenv: Используется для загрузки переменных окружения из файла .env.

twilio: Библиотека Twilio для Python для отправки SMS-сообщений.

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

Этот скрипт гарантирует, что все зависимости упакованы правильно с вашей функцией. Команда chmod +x из Шага 4 гарантирует, что он исполняемый.

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:
    Код третьего дня был обернут внутри функции 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: Протестируйте вашу развернутую функцию

После развертывания вашей функции пришло время ее протестировать. Вы можете вручную вызвать функцию, чтобы убедиться, что она работает как ожидается. Существует два способа сделать это:

Вариант 1: Использование CLI DigitalOcean

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. Перейдите к Функциям и найдите вашу функцию напоминаний/дней рождения.
  3. Нажмите Запустить, чтобы запустить ее вручную.
  4. Просматривайте вывод и журналы непосредственно в консоли.

Этот метод особенно полезен, если вы предпочитаете визуальный интерфейс или хотите проверить журналы в чистом, легко читаемом формате.

Советы по тестированию

Когда вы вызываете функцию, она будет проверять дни рождения, совпадающие с сегодняшней датой. Если есть совпадение, вы получите текстовое сообщение с деталями. Чтобы эффективно протестировать функцию:

  • Добавьте одно или несколько дней рождения в вашу базу данных, которые совпадают с текущей датой.
  • Проверьте консоль или логи CLI, чтобы подтвердить успешное выполнение функции.

🎁 Итоги

Вот что мы сделали сегодня:

✅ Настроили doctl и создали пространство имен для нашего проекта.
✅ Рефакторили Python-скрипт для развертывания.
✅ Упаковали и развернули Службу Напоминания о Днях Рождения в DigitalOcean Functions.
✅ Протестировали функцию в облаке, используя как CLI, так и панель управления DigitalOcean.

Далее: Хотя это большой шаг вперёд, мы всё еще запускаем функцию вручную. В следующем посте мы автоматизируем этот процесс, чтобы Служба Напоминания о Днях Рождения работала автоматически каждый день в определенное время. Представьте, что вы просыпаетесь с текстовым напоминанием, ничего не делая—давайте сделаем это завтра! 🚀

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