ブログに戻る

Docker、Nginx、Let’s EncryptでDjangoアプリケーションを保護し、スケールする方法

Docker、Nginx、Let’s EncryptでDjangoアプリケーションを保護し、スケールする方法

何百万人ものユーザーが、学習、エンターテインメント、ニュース、友人との近況’の共有など、さまざまな目的で情報にアクセスするためにインターネットを利用しています。したがって、アプリをデプロイする際には、アプリケーションのために安全性が高くスケーラブルなインフラストラクチャを実装することが最善の利益となります。クラウドは、Django アプリケーションを保護し、スケールするためのさまざまな方法を提供します。水平スケーリングは、アプリの複数のコピーを実行できるようにする手法の1つです。これにより、耐障害性と高可用性が向上します。また、複数のリクエストを同時に処理するためのパフォーマンスも向上します。

Djangoアプリケーションの水平スケーリング

Djangoアプリケーションと、そのWSGI HTTPサーバー(GunicornuWSGI など)を実行する複数のアプリサーバーをプロビジョニングすることで、Djangoアプリケーションを水平スケーリングできます。その後、これらのアプリサーバーに受信リクエストを分散するためのインフラストラクチャをセットアップする必要があります。ロードバランサーとNginxのようなリバースプロキシ は、インフラストラクチャのトラフィック分散に役立ちます。NginxはSSL certificatesをデプロイして、HTTPSを介したアプリへの安全な接続を確保できます。最後に、Nginxは静的コンテンツのキャッシュを提供して、サーバーへの負荷を最小限に抑えることもできます。

これらのさまざまなコンポーネントを個別に構成し、それらが確実に通信できるようにすることは、困難な作業となる場合があります。幸いなことに、Dockerを使用すると、構成プロセスが簡素化され、どこにデプロイされてもさまざまなコンポーネントが同じように動作するようになります。

このガイドで行うこと

このガイドでは、Gunicorn WSGI HTTPサーバーで提供される、コンテナ化されたDjangoアプリケーションを水平スケーリングする方法を学びます。Dockerがインストールされ、DjangoとGunicornのアプリコンテナの同じコピーを実行している2つのアプリケーションサーバーをプロビジョニングします。

また、Let’s Encrypt SSL証明書を使用してアプリケーションを保護します。そのために、3番目のプロキシサーバーをプロビジョニングおよび構成し、そこでNginx reverse proxyコンテナとCertbotクライアントコンテナを実行します。Certbotは、Let’s Encrypt認証局からのSSL証明書の管理を支援するパッケージです。証明書を取得し、証明書の場所を使用してNginxサーバーブロックを構成し、自動更新を管理します。これは、証明書の有効期限が切れそうで更新が必要かどうかを定期的に確認するcronジョブを構成することによって行われます。SSL証明書を最新の状態に保つことで、Webサイトは常にSSL Labs.

で高いセキュリティ評価を得ることができます。3番目のプロキシサーバーは、分散アーキテクチャの最前面に配置され、すべての外部からの受信トラフィックを受け取ります。その後、トラフィックをアプリサーバーに分散します。アプリサーバーはファイアウォールの背後に配置され、プロキシサーバーからのアクセスのみを許可します。

このチュートリアルは、Django、Docker、およびKubernetesを扱う3つのチュートリアルシリーズの2番目です。まず、Building a Django and Gunicorn Application with Docker on Ubuntuのチュートリアルで説明されている手順に従う必要があります。そのチュートリアルでは、ベースとなるプロジェクトコードとDockerfileをセットアップし、アプリをMinIo Simple Storage Service (S3) に接続して静的ファイルを提供しました。

前提条件

このチュートリアルを進めるには、以下が必要です。

  1. 4台のUbuntu 20.04サーバー:

前提条件のチュートリアルであるBuilding a Django and Gunicorn Application with Docker on Ubuntuの手順に従っている場合、4台のサーバーのうちすでに2台が用意されています。

  • 最初のサーバーはPostgreSQL database instanceを実行します。チュートリアル「Building a Django and Gunicorn Application with Docker on Ubuntu」のステップ1と2に従ってデータベースをセットアップします。Postgresの構成は、アプリサーバーのIPのみからの外部接続を許可するように変更する必要があります。

  • The 2番目および3番目のサーバーは、アプリケーションコードのコンテナをホストします。すでに前提条件のチュートリアルから2番目のサーバーが実行されているはずです。プロキシサーバーのIPからの外部接続のみを許可するように、そのファイアウォールを変更します。Ubuntuサーバーのセットアップに役立つ、このステップバイステップのチュートリアルのステップ1から4に従うことができます。 CloudSigma上。

  • その 4番目の サーバーは、プロキシサーバーとなり、2つのアプリケーションサーバーコンテナへのロードバランシングとトラフィックの分散を処理します。

  1. Dockerは、2つのアプリサーバーとプロキシサーバーにインストールされている必要があります。

    以下のステップに従った後、前提条件のチュートリアル、サーバーの1つにすでにDockerがインストールされているはずです。当社のDockerのインストールと操作に関するチュートリアルのステップ1、2、3に従うことができます。上記で作成したsudoユーザーをDockerグループに追加することを忘れないでください。

  2. 登録済みのドメイン名を取得し、そのDNSレコードを設定してプロキシサーバーのパブリックIPアドレスを指すようにします。デモンストレーションの目的で、以下を使用します。 example_domain.com.
  1. S3オブジェクトストレージサービスをセットアップします。前提条件のチュートリアルでは、ストレージサービスとしてMinIOを使用しました。したがって、ステップ5前提条件のチュートリアルの説明に従って、MinIOストレージバケットをセットアップしてください。

ステップ1:最初のDjangoアプリケーションサーバーが動作していることの確認

以下に説明されているように、前提条件、このガイドはUbuntu上でDockerを使用してDjangoとGunicornアプリケーションを構築するに関するチュートリアルの後に続きます。そのチュートリアルから来て、すでにステップを実行している場合は、最初のサーバーが稼働しているはずです。アプリケーションのコードは、Djangoドキュメントの投票アプリケーションのチュートリアルに基づいています。初期セットアップを理解するために、これらのステップをよく読むことが重要です。チュートリアルのステップを実行済みの場合は、この最初のステップをスキップできます。

そうでない場合は、Docker化されたブランチをサーバーにクローンするだけです。まず、最初のアプリサーバーにログインし、次の gitコマンドを実行して、 django-polls-dockerブランチをdjango-pollsリポジトリから取得します:

次に、 django-pollsディレクトリに移動します:

cd django-polls

このディレクトリには、アプリケーションイメージを構築するためにDockerが使用する Dockerfile、Pythonアプリケーションコードを含む django-pollsディレクトリ、および起動時にコンテナに渡されてその動作を変更する環境変数のリストを含む envファイルがあります。この Dockerfileでは、 requirements.txtファイルを通じてDjangoパッケージの依存関係を定義します。さらに、着信トラフィックを受け入れるために使用するポート 8000を宣言し、3つのワーカーを持つ gunicornサーバーを実行するように設定する必要があります。Dockerfileの命令について詳しく知るには、チュートリアルステップ7Ubuntu上でDockerを使用してDjangoとGunicornアプリケーションを構築する.

次のコマンドを使用してDockerイメージを構築できます:

docker build -t django-polls:v1 .

Dockerがイメージを構築した後、次のコマンドを使用してサーバー上の利用可能なイメージを一覧表示できます:

docker images

コマンドを実行したときの出力は次のとおりです:

Django Application scrn 1

次に、実行環境の構成に使用される envファイルを変更する必要があります。このファイルは、コンテナの起動時にDocker実行コンテナに渡されます。nanoエディタで envファイルを開きます:

この env ファイルには、変更して正しい値を入力する必要があるプレースホルダーテキストが含まれています。

  • DJANGO_SECRET_KEY:次のドキュメントで説明されているように、一意で予測不可能な値を生成します:Djangoドキュメント。このコマンドを使用してランダムな文字列を生成し、変数に設定できます:  python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'.

  • DJANGO_ALLOWED_HOSTS:この値は、HTTPホストヘッダー攻撃からアプリを保護するために使用されます。開発モードの場合は、すべてのホストに一致するワイルドカードである *に設定できます。アプリを本番環境にデプロイするときは、これを登録済みのドメイン名に設定します。このデモンストレーションでは、 example_domain.com.

  • DB_DATABASE:次のセクションで作成したPostgreSQLデータベースの名前に設定します:前提条件セクション。今回の場合は polls_db.

  • DB_USERNAME:データベース用に選択したユーザー名に設定します。

  • DB_PASSWORD:データベース用に選択したパスワードに設定します。

  • DB_HOST:次のセクションでセットアップした、データベースインスタンスを実行しているホストに設定します:前提条件セクション。これは、データベースをセットアップするためのチュートリアルBuilding a Django and Gunicorn Application with Docker on Ubuntuのステップ1と2で説明されています。.

  • DB_PORT:データベースのポートに設定します。

