DigitalOcean Managed DatabasesおよびSpacesを使用してスケーラブルなDjangoアプリをセットアップする方法

前書き

Djangoは、PythonアプリケーションまたはWebサイトをすばやく立ち上げるのに役立つ強力なWebフレームワークです。 これには、object-relational mapper、Python API、アプリケーション用のカスタマイズ可能な管理インターフェイスなど、いくつかの便利な機能が含まれています。 また、caching frameworkが含まれており、URL DispatcherTemplate systemを介してクリーンなアプリの設計を促進します。

すぐに使用できるDjangoには、テストおよびローカル開発用の最小限のWebサーバーが含まれていますが、実稼働ユースケース向けのより堅牢なサービスインフラストラクチャと組み合わせる必要があります。 Djangoは、静的ファイル要求とHTTPSリダイレクトを処理するためのNginx Webサーバーと、アプリを提供するためのGunicornWSGIサーバーとともに展開されることがよくあります。

このガイドでは、JavascriptやCSSスタイルシートなどの静的ファイルをDigitalOcean Spacesにオフロードし、オプションでContentDeliveryNetwork、またはこれらを格納するCDNを使用して配信することにより、このセットアップを強化します。転送時間を短縮するためにエンドユーザーに近いファイル。 また、データストアとしてDigitalOceanManaged PostgreSQL databaseを使用して、データレイヤーを簡素化し、スケーラブルなPostgreSQLデータベースを手動で構成する必要をなくします。

前提条件

このガイドを始める前に、次のものを用意しておく必要があります。

  • 基本的なファイアウォールとsudo特権が構成された非rootユーザーを持つ新しいUbuntu18.04サーバーインスタンス。 Initial Server Setup with Ubuntu 18.04を実行することで、これを設定する方法を学ぶことができます。

  • DigitalOceanが管理するPostgreSQLクラスター。 クラスターの作成方法については、DigitalOceanManaged Databases product documentationを参照してください。

  • Djangoプロジェクトの静的ファイルとこのスペースのアクセスキーのセットを保存するDigitalOceanスペース。 スペースの作成方法については、How to Create Spacesの製品ドキュメントを参照してください。スペースのアクセスキーの作成方法については、Sharing Access to Spaces with Access Keysを参照してください。

  • Nginxは、選択したドメイン名で動作するようにサーバーにインストール、保護、および構成されます。 Aレコードの設定と、Let’s Encryptを使用したNginxインストールの保護の詳細については、How To Secure Nginx with Let’s Encrypt on Ubuntu 18.04を参照してください。

[[step-1 -—- installing-packages-from-the-ubuntu-repositories]] ==ステップ1—Ubuntuリポジトリからのパッケージのインストール

まず、必要なすべてのアイテムをUbuntuリポジトリからダウンロードしてインストールします。 少し後で、Pythonパッケージマネージャーpipを使用して追加のコンポーネントをインストールします。

最初にローカルのaptパッケージインデックスを更新してから、パッケージをダウンロードしてインストールする必要があります。

このガイドでは、Python 3でDjangoを使用します。 必要なライブラリをインストールするには、サーバーにログインして次を入力します。

sudo apt update
sudo apt install python3-pip python3-dev libpq-dev curl postgresql-client

これにより、pip、Gunicornの構築に必要なPython開発ファイル、Pyscopg PostgreSQL Pythonアダプターの構築に必要なlibpqヘッダーファイル、およびPostgreSQLコマンドラインクライアントがインストールされます。

パッケージのダウンロードとインストールを開始するように求められたら、Yを押してからENTERを押します。

次に、Djangoアプリで動作するようにデータベースを構成します。

[[step-2 -—- creating-the-postgresql-database-and-user]] ==ステップ2—PostgreSQLデータベースとユーザーの作成

次に、Djangoアプリケーション用のデータベースとデータベースユーザーを作成します。

まず、Cloud Control PanelからDatabasesに移動し、データベースをクリックして、クラスターのConnection Parametersを取得します。 クラスタのいくつかのパラメータを含むConnection Detailsボックスが表示されます。 これらに注意してください。

コマンドラインに戻り、次の認証情報とインストールしたばかりのpsqlPostgreSQLクライアントを使用してクラスターにログインします。

psql -U username -h host -p port -d database -set=sslmode=require

プロンプトが表示されたら、Postgresユーザー名の横に表示されているパスワードを入力し、ENTERを押します。

データベースを管理できるPostgreSQLプロンプトが表示されます。

まず、pollsという名前のプロジェクトのデータベースを作成します。

CREATE DATABASE polls;

[.note]#Note:すべてのPostgresステートメントはセミコロンで終了する必要があるため、問題が発生している場合は、コマンドがセミコロンで終了していることを確認してください。

これで、pollsデータベースに切り替えることができます。

\c polls;

次に、プロジェクトのデータベースユーザーを作成します。 安全なパスワードを選択してください:

CREATE USER myprojectuser WITH PASSWORD 'password';

ここで、作成したユーザーの接続パラメーターのいくつかを変更します。 これにより、データベース操作が高速化されるため、接続が確立されるたびに正しい値を照会および設定する必要がなくなります。

Djangoが期待するデフォルトのエンコーディングをUTF-8に設定しています。 また、デフォルトのトランザクション分離スキームを「コミット済み読み取り」に設定しています。これは、コミットされていないトランザクションからの読み取りをブロックします。 最後に、タイムゾーンを設定しています。 デフォルトでは、DjangoプロジェクトはUTCを使用するように設定されます。 これらはすべてthe Django project itselfからの推奨事項です。

PostgreSQLプロンプトで次のコマンドを入力します。

ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';

