序章

Django は、Pythonアプリケーションをすばやく立ち上げるのに役立つ強力なWebフレームワークです。 これには、オブジェクトリレーショナルマッパー、ユーザー認証、アプリケーション用のカスタマイズ可能な管理インターフェイスなど、いくつかの便利な機能が含まれています。 また、キャッシングフレームワークが含まれており、URLディスパッチャーおよびテンプレートシステムを通じてクリーンなアプリの設計を促進します。

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

これらの変更は、スケーラブルなクラウドネイティブWebアプリを構築するための Twelve-Factor 方法論に触発されて、サンプルのDjango Pollsアプリに実装します。 次に、アプリケーションイメージをビルドし、Dockerを使用してコンテナー化されたアプリを実行します。

このチュートリアルの終わりまでに、スケーラブルなDjangoアプリをセットアップする方法のセットアップをコンテナ化したことになります。 このシリーズの後続のチュートリアルでは、 Docker Compose を使用してDjangoコンテナーをNginxリバースプロキシとペアリングし、このアーキテクチャをKubernetesクラスターにデプロイする方法を学習します。

チュートリアルを実行してアプリに加えている変更を理解することを強くお勧めしますが、先にスキップしたい場合は、の polls-dockerbranchから変更されたコードを取得できます。アプリのGitHubリポジトリをポーリングします。

前提条件

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

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

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

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

sudo apt update
sudo apt install postgresql-client

打つ Y その後 ENTER パッケージのダウンロードとインストールを開始するように求められたら。

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

まず、クラウドコントロールパネルからデータベースに移動し、データベースをクリックして、クラスターの接続パラメーターを取得します。 クラスタの接続パラメータを含む接続の詳細ボックスが表示されます。 これらを書き留めてください。

コマンドラインに戻り、これらの資格情報と psql インストールしたばかりのPostgreSQLクライアント:

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

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

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

まず、プロジェクトのデータベースを作成します。 polls:

CREATE DATABASE polls;

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

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

\c polls;

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

CREATE USER sammy WITH PASSWORD 'password';

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

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

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パッケージの依存関係を明示的に定義します。

ステップ2—アプリリポジトリのクローンを作成して依存関係を宣言する

Django Pollsアプリをコンテナ化するプロセスを開始するには、まず django-polls リポジトリのクローンを作成します。このリポジトリには、Djangoプロジェクトのtutorialの完全なコードが含まれています。ポーリングアプリ。

サーバーにログインし、というディレクトリを作成します polls-project と使用 git クローンを作成するには django-polls GitHubからのリポジトリ:

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

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

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

次のオブジェクトが表示されます。

  • manage.py:アプリの操作に使用されるメインのコマンドラインユーティリティ。
  • polls:が含まれています polls アプリコード。
  • mysite:Djangoプロジェクトスコープのコードと設定が含まれています。
  • templates:管理インターフェース用のカスタムテンプレートファイルが含まれています。

プロジェクトの構造とファイルの詳細については、Djangoの公式ドキュメントからプロジェクトの作成を参照してください。

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

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

polls-project / django-polls / require
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を構成可能にする

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

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

これを行うために、私たちは通過します settings.py 実行時に設定する各変数のハードコードされた値を、 os.getenv. The 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. ただし、今回は、変数が設定されていない場合にフォールバックとして使用されるデフォルト値を提供しました。 この場合、設定することを選択しました DEBUGFalse 変数が意図的に定義されて設定されていない限り、機密情報が誤って漏洩しないように値が指定されていない場合 True

この手法を適用するには、 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 を介して渡されたJSONオブジェクトを逆シリアル化する DATABASE_OPTIONS 環境変数。 ほとんどの場合、環境変数を単純な文字列として解釈すると、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互換のオブジェクトストレージサービスにオフロードされます。

ステップ4—静的資産のオフロード

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

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

django-storages パッケージは、Djangoがファイルをオフロードするために使用できるリモートストレージバックエンド(S3互換のオブジェクトストレージを含む)を提供します。 使用するように投票アプリを構成します django-storages DigitalOceanマネージドデータベースとスペースを使用してスケーラブルなDjangoアプリをセットアップする方法のステップ7で概説されているように、静的ファイルをDigitalOceanスペースにアップロードします。 このガイドでは、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',
]
. . .

The storages アプリは経由でインストールされます django-storages の中に requirements.txt 手順1で定義したファイル。

ここで、 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:
# 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: Defines the access control list (ACL) for the static files. Setting it to public-read`は、エンドユーザーがファイルにパブリックにアクセスできるようにします。
  • STATIC_URL:静的ファイルのURLを生成するときにDjangoが使用するベースURLを指定します。 ここでは、エンドポイントURLと静的ファイルサブディレクトリを組み合わせて、静的ファイルのベースURLを作成します。
  • STATIC_ROOT:静的ファイルをオブジェクトストレージにコピーする前に、ローカルで収集する場所を指定します。

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

  • AWS_ACCESS_KEY_ID:によって設定 STATIC_ACCESS_KEY_ID 環境変数。 DigitalOceanSpacesアクセスキー識別子。
  • AWS_SECRET_ACCESS_KEY: によって設定されました STATIC_SECRET_KEY. DigitalOceanSpacesの秘密鍵。
  • AWS_STORAGE_BUCKET_NAME: によって設定されました STATIC_BUCKET_NAME. Djangoがアセットをアップロードするオブジェクトストレージバケット。
  • AWS_S3_ENDPOINT_URL: によって設定されました STATIC_ENDPOINT_URL. オブジェクトストレージサービスへのアクセスに使用されるエンドポイントURL。 DigitalOcean Spacesの場合、これは次のようになります https://nyc3.digitaloceanspaces.com、Spacesバケットが配置されている地域によって異なります。