編集が終わったら、ファイルを保存して閉じます。データベースの資格情報が設定されたので、コンテナを実行し、Dockerfileで設定されたCMDコマンドをオーバーライドすることで、データベーススキーマを作成できます。詳細については、公式ドキュメントのDockerfileエントリーポイントを参照してください。次に、以下のコマンドを実行します:

このコマンドでは、 django-polls:v1イメージを実行し、先ほど変更した envファイルを渡しています。次の部分: sh -c "python manage.py makemigrations && python manage.py migrateは、アプリコードで定義されたデータベーススキーマを作成します。コマンドを初めて実行する場合は、データベーススキーマの作成を示す同様の出力が表示されるはずです:

Django Application scrn 2

スキーマが作成されたら、Djangoのスーパーユーザーを作成できます。次のコマンドを実行して、対話型シェルでコンテナを起動します:

このコマンドは、Pythonシェルと対話するために使用できるシェルプロンプトでコンテナを起動します。次のコマンドでユーザーを作成しましょう:

プロンプトに従って、ユーザー名、メールアドレス、パスワードを入力します。パスワードを再入力し、Enterキーを押してユーザーを作成します。シェルを終了し、次のキーを押してコンテナを停止します: CTRL+D.

次に、デフォルトのコマンドを collectstatic Djangoコマンドでオーバーライドして、コンテナを再度実行する必要があります。このコマンドは、アプリの静的ファイルを生成し、MinIOクラウドストレージにアップロードします:

このコマンドは、設定されたオブジェクトストレージサービスにファイルを生成してアップロードします。出力は次のとおりです:

object storage

これで、Dockerfileで定義されているデフォルトのCMDコマンドをオーバーライドする追加のコマンドを指定せずに、アプリケーションを実行できます:

Django Application scrn 3

DockerはDockerfileで定義されたデフォルトのコマンドを実行し、 gunicorn サーバーでコンテナを起動し、コンテナポート 8000を公開して、それをUbuntuのポート 80にマッピングします。これで、ブラウザのアドレスバーで最初のサーバーのIPアドレスにアクセスして、アプリケーションのインターフェースを表示できます: http://FIRST_SERVER_IP.

次のエラーが表示されます:404 Page Not Found。これは、次のものに対して何も定義していないためです: / パス。以下にアクセスします。 http://FIRST_SERVER_IP/polls にアクセスして、Polls(投票)インターフェースを確認します。

Django Application image 1

管理インターフェースにアクセスして、いくつかの投票を作成します: http://FIRST_SERVER_IP/admin:

polls

管理インターフェースにアクセスするには、上記の createsuperuser コマンドで設定した資格情報を入力します:

polls administration

ページのソースを表示すると、定義通りにストレージバケットから静的ファイルが取得されていることがわかります。コンテナが期待通りにアプリを提供していることを確認したら、ターミナルで CTRL+C を押してコンテナを終了できます。

次に、最初のサーバーのSSHセッションを終了できるように、コンテナを detached モードで実行し続ける必要があります。これにより、コンテナはバックグラウンドで実行されたままになります。次のコマンドを実行します:

The -d フラグは、コンテナを detached モードで起動し、バックグラウンドで実行し続けることができます。 --rm フラグは、コンテナの終了後にそのファイルシステムをクリーンアップします。コンテナを一覧表示したときに確認できるように、コンテナに polls という名前を付けます。

最初のサーバーのSSHセッションを終了し、ブラウザで http://FIRST_SERVER_IP/polls にアクセスして、期待通りに動作していることを確認します。投票インターフェースが表示されれば、最初のアプリサーバーのセットアップは成功です。次のステップで2番目のアプリケーションサーバーをセットアップしましょう。

ステップ 2: 2番目のアプリケーションサーバーのセットアップ