これで、新しいユーザーに新しいデータベースを管理するアクセス権を与えることができます。

GRANT ALL PRIVILEGES ON DATABASE polls TO myprojectuser;

終了したら、次のように入力してPostgreSQLプロンプトを終了します。

\q

これで、Djangoアプリはこのデータベースに接続して管理する準備ができました。

次のステップでは、virtualenvをインストールし、Djangoプロジェクト用のPython仮想環境を作成します。

[[step-3 -—- creating-a-python-virtual-environment-for-your-project]] ==ステップ3—プロジェクト用のPython仮想環境を作成する

アプリケーションと連携するようにデータベースを設定したので、このプロジェクトの依存関係をシステムのグローバルPythonインストールから分離するPython仮想環境を作成します。

これを行うには、最初にvirtualenvコマンドにアクセスする必要があります。 これはpipでインストールできます。

pipをアップグレードし、次のように入力してパッケージをインストールします。

sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv

virtualenvをインストールすると、Python仮想環境を格納するディレクトリを作成し、Djangopollsアプリで使用するディレクトリを作成できます。

envsというディレクトリを作成し、そのディレクトリに移動します。

mkdir envs
cd envs

このディレクトリ内に、次のように入力して、pollsというPython仮想環境を作成します。

virtualenv polls

これにより、envsディレクトリ内にpollsというディレクトリが作成されます。 内部では、Pythonのローカルバージョンとpipのローカルバージョンがインストールされます。 これを使用して、プロジェクトの分離されたPython環境をインストールおよび構成できます。

プロジェクトのPython要件をインストールする前に、仮想環境をアクティブ化する必要があります。 次のように入力して、それを行うことができます。

source polls/bin/activate

プロンプトが変わり、Python仮想環境内で操作していることを示す必要があります。 (polls)user@host:~/envs$のようになります。

仮想環境をアクティブにして、Django、Gunicorn、およびpipのローカルインスタンスを使用したpsycopg2PostgreSQLアダプターをインストールします。

[.note]#Note:仮想環境がアクティブ化されている場合(プロンプトの前に(polls)がある場合)、Pythonを使用している場合でも、pip3ではなくpipを使用します3.3。 ツールの仮想環境のコピーには、Pythonのバージョンに関係なく、常にpipという名前が付けられます。

pip install django gunicorn psycopg2-binary

これで、Djangopollsアプリを実行するために必要なすべてのソフトウェアが揃いました。 次のステップでは、Djangoプロジェクトを作成し、このアプリをインストールします。

[[step-4 -—- creating-the-polls-django-application]] ==ステップ4— PollsDjangoアプリケーションの作成

これで、サンプルアプリケーションをセットアップできます。 このチュートリアルでは、Django documentationのPollsデモアプリケーションを使用します。 ユーザーが投票を表示して投票できる公開サイトと、管理者が投票を変更、作成、削除できる管理コントロールパネルで構成されています。

このガイドでは、チュートリアルの手順をスキップし、DigitalOceanコミュニティdjango-polls repoから最終的なアプリケーションのクローンを作成します。

手順を手動で完了したい場合は、ホームディレクトリにdjango-pollsというディレクトリを作成し、そのディレクトリに移動します。

cd
mkdir django-polls
cd django-polls

そこから、公式のDjangoドキュメントのWriting your first Django appチュートリアルに従うことができます。 完了したら、Step 5にスキップします。

完成したアプリのクローンを作成するだけの場合は、ホームディレクトリに移動し、gitを使用してdjango-polls repoのクローンを作成します。

cd
git clone https://github.com/do-community/django-polls.git

cdをその中に入れ、ディレクトリの内容を一覧表示します。

cd django-polls
ls

次のオブジェクトが表示されるはずです。

OutputLICENSE  README.md  manage.py  mysite  polls  templates

manage.pyは、アプリの操作に使用されるメインのコマンドラインユーティリティです。 pollsにはpollsアプリコードが含まれ、mysiteにはプロジェクトスコープコードと設定が含まれます。 templatesには、管理インターフェイス用のカスタムテンプレートファイルが含まれています。 プロジェクトの構造とファイルの詳細については、Djangoの公式ドキュメントのCreating a Projectを参照してください。

アプリを実行する前に、デフォルト設定を調整してデータベースに接続する必要があります。

[[step-5 -—- adjusting-the-app-settings]] ==ステップ5—アプリ設定の調整

このステップでは、Djangoプロジェクトのデフォルト設定を変更して、セキュリティを強化し、Djangoをデータベースに接続し、静的ファイルをローカルディレクトリに収集します。

テキストエディターで設定ファイルを開くことから始めます。

nano ~/django-polls/mysite/settings.py

ALLOWED_HOSTSディレクティブを見つけることから始めます。 これは、Djangoインスタンスへの接続に使用するアドレスまたはドメイン名のリストを定義します。 このリストにないHostヘッダーを持つ着信要求は、例外を発生させます。 Djangoでは、certain class of security vulnerabilityを防ぐためにこれを設定する必要があります。

角括弧内に、Djangoサーバーに関連付けられているIPアドレスまたはドメイン名をリストします。 各項目は、エントリをカンマで区切って引用符で囲んでください。 ローカルのNginxインスタンスを介して接続をプロキシするため、リストにはlocalhostも含まれます。 ドメイン全体およびサブドメインのリクエストを含める場合は、エントリの先頭にピリオドを追加します。

以下のスニペットには、これらのエントリがどのように見えるかを示すコメントアウトされた例がいくつかあります。

~/django-polls/mysite/settings.py

. . .

