Dockerを使用してDjangoおよびGunicornアプリケーションを構築する方法

前書き

Djangoは強力なWebフレームワークであり、Pythonアプリケーションを迅速に開発するのに役立ちます。 これには、https://en.wikipedia.org/wiki/Object-relational_mapping [object-relational_mapping [object-relational mapper]、ユーザー認証、アプリケーション用のカスタマイズ可能な管理インターフェイスなど、いくつかの便利な機能が含まれています。 また、https://docs.djangoproject.com/en/2.1/topics/cache/ [キャッシュフレームワーク]が含まれており、https://docs.djangoproject.com/en/2.1/topics/httpを通じてクリーンなアプリの設計を促進します。 / urls / [URLディスパッチャ]およびhttps://docs.djangoproject.com/en/2.1/topics/templates/ [テンプレートシステム]。

このチュートリアルでは、スケーラブルで移植性の高いDjango PollsアプリをDockerコンテナで構築する方法を学びます。 すぐに使用できるDjangoアプリは、標準出力ストリームへのログ記録や、コンテナに渡された環境変数を使用した設定など、コンテナ内で効果的に実行するためにいくつかの変更が必要です。 さらに、JavaScriptやCSSスタイルシートなどの静的アセットをオブジェクトストレージにオフロードすると、マルチコンテナ環境でこれらのファイルの管理を合理化および一元化できます。

サンプルのDjango Twelve-Factor方法論に触発されたこれらの変更を実装します。 django-polls [投票]アプリ。 次に、アプリケーションイメージをビルドし、Dockerでコンテナー化されたアプリを実行します。

このチュートリアルの終わりまでに、https://www.digitalocean.com/community/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managedでセットアップをコンテナー化できます。 -databases-and-spaces [スケーラブルなDjangoアプリのセットアップ方法]。 このシリーズの後続のチュートリアルでは、https://docs.docker.com/compose/ [Docker Compose]を使用してDjangoコンテナーをNginxリバースプロキシとペアリングし、このアーキテクチャをKubernetesクラスターにデプロイする方法を学習します。

チュートリアルを読んでアプリに加えた変更を理解することを強くお勧めしますが、先にスキップしたい場合は、https://github.com/do-community/から変更されたコードを取得できます。 PollsアプリGitHubリポジトリのdjango-polls / tree / polls-docker [polls-docker branch]。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • Ubuntu 18.04サーバー。https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04 [初期サーバーセットアップガイド]に従ってセットアップします。

  • https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04のステップ1および2に従って、サーバーにインストールされたDocker Ubuntu 18.04でDockerを使用してください]。 ステップ2で詳述するように、ユーザーを `+ docker +`グループに追加してください。

  • DigitalOcean Spaceは、Djangoプロジェクトの静的ファイルとこのスペースのアクセスキーのセットを保存します。 スペースの作成方法については、https://www.digitalocean.com/docs/spaces/how-to/create/ [スペースの作成方法]製品ドキュメントを参照してください。 スペースのアクセスキーを作成する方法については、https://www.digitalocean.com/docs/spaces/how-to/administrative-access/#access-keys [アクセスキーを使用したスペースへのアクセスの共有]を参照してください。 わずかな変更で、S3互換のオブジェクトストレージサービスを使用できます。

  • DigitalOceanマネージPostgreSQLクラスター。 クラスターの作成方法については、DigitalOcean Managed Databases製品ドキュメントを参照してください。 わずかな変更を加えると、https://docs.djangoproject.com/en/2.2/ref/databases/ [Djangoがサポートする]の任意のデータベースを使用できます。

ステップ1-PostgreSQLデータベースとユーザーの作成

まず、UbuntuインスタンスからPostgreSQLサーバーに接続します。 次に、DjangoアプリのPostgreSQLデータベースとユーザーを作成し、Djangoと効果的に連携するようにデータベースを構成します。

Ubuntuマシン(アプリコンテナではない)からデータベースに接続する前に、Ubuntuリポジトリから `+ postgresql-client `パッケージをインストールする必要があります。 最初にローカルの ` apt +`パッケージインデックスを更新してから、パッケージをダウンロードしてインストールします。

sudo apt update
sudo apt install postgresql-client

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

クライアントをインストールしたので、これを使用して、Djangoアプリケーションのデータベースとデータベースユーザーを作成します。

まず、https://cloud.digitalocean.com/ [クラウドコントロールパネル]から[データベース]に移動し、データベースをクリックして、クラスターの[接続パラメーター]を取得します。 クラスターの*接続パラメーター*を含む*接続の詳細*ボックスが表示されます。 これらに注意してください。

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

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

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

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

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

CREATE DATABASE polls;

`+ polls +`データベースに切り替えることができます:

\c polls;

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

CREATE USER  WITH PASSWORD '';

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

デフォルトのエンコーディングを `+ UTF-8 `に設定していますが、これはDjangoが期待しています。 また、デフォルトのトランザクション分離スキームを「コミット済み読み取り」に設定しています。これは、コミットされていないトランザクションからの読み取りをブロックします。 最後に、タイムゾーンを設定しています。 デフォルトでは、Djangoプロジェクトは ` UTC`を使用するように設定されます。 これらはすべてhttps://docs.djangoproject.com/en/2.0/ref/databases/#optimizing-postgresql-s-configuration[Djangoプロジェクト自体]からの推奨事項です。

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

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

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

GRANT ALL PRIVILEGES ON DATABASE polls TO ;

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

\q

適切に設定されたDjangoアプリは、このデータベースに接続して管理できるようになりました。 次のステップでは、GitHubからPollsアプリコードを複製し、Pythonパッケージの依存関係を明示的に定義します。

