概要

真面目な記事ばかりだと面白くないのでPrefectを使ってチャット一個に対してDockerコンテナ一個起動するという極めて無駄が多いTelegramチャットボットシステムを作ります。

Warning

この記事を読む方は何か起きてもソースコードを読んで自力で解決できるくらいの方を想定しています!

方針

  • Githubのモノレポ構成です。
  • DockerコンテナイメージレジストリにGithub Packages Container Registryを使用します。(Docker Registryではないので注意)
  • CI/CDにGithub Actionsを使用しDockerコンテナのビルドとPrefectのデプロイを自動化します。ActionsRunnerにはGithub hosted Runnerを使用します。
  • Prefectワークフローを使ってチャットに応答します。Telegramチャットをシリアライズし、Prefectワークロードに処理を引き渡します。

開発フロー

GithubActionsを使用し、Prefectワークフローの開発インターフェースをGithubに統一します。

sequenceDiagram
    participant Developer
    participant Github
    participant ActionsRunner
    participant GithubContainerRegistry
    participant PrefectCloud
    Developer->>Github:Push Code Changes
    Github->>ActionsRunner:Detect Code Changes
    alt Github CI pipeline
        ActionsRunner->>GithubContainerRegistry:DepolyDocker
        ActionsRunner->>PrefectCloud:Depoly Prefect
    end

運用フロー

TelegramからのチャットはPrefectCloudを経由してPrefectWorkerに引き渡されます。

sequenceDiagram
    participant User
    participant Telegram
    participant PrefectCloud
    participant PrefectWorker
    participant GithubContainerRegistry
    User->>Telegram: Send Chat
    Telegram->>PrefectCloud: WebHook Event
    PrefectCloud->>PrefectWorker: Run Deployment (Automation)
    GithubContainerRegistry->>PrefectWorker: Pull Latest Code
    PrefectWorker->>Telegram: Send Reply
    Telegram->>User: Send Reply

通信フロー

sequenceDiagram
    participant telegram client
    participant telegram server
    participant prefect cloud
    participant prefect worker
    telegram client->>telegram server: MTProto
    telegram server->>prefect cloud: HTTPS
    prefect cloud->>prefect worker: WebSocketSecure
    prefect worker->>telegram server: HTTPS
    telegram server->>telegram client: MTProto

環境

  • Telegram: 無料版アカウント 1個
  • PrefectCloud: prefect.cloud 無料版アカウント 1個
  • Github: github.com 無料版アカウント privateリポジトリ 1個
  • 開発マシン: Linux系 amd64ビット系 PC 1台
  • PrefectWorker: 無料のVPS 2台
    • OracleCloudInfrastructure Compute #1: VM.Standard.E2.1.Micro
      • CPU: 1コア(OCPU), メモリ: 1GB, OS: AlmaLinux9
      • Dockerインストール済
    • OracleCloudInfrastructure Compute #2: VM.Standard.E2.1.Micro
      • CPU: 1コア(OCPU), メモリ: 1GB, OS: AlmaLinux9
      • Dockerインストール済

環境準備

Telegram

  1. https://telegram.me/BotFather からBOTを作成し、Telegram BOT API Keyを取得する。

PrefectCloud

  1. https://app.prefect.cloud/auth/sign-up からアカウント登録する。
  2. defaultワークスペース→API KeysからPrefect Cloud API Tokenを取得する。

    Warning

    セキュリティの観点でPrefect APIキーは開発マシン、PrefectWorker#1,#2の分として3つ作って使い分けること。

開発マシン

  1. 環境変数をセット。
     export PREFECT_API_KEY="Prefect Cloud API Token"
    
  2. https://docs.astral.sh/uv/#getting-started の通りuvをインストール。
     curl -LsSf https://astral.sh/uv/install.sh | sh
     echo 'source $HOME/.local/bin/env' >> ~/.bashrc
     . $HOME/.bashrc
    
  3. uvで仮想環境prefectsを作成
     mkdir -p $HOME/prefects
     uv init --native-tls $HOME/prefects
     cd $HOME/prefects
     uv --native-tls python install 3.11
     uv --native-tls python pin 3.11
    
  4. Prefect Cloud API Tokenを使用しprefect cloudにログイン。
     echo "prefect" > requirements.txt
     echo "prefect-docker" >> requirements.txt
     uv add --native-tls -r requirements.txt
     uv run --native-tls prefect cloud login -k $PREFECT_API_KEY
     # $HOME/.prefect/profiles.toml が作成されます
    
  5. prefect work-poolを作成。
     uv run --native-tls prefect work-pool create oci-pool --set-as-default -t docker --overwrite
    
  6. 実際に https://prefect.cloud/ からWork-Poolが作成されていることを確認。 work-pool.png

    Warning

    ここで、CloudManagedのWork-Poolを無効化することを推奨します。

