Flaskアプリケーションのパフォーマンスを最適化する方法

0 株式
0
0
0
0

導入

Flaskは、小規模から中規模のアプリケーションを構築するための軽量で柔軟なWebフレームワークです。シンプルな個人ブログから、REST API、SaaSプラットフォーム、eコマースウェブサイト、データ駆動型ダッシュボードといった複雑なアプリケーションまで、幅広いプロジェクトで広く利用されています。.

しかし、アプリケーションのトラフィックや複雑さが増すと、パフォーマンスのボトルネックが発生する可能性があります。コンテンツ管理システム(CMS)、モバイルアプリ用API、リアルタイムデータ可視化ツールなど、どのようなシステムを構築する場合でも、Flaskのパフォーマンスを最適化することは、応答性と拡張性に優れたユーザーエクスペリエンスを提供するために不可欠です。.

このチュートリアルでは、Flask アプリケーションのパフォーマンスを最適化するためのテクニックとベスト プラクティスについて説明します。.

前提条件
  • Ubuntu を実行しているサーバーと、sudo 権限を持つ非 root ユーザー、およびファイアウォールを有効にしてください。設定方法については、こちらのリストからディストリビューションを選択し、「サーバー入門ガイド」に従ってください。サポートされているバージョンの Ubuntu を実行していることをご確認ください。.
  • Linux コマンドラインを理解する コマンドラインの概要や復習については、Linux コマンドライン入門のガイドを参照してください。
  • Python プログラミングの基本的な理解。.
  • UbuntuシステムにPython 3.7以降がインストールされています。UbuntuでPythonスクリプトを実行する方法については、UbuntuでPythonスクリプトを実行する方法に関するチュートリアルをご覧ください。.

Flask環境の設定

Ubuntu 24.04にはデフォルトでPython 3が付属しています。ターミナルを開き、以下のコマンドを実行してPython 3のインストールを確認してください。

root@ubuntu:~# python3 --version
Python 3.12.3

Python 3が既にマシンにインストールされている場合、上記のコマンドはPython 3の現在のバージョンを返します。インストールされていない場合は、次のコマンドを実行してPython 3のインストールを取得できます。

root@ubuntu:~# sudo apt install python3

次に、システムに pip パッケージ インストーラーをインストールする必要があります。

root@ubuntu:~# sudo apt install python3-pip

pipをインストールしたら、Flaskをインストールしましょう。.

Flaskはpipを使ってインストールします。システム上の他のパッケージとの干渉を避けるため、仮想環境でインストールすることをお勧めします。.

root@ubuntu:~# python3 -m venv myprojectenv
root@ubuntu:~# source myprojectenv/bin/activate
root@ubuntu:~# pip install Flask

Flaskアプリケーションを作成する

次のステップは、Flaskアプリケーション用のPythonコードを記述することです。新しいスクリプトを作成するには、任意のディレクトリに移動してください。

root@ubuntu:~# cd ~/path-to-your-script-directory

ディレクトリに移動したら、新しいPythonファイル(app.py)を作成し、Flaskをインポートします。次に、Flaskアプリケーションを初期化し、ルートパスを作成します。.

root@ubuntu:~# nano app.py

空のテキストエディタが開きます。ここにロジックを記述するか、以下のコードをコピーしてください。

from flask import Flask, jsonify, request
app = Flask(__name__)
# Simulate a slow endpoint
@app.route('/slow')
def slow():
import time
time.sleep(2) # to simulate a slow response
return jsonify(message="This request was slow!")
# Simulate an intensive database operation
@app.route('/db')
def db_operation():
# This is a dummy function to simulate a database query
result = {"name": "User", "email": "[email protected]"}
return jsonify(result)
# Simulate a static file being served
@app.route('/')
def index():
return "<h1>Welcome to the Sample Flask App</h1>"
if __name__ == '__main__':
app.run(debug=True)

それでは、Flask アプリケーションを実行してみましょう。

root@ubuntu:~# flask run

次の curl コマンドを使用してエンドポイントをテストできます。