手順2-アプリリポジトリの複製と依存関係の宣言

Django Pollsアプリのコンテナ化プロセスを開始するには、まずhttps://github.com/do-community/django-polls[django-polls]リポジトリのクローンを作成します。これにはhttps://の完全なコードが含まれていますwww.djangoproject.com/[Django]プロジェクトのhttps://docs.djangoproject.com/en/2.1/intro/tutorial01/[tutorial]投票アプリ。

サーバーにログインし、 `+ polls-project `というディレクトリを作成し、 ` git `を使用してGitHubから ` django-polls +`リポジトリを複製します。

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

`+ django-polls +`ディレクトリにアクセスし、リポジトリの内容を一覧表示します。

cd django-polls
ls
OutputLICENSE  README.md  manage.py  mysite  polls  templates

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

  • + manage.py +:アプリの操作に使用されるメインのコマンドラインユーティリティ。

  • + polls +: `+ polls +`アプリコードが含まれています。

  • + mysite +:Djangoプロジェクトスコープのコードと設定が含まれています。

  • + templates +:管理インターフェイス用のカスタムテンプレートファイルが含まれています。

プロジェクトの構造とファイルの詳細については、Djangoの公式ドキュメントからhttps://docs.djangoproject.com/en/2.1/intro/tutorial01/#creating-a-project [プロジェクトの作成]をご覧ください。

このディレクトリには、DjangoアプリのPython依存関係を含む「+ requirements.txt +」というファイルも作成します。

選択したエディターで「+ requirements.txt +」というファイルを開き、次のPython依存関係を貼り付けます。

polls-project / django-polls / requirements.txt

boto3==1.9.252
botocore==1.12.252
Django==2.2.6
django-storages==1.7.2
docutils==0.15.2
gunicorn==19.9.0
jmespath==0.9.4
psycopg2==2.8.3
python-dateutil==2.8.0
pytz==2019.3
s3transfer==0.2.1
six==1.12.0
sqlparse==0.3.0
urllib3==1.25.6

ここで、Django、静的ストレージをオブジェクトストレージにオフロードするための + django-storages +`プラグイン、 `+ gunicorn + WSGIサーバー、 + psycopg2 + PostgreSQLアダプター、およびいくつかの追加の依存パッケージをインストールします。 アプリで必要なすべてのPythonパッケージを明示的にリストおよびバージョン化することに注意してください。

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

アプリのクローンを作成し、その依存関係を定義したので、移植性を高めるために変更に進みます。

手順3-Djangoを環境変数で構成可能にする

the 12-factor app方法論からの最も重要な推奨事項の1つは、アプリケーションのコードベースからハードコードされた構成を抽出することです。 これにより、環境変数を変更することにより、実行時にアプリケーションの動作を簡単に変更できます。 DockerとKubernetesはどちらもこのコンテナ構成方法を提案しているため、このパターンを使用するようにアプリケーションの設定ファイルを調整します。

Djangoプロジェクトのメイン設定ファイル( + django-polls / mysite / settings.py +)は、ネイティブデータ構造を使用してアプリケーションを構成するPythonモジュールです。 デフォルトでは、ファイル内のほとんどの値はハードコーディングされています。つまり、構成ファイルを編集して、アプリケーションの動作を変更する必要があります。 + os`モジュールでPythonの + getenv`関数を使用して、代わりにローカル環境変数から設定パラメーターを読み取るようにDjangoを設定できます。

これを行うには、 `+ settings.py `を実行し、実行時に設定する各変数のハードコードされた値を ` os.getenv `の呼び出しに置き換えます。 ` os.getenv +`関数は、指定された環境変数名から値を読み取ります。 オプションで、環境変数が設定されていない場合に使用されるデフォルト値を持つ2番目のパラメーターを指定できます。

これにより、次のような変数を設定できます。

polls-project / django-polls / mysite / settings.py

. . .
SECRET_KEY =
. . .
DEBUG =
. . .

`+ SECRET_KEY `の場合、Djangoは ` DJANGO_SECRET_KEY +`という環境変数を探します。 これはハードコードされてはならず、アプリケーションサーバー全体で同じである必要があるため、フォールバック値なしで外部で設定する必要があります。 これを提供しない場合、アプリケーションのさまざまなコピーが異なるキーを使用すると問題が発生する可能性があるため、アプリケーションが失敗するようにします。

`+ DEBUG `の場合、Djangoは ` DJANGO_DEBUG `と呼ばれる環境変数を探します。 ただし、今回は、変数が設定されていない場合のフォールバックとして使用されるデフォルト値を提供しました。 この場合、変数が意図的に定義されて「 True 」に設定されない限り、機密情報が誤って漏洩しないように、値が提供されない場合は「 DEBUG 」を「 False +」に設定することを選択しました。

この手法を適用するには、選択したエディターで `+ polls-project / django-polls / mysite / settings.py +`ファイルを開いて、次の変数を提供されたデフォルト値で外部化します。

  • + SECRET_KEY = +

  • + DEBUG = +

  • + ALLOWED_HOSTS = +

`+ ALLOWED_HOSTS `の場合、 ` DJANGO_ALLOWED_HOSTS `環境変数を取得し、区切り文字として `、`を使用してPythonリストに分割します。 変数が設定されていない場合、「 ALLOWED_HOSTS 」は「+127.0.0.1」に設定されます。

上記の変数を変更したら、 `+ DATABASES +`変数に移動し、次のように設定します。

polls-project / django-polls / mysite / settings.py