# The simplest case: just add the domain name(s) and IP addresses of your Django server
# ALLOWED_HOSTS = [ 'example.com', '203.0.113.5']
# To respond to 'example.com' and any subdomains, start the domain with a dot
# ALLOWED_HOSTS = ['.example.com', '203.0.113.5']
ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', . . ., 'localhost']

. . .

次に、データベースアクセスを構成するファイルのセクションを見つけます。 DATABASESで始まります。 ファイル内の構成は、SQLiteデータベース用です。 プロジェクト用にPostgreSQLデータベースをすでに作成しているため、これらの設定を調整する必要があります。

デフォルトのSQLiteエンジンの代わりに、pipでインストールしたpsycopg2データベースアダプターを使用するようにDjangoに指示します。 また、Step 2で参照されているConnection Parametersを再利用します。 この情報は、DigitalOceanCloud Control Panelの管理対象データベースセクションからいつでも見つけることができます。

データベース設定(データベース名(polls)、データベースユーザー名、データベースユーザーのパスワード、およびデータベースhostport)でファイルを更新します。 データベース固有の値を独自の情報に置き換えてください:

~/django-polls/mysite/settings.py

. . .

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'polls',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'managed_db_host',
        'PORT': 'managed_db_port',
    }
}

. . .

次に、ファイルの一番下まで移動し、静的ファイルを配置する場所を示す設定を追加します。 これは、Nginxがこれらのアイテムのリクエストを処理できるようにするために必要です。 次の行は、ベースプロジェクトディレクトリのstaticというディレクトリにそれらを配置するようにDjangoに指示しています。

~/django-polls/mysite/settings.py

. . .

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

完了したら、ファイルを保存して閉じます。

この時点で、Djangoプロジェクトのデータベース、セキュリティ、および静的ファイルの設定を構成しました。 最初からpollsチュートリアルに従い、GitHubリポジトリのクローンを作成しなかった場合は、Step 6に進むことができます。 GitHubリポジトリを複製した場合、1つの追加手順が残ります。

Django設定ファイルには、さまざまなDjangoオブジェクトのハッシュを作成するために使用されるSECRET_KEY変数が含まれています。 一意の予測不可能な値に設定することが重要です。 SECRET_KEY変数はGitHubリポジトリから削除されているため、django Pythonパッケージに組み込まれているget_random_secret_key()という関数を使用して新しい変数を作成します。 コマンドラインから、Pythonインタープリターを開きます。

python

次の出力とプロンプトが表示されます。

OutputPython 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Djangoパッケージからget_random_secret_key関数をインポートし、関数を呼び出します。

from django.core.management.utils import get_random_secret_key
get_random_secret_key()

結果のキーをクリップボードにコピーします。

CTRL+Dを押して、Pythonインタープリターを終了します。

次に、テキストエディターで設定ファイルをもう一度開きます。

nano ~/django-polls/mysite/settings.py

SECRET_KEY変数を見つけて、生成したキーを貼り付けます。

~/django-polls/mysite/settings.py

. . .

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'your_secret_key_here'

. . .

ファイルを保存して閉じます。

Django開発サーバーを使用してアプリをローカルでテストし、すべてが正しく構成されていることを確認します。

[[step-6 -—- testing-the-app]] ==ステップ6—アプリのテスト

Django開発サーバーを実行する前に、manage.pyユーティリティを使用してデータベーススキーマを作成し、静的ファイルをSTATIC_ROOTディレクトリに収集する必要があります。

プロジェクトのベースディレクトリに移動し、makemigrationsおよびmigrateコマンドを使用してPostgreSQLデータベースに初期データベーススキーマを作成します。

cd django-polls
./manage.py makemigrations
./manage.py migrate

makemigrationsは、Djangoモデルに加えられた変更に基づいて、移行またはデータベーススキーマの変更を作成します。 migrateは、これらの移行をデータベーススキーマに適用します。 Djangoでの移行の詳細については、Djangoの公式ドキュメントのMigrationsを参照してください。

次を入力して、プロジェクトの管理ユーザーを作成します。

./manage.py createsuperuser

ユーザー名を選択し、メールアドレスを入力し、パスワードを選択して確認する必要があります。

次のように入力して、すべての静的コンテンツを構成したディレクトリの場所に収集できます。

./manage.py collectstatic

静的ファイルは、プロジェクトディレクトリ内のstaticというディレクトリに配置されます。

サーバーの初期セットアップガイドに従った場合は、サーバーを保護するUFWファイアウォールが必要です。 開発用サーバーをテストするには、使用するポートへのアクセスを許可する必要があります。

次のように入力して、ポート8000の例外を作成します。

sudo ufw allow 8000

Django開発サーバーを使用したアプリのテスト

最後に、次のコマンドでDjango開発サーバーを起動して、プロジェクトをテストできます。

./manage.py runserver 0.0.0.0:8000

Webブラウザーで、サーバーのドメイン名またはIPアドレスにアクセスし、その後に:8000pollsパスを続けます。

http://server_domain_or_IP:8000/polls

Pollsアプリのインターフェースが表示されます:

Polls App Interface

管理インターフェースを確認するには、サーバーのドメイン名またはIPアドレスにアクセスし、その後に:8000と管理インターフェースのパスを続けます。

http://server_domain_or_IP:8000/admin

Pollsアプリの管理者認証ウィンドウが表示されます。

Polls Admin Auth Page

createsuperuserコマンドで作成した管理ユーザー名とパスワードを入力します。

認証後、Pollsアプリの管理インターフェースにアクセスできます。

Polls Admin Main Interface

探索が終了したら、ターミナルウィンドウでCTRL-Cを押して、開発サーバーをシャットダウンします。

Gunicornを使用したアプリのテスト