変更が完了したら settings.py、ファイルを保存して閉じます。

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

この時点で、DigitalOcean Spaceを使用している場合は、オプションでSpaceのCDNを有効にできます。これにより、地理的に分散したエッジサーバーのネットワークにキャッシュすることで、Djangoプロジェクトの静的ファイルの配信が高速化されます。 オプションで、スペースのカスタムサブドメインを構成することもできます。 CDNの詳細については、CDNを使用した静的コンテンツ配信の高速化を参照してください。 CDNの構成はこのチュートリアルの範囲を超えていますが、手順はDigitalOceanマネージドデータベースとスペースを使用したスケーラブルなDjangoアプリのセットアップ方法CDNの有効化セクションの手順と非常によく一致しています。 。

次のステップでは、次のような最終的な変更を行います。 settings.py これにより、STDOUTおよびSTDERRへのDjangoロギングが有効になり、これらのストリームをDockerエンジンで取得して、を使用して検査できるようになります。 docker logs.

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

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

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

幸い、Djangoへのログインには高度に構成可能なものが使用されます logging Python標準ライブラリのモジュールなので、 logging.config.dictConfig に渡す辞書を定義して、目的の出力とフォーマットを定義できます。 Djangoロギングを構成するためのこの手法やその他の手法の詳細については、 Django Logging、The RightWayを参照してください。

もう一度、開いてください django-polls/mysite/settings.py エディターで。

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

polls-project / django-polls / mysite / settings.py
import json
import os
import logging.config
. . .

The logging.config importを使用すると、新しいロギング構成のディクショナリをに渡すことで、Djangoのデフォルトのロギング動作をオーバーライドできます。 dictConfig 関数。

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

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_CONFIGNone Djangoが提供するデフォルトのログ設定を無効にします。 設定しました LOGLEVELINFO デフォルトですが、 DJANGO_LOGLEVEL 必要に応じてオーバーライドできるように、環境変数。

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

これは非常に最小限の構成であり、次の環境変数を使用してログの重大度レベルを指定できます。 DJANGO_LOGLEVEL、次に、そのレベル以上のすべてのメッセージを標準ストリームに記録します。 Djangoのロギングメカニズムの詳細については、公式のDjangoドキュメントのLoggingを参照してください。

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

これで、DjangoPollsアプリのコード変更は完了です。 次のステップでは、アプリのDockerfileを記述して、コンテナー化プロセスを開始します。

ステップ6—アプリケーションDockerfileを作成する

このステップでは、Djangoアプリを実行するコンテナーイメージと、それを提供するGunicornWSGIサーバーを定義します。 これには、ランタイム環境を定義し、アプリケーションとその依存関係をインストールし、いくつかの基本的な構成を完了することによって、コンテナーイメージを構築することが含まれます。 アプリケーションをコンテナイメージにカプセル化する方法はたくさんありますが、この手順に従うと、スリムで合理化されたアプリイメージが生成されます。

適切な親画像の選択

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

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

私たちのアプリケーションはDjangoで構築されているため、標準のPython環境を備えたイメージは強固な基盤を提供し、開始するために必要なツールの多くが含まれています。 Pythonの公式Dockerリポジトリは、さまざまなPythonベースのイメージを提供し、それぞれがオペレーティングシステムの上にPythonのバージョンといくつかの一般的なツールをインストールします。

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

注: Pythonイメージのタグのリストで、各イメージに複数のタグが使用可能であることに気付いたかもしれません。 Dockerタグは変更可能であり、メンテナは将来、同じタグを別のイメージに再割り当てできます。 その結果、多くのメンテナは、さまざまなユースケースを可能にするために、さまざまな程度の特異性を持つタグのセットを提供しています。 たとえば、タグ 3-alpine は、最新のAlpineバージョンで利用可能な最新のPython 3バージョンを指すために使用されるため、PythonまたはAlpineの新しいバージョンがリリースされると、別のイメージに再割り当てされます。 イメージビルドをより決定論的にするには、使用するイメージに対して見つけることができる最も具体的なタグを使用するのが最善です。

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

まず、からナビゲートします django-polls ディレクトリ。

  1. 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の構築についてさらに詳しくは、Djangoアプリ用の本番環境対応Dockerfileを参照してください。

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