. . .
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.{}'.format(
            os.getenv('DATABASE_ENGINE', 'sqlite3')
        ),
        'NAME': os.getenv('DATABASE_NAME', 'polls'),
        'USER': os.getenv('DATABASE_USERNAME', 'myprojectuser'),
        'PASSWORD': os.getenv('DATABASE_PASSWORD', 'password'),
        'HOST': os.getenv('DATABASE_HOST', '127.0.0.1'),
        'PORT': os.getenv('DATABASE_PORT', 5432),
        'OPTIONS': json.loads(
            os.getenv('DATABASE_OPTIONS', '{}')
        ),
    }
}
. . .

これにより、環境変数を使用して `+ default +`データベースパラメータが設定されます。

`+ DATABASES ['default'] ['OPTIONS'] `の場合、 ` json.loads `を使用して、 ` DATABASE_OPTIONS +`環境変数を介して渡されたJSONオブジェクトをデシリアライズしました。 ほとんどの場合、環境変数を単純な文字列として解釈すると、Django設定への変換が読みやすくなります。 ただし、この場合、任意のデータ構造を渡すことができることは有益です。 各データベースエンジンには固有の有効なオプションセットがあるため、適切なパラメーターを使用してJSONオブジェクトをエンコードできると、ある程度の読みやすさを犠牲にしてはるかに高い柔軟性が得られます。

`+ json `ライブラリを利用するには、 ` settings.py +`の一番上にインポートします:

polls-project / django-polls / mysite / settings.py

"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 2.1.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os

. . .

特別な注意が必要な他の領域は、 `+ DATABASES ['default'] ['NAME'] `です。 ほとんどのデータベースエンジンでは、これはリレーショナルデータベース管理システム内のデータベース名です。 一方、SQLiteを使用している場合は、「 NAME +」を使用してデータベースファイルを指定するので、このパラメーターを必ず設定してください。

`+ settings.py +`ファイルはPythonコードであるため、環境からの値の読み取りを処理できるさまざまな方法があります。 ここで使用した方法は、コードベースから構成を外部化するための1つの可能な手法です。

このステップでは、データベースパラメータを含む一般的な移植可能な方法で、メインのDjango設定変数を設定しました。 次の手順では、JavascriptやCSSスタイルシートなどの静的ファイルの設定を引き続き行います。これらの設定は、S3互換オブジェクトストレージサービスに集中してオフロードします。

ステップ4-静的アセットのオフロード

実稼働環境で複数のDjangoコンテナーを実行している場合、実行中のコンテナー全体にわたって特定のバージョンの静的アセットとファイルを維持するのは面倒です。 このアーキテクチャを合理化するために、すべての共有要素と状態を外部ストレージにオフロードできます。 これらのアイテムをレプリカ間で同期させたり、バックアップおよびロードルーチンを実装してデータをローカルで利用できるようにする代わりに、これらの資産へのアクセスをネットワークアクセス可能なサービスとして実装できます。

最後のステップでは、環境変数を介してデータベース接続パラメーターを渡すことができるようにDjangoを構成しました。 このステップでは、Djangoコンテナで共有される静的アセットを保存するために使用するオブジェクトストレージサービスについても同じことを行います。

django-storagesパッケージは、Djangoがファイルのオフロードに使用できるリモートストレージバックエンド(S3互換オブジェクトストレージを含む)を提供します。 https://www.digitalocean.com/community/tutorials/how-to-set-のステップ7で説明されているように、 `+ django-storages +`を使用して静的ファイルをDigitalOcean SpaceにアップロードするようにPollsアプリを構成します。 digitalocean-managed-databases-and-spaces#step-7-%E2%80%94-offloading-static-files-to-digitalocean-spaces [セットアップ方法DigitalOceanが管理するデータベースとスペースを備えたスケーラブルなDjangoアプリ]。 このガイドでは、DigitalOcean Spacesを使用しますが、S3互換のオブジェクトストレージプロバイダーを使用できます。

最初に、前の手順で変更した同じ「+ django-polls / mysite / settings.py」ファイルにいくつかの変更を加えます。

編集のために + mysite / settings.py +`ファイルを開いて、 `+ INSTALLED APPS`のDjangoリストに + storage`アプリを追加することから始めます。

polls-project / django-polls / mysite / settings.py

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

]
. . .

`+ storages `アプリは、ステップ1で定義した ` requirements.txt `ファイルの ` django-storages +`を介してインストールされます。

ここで、ファイルの下部にある `+ STATIC_URL +`変数を見つけて、次のブロックに置き換えます。