静的ファイルをオフロードする前に行う最後の作業は、Gunicornをテストして、アプリケーションにサービスを提供できることを確認することです。 これを行うには、プロジェクトディレクトリに入り、gunicornを使用してプロジェクトのWSGIモジュールをロードします。

gunicorn --bind 0.0.0.0:8000 mysite.wsgi

これにより、Django開発サーバーが実行されていたのと同じインターフェースでGunicornが起動します。 戻ってアプリをもう一度テストできます。

[.note]#Note: Gunicornはこれに関与する静的CSSコンテンツを見つける方法を知らないため、管理インターフェイスにはスタイルが適用されません。

アプリケーションへのエントリポイントであるDjangoのwsgi.pyファイルへの相対ディレクトリパスを指定して、Gunicornにモジュールを渡しました。 このファイルは、アプリケーションと通信するapplicationと呼ばれる関数を定義します。 WSGI仕様の詳細については、hereをクリックしてください。

テストが終了したら、ターミナルウィンドウでCTRL-Cを押して、Gunicornを停止します。

アプリケーションの静的ファイルをDigitalOcean Spacesにオフロードします。

[[step-7 -—- offloading-static-files-to-digitalocean-spaces]] ==ステップ7—静的ファイルをDigitalOceanSpacesにオフロードする

この時点で、GunicornはDjangoアプリケーションを提供できますが、静的ファイルは提供できません。 通常、これらのファイルを提供するようにNginxを構成しますが、このチュートリアルでは、django-storagesプラグインを使用してそれらをDigitalOceanSpacesにオフロードします。 これにより、静的コンテンツを集中化し、サーバーリソースを解放することにより、Djangoを簡単に拡張できます。 さらに、DigitalOcean Spaces CDNを使用してこの静的コンテンツを配信できます。

Django静的ファイルをオブジェクトストレージにオフロードするための完全なガイドについては、How to Set Up Object Storage with Djangoを参照してください。

django-storagesのインストールと構成

まず、django-storagesPythonパッケージをインストールします。 django-storagesパッケージは、boto3ライブラリを使用してファイルをS3互換のオブジェクトストレージサービスにアップロードするS3Boto3StorageストレージバックエンドをDjangoに提供します。

開始するには、pipを使用して `django-storages`とboto3Pythonパッケージをインストールします。

pip install django-storages boto3

次に、アプリのDjango設定ファイルをもう一度開きます。

nano ~/django-polls/mysite/settings.py

ファイルのINSTALLED_APPSセクションに移動し、インストールされているアプリのリストにstoragesを追加します。

~/django-polls/mysite/settings.py

. . .

INSTALLED_APPS = [
    . . .
    'django.contrib.staticfiles',
    'storages',
]

. . .
以前に変更したSTATIC_URLまで、ファイルをさらに下にスクロールします。 これらの値を上書きし、新しいS3Boto3Storageバックエンドパラメータを追加します。 前に入力したコードを削除し、次のブロックを追加します。これらのブロックには、スペースのアクセスおよび位置情報が含まれます。 ここで強調表示されている値を独自の情報に置き換えることを忘れないでください

~/django-polls/mysite/settings.py

. . .

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

AWS_ACCESS_KEY_ID = 'your_spaces_access_key'
AWS_SECRET_ACCESS_KEY = 'your_spaces_secret_key'

AWS_STORAGE_BUCKET_NAME = 'your_space_name'
AWS_S3_ENDPOINT_URL = 'spaces_endpoint_URL'
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = 'public-read'

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

STATIC_URL = '{}/{}/'.format(AWS_S3_ENDPOINT_URL, AWS_LOCATION)
STATIC_ROOT = 'static/'

次の構成アイテムを定義します。

  • AWS_ACCESS_KEY_ID:チュートリアルの前提条件で作成したスペースのアクセスキーID。 アクセスキーのセットを作成しなかった場合は、Sharing Access to Spaces with Access Keysを参照してください。

  • AWS_SECRET_ACCESS_KEY:DigitalOceanSpaceの秘密鍵。

  • AWS_STORAGE_BUCKET_NAME:DigitalOceanスペース名。

  • AWS_S3_ENDPOINT_URL:オブジェクトストレージサービスへのアクセスに使用されるエンドポイントURL。 DigitalOceanの場合、これはスペース領域に応じてhttps://nyc3.digitaloceanspaces.comのようになります。

  • AWS_S3_OBJECT_PARAMETERS静的ファイルにキャッシュ制御ヘッダーを設定します。

  • AWS_LOCATION:すべての静的ファイルが配置されるオブジェクトストレージバケット内のディレクトリを定義します。

  • AWS_DEFAULT_ACL:静的ファイルのアクセス制御リスト(ACL)を定義します。 public-readに設定すると、エンドユーザーがファイルにパブリックにアクセスできるようになります。

  • STATICFILES_STORAGE:Djangoが静的ファイルをオフロードするために使用するストレージバックエンドを設定します。 このバックエンドは、DigitalOcean Spacesを含むS3互換のバックエンドで動作するはずです。

  • STATIC_URL:静的ファイルのURLを生成するときにDjangoが使用するベースURLを指定します。 ここでは、エンドポイントURLと静的ファイルのサブディレクトリを組み合わせて、静的ファイルのベースURLを作成します。

  • STATIC_ROOT:静的ファイルをオブジェクトストレージにコピーする前にローカルで収集する場所を指定します。

編集が完了したら、ファイルを保存して閉じます。

今後、collectstaticを実行すると、Djangoはアプリの静的ファイルをSpaceにアップロードします。 Djangoを起動すると、このスペースからCSSやJavascriptなどの静的アセットの提供が開始されます。