Building a Django and Gunicorn Application with Docker on Ubuntu」チュートリアルで作成した、アプリケーションのDocker化されたブランチをクローンします。ここで使用するコマンドの詳細については、そのチュートリアルを参照するか、ステップ 1.

Prerequisites」セクションの説明に従って、2番目のサーバーが稼働しており、非ルートのsudoユーザーが追加され、Dockerがインストールされている必要があります。

次のステップは、このサーバーをPostgreSQLサーバーインスタンスに接続するように設定することです。「Building a Django and Gunicorn Application with Docker on Ubuntu」チュートリアルのステップ1で説明されているように、 ufw およびPostgreSQLの設定で2番目のサーバーのIPアドレスを許可する必要があります。

まず、非ルートのsudoユーザーでPostgreSQLデータベースサーバーインスタンスにログインします。 ufw ルールを追加するには、次のコマンドを実行します:

次に、このコマンドを実行して、2番目のサーバーのIPアドレスをPostgreSQLクライアント認証ファイルに追加します:

コメントを読んで設定の詳細を理解してください。次に、hostsセクションの下に、お使いのIPアドレスを指定して次の行を追加します:

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

その後、変更を有効にするためにPostgreSQLサービスを再起動します:

PostgreSQLデータベースサーバーインスタンスからログアウトし、2番目のアプリサーバーインスタンスの設定に進みます。

SSHでsecond app serversshでログインします。その後、次のコマンドを実行して、 django-polls-ブランチdjango-pollsリポジトリをクローンします:

Move into the django-polls ディレクトリに移動します:

その後、次のコマンドでイメージをビルドします:

イメージのビルドプロセスが完了したら、 env ファイルを、Step 1で説明されている設定値で修正します。nanoでファイルを開きます:

プレースホルダーのテキストを、ステップ1で追加した実際の値に置き換えてください。忘れずに DJANGO_ALLOWED_HOSTS変数を適切に変更してください。完了したらファイルを保存して閉じます。前のステップと同様に、 envファイル内のMinIO認証情報を更新します。

これで、次のコマンドを使用して、デタッチドモードでアプリコンテナを実行できます。

このコマンドはコンテナを起動し、バックグラウンドで実行し続けます。2番目のアプリサーバーのSSHセッションを終了し、ブラウザで http://SECOND_SERVER_IP/pollsにアクセスして、期待通りに動作していることを確認します。すべてが正常に機能していれば、投票(polls)インターフェースが表示されるはずです。

これで、アプリケーションの同じコピーを実行する2つのアプリサーバーが用意できました。次のステップでは、リバースプロキシとして機能するようにNginxコンテナを構成します。

ステップ3:Nginx Dockerコンテナのセットアップ

Nginxは、世界で最も人気のあるオープンソースのWebサーバーソフトウェアの1つです。インターネット上で最もトラフィックの多いサイトの可用性とスケーラビリティを確保する役割を担っています。また、セキュリティを保証し、非常に多用途です。これを使用して、リバースプロキシ、キャッシュ、およびロードバランシングを行うことができます。今回のアプリケーションは、静的ファイルとメディアファイルを処理するために別のオブジェクトストレージサービスを使用するようにセットアップされています。そのため、Nginxのキャッシュ機能は使用しません。代わりに、Nginxのリバースプロキシ機能とロードバランシング機能を使用します。フロントに位置するNginxサーバーが着信トラフィックを受信し、それをバックエンドのアプリケーションサーバーに分散します。そして、次のサービスから取得したSSL証明書を使用してトラフィックを保護し、クライアントとサーバー間の安全な通信を確保します:Let’s Encrypt.

。Nginxのリバースプロキシとロードバランシングを実装する方法はいくつかあります。方法の1つは、このチュートリアルで行ったように、Nginxリバースプロキシをバックエンドアプリケーションサーバーとは別に設定することです。このセットアップは柔軟性があり、Nginxプロキシレイヤーとアプリケーションレイヤーの両方をスケールさせることができます。複数のNginxプロキシを追加したり、クラウドロードバランサーを実装したりできます。リバースプロキシを実装する別の方法は、バックエンドアプリサーバーの1つをNginxプロキシとして使用することです。これにより、着信リクエストをローカルおよび他のアプリサーバーにプロキシできます。オプションとして、すべてのバックエンドアプリサーバーにNginxコンテナを構成し、フロントにクラウドロードバランサーを配置して着信トラフィックを受信し、それをバックエンドアプリサーバーに分散することもできます。