polls-project / 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 = os.getenv('STATIC_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('STATIC_SECRET_KEY')

AWS_STORAGE_BUCKET_NAME = os.getenv('STATIC_BUCKET_NAME')
AWS_S3_ENDPOINT_URL = os.getenv('STATIC_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/'

次の構成変数をハードコーディングします。

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

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

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

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

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

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

柔軟性と移植性を維持するために、以前と同様に、環境変数を使用して実行時に構成可能な多くのパラメーターを設定しました。 これらが含まれます:

  • + AWS_ACCESS_KEY_ID +: `+ STATIC_ACCESS_KEY_ID +`環境変数によって設定されます。 DigitalOcean Spacesアクセスキー識別子。

  • + AWS_SECRET_ACCESS_KEY +: `+ STATIC_SECRET_KEY +`で設定します。 DigitalOcean Spacesの秘密キー。

  • + AWS_STORAGE_BUCKET_NAME +: `+ STATIC_BUCKET_NAME +`で設定します。 Djangoがアセットをアップロードするオブジェクトストレージバケット。

  • + AWS_S3_ENDPOINT_URL +: `+ STATIC_ENDPOINT_URL `で設定します。 オブジェクトストレージサービスへのアクセスに使用されるエンドポイントURL。 DigitalOcean Spacesの場合、Spacesバケットが配置されている地域に応じて、これは ` https:// nyc3.digitaloceanspaces.com +`のようになります。

`+ settings.py +`に変更を加えたら、ファイルを保存して閉じます。

今後、 `+ manage.py collectstatic +`を実行してプロジェクトの静的ファイルをアセンブルすると、Djangoはこれらをリモートオブジェクトストレージにアップロードします。 Djangoは、このオブジェクトストレージサービスから静的アセットを提供するように構成されました。

この時点で、DigitalOcean Spaceを使用している場合、必要に応じてSpaceのCDNを有効にできます。これにより、エッジサーバーの地理的に分散したネットワークにDjangoプロジェクトの静的ファイルをキャッシュすることで、配信を高速化できます。 オプションで、スペースのカスタムサブドメインを構成することもできます。 CDNの詳細については、https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery [CDNを使用した静的コンテンツ配信の高速化]をご覧ください。 CDNの設定はこのチュートリアルの範囲を超えていますが、手順はhttps://www.digitalocean.com/community/tutorials/how-to-set-up-a-scalable-django-app-の手順と非常によく一致しています。 with-digitalocean-managed-databases-and-spaces#enabling-cdn-optional [Enabling CDN]セクションのhttps://www.digitalocean.com/community/tutorials/how-to-set-up-a-scalable-django -app-with-digitalocean-managed-databases-and-spaces#enabling-cdn-optional [DigitalOcean Managed Databases and SpacesでスケーラブルなDjangoアプリをセットアップする方法]。

次のステップでは、DjangoがSTDOUTおよびSTDERRにログを記録できるようにするために、 `+ settings.py `に最終的な変更を加えます。これらのストリームはDocker Engineによって取得され、 ` docker logs +`を使用して検査できます。

ステップ5-ロギングの構成

デフォルトでは、Djangoは、開発HTTPサーバーを実行するとき、または `+ DEBUG `オプションが ` True `に設定されているときに、情報を標準出力と標準エラーに記録します。 ただし、 ` DEBUG `が ` False `に設定されている場合、または異なるHTTPサーバーを使用している場合(どちらも本番環境で当てはまる可能性が高い)、Djangoは異なるロギングメカニズムを使用します。 優先度「 INFO 」以上のすべてを標準ストリームに記録する代わりに、優先度「 ERROR 」または「 CRITICAL +」のメッセージを管理用メールアカウントに送信します。

これは多くの状況で理にかなっていますが、Kubernetesおよびコンテナー化された環境では、標準出力および標準エラーへのログ記録を強くお勧めします。 ロギングメッセージは、ノードのファイルシステム上の集中ディレクトリに収集され、 `+ kubectl `および ` docker +`コマンドを使用してインタラクティブにアクセスできます。 このノードレベルの集約により、運用チームが各ノードでプロセスを実行してログを監視および転送できるようになるため、ログの収集が容易になります。 このアーキテクチャを活用するには、アプリケーションはこれらの標準シンクにログを書き込む必要があります。

幸いなことに、DjangoのログインはPython標準ライブラリの高度に設定可能な + logging +`モジュールを使用するため、辞書を定義してhttps://docs.python.org/3/library/logging.config.html#loggingに渡すことができます-config-dictschema [+ logging.config.dictConfig +`]を使用して、目的の出力とフォーマットを定義します。 Djangoロギングを設定するためのこのテクニックやその他の詳細については、https://lincolnloop.com/blog/django-logging-right-way/ [Django Logging、The Right Way]をご覧ください。

もう一度、エディターで `+ django-polls / mysite / settings.py +`を開きます。

最初に追加の `+ import +`ステートメントをファイルの先頭に追加して、ロギング設定を操作できるようにします。

polls-project / django-polls / mysite / settings.py

import json
import os

. . .

`+ logging.config `インポートにより、新しいロギング設定の辞書を ` dictConfig +`関数に渡すことにより、Djangoのデフォルトのロギング動作をオーバーライドできます。

次に、ファイルの下部に移動し、次のロギング構成コードのブロックを貼り付けます。

polls-project / django-polls / mysite / settings.py

. . .
# Logging Configuration

# Clear prev config
LOGGING_CONFIG = None

# Get loglevel from env
LOGLEVEL = os.getenv('DJANGO_LOGLEVEL', 'info').upper()

logging.config.dictConfig({
   'version': 1,
   'disable_existing_loggers': False,
   'formatters': {
       'console': {
           'format': '%(asctime)s %(levelname)s [%(name)s:%(lineno)s] %(module)s %(process)d %(thread)d %(message)s',
       },
   },
   'handlers': {
       'console': {
           'class': 'logging.StreamHandler',
           'formatter': 'console',
       },
   },
   'loggers': {
       '': {
           'level': LOGLEVEL,
           'handlers': ['console',],
       },
   },
})

ここでは、 `+ LOGGING_CONFIG `を ` None `に設定して、Djangoが提供するデフォルトのロギング設定を無効にします。 デフォルトで ` LOGLEVEL `を ` INFO `に設定しますが、必要に応じてオーバーライドできるように、 ` DJANGO_LOGLEVEL +`環境変数を確認します。

最後に、 `+ dictConfig `関数を使用して、 ` logging.config `モジュールを使用して新しい設定辞書を設定します。 辞書では、 ` formatters `を使用してテキスト形式を定義し、 ` handlers `を設定して出力を定義し、 ` loggers +`を使用して各ハンドラーに送信するメッセージを設定します。

これはかなり最小限の設定で、 `+ DJANGO_LOGLEVEL +`という環境変数を使用してロギングの重大度レベルを指定し、そのレベル以上のすべてのメッセージを標準ストリームに記録できます。 Djangoのログメカニズムの詳細については、公式のDjangoドキュメントのhttps://docs.djangoproject.com/en/2.2/topics/logging/[Logging]を参照してください。

この構成では、アプリケーションをコンテナー化するときに、Dockerはこれらのログを `+ docker logs `コマンドで公開します。 同様に、Kubernetesは出力をキャプチャし、 ` kubectl logs +`コマンドで出力を公開します。

これで、Django Pollsアプリへのコードの変更は終わりです。 次のステップでは、アプリのDockerfileを作成してコンテナ化プロセスを開始します。

ステップ6-アプリケーションDockerfileの作成

このステップでは、Djangoアプリを実行するコンテナーイメージと、それを提供するGunicorn WSGIサーバーを定義します。 ランタイム環境を定義してコンテナイメージを構築し、アプリケーションとその依存関係をインストールし、基本的な構成を完了する必要があります。 アプリケーションをコンテナイメージにカプセル化する方法は多数ありますが、この手順で実行するプラクティスにより、スリムで効率的なアプリイメージが生成されます。

適切な親画像の選択

コンテナイメージを構築する際に最初に決定しなければならない重要な決定は、構築の基礎となります。 コンテナイメージは、空のファイルシステムを示す `+ SCRATCH +`から、または既存のコンテナイメージから構築できます。 多くの異なるベースコンテナイメージが利用可能で、それぞれがファイルシステムを定義し、プリインストールされたパッケージの一意のセットを提供します。 Ubuntu 18.04のようなバニラLinuxディストリビューションに基づくイメージは一般的な動作環境を提供しますが、より専門的なイメージには特定のプログラミング言語用の共通ライブラリとツールが含まれることがよくあります。

可能な限り、ベースとしてhttps://hub.docker.com/explore/[Dockerの公式リポジトリ]の画像を使用することをお勧めします。 これらの画像は、ベストプラクティスに従うためにDockerによって検証されており、セキュリティの修正と改善のために定期的に更新されます。

アプリケーションはDjangoで構築されているため、標準のPython環境のイメージは強固な基盤を提供し、開始するために必要な多くのツールを含みます。 Pythonの公式Dockerリポジトリはhttps://hub.docker.com/_/python/[Pythonベースのイメージの幅広い選択]を提供し、それぞれがオペレーティングシステムの上にPythonのバージョンといくつかの一般的なツールをインストールします。

機能の適切なレベルはユースケースに依存しますが、https://alpinelinux.org/ [Alpine Linux]に基づく画像は、多くの場合、確実な出発点です。 Alpine Linuxは、アプリケーションを実行するための堅牢で最小限のオペレーティング環境を提供します。 デフォルトのファイルシステムは非常に小さいですが、機能を簡単に追加できるように、かなり広範なリポジトリを備えた完全なパッケージ管理システムが含まれています。

このガイドでは、Djangoアプリケーションの親画像として、「+ 3.7.4-alpine3.10 」とタグ付けされたPython画像を使用します。 ` FROM `命令を使用して、 ` Dockerfile +`で親イメージのリポジトリとタグを指定します。

最初に、 `+ django-polls +`ディレクトリから移動します。

cd ..

次に、選択したエディターで「+ Dockerfile +」というファイルを開きます。 次の親画像定義に貼り付けます。

polls-project / Dockerfile

FROM python:3.7.4-alpine3.10

これは、アプリケーションを実行するために構築しているカスタムDockerイメージの開始点を定義します。

アプリケーションをセットアップするための指示の追加

親イメージを選択したら、依存関係のインストール、アプリケーションファイルのコピー、実行環境のセットアップの手順を追加できます。 このプロセスは通常、アプリケーションのサーバーをセットアップするために実行する手順を反映していますが、コンテナーの抽象化を説明するための重要な違いがいくつかあります。

`+ FROM +`行の後に、次のDockerfileコードのブロックを貼り付けます。

polls-project / Dockerfile

. . .

ADD django-polls/requirements.txt /app/requirements.txt

RUN set -ex \
   && apk add --no-cache --virtual .build-deps postgresql-dev build-base \
   && python -m venv /env \
   && /env/bin/pip install --upgrade pip \
   && /env/bin/pip install --no-cache-dir -r /app/requirements.txt \
   && runDeps="$(scanelf --needed --nobanner --recursive /env \
       | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
       | sort -u \
       | xargs -r apk info --installed \
       | sort -u)" \
   && apk add --virtual rundeps $runDeps \
   && apk del .build-deps