次のセクションでは、このスペースのCDNを有効にし、オプションでスペースCDNのカスタムサブドメインを構成します。 これにより、エッジサーバーの地理的に分散したネットワークでDjangoプロジェクトの静的ファイルをキャッチすることで、静的ファイルの配信が高速化されます。 CDNの詳細については、Using a CDN to Speed Up Static Content Deliveryを参照してください。 Spaces CDNを有効にしたくない場合は、Configuring CORS Headersまでスキップしてください。

CDNの有効化(オプション)

DigitalOcean Spaces CDNを介した静的ファイル配信を有効にするには、DigitalOcean SpaceのCDNを有効にすることから始めます。 これを行う方法については、DigitalOcean製品ドキュメントのHow to Enable the Spaces CDNを参照してください。

Spaces CDNでcustom domainを使用する場合は、How to Customize the Spaces CDN Endpoint with a Subdomainを使用して、サブドメインのCNAMEレコードと適切なSSL証明書を作成します。

Spaces CDNでカスタムドメインを使用することを強くお勧めします。 これにより、オフロードされたアセットURLをDjangoサイトのURLに近づけることにより、サイトの検索エンジン最適化(SEO)が大幅に改善されます。 Spaces CDNでカスタムドメインを使用するには、まずDigitalOceanアカウントにドメインを追加する必要があります。 これを行う方法については、How to Add Domainsを参照してください。

スペースのCDNを有効にし、オプションでそのカスタムサブドメインを作成したら、Cloud Control Panelを使用してスペースに移動します。 スペース名の下に新しいEndpointsリンクが表示されます。

List of Space Endpoints

これらのエンドポイントには、スペース名が含まれている必要があります。 Spaces CDNのカスタムサブドメインを作成した場合、このリストにはSubdomainという追加のエンドポイントが含まれます。

Edgeエンドポイントは、SpacesオブジェクトのリクエストをCDN経由でルーティングし、可能な限りエッジキャッシュからそれらを提供します。 このEdgeプラグインを構成するために使用するので、このEdgeエンドポイントを書き留めてください。 Spaces CDNのサブドメインを作成した場合、SubdomainエンドポイントはこのEdgeエンドポイントのエイリアスです。

次に、アプリのDjango設定ファイルをもう一度編集します。

nano ~/django-polls/mysite/settings.py

最近変更した[静的ファイル]セクションに移動します。 AWS_S3_CUSTOM_DOMAINパラメータを追加してdjango-storagesプラグインCDNエンドポイントを設定し、STATIC_URLパラメータを更新してこの新しいCDNエンドポイントを使用します。

~/django-polls/mysite/settings.py

. . .

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

# Moving static assets to DigitalOcean Spaces as per:
# https://www.digitalocean.com/community/tutorials/how-to-set-up-object-storage-with-django
AWS_ACCESS_KEY_ID = 'your_spaces_access_key'
AWS_SECRET_ACCESS_KEY = 'your_spaces_secret_key'

AWS_STORAGE_BUCKET_NAME = 'your_space_name'
AWS_S3_ENDPOINT_URL = 'spaces_endpoint_URL'
AWS_S3_CUSTOM_DOMAIN = 'spaces_edge_endpoint_URL'
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = 'public-read'

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