/ エンドポイントをテストします (静的コンテンツを返します)。

root@ubuntu:~# curl http://127.0.0.1:5000/
[secondary_lebel Output]
<h1>Welcome to the Sample Flask App</h1>%

エンドポイント/低速をテストします (低速応答をシミュレートします)。

root@ubuntu:~# time curl http://127.0.0.1:5000/db

この遅いエンドポイントを確認するには、Linuxのtimeコマンドを使用します。timeコマンドは、特定のコマンドまたはプログラムの実行時間を測定するために使用され、主に3つの情報を提供します。

  1. 実時間: コマンドの開始から終了までの実際の経過時間。.
  2. ユーザー時間: ユーザー モードで費やされた CPU 時間の量。.
  3. システム時間: カーネル モードで費やされた CPU 時間の量。.

これにより、遅いエンドポイントで実際にかかった時間を測定できます。出力は次のようになります。

Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.01s system 0% cpu 2.023 total

time.sleep(2) 呼び出しが低速応答をシミュレートするため、この要求の応答には約 2 秒かかります。.

/db エンドポイントをテストしてみましょう (データベース操作をシミュレートします)。

root@ubuntu:~# curl http://127.0.0.1:5000/db
Output
{"email":"[email protected]","name":"User"}

curl を使用してこれらのエンドポイントをテストすることで、Flask アプリケーションが正しく実行され、応答が期待どおりであることを確認できます。.

次のセクションでは、さまざまな手法を使用してアプリケーションのパフォーマンスを最適化する方法を学習します。.

本番環境対応のWSGIサーバーを使用する

Flaskに組み込まれている開発サーバーは、本番環境向けに設計されていません。同時リクエストを効率的に処理するには、Gunicornのような本番環境対応のWSGIサーバーに移行する必要があります。.

Gunicornをインストールして起動する

Gunicornをインストールしましょう。

root@ubuntu:~# pip install gunicorn

4 つのワーカー プロセスで Gunicorn を使用して Flask アプリケーションを実行します。

root@ubuntu:~# gunicorn -w 4 -b 0.0.0.0:8000 app:app
Output
% /Library/Python/3.9/bin/gunicorn -w 4 -b 0.0.0.0:8000 app:app
[2024-09-13 18:37:24 +0530] [99925] [INFO] Starting gunicorn 23.0.0
[2024-09-13 18:37:24 +0530] [99925] [INFO] Listening at: http://0.0.0.0:8000 (99925)
[2024-09-13 18:37:24 +0530] [99925] [INFO] Using worker: sync
[2024-09-13 18:37:24 +0530] [99926] [INFO] Booting worker with pid: 99926
[2024-09-13 18:37:25 +0530] [99927] [INFO] Booting worker with pid: 99927
[2024-09-13 18:37:25 +0530] [99928] [INFO] Booting worker with pid: 99928
[2024-09-13 18:37:25 +0530] [99929] [INFO] Booting worker with pid: 99929
[2024-09-13 18:37:37 +0530] [99925] [INFO] Handling signal: winch
^C[2024-09-13 18:38:51 +0530] [99925] [INFO] Handling signal: int
[2024-09-13 18:38:51 +0530] [99927] [INFO] Worker exiting (pid: 99927)
[2024-09-13 18:38:51 +0530] [99926] [INFO] Worker exiting (pid: 99926)
[2024-09-13 18:38:51 +0530] [99928] [INFO] Worker exiting (pid: 99928)
[2024-09-13 18:38:51 +0530] [99929] [INFO] Worker exiting (pid: 99929)
[2024-09-13 18:38:51 +0530] [99925] [INFO] Shutting down: Master