プロキシサーバーの構成を開始しましょう。Nginxプロキシとして使用するように設定した4番目のUbuntuサーバーにログインし、構成ディレクトリを作成します。

ディレクトリ内でnanoを使用して構成ファイルを開きます。

次に、ファイルに以下の構成を追加します。

この設定ファイルでは、 server, upstream、および location ブロックを指定して、NginxにHTTPリクエストをHTTPSにリダイレクトし、ステップ 1ステップ 2 でセットアップした2つのアプリサーバーにリクエストを分散するように指示します。一般的な情報については、公式ドキュメントの「Nginx設定ファイルの構造」を参照してください。.

私たちは、Docker HubのNginxイメージドキュメント、, Certbot、およびGunicornによって提供されている設定ファイルを参考にしました。この最小限のNginx設定ファイルを作成しました。これはデモンストレーション目的であり、セットアップを稼働させるためだけのものですが、Nginxガイドに従ったその他の設定を自由に探索し、実験することができます。.

upstreamブロックは、受信リクエストを処理するサーバーのグループを定義するために使用されます。グループには 名前が与えられ、 proxy_passディレクティブによって呼び出されます。ブロックの名前を djangoとし、2つのバックエンドアプリサーバーのIPアドレスを指定しました:

また、3つのserverブロックも定義しました。最初のserverブロックは、ドメインに一致しないすべてのリクエストをキャプチャし、 444コードを返します(クライアントにレスポンスを送信せずに接続を閉じるため、悪意のあるリクエストや不正なリクエストを拒否します)。default_serverとして定義されているため、サーバーのIPアドレスへの直接のHTTPリクエストはこのブロックによって処理されます。:

The 2番目のserverブロックは、受信HTTP(ポート 80)リクエストを、443に、HTTP 301リダイレクトを使用してリダイレクトします。:

現在、3番目のserverブロックがリクエストを処理します。これにはいくつかのディレクティブがあり、その重要性について以下で説明します。

CertbotによってプロビジョニングされたTLS証明書とキーへのパスを定義する2つのディレクティブがあります。証明書は、起動時にNginxコンテナにマウントされます。

次に、Certbotによって推奨されているSSLセキュリティのデフォルト設定があります。詳細については、ngx_http_ssl_moduleに関するNginx公式ドキュメントを参照してください。Mozillaも、サーバー側のセキュリティに関する詳細情報を提供しています。 ssl_ciphersの値は、 confファイルにおいて、Mozillaのページから引用されています:

次の2つのディレクティブでは、クライアントリクエストボディの最大許容サイズを定義し、クライアントとの keep-alive接続のタイムアウトを設定します。Nginxは、 keepalive_timeoutディレクティブで設定した秒数が経過すると、クライアントとの接続を閉じます。詳細については、公式ドキュメントのGunicornをデプロイするためのNginx設定を参照してください:

設定ファイルでは、2つのlocationブロックも定義しています。最初のブロックは、proxyディレクティブで定義されているリクエストのプロキシ処理を処理します。受信リクエストは、前に定義した upstream djangoサーバーにプロキシされます:

プロキシディレクティブに関する詳細情報は、以下から確認できます:Nginx Module ngx_http_proxy_module および、次のドキュメント:deploying a Gunicorn server.

2つ目のlocationブロックでは、パスを定義します: /well-known/acme-challenge/。これは通常、SSL証明書のプロビジョニングまたは更新を行う前に、CertbotがLet's Encryptでドメイン名を検証するために使用されます:

Nginx設定ファイルに関する説明は以上です。編集が完了したら、ファイルを保存して閉じることができます。

定義したばかりの設定ファイルは、Nginxコンテナを実行するために使用できます。ただし、Let's EncryptからSSL証明書をプロビジョニングしていないため、失敗します。このチュートリアルでは、 nginx:1.20.2 の公式 Docker Hub上のNginxイメージリポジトリから、Dockerイメージバージョン1.20.2を使用します。.

以下のコマンドを実行してイメージをダウンロードし、すべてが正しく動作しているか確認できます:

このコマンドは、 nginx という名前のコンテナを作成し、ホストシステムとコンテナの間でポート 80443 をマッピングします。 --rm フラグは、ビルドが成功した後に中間コンテナを削除します。また、 -v フラグを使用して、設定ファイルをコンテナ内の /etc/nginx/conf.d/nginx.conf(デフォルトのNginx設定ディレクトリ)にマウントします。Nginxコンテナによる変更を防ぐため、 ro フラグを使用して読み取り専用モードでマウントされます。デフォルトの webroot ディレクトリを設定し、それを /var/www/html としてマウントします。最後に、Dockerにこのビルドで nginx:1.20.2 イメージを使用するように指示します。次のステップで、Let's EncryptからTLS/SSL証明書とキーを取得しましょう。

ステップ4:Let's EncryptからのSSL/TLS証明書のプロビジョニングとCertbot自動更新の設定

Certbotは、Let’s Encryptから無料のTLS証明書をプロビジョニングし、有効期限が切れる前の自動更新を管理するのに役立ちます。これにより、ウェブサイトのセキュリティが向上し、HTTPS経由で提供されるようになります。アーキテクチャをコンテナ化した状態に保つため、CertbotのDockerイメージを使用してSSL/TLS証明書を取得し、自動更新を設定します。プロキシサーバーにDockerがインストールされていることを、前提条件の指示に従って確認してください。

また、登録済みのドメイン名のDNS AレコードがプロキシサーバーのIPアドレスを指している必要があります。certbotのDockerイメージを実行し、 --staging フラグを渡すことで検証できます:

このコマンドは、Certbotイメージをダウンロードし、インタラクティブモードで実行します。つまり、シェルが起動し、詳細情報を入力できるようになります。ホストのポート 80 をコンテナ内のポート 80 にマッピングします。 -v フラグを使用して、ホストの2つのディレクトリをコンテナにマウントします: /etc/letsencrypt/ および /var/lib/letsencrypt/ です。 --standalone フラグは、Nginxを使用せずにCertbotイメージを実行することを指定します。最後に、 --staging フラグがあり、これによりCertbotがステージングサーバー上で実行され、ドメイン名が検証されます。

メールアドレスを入力し、利用規約に同意してください。以下は検証に成功したときの出力です:

次のステップ:

証明書は有効期限が切れる前に更新する必要があります。Certbotはバックグラウンドで自動的に証明書を更新できますが、その機能を有効にするための手順を実行する必要がある場合があります。こちらのリンク で手順を確認してください。

次の cat コマンドを使用して証明書を表示できます:

上記のコマンドを実行すると、ターミナルに証明書が表示されます。Certbotが証明書をプロビジョニングしたことを確認したら、ステップ 3 で作成したNginxの設定をテストできます。以下のDockerコマンドを実行して、Nginxコンテナを起動します:

このコマンドでは、 -v フラグを使用して、Let's EncryptのSSL/TLS証明書ディレクトリの場所をマウントしています。

コンテナが起動して実行されたら、ブラウザでウェブページを開きます: http://example_domain.com。ウェブサイトが安全ではないという警告が表示される可能性があります:

これは、Let's Encryptから本番用(production) の証明書ではなく、ステージング/テスト用の証明書のみをプロビジョニングしたためです。以下のCertbotコマンドを --staging フラグなしで実行して、本番用の証明書を取得しましょう:

プロンプトで、既存の証明書を更新して置き換える(renew and replace) ことを確認するために、 2 を入力して ENTER を押します。これにより、本番環境に対応した証明書がプロビジョニングされます。これでNginxコンテナを実行でき、すべてが正常に動作するはずです:

コンテナが起動して実行されたら、ブラウザでウェブページをもう一度開きます: http://example_domain.com。HTTPを入力した場合でも、ブラウザがHTTPSにリダイレクトされることに注意してください。これは、Nginx設定内のサーバーとプロビジョニングされたSSL/TLS証明書が正常に動作していることを意味します。ホームパスのルートが定義されていないため、 polls ルート http://example_domain.com/polls に移動してください。 /. 投票インターフェースが表示されるはずです:

ここまでの手順で、本番環境に対応したアーキテクチャの構成に成功しました。プロキシサーバーからプロキシされたリクエストを処理する2つのバックエンドサーバーを実装しました。プロキシサーバーは、プロビジョニングされたTLS証明書を使用して、負荷分散とトラフィックのセキュリティ保護を処理します。