STATIC_URL = '{}/{}/'.format(AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATIC_ROOT = 'static/'

ここで、spaces_edge_endpoint_URLを、書き留めたばかりのEdgeエンドポイントに置き換え、https://プレフィックスを切り捨てます。 たとえば、エッジエンドポイントURLがhttps://example.sfo2.cdn.digitaloceanspaces.comの場合、AWS_S3_CUSTOM_DOMAINexample.sfo2.cdn.digitaloceanspaces.comに設定する必要があります。

カスタムサブドメインを作成した場合は、spaces_edge_endpoint_URLをカスタムサブドメインエンドポイントに置き換え、https://プレフィックスを切り捨てます。 たとえば、サブドメインのエンドポイントURLがhttps://assets.example.comの場合、AWS_S3_CUSTOM_DOMAINassets.example.comに設定する必要があります。

完了したら、ファイルを保存して閉じます。

Djangoを起動すると、DigitalOcean SpaceのCDNを使用して静的コンテンツが提供されるようになります。

これがすべて正しく機能していることをテストする前に、SpacesファイルのCross-Origin Resource Sharing (CORS)ヘッダーを構成する必要があります。そうしないと、特定の静的アセットへのアクセスがWebブラウザーによって拒否される場合があります。 Djangoで使用されているのと同じドメインに対してSpacesCDNでカスタムサブドメインを使用している場合は、Testing Spaces Static File Deliveryまでスキップできます。

CORSヘッダーの構成

CORSヘッダーは、あるドメインで実行されているアプリケーションが別のドメインにあるスクリプトまたはリソースにアクセスできることをWebブラウザーに伝えます。 この場合、Djangoサーバーのドメインのクロスオリジンリソース共有を許可する必要があります。これにより、スペース内の静的ファイルのリクエストがWebブラウザーによって拒否されません。

[.note]#Note:この手順は、スペースCDNでカスタムサブドメインを使用しているnotの場合にのみ必要です。

まず、クラウドコントロールパネルを使用して、スペースのSettingsページに移動します。

Screenshot of the Settings tab

CORS Configurationsセクションで、Addをクリックします。

CORS advanced settings

ここで、Originの下に、ワイルドカードの起点*を入力します

[.warning]#Warning:アプリを本番環境にデプロイするときは、必ずこの値を正確なオリジンドメイン(http://またはhttps://プロトコルを含む)に変更してください。 ワイルドカードオリジンとしてこれを残すことは安全ではありません。オリジンをhttp://example.com:8000に設定する(非標準ポートを使用する)ことは現在サポートされていないため、ここではテスト目的でのみこれを行います。

Allowed Methodsの下で、GETを選択します。

Add Headerをクリックし、表示されるテキストボックスにAccess-Control-Allow-Originと入力します。

作成したヘッダーが10分ごとに期限切れになるように、Access Control Max Age600に設定します。

Save Optionsをクリックします。

今後、スペース内のオブジェクトには適切なAccess-Control-Allow-Origin応答ヘッダーが含まれるようになり、最新の安全なWebブラウザーがドメイン間でこれらのファイルをフェッチできるようになります。

スペースの静的ファイル配信のテスト

DjangoがDigitalOcean Spaceから静的ファイルを正しく提供していることをテストします。

Djangoアプリディレクトリに移動します。

cd ~/django-polls

ここから、collectstaticを実行して、静的ファイルを収集し、DigitalOceanSpaceにアップロードします。

python manage.py collectstatic

次のような出力が表示されるはずです。

OutputYou have requested to collect static files at the destination
location as specified in your settings.

This will overwrite existing files!
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel:

yesと入力し、ENTERを押して確認します。

次のような出力が表示されます。

Output121 static files copied.

これにより、Djangoがpollsアプリの静的ファイルをSpaceに正常にアップロードしたことが確認されます。 Cloud Control Panelを使用してスペースに移動し、staticディレクトリ内のファイルを検査できます。

次に、Djangoが適切なURLを書き換えていることを確認します。

Gunicornサーバーを起動します。

gunicorn --bind 0.0.0.0:8000 mysite.wsgi

Webブラウザーで、サーバーのドメイン名またはIPアドレスにアクセスし、その後に:8000/adminを続けます。

http://server_domain_or_IP:8000/admin

Pollsアプリの管理者認証ウィンドウが表示されるはずです。今回は正しいスタイルが設定されています。

次に、ブラウザの開発者ツールを使用してページの内容を調べ、ソースファイルの保存場所を明らかにします。

Google Chromeを使用してこれを行うには、ページを右クリックして、Inspectを選択します。

次のウィンドウが表示されます。

Chrome Dev Tools Window

ここから、ツールバーのSourcesをクリックします。 左側のペインのソースファイルのリストで、Djangoサーバーのドメインの下に/admin/loginが表示され、SpaceのCDNエンドポイントの下にstatic/adminが表示されます。 static/admin内に、cssディレクトリとfontsディレクトリの両方が表示されます。

これにより、CSSスタイルシートとフォントがSpaceのCDNから正しく提供されていることが確認されます。

テストが終了したら、ターミナルウィンドウでCTRL-Cを押して、Gunicornを停止します。

deactivateと入力すると、アクティブなPython仮想環境を無効にできます。

deactivate

プロンプトは通常に戻ります。

この時点で、Djangoサーバーから静的ファイルを正常にオフロードし、オブジェクトストレージから静的ファイルを提供しています。 これで、Gunicornをシステムサービスとして自動的に開始するように構成できます。

[[step-8 -—- creating-systemd-socket-and-service-files-for-gunicorn]] ==ステップ8—gunicorn用のsystemdソケットおよびサービスファイルの作成

Step 6で、GunicornがDjangoアプリケーションと対話できることをテストしましたが、アプリケーションサーバーを起動および停止するためのより堅牢な方法を実装する必要があります。 これを実現するために、systemdサービスとソケットファイルを作成します。

Gunicornソケットは起動時に作成され、接続をリッスンします。 接続が発生すると、systemdはGunicornプロセスを自動的に開始して接続を処理します。

sudo権限を持つGunicornのsystemdソケットファイルを作成して開くことから始めます。

sudo nano /etc/systemd/system/gunicorn.socket

内部では、ソケットを説明するための[Unit]セクション、ソケットの場所を定義するための[Socket]セクション、およびソケットが適切なタイミングで作成されることを確認するための[Install]セクションを作成します。 ファイルに次のコードを追加します。

/etc/systemd/system/gunicorn.socket

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

完了したら、ファイルを保存して閉じます。

次に、テキストエディタでsudo権限を持つGunicornのsystemdサービスファイルを作成して開きます。 サービスファイル名は、拡張子を除き、ソケットファイル名と一致する必要があります。

sudo nano /etc/systemd/system/gunicorn.service

メタデータと依存関係を指定する[Unit]セクションから始めます。 ここにサービスの説明を入力し、ネットワーキングターゲットに到達した後にのみ開始するようにinitシステムに指示します。 私たちのサービスはソケットファイルのソケットに依存しているため、その関係を示すためにRequiresディレクティブを含める必要があります。

/etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

次に、[Service]セクションを開きます。 実行するために処理するユーザーとグループを指定します。 関連するすべてのファイルを所有しているため、通常のユーザーアカウントにプロセスの所有権を付与します。 NginxがGunicornと簡単に通信できるように、www-dataグループにグループの所有権を付与します。

次に、作業ディレクトリをマップし、サービスを開始するために使用するコマンドを指定します。 この場合、仮想環境内にインストールされているGunicorn実行可能ファイルへのフルパスを指定する必要があります。 プロセスがNginxと通信できるように、/runディレクトリ内に作成したUnixソケットにプロセスをバインドします。 journaldプロセスがGunicornログを収集できるように、すべてのデータを標準出力に記録します。 ここで、ワーカープロセスの数など、オプションのGunicorn調整を指定することもできます。 ここでは、3つのワーカープロセスでGunicornを実行します。

次のサービスセクションをファイルに追加します。 ここにリストされているユーザー名を自分のユーザー名に置き換えてください:

/etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/django-polls
ExecStart=/home/sammy/envs/polls/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          mysite.wsgi:application

最後に、[Install]セクションを追加します。 これにより、起動時に起動できるようにした場合、このサービスをリンクする対象がsystemdに指示されます。 通常のマルチユーザーシステムが稼働しているときにこのサービスを開始する必要があります。

/etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/django-polls
ExecStart=/home/sammy/envs/polls/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          mysite.wsgi:application

[Install]
WantedBy=multi-user.target

これで、systemdサービスファイルが完成しました。 今すぐ保存して閉じます。

これで、Gunicornソケットを開始して有効にすることができます。 これにより、現在および起動時に/run/gunicorn.sockにソケットファイルが作成されます。 そのソケットに接続が確立されると、systemdは自動的にgunicorn.serviceを開始してそれを処理します。

sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

ソケットファイルを確認することで、操作が成功したことを確認できます。

Gunicornソケットファイルの確認

プロセスのステータスをチェックして、正常に開始したかどうかを確認します。

sudo systemctl status gunicorn.socket

次のような出力が表示されるはずです。

OutputFailed to dump process list, ignoring: No such file or directory
● gunicorn.socket - gunicorn socket
   Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2019-03-05 19:19:16 UTC; 1h 22min ago
   Listen: /run/gunicorn.sock (Stream)
   CGroup: /system.slice/gunicorn.socket

Mar 05 19:19:16 django systemd[1]: Listening on gunicorn socket.

次に、/runディレクトリ内にgunicorn.sockファイルが存在するかどうかを確認します。

file /run/gunicorn.sock
Output/run/gunicorn.sock: socket

systemctl statusコマンドがエラーの発生を示した場合、またはディレクトリにgunicorn.sockファイルが見つからない場合は、Gunicornソケットが正しく作成されなかったことを示しています。 次を入力して、Gunicornソケットのログを確認します。

sudo journalctl -u gunicorn.socket

続行する前に、/etc/systemd/system/gunicorn.socketファイルをもう一度確認して、問題を修正してください。

ソケットアクティベーションのテスト

現在、gunicorn.socketユニットのみを起動した場合、ソケットはまだ接続を受信して​​いないため、gunicorn.serviceはアクティブになりません。 これを確認するには、次のように入力します。

sudo systemctl status gunicorn
Output● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

ソケットのアクティブ化メカニズムをテストするには、次のように入力して、curlを介してソケットに接続を送信します。

curl --unix-socket /run/gunicorn.sock localhost

端末にアプリケーションからのHTML出力が表示されます。 これは、Gunicornが開始され、Djangoアプリケーションを提供できることを示しています。 次のように入力して、Gunicornサービスが実行されていることを確認できます。

sudo systemctl status gunicorn
Output● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: active (running) since Tue 2019-03-05 20:43:56 UTC; 1s ago
 Main PID: 19074 (gunicorn)
    Tasks: 4 (limit: 4915)
   CGroup: /system.slice/gunicorn.service
           ├─19074 /home/sammy/envs/polls/bin/python3 /home/sammy/envs/polls/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock mysite.wsgi:application
           ├─19098 /home/sammy/envs/polls/bin/python3 /home/sammy/envs/polls/bin/gunicorn
. . .

Mar 05 20:43:56 django systemd[1]: Started gunicorn daemon.
Mar 05 20:43:56 django gunicorn[19074]: [2019-03-05 20:43:56 +0000] [19074] [INFO] Starting gunicorn 19.9.0
. . .
Mar 05 20:44:15 django gunicorn[19074]:  - - [05/Mar/2019:20:44:15 +0000] "GET / HTTP/1.1" 301 0 "-" "curl/7.58.0"

curlからの出力またはsystemctl statusの出力が問題が発生したことを示している場合は、ログで詳細を確認してください。

sudo journalctl -u gunicorn

/etc/systemd/system/gunicorn.serviceファイルに問題がないか確認することもできます。 このファイルに変更を加えた場合は、デーモンをリロードしてサービス定義を再読み込みし、Gunicornプロセスを再起動してください。

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

Nginxサーバーの構成に進む前に、問題をトラブルシューティングしてください。

[[step-8 -—- configuring-nginx-https-and-gunicorn-proxy-passing]] ==ステップ8— NginxHTTPSとGunicornプロキシパスの構成

Gunicornがより堅牢な方法で設定されたので、接続を暗号化し、トラフィックをGunicornプロセスに渡すようにNginxを構成する必要があります。

前提条件に従い、Let’s Encryptを使用してNginxを設定した場合、Nginxのsites-availableディレクトリにドメインに対応するサーバーブロックファイルがすでに用意されているはずです。 そうでない場合は、How To Secure Nginx with Let’s Encrypt on Ubuntu 18.04に従って、このステップに戻ります。

このexample.comサーバーブロックファイルを編集する前に、Nginxのインストール後にデフォルトでロールアウトされるdefaultサーバーブロックファイルを最初に削除します。

sudo rm /etc/nginx/sites-enabled/default

次に、前提条件の手順で構成されたデフォルトのindex.htmlページの代わりに、トラフィックをGunicornに渡すようにexample.comサーバーブロックファイルを変更します。

エディターでドメインに対応するサーバーブロックファイルを開きます。

sudo nano /etc/nginx/sites-available/example.com

次のようなものが表示されるはずです。

/etc/nginx/sites-available/example.com

server {

        root /var/www/example.com/html;
        index index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                try_files $uri $uri/ =404;
        }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;

        server_name example.com www.example.com;
    return 404; # managed by Certbot


}