Gunicorn を使用する利点は次のとおりです。

  • 同時リクエスト処理: Gunicorn では、複数のワーカー プロセスを使用して複数のリクエストを同時に処理できます。.
  • 負荷分散: ワーカー プロセス間で受信要求のバランスを取り、サーバー リソースを最適に使用します。.
  • 非同期ワーカー: gevent のような非同期ワーカーを使用すると、他のリクエストをブロックすることなく、長時間実行されるタスクを効率的に実行できます。.
  • スケーラビリティ: Gunicorn は、ワーカー プロセスの数を増やしてより多くの同時リクエストを処理できるようにすることで、水平方向に拡張できます。.
  • フォールト トレランス: 応答しないワーカーや壊れたワーカーを自動的に置き換え、高可用性を確保します。.
  • 本番環境対応: Flask 開発サーバーとは異なり、Gunicorn はセキュリティ、安定性、パフォーマンス機能が優れており、本番環境向けに最適化されています。.

本番環境で Gunicorn に切り替えると、Flask アプリケーションのスループットと応答性が大幅に向上し、実際のトラフィックを効率的に処理できるようになります。.

負荷を軽減するためにキャッシュを有効にします。

キャッシュは、オーバーヘッドを削減することでFlaskのパフォーマンスを向上させる最良の方法の一つです。ここでは、Flask-Cachingを追加して、遅いパスの結果をキャッシュします。.

RedisでFlask-Cachingをインストールして設定する

必要なパッケージをインストールします。

root@ubuntu:~# pip install Flask-Caching redis

app.py を更新して、slow/path にキャッシュを追加します。

エディターを開き、次のコマンドで app.py ファイルを更新します。

root@ubuntu:~# nano app.py
from flask_caching import Cache
app = Flask(__name__)
# Configure Flask-Caching with Redis
app.config['CACHE_TYPE'] = 'redis'
app.config['CACHE_REDIS_HOST'] = 'localhost'
app.config['CACHE_REDIS_PORT'] = 6379
cache = Cache(app)
@app.route('/slow')
@cache.cached(timeout=60)
def slow():
import time
time.sleep(2) # Simulate a slow response
return jsonify(message="This request was slow!")

/slowへの最初のリクエスト後、以降のリクエストはtime.sleep()関数をバイパスし、60秒以内にキャッシュから処理されます。これにより、サーバーの負荷が軽減され、応答速度が向上します。.

注: このチュートリアルでは、Redisホストとしてlocalhostを使用しています。ただし、本番環境では、DigitalOcean Managed RedisなどのマネージドRedisサービスを使用することをお勧めします。これにより、ストレージニーズに合わせて、より優れたスケーラビリティ、信頼性、セキュリティを実現できます。DigitalOcean Managed Redisを本番環境のアプリケーションに統合する方法については、アプリケーションプラットフォームでのDigitalOcean Redisを使用したストレージに関するこちらのチュートリアルをご覧ください。.

 

データがキャッシュされているかどうかを確認するには、/slow エンドポイントに対して次のコマンドを実行します。.

これは /slow エンドポイントへの最初のリクエストです。このリクエストが完了すると、/slow ルートの結果がキャッシュされます。.

root@ubuntu:~# time curl http://127.0.0.1:5000/slow
Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.01s system 0% cpu 2.023 total

これは、60 秒以内に /slow エンドポイントに送信される後続のリクエストです。

root@ubuntu:~# time curl http://127.0.0.1:5000/slow
Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.00s system 0% cpu 0.015 total

データベースクエリを最適化する

データベースクエリはパフォーマンスのボトルネックになることがよくあります。このセクションでは、SQLAlchemyと結合プーリングを使用して、データベースクエリの最適化をシミュレートします。.

結合集計を使用したデータベースクエリのシミュレーション

まず、SQLAlchemy をインストールしましょう。

root@ubuntu:~# pip install Flask-SQLAlchemy

接続統合を構成するには、app.py を更新します。

rom flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
# Simulate an intensive database operation
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_POOL_SIZE'] = 5 # Connection pool size
db = SQLAlchemy(app)
@app.route('/db1')
def db_operation_pooling():
# Simulate a database query
result = db.session.execute(text('SELECT 1')).fetchall()
return jsonify(result=str(result))

ここで、db1 パスへの curl リクエストを実行すると、次の出力が表示されるはずです。

root@ubuntu:~# curl http://127.0.0.1:5000/db1 
output
{"result":"[(1,)]"}