ADD django-polls /app
WORKDIR /app

ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH

EXPOSE 8000

これらの指示に目を通し、それほど明白でない選択肢のいくつかを説明しましょう。 Djangoアプリの本番対応Dockerfilesの構築についてさらに学ぶには、https://www.caktusgroup.com/blog/2017/03/14/production-ready-dockerfile-your-python-django-app/ [A Production -DjangoアプリのDockerfileの準備完了]。

最初のDockerは、 `+ requirements.txt `ファイルを ` / app / requirements.txt `にコピーして、アプリケーションの依存関係が画像のファイルシステムで利用できるようにします。 これを使用して、アプリケーションを実行するために必要なすべてのPythonパッケージをインストールします。 依存関係ファイルをコードベースの別のステップとしてコピーし、依存関係ファイルを含むイメージレイヤーをDockerがキャッシュできるようにします。 ` requirements.txt +`ファイルがビルド間で変更されない場合、Dockerはキャッシュされたレイヤーを再構築する代わりに再利用できるため、プロセスを高速化できます。

次に、コマンドの長いリストを実行する単一の `+ RUN `命令があり、それぞれがLinuxの ` && +`演算子を使用して連結されています。 要約すると、これらのコマンドは次のとおりです。

  • Alpineの `+ apk +`パッケージマネージャーを使用して、PostgreSQL開発ファイルと基本的なビルドの依存関係をインストールします

  • 仮想環境を作成する

  • + requirements.txt`にリストされているPython依存関係を + pip`でインストールします

  • インストールされたPythonパッケージの要件を分析して、実行時に必要なパッケージのリストをコンパイルします

  • 不要なビルド依存関係をアンインストールします