ただし、Let’s Encryptの証明書の有効期限は90日であることに注意してください。そのため、90日が経過する前に更新する必要があります。Nginxコンテナが実行されているため、証明書更新のためにcertbotコマンドを実行する際は、 webroot モードを使用し、 standalone モードは使用しないようにしてください。以前に、 /var/www/html/.well-known/acme-challenge/ ディレクトリをNginx設定ファイルで指定したことを思い出してください(Step 3)。Certbotはこのパスを使用して検証ファイルを保存します。また、証明書を更新しようとすると、Let’s Encryptクライアントが検証リクエストを伴ってこのパスを呼び出します。更新コマンドの実行が完了したら、Nginxをリロードして変更を適用できます。

ターミナルで CTRL+C を押してコンテナを終了し、 -d フラグを使用して、デタッチモードで再度起動しましょう。

これにより、Nginxコンテナはバックグラウンドで実行されたままになります。以下のコマンドを実行して、 --dry-run フラグを使用して証明書の更新手順をテストしてみましょう。

このコマンドでは、 --webroot プラグインと、 -w フラグで検証リクエストに使用するパスを指定しました。また、 --dry-run フラグを指定して、実際に証明書をプロビジョニングすることなく自動更新手順を検証します。

シミュレーションが成功すると、以下のような出力が表示されるはずです:

実行中のアプリケーションの証明書を更新するときは常に、コンテナが新しい証明書の使用を開始できるようにNginxをリロードする必要があります。次のDockerコマンドは、 nginx (コンテナ名をnginxとしたことを思い出してください)コンテナをリロードします:

このコマンドは、HUP Unixシグナルを、 nginx Dockerコンテナ内で実行されているNginxプロセスに送信します。これにより、Nginxはその設定をリロードし、更新された証明書の使用を開始します。

プロキシサーバーにTLS/SSLがインストールされ、ウェブサイトがHTTPSで提供されるようになったため、次はバックエンドのアプリサーバーを保護して、プロキシサーバーからのリクエストのみを許可するようにする必要があります。

Step 5: Securing the Backend Django Servers from External Access

このチュートリアルで実装したプロキシサーバーは、SSL接続を復号して暗号化されていないパケットをバックエンドのアプリサーバーに転送するSSL終端を処理します。バックエンドサーバーを外部アクセスから保護するため、ほとんどの場合、このレベルのセキュリティで十分です。ただし、銀行情報や健康データなどの機密データを送信するアプリケーションをデプロイする場合は、以下を実装する必要があります。エンドツーエンド暗号化.

このチュートリアルでは、バックエンドの Gunicorn サーバーは外部に直接公開されることを想定していないため、Nginx によって保護されています。Nginx プロキシサーバーはバックエンドサーバーへのゲートウェイのような役割を果たし、外部クライアントがバックエンドのアプリサーバーに直接アクセスするのを防ぎます。すべてのリクエストがプロキシサーバーを経由するようにする必要があります。そうした状況において、Docker にはufwをバイパスする問題 があり、ファイアウォールルールをすり抜けてポートを外部に開放してしまい、インフラストラクチャのセキュリティが損なわれる可能性があります。これは、実際にステップ 1 および ステップ 2 で、ポート 80ufw ルールで許可することなくアプリサーバーをセットアップしたことからも明らかです。しかし、ブラウザでいずれかのサーバーのパブリックIPアドレスにアクセスすると、依然としてウェブページにアクセスできます。この問題を解決する1つの方法は、iptables を、 ufw を経由せずに直接使用することです。詳細については、Docker and iptables の公式ドキュメントを参照してください。もう1つの推奨される方法は、クラウドファイアウォールを使用することです。

Dockerによって開かれた可能性のあるすべてのポートへの外部アクセスをブロックするために、UFWの設定を変更しましょう。ホストのポート 80 をDockerコンテナのポート 8000 に、Dockerコマンドのフラグ -p 80:8000 でマッピングした際、意図せずホストマシンのポート 80 も開いてしまいました。このアクセスを無効にするには、ufw-docker リポジトリ の README に記載されている通りに UFW の設定を変更します。

最初の Django アプリサーバーの設定を変更しましょう。サーバーにログインし、 /etc/ufw/after.rules のファイルを、sudo ユーザーとして nano で開きます:

このファイルには、以下の ufw ルールが含まれています:

