見出し画像

Django アプリケーションを本番リリースする際の考慮事項とベストプラクティス

この記事では、本番環境対応のDjangoアプリケーションに必要な考慮事項について説明します。

関心の分離またはデカップリング

この原則は、システムの異なる側面を個別のコンポーネントまたはレイヤーに分離することで、コードベースをモジュール化し、保守性と柔軟性を維持することを目的としています。関心を分離することで、アプリケーションを異なる環境に適応しやすくし、設定変更を容易にし、全体的な保守性と再利用性を向上させることができます。

コードと設定の分離

アプリケーションのコードロジックから設定設定やパラメータを分離する必要があります。これにより、コードを再コンパイルまたは再デプロイすることなく設定を変更でき、異なる環境間でのコードの再利用を容易にし、確実にアプリケーションのセキュリティを向上させます。Djangoでは、多くの設定値(シークレットキー、デバッグ、許可されたホストなど)がsettings.pyファイルにあります。これらの値を.envという別のファイル(gitにコミットしない)に移動することができます。設定をコードから分離するのに役立つパッケージが多数あります。python-decouplepython-dotenvが人気のあるものです。

要件の分離

要件ファイルを分離することは、プロジェクトの依存関係を管理し、異なる環境間での一貫性を確保するための一般的な方法です。プロジェクトのルートフォルダにrequirementsディレクトリを作成し、異なる環境用に別々の要件ファイルを作成することができます。

  • 基本要件ファイル:requirements/base.txt このファイルには、環境に関係なくプロジェクトを実行するために必要な主要な依存関係を含める必要があります。通常、Django自体、データベースコネクタ、その他の必須ライブラリなどのパッケージが含まれます。

# base.txt
Django==3.2.10
psycopg2-binary==2.9.1
djangorestframework==3.12.4
python-decouple==3.4
  • 環境固有の要件ファイル:requirements/local.txt、requirements/development.txt、requirements/production.txt。プロジェクト固有の要件に応じてこれらのファイルを作成できます。これらのファイルはbase.txtファイルを継承し、環境固有の要件を追加します。

# local.txt、ローカル要件ファイルはテスト要件ファイルを拡張できます
-r tests.txt

django-debug-toolbar==3.2.1
django-silk==5.0.3
# production.txt
-r base.txt

gunicorn==20.1.0
sentry-sdk==1.1.0
  • テスト要件ファイル:requirements/tests.txt このファイルはbase.txtを継承し、アプリケーション内で使用されるテストライブラリを追加します。

# tests.txt
-r base.txt

black==21.6b0
coverage==5.5
factory-boy==3.2.0
flake8==3.9.2
isort==5.9.1
tox==3.23.1

設定の分離

設定を特定のファイルに分離することも有用です。要件に応じてこれらのファイルを作成できますが、理想的なケースではbase.py、local.py、tests.py、production.pyで十分です。ほとんどの設定はbase.pyにあります。他のファイルはbase.pyをインポートし、環境固有の設定を追加します。

# base.py
from pathlib import Path

import dj_database_url
from decouple import Csv, config

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = config("SECRET_KEY", default="django-insecure$simple.settings.local")

DEBUG = config("DEBUG", default=True, cast=bool)

ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="127.0.0.1,localhost", cast=Csv())

INSTALLED_APPS = [...]

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

ROOT_URLCONF = "simple.urls"

INTERNAL_IPS = ["127.0.0.1"]

WSGI_APPLICATION = "simple.wsgi.application"

MIDDLEWARE = [...]

TEMPLATES = [...]

DATABASES = {...}

AUTH_PASSWORD_VALIDATORS = [...]

STATIC_URL = "/static/"

MEDIA_URL = "/media/"
# local.py
from .base import *

INSTALLED_APPS += ["debug_toolbar"]

MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
# production.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

import simple
from .base import *

CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True

SECURE_HSTS_SECONDS = 60 * 60 * 24 * 7 * 52  # 1年
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_SSL_REDIRECT = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

SESSION_COOKIE_SECURE = True

sentry_sdk.init(
    dsn=config("SENTRY_DSN", default=""),
    environment=SIMPLE_ENVIRONMENT,
    release="simple@%s" % simple.__version__,
    integrations=[DjangoIntegration()],
)

国際化とローカライゼーション

Webアプリケーションにおける国際化とローカライゼーションの目的は、アプリケーションのコンテンツ、言語、フォーマットを多様な視聴者に適応させることです。Djangoはテキストの翻訳日付、時間、数値のフォーマット、およびタイムゾーンを完全にサポートしています。

データベース設定

データベース設定は、アプリケーションのパフォーマンス、スケーラビリティ、信頼性を確保する上で重要な役割を果たします。これは適切なデータベースバックエンドの選択から始まります。DjangoはPostgreSQL、MySQL、SQLite、Oracleをサポートしています。
適切なデータベースを選択した後、データベース接続を効率的に管理するために接続プールを設定する必要があります。接続プーリングは、各リクエストに対して新しい接続を確立するオーバーヘッドを削減し、パフォーマンスを向上させます。Djangoは接続年齢に直接影響を与えるCONN_MAX_AGE設定を提供しています。デフォルトでは、Djangoはデータベース接続を閉じません。ただし、データベース設定でCONN_MAX_AGEを正の整数値に設定すると、Djangoは指定された秒数後に自動的にデータベース接続を閉じて再開します。さらに、PostgreSQLのpgBouncerやMySQLのmysql.connector.poolingなどのツールを使用してデータベース接続を管理することもできます。