Dockerがイメージレイヤーを構築する方法のため、コマンドを個別の `+ RUN `ステップで実行する代わりに、一緒にチェーンします。 「 ADD」、「+ COPY」、および「+ RUN 」の各命令について、Dockerは既存のファイルシステムの上に新しいイメージレイヤーを作成し、命令を実行して、結果のレイヤーを保存します。 つまり、 ` RUN +`命令でコマンドを圧縮すると、画像レイヤーが少なくなります。

アイテムがイメージレイヤーに書き込まれると、イメージサイズを小さくするために後続のレイヤーでアイテムを削除することはできません。 ビルドの依存関係をインストールしたが、アプリケーションのセットアップ後にそれらを削除する場合は、同じ命令内で削除して、イメージサイズを縮小する必要があります。 この `+ RUN `コマンドでは、ビルドの依存関係をインストールし、それらを使用してアプリのパッケージをビルドし、その後、 ` apk del +`を使用してそれらを削除します。

+ RUN +`命令の後、 `+ ADD`を使用してアプリケーションコードをコピーし、 + WORKDIR`を使用してイメージの作業ディレクトリをコードディレクトリに設定します。

次に、 + ENV +`命令を使用して、イメージから生成されたコンテナ内で使用できる2つの環境変数を設定します。 最初のものは `+ VIRTUALENV`を + / env + に設定し、2番目の命令は + PATH + 変数を変更して + / env / bin + `ディレクトリを含めます。 これらの2行は、仮想環境をアクティブ化する従来の方法である `+ / env / bin / activate +`スクリプトのソースの結果をエミュレートします。

最後に、コンテナが実行時にポート `+ 8000 `でリッスンすることをDockerに通知するために ` EXPOSE +`を使用します。

この時点で、 `+ Dockerfile +`はほぼ完成しています。 イメージを使用してコンテナを起動するときに実行されるデフォルトのコマンドを定義するだけです。

デフォルトのコマンドの定義

Dockerイメージのデフォルトのコマンドは、実行するコマンドを明示的に指定せずにコンテナーが開始されたときに何が起こるかを決定します。 `+ ENTRYPOINT `および ` CMD `命令は、単独でまたはタンデムで使用して、 ` Dockerfile +`内でデフォルトのコマンドを定義できます。

+ ENTRYPOINT +`と `+ CMD +`の両方が定義されると、 `+ ENTRYPOINT +`はコンテナによって実行される実行可能ファイルを定義し、 `+ CMD +`はそのコマンドのデフォルトの引数リストを表します。 ユーザーはコマンドラインに代替引数を追加することでデフォルトの引数リストを上書きできます: `+ docker run <image> <arguments> +。 この形式では、ユーザーは `+ ENTRYPOINT `コマンドを簡単にオーバーライドできません。そのため、 ` ENTRYPOINT +`コマンドは、環境を設定し、受け取った引数リストに基づいて異なるアクションを実行するスクリプトに設定されることがよくあります。

単独で使用する場合、 `+ ENTRYPOINT `はコンテナの実行可能ファイルを設定しますが、デフォルトの引数リストを定義しません。 ` CMD +`のみが設定されている場合、デフォルトのコマンドと引数のリストとして解釈され、実行時に上書きできます。

この画像では、コンテナがデフォルトで `+ gunicorn`アプリケーションサーバーを使用してアプリケーションを実行するようにします。 「+ gunicorn 」に渡す引数リストは、実行時に設定する必要はありませんが、必要に応じて管理タスクをデバッグまたは実行するために他のコマンドを簡単に実行できる機能が必要です(静的アセットの収集やデータベースの初期化など)。 これらの要件を念頭に置いて、 ` CMD `を使用して ` ENTRYPOINT +`なしでデフォルトのコマンドを定義することは理にかなっています。

`+ CMD +`命令は、次の形式のいずれかを使用して定義できます。

  • + CMD [" "、" "、。 . . 、 ""] + `:引数リストの形式( + ENTRYPOINT + `のデフォルトの引数リストを定義するために使用)

  • + CMD [" "、" "、" "、。 . . 、 ""] + `: + exec + `形式

  • `+ CMD" "" "。 . . "" + `:シェル形式

最初の形式は引数のみをリストし、 `+ ENTRYPOINT `と組み合わせて使用​​されます。 他の2つの形式では、コマンドとその引数を指定しますが、いくつかの重要な違いがあります。 推奨される ` exec `形式は、コマンドを直接実行し、シェル処理なしで引数リストを渡します。 一方、シェル形式では、リスト全体が ` sh -c +`に渡されます。 これは、たとえば、コマンドで環境変数の値を置き換える必要がある場合に必要ですが、一般的には予測不可能と見なされます。

私たちの目的では、 `+ Dockerfile +`の最後の命令は次のようになります。

polls-project / Dockerfile

. . .
CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi:application"]

デフォルトでは、このイメージを使用するコンテナは、3つのワーカーでlocalhostポート「8000」にバインドされた「+ gunicorn 」を実行し、「 mysite 」にある「 wsgi.py 」ファイルで「 application 」関数を実行しますディレクトリ。 オプションで、実行時に「 gunicorn +」の代わりに別のプロセスを実行するコマンドを提供できます。