ファイルの末尾に以下の UFW 設定行のブロックを追加します。

追加したルールにより、Dockerが開放するポートへのパブリックアクセスが防止されます。さらに、 10.0.0.0/8, 172.16.0.0/12、および 192.168.0.0/16 のプライベートIP範囲からのアクセスが許可されます。ルールの詳細については、ufw-dockerのREADMEを参照してください。編集が終わったら、ファイルを保存して閉じます。この設定は、仮想プライベートクラウドネットワーク(VPC)をセットアップし、3つのサーバーすべてがそのVPC内にあり、NginxのupstreamディレクティブでDjangoサーバーのプライベートIPを指定している場合に機能します。 設定 ファイル。

しかし、パブリックIPを使用しており、VPCがない可能性があります。そのため、 ufwにルールを追加して、Nginxプロキシサーバーからポート 80を介した両方のDjangoアプリサーバーへのトラフィックを許可する必要があります。許可ルールを ufwに追加し、送信元のサーバーIPからポート 80へのアクセスを、次のコマンドを使用して指定できます。

変更が完了したら、Djangoアプリサーバーを再起動して変更を適用します。 sudo ufw reloadを実行するだけでは変更が適用されないようです。

サーバーが再起動したら、ステップ1 または ステップ2:

次に、ブラウザで最初のDjangoサーバーのIPにアクセスして、Pollsインターフェースが表示されるか確認します。 http://FIRST_SERVER_IP/polls。これは失敗します。ここで、最初のサーバーからログアウトし、2番目のサーバーに対してここで行った手順を繰り返します。sudoユーザーとしてnanoで /etc/ufw/after.rulesを開きます。

先ほどと同様に、一番下までスクロールしてUFW設定ブロックを追加します。

上記のブロックを追加したら、ファイルを保存して閉じます。

次に、許可ルールを ufw に追加し、送信元のserver IPからポート 80への接続を指定するために、次のコマンドを使用します:

変更を有効にするためにサーバーを再起動します:

サーバーが再起動したら、次のコマンドでコンテナを再度起動します:

2番目のサーバーのIPアドレスに直接アクセスして、pollsインターフェースが表示されるかテストします: http://SECOND_SERVER_IP/polls。これも失敗するはずです。

これで、このアーキテクチャをテストする準備が整いました。ブラウザから https://example_domain_here/pollsにアクセスして、デフォルトのPollsインターフェースを表示できます。これは、Nginxプロキシサーバーが依然としてDjangoバックエンドサーバーにアクセスできることを意味します。

結論

このガイドでは、Dockerコンテナを使用してスケーラブルなインフラストラクチャを構築する方法を説明しました。このインフラストラクチャには、独立したPostgreSQLデータベースサーバー、2つのバックエンドアプリケーションサーバー、および2つのサーバー間でトラフィックを負荷分散して分散するNginxプロキシサーバーが含まれています。アプリケーションのベースとしてDjango Pollsアプリケーションを使用しましたが、Node.js, Laravelなどの異なるフレームワークを使用して、さまざまなアプリケーション向けにこのアーキテクチャをカスタマイズできます。

これは始めるための基本的なガイドラインです。追加できるいくつかの改善点としては、イメージをDocker Hubなどのイメージリポジトリでホストし、複数のサーバーにイメージを簡単に配布できるようにすることです。また、イベントが発生したときにいつでもアプリサーバーにイメージを自動的にビルド、テスト、デプロイするための継続的インテグレーションおよびデプロイメントパイプラインを追加することもできます。たとえば、イベントとは、gitリポジトリの指定されたブランチに新しいコードをプッシュすることなどです。また、コンテナでエラーが発生したときの動作を自動化することもできます。Dockerの公式ドキュメントには、エラーやシステムの再起動が発生した場合のコンテナの自動起動に関する優れたガイドラインが記載されています。

ハッピーコンピューティング!

author

Hark Labs

著者 · CloudSigma

Preslav DobrevはCloudSigmaのクリエイティブデザイナーであり、従来型および革新的なマーケティングチャネルを活用した一貫性のあるビジネスアイデンティティに注力しています。彼は芸術的なビジョンと戦略的マーケティングを融合させ、インパクトのあるブランドナラティブを生み出すことに長けています。

コメント

コメントはまだありません。最初のコメントを投稿しましょう。