Djangoをスケーリングする方法:基本を超えて
入門
ドロップレットにDjangoをデプロイしましたが、生活は良好です。 サイトのトラフィックが増加するにつれてパフォーマンスの問題が発生しましたが、ボトルネックを見つけて修正しました。 ただし、サイトのトラフィックは増え続けています。 どういうわけかあなたはより多くのパフォーマンスを必要としています…あなたは何ができますか?
アプリケーションとサーバー構成の本質を少し掘り下げてみましょう。 この記事は、Ubuntu 12.04を使用していることを前提に書かれていますが、原則はどのバージョンのLinuxでも機能します。
Apacheを使用している場合は、Webサーバーの最適化の手順に従う必要があります。 Nginxを使用している場合は、これらのヒントも役立ちます。
すべてをキャッシュする
サイトを管理するためのCMSを作成した場合は、非常に積極的なキャッシュを許容できる可能性があります。 その場合、私のお気に入りのツールは、ApacheまたはNginxの前にあるフロントエンドキャッシュであるVarnishです。 その場合は、この既存のApacheチュートリアルとこのNginxチュートリアルを確認してください(そうです、Nginxの記事ではPHPについて説明していますが、Djangoでも機能します)。
欠点は、慎重に構成しない限り、Varnishがサイト全体をキャッシュすることです。 動的コンテンツがある場合、たとえばサイトがアプリのようなものである場合、Varnishは多くの問題を引き起こす可能性があります。 ユーザーAがサイトにアクセスし、ショッピングカートに商品を追加したとします。 次に、ユーザーBがサイトにアクセスし、ユーザーAのアイテムが入ったキャッシュバージョンのショッピングカートを確認します。 良くない。
もう1つの欠点は、Djangoがデフォルトで、アップストリームキャッシュにコンテンツのキャッシュを強制しようとすることです。これにより、少しの戦いが発生し、動作が不安定になります。
しかし、すばらしいニュースがあります。Djangoには、2つのことを提供する独自のキャッシュフレームワークがあります。
-
これにより、アプリケーションがよりキャッシュフレンドリーになり、Varnishでうまく機能するようになります(使用することを選択した場合)
-
これにより、サイトのどの部分をキャッシュするかを制御できます
小さい液滴を使用している場合は、データベースキャッシングを使用することをお勧めします。 オンにするのは簡単で、別のサーバープロセスを実行する必要がありません。 十分なRAMがある場合、memcachedは優れたキャッシュバックエンドを提供します。 この記事では、データベースベースのキャッシングについて説明します。
上記のリンク先のドキュメントをよく読んで、多くの優れた詳細を確認してください。ただし、開始するには次のことを行う必要があります。
キャッシュ用のデータベーステーブルを作成します。 この場合、テーブルは「cache_table」と呼ばれます。
python manage.py createcachetable cache_table
次に、 settings.py ファイルを編集し、次の行を追加してキャッシュを構成します。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'cache_table',
}
}
構成できる追加の構成オプションがあります。 それらについては、ドキュメントでお読みください。 特にTIMEOUTとCULL_FREQUENCYをチェックします。
次に、何をキャッシュするかを決定する必要があります。 Varnishを使用してサイトをより適切に機能させたい場合は、サイトごとのキャッシュ構成を確認してください。 ただし、これはそれほどエキサイティングではないため、ビューごとのキャッシュについて詳しく見ていきましょう。
変更されることはめったになく、あまり動的ではないショッピングカートの商品ページがあるとします。 キャッシュに最適なproduct_detail(request, product_id)
というビューがあります。
単純なデコレータを使用して、次のキャッシュを有効にすることができます。
from django.views.decorators.cache import cache_page
@cache_page(60 * 60)
def product_detail(request, product_id):
...
これにより、そのビューを60分間(60秒* 60分)キャッシュできます。
繰り返しになりますが、いくつかの優れたヒントについては、ドキュメントを参照したいと思います。 特に強力な機能の1つは、ヘッダーに基づいて変更することです。 複数の言語に翻訳されているサイトがある場合、またはブラウザのバージョンに基づいて異なるコンテンツを表示する場合、これは非常に役立ちます。
頻繁に変更されるコンテンツはどうですか?
半動的なサイトがある場合、たとえば、アプリケーションが大量のコメントを受信したり、データが頻繁に更新されたりする場合は、キャッシュの使用について心配する必要があります。 これは有効な懸念事項です。
まず、積極的にキャッシュできるすべてのビューを特定してから、それらをキャッシュします。
次に、古いコンテンツが表示されるのを誰かが許容できる遅延の長さを特定します。 たとえば、アクティブなコメントがあるサイトは、おそらく1〜5分の遅延を許容できます。 可能な限りページをキャッシュします。
驚かれることでしょう。10秒のキャッシュタイムアウトでも非常に役立ちます。 多くの場合、突然過負荷になっている忙しいサイトは、通常、特定のページへのトラフィックの猛攻撃に見舞われています。
10,000人が1分以内にサイトのページにアクセスすると、データベースへのこれらのクエリがすべて一度に実行されると、サーバーが溶けてしまいます。 キャッシュを10秒に設定すると、リクエストした人数に関係なく、ページは1分間に6回しか再生成されません。 どのサーバーでもそれを行うことができます!
Eコマースサイトやセッションデータを使用するサイトは、細心の注意を払う必要があります。 vary ヘッダーオプションを調べて、ご使用の状況でキャッシュが機能するかどうかを確認してください。 創造的であることを恐れないでください!
サイトは、すべてのHTMLページを静的コンテンツとして提供し、Ajaxを使用してユーザーごとのセッション情報をロードする場合があります。 eコマースサイトの場合、ユーザーは、ページの残りの部分を正常に閲覧できる限り、ショッピングカートの数が読み込まれるのに5秒かかるかどうかに気付かないことがよくあります。
静的コンテンツを提供する
Djangoの組み込み開発サーバーは、静的コンテンツを適切に提供します。 多くの場合、Djangoによって提供される静的コンテンツを使用して本番環境で使用するように構成されたDjangoサイトを目にします。
これを行う必要があるのは、コンテンツへのアクセスを制御する必要がある場合のみです。 しかし、それでも、他の方法を検討してください。
-
動的に生成されたCSS-いいえ、 Grunt 以下のようなビルドツールを使用して、デプロイ前にCSSをビルドします。 サーバー上で定期的に生成する必要がある場合は、バックグラウンドタスクを使用してください。
-
認証されたユーザーアクセス-はい、おそらくこれにはDjangoを使用する必要があります。
-
その他すべて-いいえ、Webサーバーで静的コンテンツホスティングを構成しましょう。
static-files のドキュメントを数回読んでから、入手する必要がありました。 自分で読んでおくべきだと思いますが、苦労している場合は、次のシナリオを使用できます。
ローカル開発マシンで、「settings.py」に次のような行を追加します。
STATIC_ROOT = os.path.join(BASE_DIR, "static")
次に、次のようにmanage.pyコマンドを実行します。
python manage.py collectstatic
これで、プロジェクトルートに「static」というフォルダが表示され、CSSやJavascriptなどの多くの静的ファイルが表示されます。 特に、管理アプリを有効にしている場合。
これらのファイルを使用するには、Djangoがそれらを探す場所を認識できるように構成する必要があります。 settings.py フォルダーには、次のような構成オプションが必要です。
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
ここで、開発者サーバーを実行して管理領域にアクセスし、任意のページのソースを表示すると、CSSファイルが/static/admin/css/
から提供されていることがわかります。
アプリケーションをデプロイするときに、ApacheまたはNginxをポイントして、 / static / で始まるファイルを直接ファイルにポイントすることで、それらを提供できるようになりました。
アプリが/srv / myapp / にデプロイされているため、/srv/myapp/static/admin/css/base.css
というファイルがある場合は、次のようにWebサーバーを構成できます。
Apache:
Alias /static/ "/srv/myapp/static/"
Nginx:
server {
...
location /static/ {
alias /srv/myapp/static/;
}
}
どちらの場合も、/ static / URLを、ファイルが見つかったハードドライブ上の正確な場所にポイントしているだけです。 ApacheとNginxは、Djangoよりもはるかに高速にこれらを提供できます。
uWSGIを使用する
サイトをすばやく公開したい場合は、Apacheのmod_wsgiを使用します。 最高のパフォーマンスが必要な場合は、uWSGIを使用します。 それをどう発音するか知りたいのなら、それは「ウイスキー」です。
uWSGIは、Webサーバーの外部でアプリケーションを実行するサーバープロセスです。 Apacheの場合、次のパッケージをインストールする必要があります。
sudo apt-get install uwsgi uwsgi-plugin-python uwsgi-plugin-cgi
WebサーバーとしてApacheを使用している場合は、以下もインストールします。
sudo apt-get install libapache2-mod-uwsgi
これを行うと、ApacheまたはNginxを使用した仮想ホスト構成の場合と同様に、/etc/uwsgi/apps-available
と/etc/uwsgi/apps-enabled
の2つの新しい構成フォルダーがあることに気付くでしょう。 。
新しいアプリケーションを作成する場合は、/etc/uwsgi/apps-available
に構成ファイルを配置し、アプリ対応からそのアプリケーションへのシンボリックリンクを作成します。
newprojectというプロジェクトを作成し、それを/srv/newproject
に配置したとします。 Djangoプロジェクトで一般的であるように、settings.pyファイルが/srv/newproject/newproject/settings.py
にあります。
`/etc/uwsgi/apps-available/newproject.iniというファイルを作成し、次のように編集します。
[uwsgi]
touch-reload = /tmp/newproject
socket = 127.0.0.1:3031
workers = 2
chdir = /srv/newproject
env = DJANGO_SETTINGS_MODULE=newproject.settings
module = django.core.handlers.wsgi:WSGIHander()
次に、/tmp/newproject
という名前の空のファイルを作成します。
touch /tmp/newproject
ファイルをアプリ対応にリンクして、アプリケーションをアクティブ化する必要があります。
sudo ln -s /etc/uwsgi/apps-available/newproject.ini /etc/uwsgi/apps-enabled/newproject.ini
次に、uWSGIを再起動します。
sudo service uwsgi restart
「psax」の出力を確認すると、アプリケーションに関連して実行されているいくつかのプロセスが表示されます。
同じサーバーで複数のアプリケーションを実行している場合は、アプリケーションのソケットごとに異なるポートを使用する必要があることに注意してください。
また、アプリケーションを更新する場合は、ファイル/ tmp / newprojectに「タッチ」するだけで、uWSGIが自動的にリロードします。
いつでも/var/log/uwsgi/app/newproject.log
を表示して、情報メッセージとエラーを確認できます。
Apache構成
mod_wsgiを使用している場合は、今が真実の瞬間です。 mod_wsgiを無効にし、mod_uwsgiを有効にする必要があります。 これによりエラーが発生する可能性があるため、適切な構成が見つかるまでテストサーバーでこれを実行してください。
sudo a2dismod wsgi
sudo a2enmod uwsgi
次に、アプリの構成を更新します。 apache構成には、次のような行があります。
WSGIScriptAlias / /srv/newproject/newproject.wsgi
そして、次のように更新します。
SetHandler uwsgi-handler
uWSGISocket 127.0.0.1:3031
次に、Apache構成をリロードします。
sudo service apache2 reload
Nginx構成
サーバー構成では、既存のwsgiディレクティブを次のように置き換える必要があります。
uwsgi_pass 127.0.0.1:3031;
include uwsgi_params;
uwsgi_param UWSGI_SCHEME $scheme;
ものすごく単純!
uWSGIのメリット
uWSGIを使用することのすばらしい側面の1つは、アプリケーションがメモリ内で実行される回数を制限できることです。 通常、親プロセスは1つで、上記で共有した構成を使用した場合は、2つのワーカーがあります。 より多くのワーカーが必要な場合は、アプリケーションの.iniファイルを編集して、「ワーカー」の数を増やすことができます。 次に、「sudo service uwsgi reload」を使用してuWSGIをリロードし、変更を更新します。
プロセスの数を増やす前に、使用しているサーバーメモリの量を検討してください。 以前にmod_wsgiを実行していた場合は、メモリ使用量が減少するはずです。 uWSGIワーカーのサイズに注意してください。 アプリケーションを使用すると、サイズが大きくなる傾向があります。 メモリリークが原因ではなく、単にメモリ内のデータセットが小さいためです。
使用率の最大化
アプリケーションがウォームアップしてしばらく使用されたら、サーバーのメモリ使用率を確認します。
コマンドfree -m
を使用して、バッファ/キャッシュによって使用されているメモリの量とを確認します。 アプリケーション。 キー値は、「無料」列の一番上の数字です。 理想的には、ビジー状態のサーバーがほぼすべてのRAMを使用します。
RAMに余裕があり、サイトの速度が遅い場合は、十分に活用されていないものを調べてください。
多くの場合、プロセスがMySQLを待機していることがわかります。 データベースにより多くのシステムメモリを割り当てる必要がある可能性があります。 MySQLのデフォルト構成ではRAMの使用が非常に少ないため、これは非常に一般的です。
ディスクキャッシュに十分なRAMが割り当てられていない可能性もあります。 メモリを占有する不要なプロセスがありますか? デフォルトでは、Linuxはできるだけ多くのRAMをディスクキャッシュに割り当て、アプリケーションが必要とするときにそれを惜しみなくあきらめます。
メモリに制約のあるVPS環境では発生しない理想的な状況では、すべての読み取り専用アセットがキャッシュにバッファリングされます。 したがって、不要なバックグラウンドプロセスを取り除きます。
要約
Varnishを使用してサイト全体をキャッシュしたり、ビューごとにキャッシュ設定を制御したりするなど、キャッシュのいくつかの手法を学びました。
静的ファイルがDjangoではなくWebサーバーで提供されていることを確認しました。
uWSGIを使用して、ウェブサーバーからプロセス外で実行することにより、Djangoアプリのパフォーマンスを向上させる方法を学びました。 (ちなみに、優れたApache / uWSGIドキュメントを提供してくれたRicardoPascalに感謝します)
最後に、使用率を最大化するために、サーバーアプリケーションにできるだけ多くのメモリを割り当てる方法について学習しました。