次に、シングルがあります RUN Linuxを使用してそれぞれがチェーンされたコマンドの長いリストを実行する命令 && オペレーター。 要約すると、これらのコマンドは次のとおりです。

  • Alpineを使用してPostgreSQL開発ファイルと基本的なビルドの依存関係をインストールします apk パッケージマネージャー
  • 仮想環境を作成する
  • にリストされているPython依存関係をインストールします requirements.txtpip
  • インストールされたPythonパッケージの要件を分析して、実行時に必要なパッケージのリストをコンパイルします
  • 不要なビルドの依存関係をアンインストールします

コマンドを個別に実行するのではなく、連鎖させます RUN Dockerがイメージレイヤーを構築する方法のためのステップ。 それぞれについて ADD, COPY、 と RUN 命令の場合、Dockerは既存のファイルシステムの上に新しいイメージレイヤーを作成し、命令を実行して、結果のレイヤーを保存します。 これは、コマンドを圧縮することを意味します RUN 指示により、画像レイヤーが少なくなります。

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

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

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

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

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

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

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

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

単独で使用する場合、 ENTRYPOINT コンテナの実行可能ファイルを設定しますが、デフォルトの引数リストは定義しません。 だけなら CMD が設定されている場合、デフォルトのコマンドおよび引数リストとして解釈され、実行時にオーバーライドできます。

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

The 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つの形式は、コマンドとその引数を指定しますが、いくつかの重要な違いがあります。 The exec 推奨されるformatは、コマンドを直接実行し、シェル処理なしで引数リストを渡します。 一方、シェル形式はリスト全体をに渡します sh -c. これは、たとえば、コマンドで環境変数の値を置き換える必要がある場合に必要ですが、一般的に予測可能性が低いと見なされます。

私たちの目的のために、私たちの最後の指示 Dockerfile このように見えます:

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

デフォルトでは、このイメージを使用するコンテナが実行されます gunicorn ローカルホストポートにバインドされています 8000 3人の労働者と、そして実行します application の機能 wsgi.py で見つかったファイル mysite ディレクトリ。 オプションで、実行時にコマンドを提供して、代わりに別のプロセスを実行できます。 gunicorn.

この時点で使用できます docker build アプリの画像を作成し、 docker run マシンでコンテナを実行します。

Dockerイメージの構築

デフォルトでは、 docker build コマンドは Dockerfile 現在のディレクトリで、ビルド手順を確認します。 また、ビルドプロセス中に使用可能になるはずのローカルファイルシステム階層であるビルド「コンテキスト」をDockerデーモンに送信します。 多くの場合、現在のディレクトリがビルドコンテキストとして設定されます。

あなたを含むディレクトリにアクセスした後 Dockerfile、 走る docker build、画像とタグ名を -t フラグを立て、現在のディレクトリをビルドコンテキストとして使用します。 ここでは、画像に名前を付けます django-polls バージョンでタグ付けします v0:

  1. docker build -t django-polls:v0 .

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

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

Output
Successfully built 8260b58f5713 Successfully tagged django-polls:v0

イメージのビルドに成功すると、を使用してアプリコンテナを実行できるようになります docker run. しかし run コンテナの実行環境をまだ構成していないため、コマンドはここで失敗する可能性があります。 のような外部化された変数 SECRET_KEY およびデータベース設定 settings.py 空白になるか、デフォルト値に設定されます。

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

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

Dockerは、コンテナー内で環境変数を設定するためのいくつかのメソッドを提供します。 手順1で外部化したすべての変数を設定する必要があるため、 --env-file メソッド。これにより、環境変数とその値のリストを含むファイルを渡すことができます。

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

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 DjangoAppチュートリアルのAppSettingsの調整にあります。
  • 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を本番環境で実行する場合は、必ず設定してください DEBUGFalse 必要な詳細度に応じてログレベルを調整します。

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

これから使用します docker run をオーバーライドするには CMD Dockerfileに設定し、を使用してデータベーススキーマを作成します manage.py makemigrationsmanage.py migrate コマンド:

  1. 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"、アプリコードで定義されたデータベーススキーマを作成します。 コマンドを実行すると、次のように表示されます。

Output
No 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プロジェクトの管理ユーザーを作成します。

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

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

  1. python manage.py createsuperuser

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

最後に、アプリの静的ファイルを生成し、を使用してDigitalOceanSpaceにアップロードします。 collectstatic:

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

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

  1. 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 そのポート 80 Ubuntuサーバーでポートにマップされます 8000django-polls:v0 容器。

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

案内する http://your_server_ip/polls 投票アプリのインターフェースを表示するには:

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

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

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

の静的アセットに注意してください adminpolls アプリはオブジェクトストレージから直接配信されています。 これを確認するには、テストスペースの静的ファイル配信を参照してください。

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

結論

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

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

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

このアーキテクチャでは、静的アセットをオブジェクトストレージにオフロードする設計を選択しました。これにより、コンテナはこれらのアセットのバージョンをバンドルしてNginxを使用して提供する必要がなくなり、Kubernetesなどのマルチコンテナクラスタ環境での管理が煩雑になる可能性があります。 。 ユースケースによっては、これは効果的な設計ではない可能性があるため、それに応じてこのチュートリアルの手順を調整する必要があります。

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