🐙

Cloud Runでハマったslack-bolt + flaskをデプロイした際のエラー対処法

に公開1

はじめに

Slackアプリ開発でslack-bolt-pythonとFlaskを組み合わせてCloud Runにデプロイしようとしたところ、エラーに遭遇し、かなり苦戦しました...

遭遇したエラーとその解決方法をまとめます。

環境・構成

  • Python: 3.10
  • slack-bolt: 1.23.0
  • Flask: 3.1.1
  • Google Cloud Run

発生したエラー

エラー1: Slackのイベント検証失敗

症状

Your URL didn't respond.

もしくは

Your URL didn't respond with the value of the `challenge` parameter.

Slack側でEvent Subscriptionsの設定を行う際に、エンドポイントURLの検証で失敗するエラーです。

エラー2: Cloud Runと疎通失敗

症状

"The request failed because either the HTTP response was malformed or connection to the instance had an error. Additional troubleshooting documentation can be found at: https://cloud.google.com/run/docs/troubleshooting#malformed-response-or-connection-error"

このエラーメッセージを調べてもあまり情報がなく、どこが原因なのか全くわかりませんでした。

解決方法:変数名の修正

問題のあったコード

main.py
import os
from slack_bolt import App
from flask import Flask, request
from slack_bolt.adapter.flask import SlackRequestHandler

app = App(
    token=os.environ.get("SLACK_BOT_TOKEN"),
    signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
    process_before_response=True
)
flask_app  = Flask(__name__)
handler = SlackRequestHandler(app)

@flask_app.route("/slack/events", methods=["POST"])
def slack_events():
    return handler.handle(request)

@app.event("app_mention")
def handle_mention(event, say):
    say("Hello!")

修正後のコード

変数名を以下のように変更しました:

  • appslack_app
  • flask_appapp
main.py
import os
from slack_bolt import App
from flask import Flask, request
from slack_bolt.adapter.flask import SlackRequestHandler

slack_app = App(
    token=os.environ.get("SLACK_BOT_TOKEN"),
    signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
    process_before_response=True
)
app  = Flask(__name__)
handler = SlackRequestHandler(slack_app)

@app.route("/slack/events", methods=["POST"])
def slack_events():
    return handler.handle(request)

@slack_app.event("app_mention")
def handle_mention(event, say):
    say("Hello!")

原因の詳細

Cloud RunでコンテナイメージをCloud Buildで作成していましたが、デフォルトでは以下のようにエントリーポイントが設定されていました:

[builder] Setting default entrypoint: "gunicorn -b :8080 main:app"

Cloud Buildもgunicornもそれまで詳しくなかったのですが、調べてみると、gunicornでFlaskアプリケーションを起動する際の正しいコマンド形式は {module_import}:{app_variable} という形であることがわかりました。

つまり、main:app は「mainモジュール内のapp変数」を指しています。しかし、元のコードではFlaskアプリケーションの変数名が flask_app だったため、gunicornが正しくアプリケーションを見つけられず、疎通がうまくいかなかったのです。

Flask公式ドキュメントでも、この命名規則について説明されています。

まとめ

今回のエラーは、変数名という非常にシンプルな問題が原因でした。
Cloud Runやgunicornの仕組みを理解していれば早期に解決できた問題ですが、エラーメッセージからは原因を特定するのが困難でした。。。同じような問題で困っている方の参考になれば幸いです。

参考資料

Discussion

ラーマ2ラーマ2

記事の公開前日に絶賛この件でハマってまして、CloudRunでの実行諦めたのですがスッキリしました、ありがとうございます。