これは、How to Install Nginx on Ubuntu 18.04で作成されたデフォルトのサーバーブロックファイルと、Let’sEncryptによって自動的に追加された追加の組み合わせです。 このファイルの内容を削除し、HTTPトラフィックをHTTPSにリダイレクトし、前の手順で作成したGunicornソケットに着信要求を転送する新しい構成を作成します。

必要に応じて、cpを使用してこのファイルのバックアップを作成できます。 テキストエディタを終了し、example.com.oldという名前のバックアップを作成します。

sudo cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.old

次に、ファイルを再度開き、その内容を削除します。 ブロックごとに新しい構成を作成します。

次のブロックに貼り付けることから始めます。これにより、ポート80のHTTPリクエストがHTTPSにリダイレクトされます。

/etc/nginx/sites-available/example.com

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://example.com$request_uri;
}

ここでは、ポート80でHTTP IPv4およびIPv6要求をリッスンし、301応答ヘッダーを送信して、example.comドメインを使用して要求をHTTPSポート443にリダイレクトします。 これにより、直接HTTPリクエストがサーバーのIPアドレスにリダイレクトされます。

このブロックの後に、example.comドメインのHTTPS要求を処理する構成コードの次のブロックを追加します。

/etc/nginx/sites-available/example.com

. . .
server {
    listen [::]:443 ssl ipv6only=on;
    listen 443 ssl;
    server_name example.com www.example.com;

    # Let's Encrypt parameters
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location = /favicon.ico { access_log off; log_not_found off; }

    location / {
        proxy_pass         http://unix:/run/gunicorn.sock;
        proxy_redirect     off;

        proxy_set_header   Host              $http_host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto https;
    }
}

