Flask Migrate ― マルチテナントの問題
By hientd, at: 2023年5月16日17:53
Estimated Reading Time: __READING_TIME__ minutes


最近、PostgreSQLスキーマを使用したマルチテナント環境でFlask-Migrateに予期せぬ問題が発生しました。問題とその解決方法を詳しく説明します。
flask db upgrade
コマンド実行時に発生した例外は次のとおりです。
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) connection to server at "192.9.2.1", port 5432 failed: FATAL: password authentication failed for user "server_username"
connection to server at "192.9.2.1", port 5432 failed: FATAL: no pg_hba.conf entry for host "local", user "server_username", database "proddb", no encryption
ローカルマシンではすべて正常に動作しましたが、Flask-Migrateはサーバーデータベースで動作しませんでした。
1. 問題
最初に、virtualenv、pip
、および必要なパッケージをすべてインストールしました。
psycopg2==2.9.6
Flask==2.3.2
Flask-Migrate==2.5.3
Flask-SQLAlchemy==3.0.3
Flaskアプリケーション用のlocal.json
ファイルを設定しました。
{
"SECRET_KEY": "my secret",
"SQLALCHEMY_DATABASE_URI": "postgresql://joe:password@localhost:5432/testdb",
"DATABASE_SCHEMA": "public"
}
app.py
ファイルには次のコードが含まれていました。
import json
from os import environ
from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from blueprints import api_blueprint
db = SQLAlchemy()
def create_app():
environment = environ.get("ENVIRONMENT", "local")
app = Flask(__name__)
app.config.from_file(f"{environment}.json", load=json.load)
db.metadata.schema = app.config["DATABASE_SCHEMA"]
db.init_app(app)
migrate = Migrate(app, db)
app.register_blueprint(api_blueprint)
return app
app = create_app()
さらに、次の内容のmain.py
ファイルがありました。
from app import create_app
if __name__ == "__main__":
app = create_app()
app.run(host="127.0.0.1", port=5000, debug=True)
最後に、models.py
ファイルでTask
モデルを定義しました。
from app import db
class Task(db.Model):
__tablename__ = "task"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.Text, nullable=False)
python main.py
を実行した後、ウェブサイトは127.0.0.1:5000で正常に動作しました。
Flask-Migrate==2.5.3
のインストール後、flask db init
コマンドを実行して、新しいmigrations
ディレクトリを作成しました。
モデルクラスを追加してmigrations/env.py
の内容を更新しました。
from models import Task
次に、ターミナルでflask db migrate
コマンドを実行し、migrations/versions
の下に新しいマイグレーションファイル(例:migrations/versions/48xw123sdfasf_initial_migration.py
)を生成しました。
この変更を先に作成したtestdb
データベースに適用するために、次のコマンドを実行しました。
flask db upgrade
ローカルマシンではすべて正常に動作しました。しかし、IPが192.9.2.1
、ポートが5432
である別のサーバー上にデータベースが存在するサーバーにデプロイしたところ、問題が発生しました。
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) connection to server at "192.9.2.1", port 5432 failed: FATAL: password authentication failed for user "server_username"
connection to server at "192.9.2.1", port 5432 failed: FATAL: no pg_hba.conf entry for host "local", user "server_username", database "proddb", no encryption
驚くべきことに、コマンドラインを使用してデータベースに接続し、set schema 'internal';
を使用してスキーマにアクセスすることができました。
2. 問題解決戦略
問題の調査とデバッグのために、次の手順を実行しました。
1. データベース接続URIの検証
app.py
とmigrations/env.py
の両方のファイルにbreakpoint()
ステートメントを挿入して、接続URIが正しいかどうかを確認しました。接続URIはpostgresql://server_username:***@localhost:5432/proddb
形式で正しく見えました。隠されたパスワード(***
)は、機密情報を保護するためのセキュリティ対策であると思われます。
2. Flask-Migrateのアップグレード
Flask-Migrateのバージョンの問題の可能性も考慮して、コマンドラインを使用してアップグレードすることにしました。
pip install Flask-Migrate==4.0.4 # 最新バージョン
しかし、アップグレード後も問題は解決しませんでした。
3. 深層デバッグ
Pythonのトレースバックエラーに示されているように、Flask-Migrate、Alembic、SQLAlchemyライブラリ内のさまざまな関数にbreakpoint()
ステートメントを配置しました。残念ながら、これらの試みは役に立つ洞察や解決策を提供しませんでした。接続の問題は解決しませんでした。
この時点で、少し休憩を取り、頭をクリアして集中力を回復しました。
新鮮な視点で戻って、migrations/env.py
ファイルの調査を続けました。このファイルはflask db upgrade
コマンドで重要な役割を果たします。再度、接続URIを検査し、隠されたパスワードを含めて正しいことを確認しました。
しかし、migrations/env.py
ファイルのある特定の行が私の注意を引きました。
with connectable.connect() as connection:
ライブラリ(Alembic、SQLAlchemy、Flask-Migrate)をさらに調査しても、問題に対する明白な解決策や修正は見つかりませんでした。
もう1回15分間の休憩を取り、新たな決意を持って戻ってきました。
熟考した結果、隠されたパスワード(***
)を含むURIは適切ではないことに気づきました。何かおかしいと感じました。それはDjangoでの経験を思い出させました。Djangoでは、パスワードは同様の方法で隠されていませんでした。Djangoのアプローチの方がより直接的でした。
これを踏まえ、変更を加えることにしました。`sqlalchemy.url`を`'postgresql://server_username:***@:5432/proddb'`から`'postgresql://server_username:server_password@:5432/proddb'`に更新しました。驚いたことに、この修正によって問題は解決し、マイグレーションプロセスが正常に動作しました。
したがって、問題はパスワードが隠されていることであり、パスワードが`***`に変更されていることが完全に間違っていました。
Flask-Migrateの所有者がこの問題を以前から特定して対処しなかった理由を考えました。あるいは…私が間違いを犯したか、または問題は私の環境特有のものである可能性があります。
熟考した結果、ステップ2.2でFlask-Migrateの最新バージョンをインストールする際に、既存の`migrations/env.py`ファイルを削除して再度初期化せずに、古い`env.py`の内容をそのまま残してしまったという別の間違いに気づきました。
3. まとめ
結論として、Flask-MigrateはFlaskのデータベースマイグレーションに優れたツールです。Flask-Migrateでスムーズなエクスペリエンスを確保し、同様の問題が発生するのを回避するには、次のベストプラクティスに従うことが重要です。
- データベース接続URIの再確認:Flaskアプリケーションを別のサーバーにデプロイする際には、データベース接続URIが正しく設定されていることを確認します。サーバーのIP、ポート、ユーザー名、パスワード、スキーマに注意を払ってください。場合によっては、URIでパスワードを`***`で隠すと認証の問題が発生する可能性があるため、パスワードを明示的に提供してください。
- パッケージの適切なアップグレード:Flask-Migrateまたは他のパッケージをアップグレードする際には、適切なアップグレードプロセスに従うことが不可欠です。インストールが成功し、必要な構成や更新が適切に適用されていることを確認します。また、Flask-Migrateをアップグレードした後、既存の`migrations`ディレクトリを削除して再度初期化し、最新変更との互換性を確保してください。
- 徹底的なデバッグ:Flask-Migrateで問題が発生した場合は、徹底的なデバッグを実行します。コードベースに戦略的に`breakpoint()`ステートメントを使用して実行フローを追跡し、変数、構成、接続情報を検査します。必要に応じて、関連するライブラリのドキュメントを確認し、コミュニティの助けを求めます。
- 類似のフレームワークからの学習:Djangoなどの他のフレームワークでの経験から、データベースマイグレーションの一般的なパターンとベストプラクティスについての洞察を得ることができます。Djangoのデータベース接続とパスワード処理へのアプローチは、問題解決に役立つ代替的な視点を提供する可能性があります。
これらのガイドラインに従い、体系的なデバッグアプローチを採用することで、Flask-Migrateのメリットを最大限に活用し、データベースマイグレーションプロセス中に発生する障害を克服できます。積極的で、情報通で、活気のあるFlaskコミュニティのサポートとガイダンスを活用してください。楽しいFlask開発を!