PrefectWorker(#1,#2 共通)

  1. https://docs.astral.sh/uv/#getting-started の通りuvをインストール。
     curl -LsSf https://astral.sh/uv/install.sh | sh
     echo 'source $HOME/.local/bin/env' >> ~/.bashrc
     . $HOME/.bashrc
    
  2. uvで仮想環境prefectsを作成。
     mkdir -p $HOME/prefects
     uv init --native-tls $HOME/prefects
     cd $HOME/prefects
     uv python --native-tls install 3.11
     uv python --native-tls pin 3.11
    
  3. Prefect Cloud API Tokenを使用しprefect cloudにログイン。
     echo "httpx" > requirements.txt
     echo "prefect" > requirements.txt
     echo "prefect-docker" >> requirements.txt
     uv add --native-tls -r requirements.txt
     uv run --native-tls prefect cloud login -k $PREFECT_API_KEY
     # $HOME/.prefect/profiles.toml が作成されます
    
  4. prefect workerを起動
     nohup uv run --native-tls prefect worker start --pool "oci-pool" --name oci01 >~/prefect.log 2>&1 &
     # nohup uv run --native-tls prefect worker start --pool "oci-pool" --name oci02 >~/prefect.log 2>&1 & #二号機は名前をoci02にしよう
    

    Warning

    prefect workerを動かすユーザはDockerグループに所属するなど、dockerコマンドが使える状態にしてください。最初は1号機のみで試験動作をした方が、問題があった際にみるべきprefect.logを特定するのに困りません。

  5. 実際に https://prefect.cloud/ からWorkerが登録されていることを確認 worker.png

以降、PrefectWorker側は一切触りません!

Github

  1. prefects.templateをクローンし、prefectsという名前でGithubプライベートリポジトリを作成する。(名前は何でも良い)
  2. 自身のアカウントのGithub Personal Access Tokenを取得しておく。権限は以下。
    • read:packages: Dockerコンテナレジストリの利用に必要。

    Warning

    今回Dockerコンテナ内にソースコードもパッケージングするので、暗にread:repo権限も含む点にご注意ください。発行するGithub Personal Access Tokenの種類はTokens (classic)を選択してください。本記事執筆時点で、Fine-grained TokensはGithub Packagesに対応していません。

  3. prefectsリポジトリ→Settings→Actions→General→Workflow permisiionsから、GithubワークフローがGITHUB_TOKEN環境変数を使ってGithub Container Registryにアクセス可能にします。 action_priv.png
  4. prefectsリポジトリ→Settings→Secrets and Variables→Actions→Secretsから、CLASSIC_PAT_GITHUBという名前で取得したGithub Personal Access Tokenを登録します。
  5. prefectsリポジトリ→Settings→Secrets and Variables→Actions→Secretsから、PREFECT_API_KEYという名前で取得したPrefect Cloud API Tokenを登録します。GithubワークフローがPREFECT_API_KEY環境変数を使ってPrefectCloudにアクセスできるようにします。
  6. prefectsリポジトリ→Settings→Secrets and Variables→Actions→Secretsから、TG_BOT_TOKENという名前で取得したTelegram BOT API Keyを登録します。 github_secrets.png

PrefectCloud

  1. Automationを作成する

automa01.png

automa02.png

補足: aiogramの主要な機能

  • メッセージのpin: message.pin()
  • メッセージの応答: message.reply(“pong”)
  • メッセージへのリアクション:
    • aiogram.types.reaction_type_emoji.ReactionTypeEmoji

まとめ

20秒くらいかかるやんけ!