ここでは、最初にポート443で、example.comおよびwww.example.comドメインにヒットするリクエストをリッスンします。

次に、デフォルトのサーバーブロックファイルに含まれる同じLet's Encrypt構成を提供します。これは、SSL証明書と秘密キーの場所、および追加のセキュリティパラメーターを指定します。

location = /favicon.ico行は、ファビコンの検索に関する問題を無視するようにNginxに指示します。

最後のlocation = /ブロックは、Step 8で構成されたGunicornソケットにリクエストを渡すようにNginxに指示します。 さらに、ヘッダーを追加して、リクエストが転送されたことをアップストリームのDjangoサーバーに通知し、さまざまなリクエストプロパティを提供します。

これら2つの構成ブロックを貼り付けた後、最終ファイルは次のようになります。

/etc/nginx/sites-available/example.com

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://example.com$request_uri;
}
server {
        listen [::]:443 ssl ipv6only=on;
        listen 443 ssl;
        server_name example.com www.example.com;

        # Let's Encrypt parameters
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        location = /favicon.ico { access_log off; log_not_found off; }

        location / {
          proxy_pass         http://unix:/run/gunicorn.sock;
          proxy_redirect     off;

          proxy_set_header   Host              $http_host;
          proxy_set_header   X-Real-IP         $remote_addr;
          proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
          proxy_set_header   X-Forwarded-Proto https;
        }
}

完了したら、ファイルを保存して閉じます。

次のように入力して、構文エラーのNginx設定をテストします。

sudo nginx -t

設定にエラーがない場合は、次を入力してNginxを再起動します。

sudo systemctl restart nginx

これで、サーバーのドメインまたはIPアドレスにアクセスして、アプリケーションを表示できるようになります。 ブラウザは安全なHTTPS接続を使用してDjangoバックエンドに接続する必要があります。

Djangoプロジェクトを完全に保護するには、settings.pyファイルにいくつかのセキュリティパラメーターを追加する必要があります。 エディターでこのファイルを再度開きます。

nano ~/django-polls/mysite/settings.py

ファイルの一番下までスクロールし、次のパラメーターを追加します。

~/django-polls/mysite/settings.py

. . .

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True

これらの設定は、サーバーでHTTPSを有効にしたことをDjangoに伝え、「安全な」Cookieを使用するよう指示します。 これらの設定の詳細については、Security in DjangoSSL/HTTPS sectionを参照してください。

完了したら、ファイルを保存して閉じます。

最後に、Gunicornを再起動します。

sudo systemctl restart gunicorn

この時点で、HTTP要求をリダイレクトし、これらの要求をGunicornに渡すようにNginxを構成しました。 これで、DjangoプロジェクトとアプリでHTTPSが完全に有効になります。 エラーが発生した場合は、troubleshooting Nginx and Gunicornに関するこの説明が役立つ場合があります。

[.warning]#Warning:Configuring CORS Headersで説明されているように、notでSpaces CDNのカスタムサブドメインを構成した場合は、必ずOriginをワイルドカード%(エンドユーザーがアプリにアクセスできるようにする前に、t4)sドメインをドメイン名(このガイドではhttps://example.com)に変更します。

結論

このガイドでは、Ubuntu 18.04サーバーで実行されるスケーラブルなDjangoアプリケーションをセットアップおよび構成しました。 このセットアップを複数のサーバーに複製して、可用性の高いアーキテクチャを作成できます。 さらに、このアプリとその構成は、Dockerまたは別のコンテナーランタイムを使用してコンテナー化して、デプロイとスケーリングを容易にすることができます。 これらのコンテナーは、Kubernetesのようなコンテナークラスターにデプロイできます。 今後のチュートリアルシリーズでは、このDjangopollsアプリをコンテナ化して最新化し、Kubernetesクラスターで実行できるようにする方法について説明します。

静的ファイルに加えて、Django Mediaファイルをオブジェクトストレージにオフロードすることもできます。 これを行う方法については、Using Amazon S3 to Store your Django Site’s Static and Media Filesを参照してください。 静的ファイルを圧縮して、エンドユーザーへの配信をさらに最適化することも検討してください。 これを行うには、Django compressorのようなDjangoプラグインを使用できます。

Related