前書き
Djangoは、Pythonアプリケーションをすぐに立ち上げるのに役立つ強力なWebフレームワークです。 これには、object-relational mapper、ユーザー認証、アプリケーションのカスタマイズ可能な管理インターフェイスなど、いくつかの便利な機能が含まれています。 また、caching frameworkが含まれており、URL DispatcherとTemplate systemを介してクリーンなアプリの設計を促進します。
このチュートリアルでは、Dockerコンテナを使用してスケーラブルでポータブルなDjangoPollsアプリを構築する方法を学習します。 すぐに使用できるDjangoアプリは、標準出力ストリームへのログ記録や、コンテナに渡された環境変数を使用した設定など、コンテナ内で効果的に実行するためにいくつかの変更が必要です。 さらに、JavaScriptやCSSスタイルシートなどの静的アセットをオブジェクトストレージにオフロードすると、マルチコンテナ環境でこれらのファイルの管理を合理化および一元化できます。
これらの変更は、スケーラブルなクラウドネイティブWebアプリを構築するためのTwelve-Factor手法に触発されて、サンプルのDjangoPollsアプリに実装します。 次に、アプリケーションイメージをビルドし、Dockerでコンテナー化されたアプリを実行します。
このチュートリアルの終わりまでに、セットアップをHow to Set Up a Scalable Django Appでコンテナ化したことになります。 このシリーズの後続のチュートリアルでは、Docker Composeを使用してDjangoコンテナをNginxリバースプロキシとペアリングし、このアーキテクチャをKubernetesクラスタにデプロイする方法を学習します。
チュートリアルを進めてアプリに加えている変更を理解することを強くお勧めしますが、先にスキップしたい場合は、PollsアプリのGitHubリポジトリのpolls-docker branchから変更されたコードを取得できます。
前提条件
このチュートリアルに従うには、次のものが必要です。
-
このInitial Server Setup guideに従ってセットアップされたUbuntu18.04サーバー。
-
How To Install and Use Docker on Ubuntu 18.04のステップ1と2に従って、サーバーにDockerがインストールされました。 手順2で説明したように、必ずユーザーを
docker
グループに追加してください。 -
Djangoプロジェクトの静的ファイルとこのスペースのアクセスキーのセットを保存するためのDigitalOcean Space。 スペースの作成方法については、How to Create Spacesの製品ドキュメントを参照してください。 スペースのアクセスキーを作成する方法については、Sharing Access to Spaces with Access Keysを参照してください。 わずかな変更で、S3互換のオブジェクトストレージサービスを使用できます。
-
DigitalOceanマネージPostgreSQLクラスター。 クラスターの作成方法については、DigitalOceanManaged Databases product documentationを参照してください。 小さな変更を加えるだけで、Django supportsの任意のデータベースを使用できます。
[[step-1 -—- creating-the-postgresql-database-and-user]] ==ステップ1—PostgreSQLデータベースとユーザーの作成
まず、UbuntuインスタンスからPostgreSQLサーバーに接続します。 次に、DjangoアプリのPostgreSQLデータベースとユーザーを作成し、Djangoと効果的に連携するようにデータベースを構成します。
Ubuntuマシン(アプリコンテナーではない)からデータベースに接続する前に、Ubuntuリポジトリからpostgresql-client
パッケージをインストールする必要があります。 最初にローカルのapt
パッケージインデックスを更新してから、パッケージをダウンロードしてインストールします。
sudo apt update
sudo apt install postgresql-client
パッケージのダウンロードとインストールを開始するように求められたら、Y
を押してからENTER
を押します。
クライアントをインストールしたので、これを使用して、Djangoアプリケーションのデータベースとデータベースユーザーを作成します。
まず、Cloud Control PanelからDatabasesに移動し、データベースをクリックして、クラスターのConnection Parametersを取得します。 クラスタのいくつかのConnection parametersを含むConnection Detailsボックスが表示されます。 これらに注意してください。
コマンドラインに戻り、次の認証情報とインストールしたばかりのpsql
PostgreSQLクライアントを使用してクラスターにログインします。
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 sammy WITH PASSWORD 'password';
ここで、作成したユーザーの接続パラメーターのいくつかを変更します。 これにより、データベース操作が高速化されるため、接続が確立されるたびに正しい値を照会および設定する必要がなくなります。
Djangoが期待するデフォルトのエンコーディングをUTF-8
に設定しています。 また、デフォルトのトランザクション分離スキームを「コミット済み読み取り」に設定しています。これは、コミットされていないトランザクションからの読み取りをブロックします。 最後に、タイムゾーンを設定しています。 デフォルトでは、DjangoプロジェクトはUTC
を使用するように設定されます。 これらはすべてthe Django project itselfからの推奨事項です。
PostgreSQLプロンプトで次のコマンドを入力します。
ALTER ROLE sammy SET client_encoding TO 'utf8';
ALTER ROLE sammy SET default_transaction_isolation TO 'read committed';
ALTER ROLE sammy SET timezone TO 'UTC';
これで、新しいユーザーに新しいデータベースを管理するアクセス権を与えることができます。
GRANT ALL PRIVILEGES ON DATABASE polls TO sammy;
終了したら、次のように入力してPostgreSQLプロンプトを終了します。
\q
適切に設定されたDjangoアプリは、このデータベースに接続して管理できるようになりました。 次のステップでは、GitHubからPollsアプリコードを複製し、Pythonパッケージの依存関係を明示的に定義します。
[[step-2 -—- cloning-app-repository-and-declaring-dependencies]] ==ステップ2—アプリリポジトリのクローンを作成して依存関係を宣言する
Django Pollsアプリをコンテナ化するプロセスを開始するには、最初にdjango-pollsリポジトリのクローンを作成します。このリポジトリには、DjangoプロジェクトのtutorialPollsアプリの完全なコードが含まれています。
サーバーにログインし、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の公式ドキュメントの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-storages
プラグイン、gunicorn
WSGIサーバー、psycopg2
PostgreSQLアダプター、およびいくつかの追加の依存関係パッケージであるDjangoをインストールします。 アプリで必要なすべてのPythonパッケージを明示的にリストおよびバージョン化することに注意してください。
ファイルを保存して閉じます。
アプリのクローンを作成し、その依存関係を定義したので、移植性を高めるために変更に進みます。
[[step-3 -—- making-django-configurable-by-environment-variables]] ==ステップ3—Djangoを環境変数で構成可能にする
the twelve-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 = os.getenv('DJANGO_SECRET_KEY')
. . .
DEBUG = os.getenv('DJANGO_DEBUG', False)
. . .
SECRET_KEY
の場合、DjangoはDJANGO_SECRET_KEY
という環境変数を探します。 これはハードコードされてはならず、アプリケーションサーバー全体で同じである必要があるため、フォールバック値なしで外部で設定する必要があります。 これを提供しない場合、アプリケーションのさまざまなコピーが異なるキーを使用すると問題が発生する可能性があるため、アプリケーションが失敗するようにします。
DEBUG
の場合、DjangoはDJANGO_DEBUG
という環境変数を探します。 ただし、今回は、変数が設定されていない場合のフォールバックとして使用されるデフォルト値を提供しました。 この場合、変数が意図的に定義されてTrue
に設定されていない限り、機密情報が誤って漏洩しないように、値が指定されていない場合はDEBUG
をFalse
に設定することを選択しました。 。
この手法を適用するには、選択したエディターでpolls-project/django-polls/mysite/settings.py
ファイルを開き、ファイル内を移動して、提供されたデフォルト値で次の変数を外部化します。
-
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')
-
DEBUG = os.getenv('DEBUG', False)
-
ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '127.0.0.1').split(',')
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
import json
. . .
特別な注意が必要な他の領域はDATABASES['default']['NAME']
です。 ほとんどのデータベースエンジンでは、これはリレーショナルデータベース管理システム内のデータベース名です。 一方、SQLiteを使用している場合は、NAME
を使用してデータベースファイルを指定するため、このパラメータを適宜設定してください。
settings.py
ファイルはPythonコードであるため、環境からの値の読み取りを処理する方法は多数あります。 ここで使用した方法は、コードベースから構成を外部化するための1つの可能な手法にすぎません。
このステップでは、データベースパラメータを含む一般的な移植可能な方法で、メインのDjango設定変数を設定しました。 次の手順では、JavascriptやCSSスタイルシートなどの静的ファイルの設定を引き続き行います。これらの設定は、S3互換のオブジェクトストレージサービスに集中してオフロードします。
[[step-4 -—- offloading-static-assets]] ==ステップ4—静的アセットのオフロード
実稼働環境で複数のDjangoコンテナーを実行している場合、実行中のコンテナー全体にわたって特定のバージョンの静的アセットとファイルを維持するのは面倒です。 このアーキテクチャを合理化するために、すべての共有要素と状態を外部ストレージにオフロードできます。 これらのアイテムをレプリカ間で同期させたり、バックアップおよびロードルーチンを実装してデータをローカルで利用できるようにする代わりに、これらの資産へのアクセスをネットワークアクセス可能なサービスとして実装できます。
最後のステップでは、環境変数を介してデータベース接続パラメーターを渡すことができるようにDjangoを構成しました。 このステップでは、Djangoコンテナで共有される静的アセットを保存するために使用するオブジェクトストレージサービスについても同じことを行います。
django-storagesパッケージは、Djangoがファイルをオフロードするために使用できるリモートストレージバックエンド(S3互換のオブジェクトストレージを含む)を提供します。 How to Set Up a Scalable Django App with DigitalOcean Managed Databases and Spacesのステップ7で概説されているように、django-storages
を使用して静的ファイルをDigitalOceanSpaceにアップロードするようにPollsアプリを構成します。 このガイドでは、DigitalOcean Spacesを使用しますが、S3互換のオブジェクトストレージプロバイダーを使用できます。
まず、前の手順で変更したのと同じdjango-polls/mysite/settings.py
ファイルにいくつかの変更を加えます。
mysite/settings.py
ファイルを開いて編集し、storages
アプリをDjangoのINSTALLED_APPS
リストに追加することから始めます。
polls-project/django-polls/mysite/settings.py
. . .
INSTALLED_APPS = [
. . .
'django.contrib.staticfiles',
'storages',
]
. . .
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
バックエンドは、DigitalOceanSpacesを含むすべてのS3互換バックエンドで動作するはずです。 -
AWS_S3_OBJECT_PARAMETERS
静的ファイルにキャッシュ制御ヘッダーを設定します。 -
AWS_LOCATION
:すべての静的ファイルが配置されるオブジェクトストレージバケット内のstatic
というディレクトリを定義します。 -
`+AWS_DEFAULT_ACL+
:静的ファイルのアクセス制御リスト(ACL)を定義します。public-read
に設定すると、エンドユーザーがファイルにパブリックにアクセスできるようになります。 -
STATIC_URL
:静的ファイルのURLを生成するときにDjangoが使用するベース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の詳細については、Using a CDN to Speed Up Static Content Deliveryを参照してください。 CDNの構成はこのチュートリアルの範囲を超えていますが、手順はHow to Set Up a Scalable Django App with DigitalOcean Managed Databases and SpacesのEnabling CDNセクションの手順と非常によく一致しています。
次のステップでは、settings.py
に最終的な変更を加えます。これにより、DjangoがSTDOUTとSTDERRにログを記録できるようになり、これらのストリームをDocker Engineが取得して、docker logs
を使用して検査できるようになります。
[[step-5 -—- configuring-logging]] ==ステップ5—ロギングの構成
デフォルトでは、Djangoは、開発HTTPサーバーを実行しているとき、またはDEBUG
オプションがTrue
に設定されているときに、情報を標準出力と標準エラーに記録します。 ただし、DEBUG
がFalse
に設定されている場合、または別のHTTPサーバーを使用している場合は、どちらも実稼働環境で当てはまる可能性があり、Djangoは別のログメカニズムを使用します。 優先度INFO
以上のすべてを標準ストリームに記録する代わりに、優先度ERROR
またはCRITICAL
のメッセージを管理用電子メールアカウントに送信します。
これは多くの状況で理にかなっていますが、Kubernetesおよびコンテナー化された環境では、標準出力および標準エラーへのログ記録を強くお勧めします。 ロギングメッセージは、ノードのファイルシステム上の一元化されたディレクトリに収集され、kubectl
およびdocker
コマンドを使用してインタラクティブにアクセスできます。 このノードレベルの集約は、運用チームが各ノードでプロセスを実行してログを監視および転送できるようにすることで、ログの収集を容易にします。 このアーキテクチャを活用するには、アプリケーションはこれらの標準シンクにログを書き込む必要があります。
幸い、Djangoにログインするには、Python標準ライブラリの高度に構成可能なlogging
モジュールを使用するため、logging.config.dictConfig
に渡す辞書を定義して、目的の出力とフォーマットを定義できます。 Djangoロギングを構成するためのこの手法やその他の手法の詳細については、Django Logging, The Right Wayを参照してください。
もう一度、エディターでdjango-polls/mysite/settings.py
を開きます。
まず、ログ構成を操作できるように、ファイルの先頭にimport
ステートメントを追加します。
polls-project/django-polls/mysite/settings.py
import json
import os
import logging.config
. . .
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ドキュメントのLoggingを参照してください。
この構成では、アプリケーションをコンテナー化するときに、Dockerはdocker logs
コマンドを介してこれらのログを公開します。 同様に、Kubernetesは出力をキャプチャし、kubectl logs
コマンドを使用して公開します。
これで、Django Pollsアプリへのコードの変更は終わりです。 次のステップでは、アプリのDockerfileを作成してコンテナ化プロセスを開始します。
[[step-6 -—- writing-the-application-dockerfile]] ==ステップ6—アプリケーションDockerfileの書き込み
このステップでは、Djangoアプリを実行するコンテナーイメージと、それを提供するGunicorn WSGIサーバーを定義します。 ランタイム環境を定義してコンテナイメージを構築し、アプリケーションとその依存関係をインストールし、基本的な構成を完了する必要があります。 アプリケーションをコンテナイメージにカプセル化する方法は多数ありますが、この手順で実行するプラクティスにより、スリムで効率的なアプリイメージが生成されます。
適切な親画像の選択
コンテナイメージを構築する際に最初に決定しなければならない重要な決定は、構築の基礎となります。 コンテナイメージは、空のファイルシステムを示すSCRATCH
から、または既存のコンテナイメージから構築できます。 多くの異なるベースコンテナイメージが利用可能で、それぞれがファイルシステムを定義し、プリインストールされたパッケージの一意のセットを提供します。 Ubuntu 18.04のようなバニラLinuxディストリビューションに基づくイメージは、一般的な動作環境を提供しますが、より専門的なイメージには、特定のプログラミング言語用の共通ライブラリとツールが含まれることがよくあります。
可能な限り、Docker’s official repositoriesのいずれかからの画像をベースとして使用することをお勧めします。 これらの画像は、ベストプラクティスに従うためにDockerによって検証されており、セキュリティの修正と改善のために定期的に更新されます。
アプリケーションはDjangoで構築されているため、標準のPython環境のイメージは強固な基盤を提供し、開始するために必要な多くのツールを含みます。 Pythonの公式Dockerリポジトリはa wide selection of Python-based imagesを提供し、それぞれがオペレーティングシステムの上にPythonのバージョンといくつかの一般的なツールをインストールします。
適切な機能レベルはユースケースによって異なりますが、Alpine Linuxに基づく画像は、多くの場合、確実な出発点になります。 Alpine Linuxは、アプリケーションを実行するための堅牢で最小限のオペレーティング環境を提供します。 デフォルトのファイルシステムは非常に小さいですが、機能を簡単に追加できるように、かなり広範なリポジトリを備えた完全なパッケージ管理システムが含まれています。
[.note]#Note:list of tags for Python imagesで、各画像に複数のタグが使用可能であることに気付いたかもしれません。 Dockerタグは変更可能であり、メンテナは将来、同じタグを別のイメージに再割り当てできます。 その結果、多くのメンテナは、さまざまなユースケースを可能にするために、さまざまな程度の特異性を持つタグのセットを提供しています。 たとえば、タグ3-alpine
は、最新のAlpineバージョンで利用可能な最新のPython 3バージョンを指すために使用されるため、PythonまたはAlpineの新しいバージョンがリリースされると、別のイメージに再割り当てされます。 イメージビルドをより決定論的にするには、使用するイメージに対して見つけることができる最も具体的なタグを使用するのが最善です。
#
このガイドでは、3.7.4-alpine3.10
としてタグ付けされたPythonイメージをDjangoアプリケーションの親イメージとして使用します。 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アプリ用の本番環境対応のDockerfileの構築についてさらに詳しくは、A Production-Ready Dockerfile for your Django Appを参照してください。
最初に、Dockerはrequirements.txt
ファイルを/app/requirements.txt
にコピーして、アプリケーションの依存関係をイメージのファイルシステムで利用できるようにします。 これを使用して、アプリケーションを実行するために必要なすべてのPythonパッケージをインストールします。 依存関係ファイルをコードベースの別のステップとしてコピーし、依存関係ファイルを含むイメージレイヤーをDockerがキャッシュできるようにします。 ビルド間でrequirements.txt
ファイルが変更されない場合はいつでも、Dockerはキャッシュされたレイヤーを再構築する代わりに再利用できるため、プロセスが高速化されます。
次に、コマンドの長いリストを実行する単一のRUN
命令があり、それぞれがLinuxの&&
演算子を使用してチェーン化されています。 要約すると、これらのコマンドは次のとおりです。
-
Alpineの
apk
パッケージマネージャーを使用して、PostgreSQL開発ファイルと基本的なビルドの依存関係をインストールします -
仮想環境を作成する
-
requirements.txt
にリストされているPython依存関係をpip
でインストールします -
インストールされたPythonパッケージの要件を分析して、実行時に必要なパッケージのリストをコンパイルします
-
不要なビルド依存関係をアンインストールします
Dockerがイメージレイヤーを構築する方法のため、コマンドを個別のRUN
ステップで実行するのではなく、コマンドをチェーンします。 Dockerは、ADD
、COPY
、およびRUN
命令ごとに、既存のファイルシステムの上に新しいイメージレイヤーを作成し、命令を実行して、結果のレイヤーを保存します。 これは、RUN
命令でコマンドを圧縮すると、画像レイヤーが少なくなることを意味します。
アイテムがイメージレイヤーに書き込まれると、イメージサイズを小さくするために後続のレイヤーでアイテムを削除することはできません。 ビルドの依存関係をインストールしたが、アプリケーションのセットアップ後にそれらを削除する場合は、同じ命令内で削除して、イメージサイズを縮小する必要があります。 このRUN
コマンドでは、ビルドの依存関係をインストールし、それらを使用してアプリのパッケージをビルドし、その後、apk del
を使用してそれらを削除します。
RUN
命令の後、ADD
を使用してアプリケーションコードをコピーし、WORKDIR
を使用してイメージの作業ディレクトリをコードディレクトリに設定します。
次に、ENV
命令を使用して、イメージから生成されたコンテナー内で使用できる2つの環境変数を設定します。 最初の命令はVIRTUAL_ENV
を/env
に設定し、2番目の命令はPATH
変数を変更して/env/bin
ディレクトリを含めます。 これらの2行は、仮想環境をアクティブ化する従来の方法である/env/bin/activate
スクリプトをソーシングした結果をエミュレートします。
最後に、EXPOSE
を使用して、コンテナーが実行時にポート8000
でリッスンすることをDockerに通知します。
この時点で、Dockerfile
はほぼ完了しています。 イメージを使用してコンテナを起動するときに実行されるデフォルトのコマンドを定義するだけです。
デフォルトのコマンドの定義
Dockerイメージのデフォルトのコマンドは、実行するコマンドを明示的に指定せずにコンテナーが開始されたときに何が起こるかを決定します。 ENTRYPOINT
およびCMD
命令は、独立して、またはタンデムで使用して、Dockerfile
内にデフォルトコマンドを定義できます。
ENTRYPOINT
とCMD
の両方が定義されている場合、ENTRYPOINT
はコンテナーによって実行される実行可能ファイルを定義し、CMD
はそのコマンドのデフォルトの引数リストを表します。 ユーザーは、コマンドラインに代替引数docker run <image> <arguments>
を追加することにより、デフォルトの引数リストを上書きできます。 この形式では、ユーザーはENTRYPOINT
コマンドを簡単にオーバーライドできないため、ENTRYPOINT
コマンドは、環境をセットアップし、受け取った引数リストに基づいてさまざまなアクションを実行するスクリプトに設定されることがよくあります。 。
単独で使用する場合、ENTRYPOINT
はコンテナの実行可能ファイルを構成しますが、デフォルトの引数リストを定義しません。 CMD
のみが設定されている場合、デフォルトのコマンドおよび引数リストとして解釈され、実行時にオーバーライドできます。
このイメージでは、コンテナがデフォルトでgunicorn
アプリケーションサーバーを使用してアプリケーションを実行するようにします。 gunicorn
に渡す引数リストは、実行時に構成可能である必要はありませんが、必要に応じて他のコマンドを簡単に実行して、管理タスク(静的アセットの収集やデータベースの初期化など)をデバッグまたは実行できるようにする必要があります。 。 これらの要件を念頭に置いて、CMD
を使用して、ENTRYPOINT
を含まないデフォルトのコマンドを定義することは理にかなっています。
CMD
命令は、次のいずれかの形式を使用して定義できます。
-
CMD ["argument 1", "argument 2", . . . ,"argument n"]
:引数リスト形式(ENTRYPOINT
のデフォルトの引数リストを定義するために使用) -
CMD ["command", "argument 1", "argument 2", . . . ,"argument n"]
:exec
形式 -
CMD command "argument 1" "argument 2" . . . "argument n"
:シェル形式
最初の形式は引数のみをリストし、ENTRYPOINT
と組み合わせて使用されます。 他の2つの形式では、コマンドとその引数を指定しますが、いくつかの重要な違いがあります。 推奨されるexec
形式は、コマンドを直接実行し、シェル処理なしで引数リストを渡します。 一方、シェル形式では、リスト全体がsh -c
に渡されます。 これは、たとえば、コマンドで環境変数の値を置き換える必要がある場合に必要ですが、一般的には予測不可能と見なされます。
私たちの目的では、Dockerfile
の最後の命令は次のようになります。
polls-project/Dockerfile
. . .
CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi:application"]
デフォルトでは、このイメージを使用するコンテナーは、ローカルホストポート8000
にバインドされたgunicorn
を3つのワーカーで実行し、mysite
にあるwsgi.py
ファイルでapplication
関数を実行します。 )sディレクトリ。 オプションで、実行時にgunicorn
の代わりに別のプロセスを実行するコマンドを提供できます。
この時点で、docker build
を使用してアプリイメージを構築し、docker run
を使用してマシン上でコンテナーを実行できます。
Dockerイメージの構築
デフォルトでは、docker build
コマンドは現在のディレクトリでDockerfile
を検索して、ビルド手順を検索します。 また、ビルド「コンテキスト」、つまりビルドプロセス中に使用できるローカルファイルシステム階層をDockerデーモンに送信します。 多くの場合、現在のディレクトリはビルドコンテキストとして設定されます。
Dockerfile
を含むディレクトリにアクセスした後、docker build
を実行し、-t
フラグを使用してイメージとタグ名を渡し、現在のディレクトリをビルドコンテキストとして使用します。 ここでは、イメージにdjango-polls
という名前を付け、バージョンv0
でタグ付けします。
docker build -t django-polls:v0 .
このコマンドは、Dockerfile
と現在のディレクトリをビルドコンテキストとしてDockerデーモンに渡します。 デーモンは、Dockerfile
命令を処理するときに、一連のイメージレイヤーを作成することによってイメージを構築します。
docker build
が完了すると、次の出力が表示されます。
OutputSuccessfully built 8260b58f5713
Successfully tagged django-polls:v0
イメージのビルドに成功すると、docker run
を使用してアプリコンテナを実行できるようになります。 ただし、コンテナの実行環境をまだ構成していないため、run
コマンドはここで失敗する可能性があります。 SECRET_KEY
などの外部化変数およびsettings.py
のデータベース設定は、空白になるか、デフォルト値に設定されます。
最後のステップでは、環境変数ファイルを使用してコンテナの実行環境を構成します。 次に、データベーススキーマを作成し、アプリの静的ファイルを生成してオブジェクトストレージにアップロードし、最後にアプリをテストします。
[[step-7 -—- configuring-the-running-environment-and-testing-the-app]] ==ステップ7—実行環境の構成とアプリのテスト
Dockerは、コンテナー内で環境変数を設定するためのseveral methodsを提供します。 手順1で外部化したすべての変数を設定する必要があるため、--env-file
メソッドを使用します。これにより、環境変数とその値のリストを含むファイルを渡すことができます。
polls-project
ディレクトリにenv
というファイルを作成し、次の変数リストに貼り付けます。
polls-project/env
DJANGO_SECRET_KEY=your_secret_key
DEBUG=True
DJANGO_ALLOWED_HOSTS=your_server_IP_address
DATABASE_ENGINE=postgresql_psycopg2
DATABASE_NAME=polls
DATABASE_USERNAME=sammy
DATABASE_PASSWORD=your_database_password
DATABASE_HOST=your_database_host
DATABASE_PORT=your_database_port
STATIC_ACCESS_KEY_ID=your_space_access_key_id
STATIC_SECRET_KEY=your_space_secret_key
STATIC_BUCKET_NAME=your_space_name
STATIC_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com
DJANGO_LOGLEVEL=info
このファイルの次の値を置き換えます。
-
DJANGO_SECRET_KEY
:Django docsで詳しく説明されているように、これを一意の予測できない値に設定します。 このキーを生成する1つの方法は、Scalable Django AppチュートリアルのAdjusting the App Settingsに記載されています。 -
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
:これを適切なスペースエンドポイント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
を使用してDigitalOceanSpaceにアップロードします。
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サーバーのポート80
がポート8000
にマップされるようにします。 django-polls:v0
コンテナ。
これで、URLバーにhttp://your_server_ip
と入力して、Webブラウザーを使用してpolls
アプリに移動できるようになります。 /
パスにルートが定義されていないため、404ページが見つかりませんというエラーが表示される可能性があります。これは予想どおりです。
http://your_server_ip/polls
に移動して、Pollsアプリのインターフェイスを確認します。
管理インターフェイスを確認するには、http://your_server_ip/admin
にアクセスしてください。 Pollsアプリの管理者認証ウィンドウが表示されます。
createsuperuser
コマンドで作成した管理ユーザー名とパスワードを入力します。
認証後、Pollsアプリの管理インターフェースにアクセスできます。
admin
およびpolls
アプリの静的アセットは、オブジェクトストレージから直接配信されていることに注意してください。 これを確認するには、Testing Spaces Static File Deliveryを参照してください。
探索が終了したら、Dockerコンテナを実行しているターミナルウィンドウでCTRL-C
を押して、コンテナを強制終了します。
結論
このチュートリアルでは、コンテナベースのクラウドネイティブ環境で効果的に動作するようにDjango Webアプリを調整しました。 次に、コンテナイメージ用の最小限のDockerfileを作成し、ローカルでビルドして、Docker Engineを使用して実行しました。 PollsアプリのGitHubリポジトリのpolls-docker branchに実装した変更のdiffを確認できます。 このブランチには、このチュートリアルで説明されているすべての変更が含まれています。
ここから、Django / GunicornコンテナをNginxリバースプロキシコンテナと組み合わせて着信HTTPリクエストを処理およびルーティングし、CertbotコンテナをペアにしてTLS証明書を取得できます。 このマルチコンテナアーキテクチャは、Docker Composeを使用して管理できます。これについては、後続のチュートリアルで説明します。
低速のクライアントをバッファリングするためにHTTPプロキシの背後でGunicornを実行する必要があるため、このセットアップはそのままでnotの本番環境に対応していることに注意してください。 そうでない場合、Django WebアプリはDoS攻撃に対して脆弱になります。 このチュートリアルでは、Gunicornワーカーの任意の数として3を選択しました。 実稼働環境では、パフォーマンスベンチマークを使用してワーカーとスレッドの数を設定する必要があります。
このアーキテクチャでは、コンテナがこれらのアセットのバージョンをバンドルしてNginxを使用してサービスを提供する必要がないように、静的アセットをオブジェクトストレージにオフロードする設計上の選択を行いました。 。 ユースケースによっては、これは効果的な設計ではない可能性があるため、このチュートリアルの手順を適宜変更する必要があります。
最後に、Django Pollsアプリを完全にコンテナ化したので、イメージをDockerhubなどのコンテナレジストリにプッシュして、Dockerが利用可能なすべてのシステム(Ubuntuサーバー、仮想マシン、コンテナクラスター)で利用できるようにすることができます。 Kubernetesのように。