この時点で、 + docker build`を使用してアプリイメージをビルドし、 + docker run`を使用してマシンでコンテナーを実行できます。

Dockerイメージの構築

デフォルトでは、 + docker build`コマンドは現在のディレクトリで + Dockerfile`を探し、そのビルド指示を見つけます。 また、ビルド「コンテキスト」、つまりビルドプロセス中に使用できるローカルファイルシステム階層をDockerデーモンに送信します。 多くの場合、現在のディレクトリはビルドコンテキストとして設定されます。

+ Dockerfile`を含むディレクトリにアクセスした後、 + docker build`を実行し、イメージとタグ名を `+ -t `フラグで渡し、現在のディレクトリをビルドコンテキストとして使用します。 ここでは、画像に「 django-polls 」という名前を付け、バージョン「 v0 +」でタグ付けします。

docker build -t : .

このコマンドは、 `+ Dockerfile `と現在のディレクトリをビルドコンテキストとしてDockerデーモンに渡します。 デーモンは、 ` Dockerfile +`命令を処理するときに一連の画像レイヤーを作成することにより、画像を構築します。

`+ docker build`が完了すると、次の出力が表示されます。

OutputSuccessfully built
Successfully tagged django-polls:v0

画像を正常に作成したら、 `+ docker run `を使用してアプリコンテナーを実行できます。 ただし、コンテナの実行環境をまだ設定していないため、「 run 」コマンドはここで失敗する可能性が高いです。 ` SECRET_KEY `のような外部化された変数と ` settings.py +`からのデータベース設定は空白かデフォルト値に設定されます。

最後のステップでは、環境変数ファイルを使用してコンテナの実行環境を構成します。 次に、データベーススキーマを作成し、アプリの静的ファイルを生成してオブジェクトストレージにアップロードし、最後にアプリをテストします。

ステップ7-実行環境の構成とアプリのテスト

Dockerは、内部の環境変数を設定するためのhttps://docs.docker.com/engine/reference/commandline/run/#set-environment-variables—​e---env---env-file [いくつかのメソッド]を提供します容器。 手順1で外部化したすべての変数を設定する必要があるため、環境変数とその値のリストを含むファイルを渡すことができる `+-env-file +`メソッドを使用します。

`+ polls-project `ディレクトリに ` env +`というファイルを作成し、次の変数リストに貼り付けます。

polls-project / env

DJANGO_SECRET_KEY=
DEBUG=True
DJANGO_ALLOWED_HOSTS=
DATABASE_ENGINE=postgresql_psycopg2
DATABASE_NAME=polls
DATABASE_USERNAME=
DATABASE_PASSWORD=
DATABASE_HOST=
DATABASE_PORT=
STATIC_ACCESS_KEY_ID=
STATIC_SECRET_KEY=
STATIC_BUCKET_NAME=
STATIC_ENDPOINT_URL=https://.digitaloceanspaces.com
DJANGO_LOGLEVEL=info

このファイルの次の値を置き換えます。

  • + DJANGO_SECRET_KEY +:https://docs.djangoproject.com/en/2.2/ref/settings/#secret-key[Django docs]で詳述されているように、これを一意の予測不可能な値に設定します。 このキーを生成する1つの方法は、https://www.digitalocean.com/community/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managed-databases-and-spacesで提供されていますhttps://www.digitalocean.com/community/tutorials/how-to-set-up-aの#step-5-%E2%80%94-adjusting-the-app-settings [アプリ設定の調整] -scalable-django-app-with-digitalocean-managed-databases-and-spaces#step-5-%E2%80%94-adjusting-the-app-settings [Scalable Django App]チュートリアル。

  • + DJANGO_ALLOWED_HOSTS +:UbuntuサーバーのIPアドレスに設定します。 テストのために、すべてのホストに一致するワイルドカードである「+ * +」に設定することもできます。 本番環境でDjangoを実行するときは、この値を適切に設定してください。

  • + DATABASE_USERNAME +:これを前の手順で作成したデータベースユーザーに設定します。

  • + DATABASE_PASSWORD +:これを前の手順で作成したユーザーパスワードに設定します。

  • + DATABASE_HOST:これをデータベースのホスト名に設定します。

  • + DATABASE_PORT +:これをデータベースのポートに設定します。

  • + STATIC_ACCESS_KEY_ID +:これをスペースのアクセスキーに設定します。

  • + STATIC_SECRET_KEY +:これをスペースのアクセスキーシークレットに設定します。

  • + STATIC_BUCKET_NAME +:これをスペース名に設定します。

  • + STATIC_ENDPOINT_URL +:これを適切なSpaceエンドポイントURLに設定します。

本番環境でDjangoを実行するときは、必ず `+ DEBUG `を ` False +`に設定し、必要な詳細度に応じてログレベルを調整してください。

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

ここで、 `+ docker run `を使用してDockerfileに設定された ` CMD `をオーバーライドし、 ` manage.py makemigrations `および ` manage.py migrate +`コマンドを使用してデータベーススキーマを作成します。

docker run --env-file env django-polls:v0 sh -c "python manage.py makemigrations && python manage.py migrate"

ここでは、 `+ django-polls:v0 `コンテナイメージを実行し、作成したばかりの環境変数ファイルを渡し、Dockerfileコマンドを ` sh -c" python manage.py makemigrations && python manage.py migrate "でオーバーライドします+ `。アプリコードで定義されたデータベーススキーマを作成します。 コマンドを実行すると、次のように表示されます。