本番環境でコネクションプールを実装することで、Flaskアプリケーションのパフォーマンスを大幅に最適化できます。コネクションプールを使用すると、アプリケーションはリクエストごとに新しい接続を作成する代わりに、既存のデータベース接続を再利用できます。これにより、新しい接続を作成するオーバーヘッドが削減され、応答時間が短縮され、スケーラビリティが向上します。.

先ほど設定したSQLALCHEMY_POOL_SIZE設定は、プール内の接続数を制限します。本番環境では、具体的なニーズとサーバーの能力に応じてこの値を調整する必要があります。また、プールがいっぱいになった場合に追加の接続を許可するSQLALCHEMY_MAX_OVERFLOWや、リクエストが接続を待機する時間を指定するSQLALCHEMY_POOL_TIMEOUTなど、他の統合オプションも検討すると良いでしょう。.

この例では簡潔にするためにSQLiteを使用していますが、実際のシナリオではPostgreSQLやMySQLといったより堅牢なデータベースを使用することになります。これらのデータベースには独自の接続プールメカニズムがあり、SQLAlchemyとの連携によりパフォーマンスを向上させることができます。.

接続プールを慎重に構成して使用することで、Flask アプリケーションは高負荷時でもデータベース操作を効率的に実行できるようになり、全体的なパフォーマンスが大幅に向上します。.

Gzip圧縮を有効にする

応答を圧縮すると、サーバーとクライアント間で転送されるデータの量が大幅に削減され、パフォーマンスが向上します。.

Flask-Compressのインストールと設定

Flask-compress パッケージをインストールしましょう。.

root@ubuntu:~# pip install Flask-Compress

次に、app.py を更新して圧縮を有効にします。.

from flask_compress import Compress
# This below command enables Gzip compression for the Flask app
# It compresses responses before sending them to clients,
# reducing data transfer and improving performance
Compress(app)
@app.route('/compress')
def Compress():
return "<h1>Welcome to the optimized Flask app !</h1>"

これにより、500 バイトを超える応答が自動的に圧縮され、大きな応答の送信時間が短縮されます。.

実稼働環境では、Gzip 圧縮により、特に HTML、CSS、JavaScript などのテキストベースのコンテンツの場合、サーバーとクライアント間で転送されるデータの量を大幅に削減できます。.

データ転送量の削減により、ページの読み込み時間が短縮され、ユーザーエクスペリエンスが向上し、帯域幅コストも削減されます。さらに、多くの最新のウェブブラウザはGzip圧縮を自動的にサポートしているため、完全に互換性のある最適化手法となっています。Gzip圧縮を有効にすると、クライアント側に変更を加えることなく、Flaskアプリケーションのパフォーマンスとスケーラビリティを効果的に向上させることができます。.

集中的なタスクをCeleryにアップロードする

メールの送信や大規模なデータセットの処理など、リソースを大量に消費する操作は、Celery を使用してバックグラウンドジョブにオフロードするのが最適です。これにより、長時間実行されるタスクが受信リクエストをブロックするのを防ぐことができます。.

Celeryは、時間のかかるタスクを非同期的に実行できる強力な分散タスクキューイングシステムです。負荷の高い処理をCeleryにオフロードすることで、Flaskアプリケーションの応答性とスケーラビリティを大幅に向上させることができます。Celeryは、別々のマシンで実行可能なワーカープロセスにタスクを委任することで、リソース利用率の向上と並列処理を実現します。.

セロリを使用する主な利点は次のとおりです。

  1. ユーザーリクエストへの応答時間の改善
  2. スケーラビリティとリソース管理の向上
  3. メインプログラムをブロックすることなく、複雑で時間のかかるタスクを実行する機能
  4. タスクのスケジュール設定と失敗したタスクの再試行の組み込みサポート
  5. RabbitMQやRedisなどのさまざまなメッセージブローカーとの簡単な統合

Celery を使用すると、計算集約型タスクや I/O バウンド タスクを処理する場合でも、Flask アプリケーションの応答性を維持できます。.