静的ファイルとメディアファイル

静的ファイルを提供するために、別の静的ファイルサーバーまたはコンテンツ配信ネットワーク(CDN)を使用します。これにより、Djangoアプリケーションサーバーから静的ファイルの提供を分離し、パフォーマンスとスケーラビリティを向上させます。

django.contrib.staticfilesがINSTALLED_APPSに含まれていることを確認してください。

同じサーバーから静的ファイルとメディアファイルを提供する

collectstaticコマンドを実行して、すべての静的ファイルをSTATIC_ROOTにコピーするか、静的ファイルが変更されたときに実行します。
設定でSTATIC_ROOTを定義し、静的ファイルが提供されるSTATIC_URLを設定します。また、アップロードされたメディアファイルが保存されるMEDIA_ROOT設定も定義します。

専用サーバーから静的ファイルとメディアファイルを提供する

静的ファイルの提供のみを行い、Djangoを実行しない別のサーバーを使用することもできます。このアプローチは、Djangoアプリケーションサーバーから静的ファイルの提供ワークロードを分離し、動的リクエストの処理に集中できるようにします。
専用サーバーにNginxやApacheの軽量版などのWebサーバーをインストールして設定します。Webサーバーをインストールしたら、静的ファイルを提供するように設定する必要があります。これには通常、静的ファイルが配置されているディレクトリを指定し、それらのファイルへのリクエストに応答するようにサーバーを設定することが含まれます。ローカル開発環境では、Djangoのcollectstatic管理コマンドを使用して、様々なDjangoアプリからすべての静的ファイルを収集し、単一のディレクトリに配置します。このコマンドは、各アプリから静的ファイルを収集し、Django設定ファイルのSTATIC_ROOT設定で指定された場所にコピーします。rsyncのようなツールを使用して、ローカルマシンから静的ファイルサーバーに静的ファイルを効率的に転送します。rsyncは、変更された静的ファイルの部分のみを転送するため、転送に必要な時間と帯域幅を削減できるため、一般的に使用されます。静的ファイルがサーバーに転送された後、変更を適用するためにWebサーバーを再起動します。

クラウドサービスまたはCDNを利用した静的ファイルの提供

Amazon S3、Google Cloud Storage、Microsoft Azure Blob Storageなどのクラウドストレージプロバイダーを選択してください。さらに、CloudFront、Cloudflare、AkamaiなどのCDNサービスも選択してください。これらのサービスは、静的ファイルの保存と配信のための信頼性が高くスケーラブルなインフラストラクチャを提供します。django-storagesのようなサードパーティパッケージを利用して、クラウドストレージプロバイダーと統合し、メディアファイルのアップロード、提供、削除をシームレスに処理することができます。
静的ファイルの圧縮と最小化を有効にして、ファイルサイズを縮小し、ページの読み込み時間を改善してください。django-compressorwhitenoiseなどのツールを使用して、このプロセスを自動化できます。デプロイメント前にフロントエンドアセット(CSS、JavaScriptなど)をバンドルおよび最適化するために、webpackやgulpなどのツールの使用を検討してください。
Django 4.2以降では、STORAGES設定を使用して静的ファイルのストレージバックエンドを定義できます。また、カスタムストレージクラスを作成することも可能です。

STORAGES = {
    # ...
    "staticfiles": {"BACKEND": "myproject.storage.S3Storage"}
}

静的チェックとスタイリングルール

適切な静的チェックの実施とスタイリングルールの遵守は、Djangoアプリケーションのコード品質、可読性、一貫性を維持するために重要です。flake8pylintなどの静的コード分析ツールを使用して、コード内の潜在的な問題、コーディングエラー、ベストプラクティスの違反を特定してください。継続的インテグレーション(CI)パイプラインに静的コード分析を組み込んで、コード品質チェックを自動的に強制してください。
blackautopep8などのツールを使用して、プロジェクトの一貫したコードフォーマットスタイルを定義してください。プリコミットフックやCI/CDパイプラインを通じてコードフォーマットルールを強制し、コードベース全体の一貫性を確保してください。リンティングとスタイリングの設定をバージョン管理に保存し、すべてのチームメンバーが同じルールにアクセスし、遵守できるようにしてください。

デバッグとロギング