OutputNo changes detected
Operations to perform:
 Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
 Applying contenttypes.0001_initial... OK
 Applying auth.0001_initial... OK
 Applying admin.0001_initial... OK
 Applying admin.0002_logentry_remove_auto_add... OK
 Applying admin.0003_logentry_add_action_flag_choices... OK
 Applying contenttypes.0002_remove_content_type_name... OK
 Applying auth.0002_alter_permission_name_max_length... OK
 Applying auth.0003_alter_user_email_max_length... OK
 Applying auth.0004_alter_user_username_opts... OK
 Applying auth.0005_alter_user_last_login_null... OK
 Applying auth.0006_require_contenttypes_0002... OK
 Applying auth.0007_alter_validators_add_error_messages... OK
 Applying auth.0008_alter_user_username_max_length... OK
 Applying auth.0009_alter_user_last_name_max_length... OK
 Applying auth.0010_alter_group_name_max_length... OK
 Applying auth.0011_update_proxy_permissions... OK
 Applying polls.0001_initial... OK
 Applying sessions.0001_initial... OK

これは、データベーススキーマが正常に作成されたことを示します。

次に、アプリコンテナの別のインスタンスを実行し、その内部でインタラクティブシェルを使用して、Djangoプロジェクトの管理ユーザーを作成します。

docker run -i -t --env-file env django-polls:v0 sh

これにより、実行中のコンテナ内にシェルプロンプトが表示され、Djangoユーザーの作成に使用できます。

python manage.py createsuperuser

ユーザーのユーザー名、メールアドレス、パスワードを入力し、ユーザーを作成した後、 `+ CTRL + D +`を押してコンテナを終了して強制終了します。

最後に、アプリの静的ファイルを生成し、 `+ collectstatic +`を使用してそれらをDigitalOcean Spaceにアップロードします。

docker run --env-file env django-polls:v0 sh -c "python manage.py collectstatic --noinput"
Output
121 static files copied.

これでアプリを実行できます:

docker run --env-file env -p 80:8000 django-polls:v0
Output[2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1)
[2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync
[2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7
[2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8
[2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

ここでは、Dockerfileで定義されているデフォルトのコマンド `+ gunicorn --bind:8000 --workers 3 mysite.wsgi:application `を実行し、コンテナポート ` 8000 `を公開して、 Ubuntuサーバーは、「 django-polls:v0 」コンテナのポート「+8000」にマッピングされます。

これで、Webブラウザを使用してURLバーに「+ http:// 」と入力して、「 polls 」アプリに移動できるはずです。 「 / +」パスにルートが定義されていないため、404ページが見つかりませんというエラーが表示される可能性があります。

`+ http:/// polls +`に移動して、Pollsアプリのインターフェースを確認します。

image:https://assets.digitalocean.com/articles/scalable_django/polls_app.png [Polls App Interface]

管理インターフェースを確認するには、 `+ http:/// admin +`にアクセスしてください。 Pollsアプリの管理者認証ウィンドウが表示されます。

image:https://assets.digitalocean.com/articles/scalable_django/polls_admin.png [Polls Admin Auth Page]

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

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

image:https://assets.digitalocean.com/articles/scalable_django/polls_admin_main.png [Polls Admin Main Interface]

`+ admin `および ` polls +`アプリの静的アセットは、オブジェクトストレージから直接配信されることに注意してください。 これを確認するには、https://www.digitalocean.com/community/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managed-databases-and-spaces#testing-spacesを参照してください-static-file-delivery [スペースの静的ファイル配信のテスト]。

探索が終了したら、Dockerコンテナを実行しているターミナルウィンドウで `+ CTRL-C +`を押してコンテナを強制終了します。

結論

このチュートリアルでは、コンテナベースのクラウドネイティブ環境で効果的に動作するようにDjango Webアプリを調整しました。 次に、コンテナイメージ用の最小限のDockerfileを作成し、ローカルでビルドして、Docker Engineを使用して実行しました。 diffを確認できます。 PollsアプリGitHubリポジトリの/ polls-docker [polls-docker branch]。 このブランチには、このチュートリアルで説明されているすべての変更が含まれています。

ここから、Django / GunicornコンテナをNginxリバースプロキシコンテナとペアリングして、着信HTTPリクエストを処理およびルーティングし、https://certbot.eff.org/ [Certbot]コンテナとペアリングしてTLS証明書を取得できます。 Docker Composeを使用して、このマルチコンテナアーキテクチャを管理できます。これについては、後続のチュートリアルで説明します。

現状では、HTTPプロキシの背後でGunicornを常に実行し、低速のクライアントをバッファリングする必要があるため、このセットアップは生産準備が整っていないことに注意してください。 そうでない場合、Django WebアプリはDoS攻撃に対して脆弱になります。 このチュートリアルでは、Gunicornワーカーの任意の数として3を選択しました。 実稼働環境では、パフォーマンスベンチマークを使用してワーカーとスレッドの数を設定する必要があります。

このアーキテクチャでは、コンテナがこれらのアセットのバージョンをバンドルしてNginxを使用してサービスを提供する必要がないように、静的アセットをオブジェクトストレージにオフロードする設計上の選択を行いました。 。 ユースケースによっては、これは効果的な設計ではない可能性があるため、このチュートリアルの手順を適宜変更する必要があります。

最後に、Django Pollsアプリを完全にコンテナー化したので、イメージをhttps://hub.docker.com/[Dockerhub]などのコンテナーレジストリにプッシュし、Dockerが利用可能なすべてのシステムで利用できるようにします。Ubuntuサーバー、仮想マシン、Kubernetesなどのコンテナクラスタ。

Related