バックグラウンドタスク用にCeleryを構成する

Celeryをインストールしましょう。.

root@ubuntu:~# pip install Celery

次に、app.py を更新して、Celery を非同期タスク用に設定しましょう。

from celery import Celery
celery = Celery(app.name, broker='redis://localhost:6379/0')
@celery.task
def long_task():
import time
time.sleep(10) # Simulate a long task
return "Task Complete"
@app.route('/start-task')
def start_task():
long_task.delay()
return 'Task started'

別のターミナルで、Celery ワーカーを起動します。

root@ubuntu:~# celery -A app.celery worker --loglevel=info
Output
------------- celery@your-computer-name v5.2.7 (dawn-chorus)
--- ***** ----- 
-- ******* ---- Linux-x.x.x-x-generic-x86_64-with-glibc2.xx 2023-xx-xx
- *** --- * --- 
- ** ---------- [config]
- ** ---------- .> app: app:0x7f8b8c0b3cd0
- ** ---------- .> transport: redis://localhost:6379/0
- ** ---------- .> results: disabled://
- *** --- * --- .> concurrency: 8 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** ----- 
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. app.long_task
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] Connected to redis://localhost:6379/0
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] mingle: searching for neighbors
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] mingle: all alone
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] celery@your-computer-name ready.

ここで、curl コマンドを実行して /start-task パスにアクセスすると、出力は次のようになります。

root@ubuntu:~# curl http://127.0.0.1:5000/start-task
Output
Task started

これにより、バックグラウンド ジョブがまだ実行中の場合でも、ほぼ即座に「タスクが開始されました」が返されます。.

start_task() 関数は次の 2 つのことを行います。

  • long_task.delay() を呼び出し、Celery タスクを非同期的に開始します。つまり、タスクはバックグラウンドで実行するためにキューに登録されますが、関数はタスクの完了を待機しません。.
  • すぐに「タスクが開始されました」という文字列を返します。.

注目すべき重要な点は、実際の長時間実行タスク(10秒間のスリープでシミュレート)はCeleryによって非同期的に実行されることです。Flaskルートは、このタスクの完了を待たずにリクエストに応答します。.

したがって、このエンドポイントをねじ込むと、実際のジョブはバックグラウンドで 10 秒間実行され続けますが、すぐに「ジョブが開始されました」という応答が返されます。.

バックグラウンド ジョブが完了してから 10 秒後に、次のレポート メッセージが表示されます。

出力は次のようになります。

[2024-xx-xx xx:xx:xx,xxx: INFO/MainProcess] Task app.long_task[task-id] received
[2024-xx-xx xx:xx:xx,xxx: INFO/ForkPoolWorker-1] Task app.long_task[task-id] succeeded in 10.xxxs: 'Task Complete'

この例では、Celery が長時間実行タスクを非同期的に実行することで Flask アプリケーションのパフォーマンスを向上させ、メインアプリケーションの応答性を維持する方法を示します。長時間実行タスクはバックグラウンドで実行されるため、Flask アプリケーションは他のリクエストを処理できるようになります。.

実稼働環境で Celery を実行するには、次の作業が必要です。

  1. RabbitMQのような堅牢なメッセージブローカーを使用する
  2. 独自の結果バックエンド(PostgreSQLなど)を使用する
  3. プロセス制御システムを使用して作業者を管理する(例:監督者)
  4. 監視ツール(例:flowers)を実装する エラー処理とログ記録を強化する
  5. エラー処理とログ記録の強化
  6. タスクの優先順位付けを使用する
  7. 異なるマシン上の複数のワーカーによるスケーリング
  8. 適切なセキュリティ対策の確保

結果

このチュートリアルでは、様々なパフォーマンス向上テクニックを実装することで、Flaskアプリケーションを最適化する方法を学びました。これらの手順に従うことで、Flaskアプリケーションのパフォーマンス、スケーラビリティ、応答性を向上させ、高負荷時でも効率的に動作させることができます。.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

あなたも気に入るかもしれない