効果的なデバッグとロギングは、本番環境のDjangoアプリケーションで問題を特定し解決するために不可欠です。本番設定ファイルでDEBUGをFalseに設定し、詳細なエラーメッセージがユーザーに表示されないようにしてください。これにより、機密情報が保護され、より安全な環境が提供されます。try-exceptブロックなどの適切なエラー処理メカニズムを実装して、例外を適切に処理し、アプリケーションのクラッシュを防止してください。
Djangoのロギング設定を構成して、デバッグとモニタリングに関連する情報を取得してください。ログメッセージの出力先、ログレベル、フォーマットを指定するために、ロガー、ハンドラ、フォーマッタを定義してください。ログメッセージの重要度を区別するために、異なるログレベル(例:DEBUG、INFO、WARNING、ERROR、CRITICAL)を使用することを検討してください。デバッグに役立つよう、リクエストデータ、ユーザー情報、セッション詳細などの関連する文脈情報をログメッセージに含めてください。
ELKスタック(Elasticsearch、Logstash、Kibana)、Splunk、または専用のログ管理サービスなどの集中型ログソリューションの使用を検討してください。これらのツールを使用すると、複数のソースからログを集約、検索、分析でき、デバッグとモニタリングが容易になります。パフォーマンスに影響を与える可能性のある過度のロギングは避けてください。情報の必要性とロギングの潜在的なオーバーヘッドのバランスを取るために、適切なレベルでログを記録してください。Prometheus、New Relic、Datadogなどのモニタリングシステムを実装して、アプリケーションのパフォーマンス、エラー率、ログパターンを追跡してください。
ログファイルのサイズを管理し、無限に増大するのを防ぐために、ログローテーションメカニズムを実装してください。

機密情報(パスワード、APIキーなど)がログに記録されたり、ログファイルに保存されたりしないようにしてください。

セキュリティに関する考慮事項

SECRET_KEY シークレットキーは大きなランダム値でなければならず、秘密に保たれなければなりません。異なる環境(開発、ステージング、本番など)ごとに一意のシークレットキーを生成してください。長期的な侵害のリスクを軽減するために、SECRET_KEYを定期的にローテーションすることをお勧めします。また、ローテーションのためにSECRET_KEY_FALLBACKSを使用することもできます。
DEBUG 本番環境でデバッグを有効にしてはいけません。DEBUGがTrueに設定されている場合、Djangoは詳細なエラーページと完全なトレースバックを提供し、プロジェクトのソースコード、設定設定、データベース構造、その他の実装の詳細に関する機密情報が潜在的に露出する可能性があります。
ALLOWED_HOSTS 本番設定ファイルでALLOWED_HOSTS設定を設定し、Djangoアプリケーションにアクセスできるホスト/ドメイン名のリストを指定してください。

DEBUG = Falseの場合、ALLOWED_HOSTSに適切な値がないとDjangoは全く動作しません。

HTTPS設定

サイト全体でHTTPSを有効にすることは、機密情報を保護するために重要です。HTTPSの強制に加えて、セキュリティを強化するためにいくつかの重要な設定を有効にする必要があります。

  • SESSION_COOKIE_SECURE = True

  • CSRF_COOKIE_SECURE = True

  • SECURE_SSL_REDIRECT = True

HSTS設定

HSTSは、ウェブサイトでHTTPS(HTTP Secure)経由の安全な接続を強制します。HSTSは、SSLストリッピングや中間者攻撃などの特定の種類の攻撃から保護するのに役立ちます。ウェブブラウザに対して、常にHTTPS接続を使用してウェブサイトと通信するよう指示します。
ウェブサイトがHSTSを有効にしている場合、HTTP応答の一部として "Strict-Transport-Security" という応答ヘッダーを送信します。このヘッダーには "max-age" ディレクティブが含まれており、ブラウザがウェブサイトにHTTPSでのみアクセスすることを記憶すべき期間(秒単位)を指定します。ユーザーのウェブブラウザがウェブサイトからHSTSヘッダーを受信すると、この情報をローカルに保存します。同じウェブサイトへの後続の訪問では、ユーザーが手動で "http://" の代わりに "https://" と入力した場合や、安全でないリンクをたどった場合でも、自動的にHTTPSを使用します。

  • SECURE_HSTS_SECONDS

  • SECURE_HSTS_PRELOAD

  • SECURE_HSTS_INCLUDE_SUBDOMAINS

python manage.py check --deploy の実行

manage.py check --deployコマンドは、Djangoが提供する便利なツールで、デプロイメント設定に特化したチェックを実行します。本番設定ファイルの潜在的な問題や誤設定を特定するのに役立ちます。正確な結果を得るために、実際の本番設定ファイルに対してこのコマンドを実行することを忘れないでください。


この記事は、2024年7月に弊社のエンジニア Binod Kafle が執筆した内容を日本語に翻訳したものです。
英語版はこちらをご覧ください。
https://articles.wesionary.team/preparing-your-django-application-for-production-key-considerations-and-best-practices-6125386df748


採用情報

私たちはプロダクト共創の仕組み化に取り組んでいます。プロダクト共創をリードするプロダクト・マネージャー、そして、私たちのビジョンを市場に届ける営業メンバーを募集しています!


開発パートナーをお探しの企業様へ

弊社は、グローバル開発のメリットを活かし、高い費用対効果と品質を両立しています。経験豊富で多様性のあるチームが、課題を正しく理解し、最適なシステムと優れた体験を実現します。業務システムの開発、新規事業の開発、業務効率化やDX化に関するお困りごと、ぜひ弊社にご相談ください。