MongoDBとDockerでFlaskを設定する方法

提供:Dev Guides
移動先:案内検索

著者は、 Write for DOnations プログラムの一環として、 InternetArchiveを選択して寄付を受け取りました。

序章

Webアプリケーションの開発は、さまざまなテクノロジーを構築および保守する場合、複雑で時間がかかる可能性があります。 アプリケーションの複雑さと生産までの時間を短縮するように設計された軽量オプションを検討すると、より柔軟でスケーラブルなソリューションが得られます。 Python 上に構築されたマイクロWebフレームワークとして、 Flask は、プロジェクトに統合できる拡張機能を通じて開発者がアプリケーションを拡張するための拡張可能な方法を提供します。 開発者の技術スタックのスケーラビリティを継続するために、 MongoDB はNoSQLデータベースであり、頻繁な変更に対応できるように拡張および動作するように設計されています。 開発者はDockerを使用して、アプリケーションのパッケージ化とデプロイのプロセスを簡素化できます。

Docker Compose は、アプリケーションサービス、ネットワークボリューム、バインドマウントなどのインフラストラクチャを単一のファイルで定義できるようにすることで、開発環境をさらに簡素化しました。 Docker Composeを使用すると、複数のdocker container runコマンドを実行するよりも使いやすくなります。 これにより、すべてのサービスを1つの作成ファイルで定義でき、1つのコマンドで、構成からすべてのサービスを作成して開始できます。 これにより、コンテナインフラストラクチャ全体でバージョン管理が確実に行われます。 Docker Composeはプロジェクト名を使用して環境を相互に分離します。これにより、単一のホストで複数の環境を実行できます。

このチュートリアルでは、Docker container 内でFlask、Nginx、およびMongoDBを使用してTo Do Webアプリケーションをビルド、パッケージ化、および実行します。 スタック構成全体をdocker-compose.ymlファイルで定義し、Python、MongoDB、およびNginxの構成ファイルも定義します。 FlaskはHTTPリクエストを処理するためにWebサーバーを必要とするため、アプリケーションを処理するためにPythonWSGIHTTPサーバーであるGunicornも使用します。 Nginxは、処理のためにリクエストをGunicornに転送するリバースプロキシサーバーとして機能します。

前提条件

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

  • 初期サーバー設定チュートリアルの手順に従って構成されたsudo権限を持つ非rootユーザー。
  • Dockerのインストールと使用方法のステップ1とステップ2の手順でDockerをインストールしました。
  • Docker Composeのインストール方法のステップ1の手順に従って、DockerComposeをインストールします。

ステップ1—DockerComposeでスタック構成を作成する

Dockerでアプリケーションを構築すると、DockerComposeで行った構成の変更に応じてインフラストラクチャを簡単にバージョン管理できます。 インフラストラクチャは、単一のファイルで定義し、単一のコマンドで構築できます。 このステップでは、Flaskアプリケーションを実行するためのdocker-compose.ymlファイルを設定します。

docker-compose.ymlファイルを使用すると、アプリケーションインフラストラクチャを個別のサービスとして定義できます。 サービスは相互に接続でき、永続ストレージ用にボリュームを接続できます。 ボリュームは、Docker(Linuxでは/var/lib/docker/volumes/)によって管理されるホストファイルシステムの一部に保存されます。

ボリューム内のデータはエクスポートしたり、他のアプリケーションと共有したりできるため、ボリュームはDockerでデータを永続化するための最良の方法です。 Dockerでのデータ共有の詳細については、Dockerコンテナとホスト間でデータを共有する方法を参照してください。

開始するには、サーバーのホームディレクトリにアプリケーションのディレクトリを作成します。

mkdir flaskapp

新しく作成したディレクトリに移動します。

cd flaskapp

次に、docker-compose.ymlファイルを作成します。

nano docker-compose.yml

docker-compose.ymlファイルは、DockerComposeファイルのバージョンを識別するバージョン番号で始まります。 DockerComposeファイルバージョン3は、このセットアップの前提条件であるDockerEngineバージョン1.13.0+を対象としています。 また、次の手順で定義するservicesタグを追加します。

docker-compose.yml

version: '3'
services:

ここで、flaskdocker-compose.ymlファイルの最初のサービスとして定義します。 次のコードを追加して、Flaskサービスを定義します。

docker-compose.yml

. . .
  flask:
    build:
      context: app
      dockerfile: Dockerfile
    container_name: flask
    image: digitalocean.com/flask-python:3.6
    restart: unless-stopped
    environment:
      APP_ENV: "prod"
      APP_DEBUG: "False"
      APP_PORT: 5000
      MONGODB_DATABASE: flaskdb
      MONGODB_USERNAME: flaskuser
      MONGODB_PASSWORD: your_mongodb_password
      MONGODB_HOSTNAME: mongodb
    volumes:
      - appdata:/var/www
    depends_on:
      - mongodb
    networks:
      - frontend
      - backend

buildプロパティは、ビルドのcontextを定義します。 この場合、Dockerfileを含むappフォルダー。

container_nameプロパティを使用して、各コンテナの名前を定義します。 imageプロパティは、イメージ名とDockerイメージのタグ付け対象を指定します。 restartプロパティは、コンテナを再起動する方法を定義します。この場合は、unless-stoppedです。 つまり、Docker Engineが停止/再起動された場合、またはコンテナーを明示的に停止した場合にのみ、コンテナーが停止します。 unless-stoppedプロパティを使用する利点は、Dockerエンジンが再起動されるか、エラーが発生すると、コンテナーが自動的に起動することです。

environmentプロパティには、コンテナに渡される環境変数が含まれています。 環境変数MONGODB_PASSWORDの安全なパスワードを提供する必要があります。 volumesプロパティは、サービスが使用しているボリュームを定義します。 あなたの場合、ボリュームappdataはコンテナ内の/var/wwwディレクトリにマウントされています。 depends_onプロパティは、Flaskが正しく機能するために依存するサービスを定義します。 この場合、mongodbサービスはアプリケーションのデータベースとして機能するため、flaskサービスはmongodbに依存します。 depends_onは、mongodbサービスが実行されている場合にのみflaskサービスが実行されるようにします。

networksプロパティは、flaskサービスがアクセスできるネットワークとしてfrontendbackendを指定します。

flaskサービスが定義されたら、MongoDB構成をファイルに追加する準備が整います。 この例では、公式の4.0.8バージョンのmongoイメージを使用します。 flask serviceに続くdocker-compose.ymlファイルに次のコードを追加します。

docker-compose.yml

. . .
  mongodb:
    image: mongo:4.0.8
    container_name: mongodb
    restart: unless-stopped
    command: mongod --auth
    environment:
      MONGO_INITDB_ROOT_USERNAME: mongodbuser
      MONGO_INITDB_ROOT_PASSWORD: your_mongodb_root_password
      MONGO_INITDB_DATABASE: flaskdb
      MONGODB_DATA_DIR: /data/db
      MONDODB_LOG_DIR: /dev/null
    volumes:
      - mongodbdata:/data/db
    networks:
      - backend

このサービスのcontainer_namemongodbで、再起動ポリシーはunless-stoppedです。 commandプロパティを使用して、コンテナの起動時に実行されるコマンドを定義します。 コマンドmongod --authは、資格情報なしでMongoDBシェルへのログインを無効にします。これにより、認証を要求することでMongoDBを保護します。

環境変数MONGO_INITDB_ROOT_USERNAMEおよびMONGO_INITDB_ROOT_PASSWORDは、指定された資格情報を持つrootユーザーを作成するため、プレースホルダーを強力なパスワードに置き換えてください。

MongoDBは、デフォルトでデータを/data/dbに保存するため、/data/dbフォルダー内のデータは、永続化のために名前付きボリュームmongodbdataに書き込まれます。 その結果、再起動してもデータベースが失われることはありません。 mongoDBサービスはポートを公開しないため、サービスにはbackendネットワーク経由でのみアクセスできます。

次に、アプリケーションのWebサーバーを定義します。 docker-compose.ymlファイルに次のコードを追加して、Nginxを構成します。

docker-compose.yml

. . .
  webserver:
    build:
      context: nginx
      dockerfile: Dockerfile
    image: digitalocean.com/webserver:latest
    container_name: webserver
    restart: unless-stopped
    environment:
      APP_ENV: "prod"
      APP_NAME: "webserver"
      APP_DEBUG: "false"
      SERVICE_NAME: "webserver"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - nginxdata:/var/log/nginx
    depends_on:
      - flask
    networks:
      - frontend

ここでは、Dockerfileを含むnginxフォルダーであるbuildcontextを定義しました。 imageプロパティを使用して、コンテナのタグ付けと実行に使用するイメージを指定します。 portsプロパティは、:80および:443を介してパブリックにアクセスできるようにNginxサービスを構成し、volumesはコンテナー内にnginxdataボリュームをマウントします/var/log/nginxディレクトリ。

Webサーバーサービスdepends_onが適用されるサービスをflaskとして定義しました。 最後に、networksプロパティは、Webサーバーサービスがfrontendにアクセスできるネットワークを定義します。

次に、ブリッジネットワークを作成して、コンテナが相互に通信できるようにします。 ファイルの最後に次の行を追加します。

docker-compose.yml

. . .
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

接続するサービス用に、frontendbackendの2つのネットワークを定義しました。 Nginxなどのフロントエンドサービスは、パブリックアクセス可能である必要があるため、frontendネットワークに接続します。 MongoDBなどのバックエンドサービスは、backendネットワークに接続して、サービスへの不正アクセスを防ぎます。

次に、ボリュームを使用して、データベース、アプリケーション、および構成ファイルを永続化します。 アプリケーションはデータベースとファイルを使用するため、それらに加えられた変更を永続化することが不可欠です。 ボリュームはDockerによって管理され、ファイルシステムに保存されます。 このコードをdocker-compose.ymlファイルに追加して、ボリュームを構成します。

docker-compose.yml

. . .
volumes:
  mongodbdata:
    driver: local
  appdata:
    driver: local
  nginxdata:
    driver: local

volumesセクションは、アプリケーションがデータを永続化するために使用するボリュームを宣言します。 ここでは、MongoDBデータベース、Flaskアプリケーションデータ、およびNginx Webサーバーログをそれぞれ永続化するためのボリュームmongodbdataappdata、およびnginxdataを定義しました。 これらのボリュームはすべて、localドライバーを使用してデータをローカルに保存します。 ボリュームはこのデータを永続化するために使用されるため、コンテナーを再起動すると、MongoDBデータベースやNginxWebサーバーログなどのデータが失われる可能性があります。

完全なdocker-compose.ymlファイルは次のようになります。

docker-compose.yml

version: '3'
services:

  flask:
    build:
      context: app
      dockerfile: Dockerfile
    container_name: flask
    image: digitalocean.com/flask-python:3.6
    restart: unless-stopped
    environment:
      APP_ENV: "prod"
      APP_DEBUG: "False"
      APP_PORT: 5000
      MONGODB_DATABASE: flaskdb
      MONGODB_USERNAME: flaskuser
      MONGODB_PASSWORD: your_mongodb_password
      MONGODB_HOSTNAME: mongodb
    volumes:
      - appdata:/var/www
    depends_on:
      - mongodb
    networks:
      - frontend
      - backend

  mongodb:
    image: mongo:4.0.8
    container_name: mongodb
    restart: unless-stopped
    command: mongod --auth
    environment:
      MONGO_INITDB_ROOT_USERNAME: mongodbuser
      MONGO_INITDB_ROOT_PASSWORD: your_mongodb_root_password
      MONGO_INITDB_DATABASE: flaskdb
      MONGODB_DATA_DIR: /data/db
      MONDODB_LOG_DIR: /dev/null
    volumes:
      - mongodbdata:/data/db
    networks:
      - backend

  webserver:
    build:
      context: nginx
      dockerfile: Dockerfile
    image: digitalocean.com/webserver:latest
    container_name: webserver
    restart: unless-stopped
    environment:
      APP_ENV: "prod"
      APP_NAME: "webserver"
      APP_DEBUG: "true"
      SERVICE_NAME: "webserver"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - nginxdata:/var/log/nginx
    depends_on:
      - flask
    networks:
      - frontend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

volumes:
  mongodbdata:
    driver: local
  appdata:
    driver: local
  nginxdata:
    driver: local

ファイルを保存し、構成を確認した後、エディターを終了します。

docker-compose.ymlファイルでアプリケーションスタック全体のDocker構成を定義しました。 次に、FlaskとWebサーバー用のDockerfileの作成に進みます。

ステップ2—フラスコとWebサーバーのDockerfileを作成する

Dockerを使用すると、Dockerfileというファイルからアプリケーションを実行するためのコンテナーを構築できます。 Dockerfileは、アプリケーションに必要なソフトウェアをインストールし、要件に基づいてコンテナーを構成するために使用できるカスタムイメージを作成できるツールです。 作成したカスタムイメージをDockerHubまたは任意のプライベートレジストリにプッシュできます。

このステップでは、FlaskおよびWebサーバーサービスのDockerfileを記述します。 開始するには、Flaskアプリケーションのappディレクトリを作成します。

mkdir app

次に、appディレクトリにFlaskアプリのDockerfileを作成します。

nano app/Dockerfile

次のコードをファイルに追加して、Flaskコンテナをカスタマイズします。

app / Dockerfile

FROM python:3.6.8-alpine3.9

LABEL MAINTAINER="FirstName LastName <[email protected]>"

ENV GROUP_ID=1000 \
    USER_ID=1000

WORKDIR /var/www/

このDockerfileでは、Python3.6.8がプリインストールされたAlpine3.9に基づく3.6.8-alpine3.9imageの上にイメージを作成しています。

ENVディレクティブは、グループとユーザーIDの環境変数を定義するために使用されます。 Linux Standard Base(LSB)は、UIDおよびGID0〜99がシステムによって静的に割り当てられることを指定します。 UID 100〜999 は、システムユーザーおよびグループに動的に割り当てられることになっています。 UID 1000-59999 は、ユーザーアカウントに動的に割り当てられることになっています。 このことを念頭に置いて、1000のUIDとGIDを安全に割り当てることができます。さらに、要件に合わせてGROUP_IDUSER_IDを更新することでUID/GIDを変更できます。

WORKDIRディレクティブは、コンテナの作業ディレクトリを定義します。 必ずLABEL MAINTAINERフィールドを自分の名前とメールアドレスに置き換えてください。

次のコードブロックを追加して、Flaskアプリケーションをコンテナーにコピーし、必要な依存関係をインストールします。

app / Dockerfile

. . .
ADD ./requirements.txt /var/www/requirements.txt
RUN pip install -r requirements.txt
ADD . /var/www/
RUN pip install gunicorn

次のコードは、ADDディレクティブを使用して、ローカルのappディレクトリからコンテナの/var/wwwディレクトリにファイルをコピーします。 次に、DockerfileはRUNディレクティブを使用して、Gunicornと、チュートリアルの後半で作成するrequirements.txtファイルで指定されたパッケージをインストールします。

次のコードブロックは、新しいユーザーとグループを追加し、アプリケーションを初期化します。

app / Dockerfile

. . .
RUN addgroup -g $GROUP_ID www
RUN adduser -D -u $USER_ID -G www www -s /bin/sh

USER www

EXPOSE 5000

CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]

デフォルトでは、Dockerコンテナはrootユーザーとして実行されます。 root ユーザーはシステム内のすべてにアクセスできるため、セキュリティ違反の影響は悲惨なものになる可能性があります。 このセキュリティリスクを軽減するために、これにより、/var/wwwディレクトリにのみアクセスできる新しいユーザーとグループが作成されます。

このコードは、最初にaddgroupコマンドを使用して、wwwという名前の新しいグループを作成します。 -gフラグは、グループIDをDockerfileで以前に定義されたENV GROUP_ID=1000変数に設定します。

adduser -D -u $USER_ID -G www www -s /bin/sh行は、ENV変数で定義されているように、1000のユーザーIDを持つwwwユーザーを作成します。 -sフラグは、ユーザーのホームディレクトリが存在しない場合はそれを作成し、デフォルトのログインシェルを/bin/shに設定します。 -Gフラグは、ユーザーの初期ログイングループを前のコマンドで作成されたwwwに設定するために使用されます。

USERコマンドは、コンテナーで実行されるプログラムがwwwユーザーを使用することを定義します。 Gunicornは:5000をリッスンするので、EXPOSEコマンドでこのポートを開きます。

最後に、CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]行は、ポート5000でリッスンしている4人のワーカーでGunicornサーバーを起動するコマンドを実行します。 数は通常、サーバーのコアあたり2〜4ワーカーである必要があります。Gunicornのドキュメントでは、最初のワーカー数として(2 x $ num_cores)+1を推奨しています。

完成したDockerfileは次のようになります。

app / Dockerfile

FROM python:3.6.8-alpine3.9

LABEL MAINTAINER="FirstName LastName <[email protected]>"

ENV GROUP_ID=1000 \
    USER_ID=1000

WORKDIR /var/www/

ADD . /var/www/
RUN pip install -r requirements.txt
RUN pip install gunicorn

RUN addgroup -g $GROUP_ID www
RUN adduser -D -u $USER_ID -G www www -s /bin/sh

USER www

EXPOSE 5000

CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]

ファイルを保存して、テキストエディタを終了します。

次に、Nginx構成を保持するための新しいディレクトリを作成します。

mkdir nginx

次に、nginxディレクトリにNginxWebサーバー用のDockerfileを作成します。

nano nginx/Dockerfile

次のコードをファイルに追加して、NginxコンテナーのイメージをビルドするDockerfileを作成します。

nginx / Dockerfile

FROM alpine:latest

LABEL MAINTAINER="FirstName LastName <[email protected]>"

RUN apk --update add nginx && \
    ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log && \
    mkdir /etc/nginx/sites-enabled/ && \
    mkdir -p /run/nginx && \
    rm -rf /etc/nginx/conf.d/default.conf && \
    rm -rf /var/cache/apk/*

COPY conf.d/app.conf /etc/nginx/conf.d/app.conf

EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]

このNginxDockerfileは、alpineベースイメージを使用します。これは、セキュリティのために構築された最小限の攻撃対象領域を持つ小さなLinuxディストリビューションです。

RUNディレクティブでは、nginxをインストールし、エラーとアクセスログを標準エラー(/dev/stderr)と出力( [ X178X])。 コンテナは一時的なものであるため、エラーを標準エラーと出力に公開することがベストプラクティスです。これを行うと、ログはDockerログに送信され、そこからログをElasticスタックなどのログサービスに転送して永続化できます。 これが完了したら、コマンドを実行してdefault.conf/var/cache/apk/*を削除し、結果の画像のサイズを縮小します。 これらすべてのコマンドを1つのRUNで実行すると、画像内のレイヤー数が減り、結果の画像のサイズも小さくなります。

COPYディレクティブは、app.confWebサーバー構成をコンテナー内にコピーします。 EXPOSEディレクティブは、コンテナーがポート:80および:443でリッスンすることを保証します。これは、アプリケーションが:80で実行され、:443がセキュアであるためです。ポート。

最後に、CMDディレクティブは、Nginxサーバーを起動するコマンドを定義します。

ファイルを保存して、テキストエディタを終了します。

Dockerfileの準備ができたので、トラフィックをFlaskアプリケーションにルーティングするようにNginxリバースプロキシを構成する準備が整いました。

ステップ3—Nginxリバースプロキシの構成

このステップでは、:5000上のGunicornにリクエストを転送するためのリバースプロキシとしてNginxを構成します。 リバースプロキシサーバーは、クライアント要求を適切なバックエンドサーバーに転送するために使用されます。 クライアントとサーバー間のネットワークトラフィックのスムーズな流れを確保するために、抽象化と制御の追加レイヤーを提供します。

nginx/conf.dディレクトリを作成することから始めます。

mkdir nginx/conf.d

Nginxを構成するには、nginx/conf.d/フォルダーに次の構成でapp.confファイルを作成する必要があります。 app.confファイルには、リバースプロキシがリクエストをGunicornに転送するために必要な構成が含まれています。

nano nginx/conf.d/app.conf

次の内容をapp.confファイルに入れます。

nginx / conf.d / app.conf

upstream app_server {
    server flask:5000;
}

server {
    listen 80;
    server_name _;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    client_max_body_size 64M;

    location / {
        try_files $uri @proxy_to_app;
    }

    location @proxy_to_app {
        gzip_static on;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_buffering off;
        proxy_redirect off;
        proxy_pass http://app_server;
    }
}

これにより、最初にアップストリームサーバーが定義されます。これは、ルーティングまたは負荷分散のためにWebサーバーまたはアプリサーバーを指定するために一般的に使用されます。

アップストリームサーバーapp_serverは、コンテナー名flask:5000で識別されるserverディレクティブを使用してサーバーアドレスを定義します。

Nginx Webサーバーの構成は、serverブロックで定義されています。 listenディレクティブは、サーバーが着信要求をリッスンするポート番号を定義します。 error_logおよびaccess_logディレクティブは、ログを書き込むためのファイルを定義します。 proxy_passディレクティブは、リクエストをhttp://app_serverに転送するためのアップストリームサーバーを設定するために使用されます。

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

Nginx Webサーバーを構成したら、Flaskto-doAPIの作成に進むことができます。

ステップ4— FlaskTo-DoAPIを作成する

環境を構築したので、アプリケーションを構築する準備が整いました。 このステップでは、POSTリクエストから送信されたToDoメモを保存して表示するToDoAPIアプリケーションを作成します。

appディレクトリにrequirements.txtファイルを作成することから始めます。

nano app/requirements.txt

このファイルは、アプリケーションの依存関係をインストールするために使用されます。 このチュートリアルの実装では、 FlaskFlask-PyMongo 、およびrequestsを使用します。 requirements.txtファイルに以下を追加します。

app / requireds.txt

Flask==1.0.2
Flask-PyMongo==2.2.0
requests==2.20.1

ファイルを保存し、要件を入力した後、エディターを終了します。

次に、app.pyファイルを作成して、appディレクトリにFlaskアプリケーションコードを含めます。

nano app/app.py

新しいapp.pyファイルに、依存関係をインポートするコードを入力します。

app / app.py

import os
from flask import Flask, request, jsonify
from flask_pymongo import PyMongo

osパッケージは、環境変数をインポートするために使用されます。 flaskライブラリから、Flaskrequest、およびjsonifyオブジェクトをインポートして、それぞれアプリケーションのインスタンス化、リクエストの処理、JSONレスポンスの送信を行いました。 flask_pymongoから、MongoDBと対話するためにPyMongoオブジェクトをインポートしました。

次に、MongoDBに接続するために必要なコードを追加します。

app / app.py

. . .
application = Flask(__name__)

application.config["MONGO_URI"] = 'mongodb://' + os.environ['MONGODB_USERNAME'] + ':' + os.environ['MONGODB_PASSWORD'] + '@' + os.environ['MONGODB_HOSTNAME'] + ':27017/' + os.environ['MONGODB_DATABASE']

mongo = PyMongo(application)
db = mongo.db

Flask(__name__)は、アプリケーションオブジェクトをapplication変数にロードします。 次に、コードはos.environを使用して環境変数からMongoDB接続文字列を構築します。 applicationオブジェクトをPyMongo()メソッドに渡すと、mongoオブジェクトが得られ、dbオブジェクトがmongo.dbから得られます。 ]。

次に、インデックスメッセージを作成するためのコードを追加します。

app / app.py

. . .
@application.route('/')
def index():
    return jsonify(
        status=True,
        message='Welcome to the Dockerized Flask MongoDB app!'
    )

@application.route('/')は、APIの/GETルートを定義します。 ここで、index()関数は、jsonifyメソッドを使用してJSON文字列を返します。

次に、/todoルートを追加して、すべてのToDoを一覧表示します。

app / app.py

. . .
@application.route('/todo')
def todo():
    _todos = db.todo.find()

    item = {}
    data = []
    for todo in _todos:
        item = {
            'id': str(todo['_id']),
            'todo': todo['todo']
        }
        data.append(item)

    return jsonify(
        status=True,
        data=data
    )

@application.route('/todo')は、APIの/todo GETルートを定義します。これにより、データベース内のToDoが返されます。 db.todo.find()メソッドは、データベース内のすべてのToDoを返します。 次に、_todosを繰り返し処理して、dataに追加するオブジェクトからidtodoのみを含むitemを構築します。 ]配列を作成し、最後にそれらをJSONとして返します。

次に、やることを作成するためのコードを追加します。

app / app.py

. . .
@application.route('/todo', methods=['POST'])
def createTodo():
    data = request.get_json(force=True)
    item = {
        'todo': data['todo']
    }
    db.todo.insert_one(item)

    return jsonify(
        status=True,
        message='To-do saved successfully!'
    ), 201

@application.route('/todo')は、APIの/todo POSTルートを定義します。これにより、データベースにToDoメモが作成されます。 request.get_json(force=True)は、ルートに投稿したJSONを取得し、itemは、ToDoに保存されるJSONを構築するために使用されます。 db.todo.insert_one(item)は、データベースに1つのアイテムを挿入するために使用されます。 To Doがデータベースに保存された後、ステータスコード201 CREATEDのJSON応答を返します。

次に、アプリケーションを実行するためのコードを追加します。

app / app.py

. . .
if __name__ == "__main__":
    ENVIRONMENT_DEBUG = os.environ.get("APP_DEBUG", True)
    ENVIRONMENT_PORT = os.environ.get("APP_PORT", 5000)
    application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)

条件__name__ == "__main__"は、モジュール内のグローバル変数__name__がプログラムへのエントリポイントであるかどうかを確認し、アプリケーションを実行するために使用されます。 __name__"__main__"と等しい場合、ifブロック内のコードは、このコマンドapplication.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)を使用してアプリを実行します。

次に、os.environ.get()を使用して、環境変数からENVIRONMENT_DEBUGENVIRONMENT_PORTの値を取得します。最初のパラメーターとしてキーを使用し、2番目のパラメーターとしてデフォルト値を使用します。 application.run()は、アプリケーションのhostport、およびdebugの値を設定します。

完成したapp.pyファイルは次のようになります。

app / app.py

import os
from flask import Flask, request, jsonify
from flask_pymongo import PyMongo

application = Flask(__name__)

application.config["MONGO_URI"] = 'mongodb://' + os.environ['MONGODB_USERNAME'] + ':' + os.environ['MONGODB_PASSWORD'] + '@' + os.environ['MONGODB_HOSTNAME'] + ':27017/' + os.environ['MONGODB_DATABASE']

mongo = PyMongo(application)
db = mongo.db

@application.route('/')
def index():
    return jsonify(
        status=True,
        message='Welcome to the Dockerized Flask MongoDB app!'
    )

@application.route('/todo')
def todo():
    _todos = db.todo.find()

    item = {}
    data = []
    for todo in _todos:
        item = {
            'id': str(todo['_id']),
            'todo': todo['todo']
        }
        data.append(item)

    return jsonify(
        status=True,
        data=data
    )

@application.route('/todo', methods=['POST'])
def createTodo():
    data = request.get_json(force=True)
    item = {
        'todo': data['todo']
    }
    db.todo.insert_one(item)

    return jsonify(
        status=True,
        message='To-do saved successfully!'
    ), 201

if __name__ == "__main__":
    ENVIRONMENT_DEBUG = os.environ.get("APP_DEBUG", True)
    ENVIRONMENT_PORT = os.environ.get("APP_PORT", 5000)
    application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)

ファイルを保存して、エディターを終了します。

次に、appディレクトリにwsgi.pyファイルを作成します。

nano app/wsgi.py

wsgi.pyファイルは、サーバーが使用できるようにアプリケーションオブジェクト(または呼び出し可能)を作成します。 リクエストが来るたびに、サーバーはこのアプリケーションオブジェクトを使用して、URLの解析時にアプリケーションのリクエストハンドラーを実行します。

次の内容をwsgi.pyファイルに入れ、ファイルを保存して、テキストエディタを終了します。

app / wsgi.py

from app import application

if __name__ == "__main__":
  application.run()

このwsgi.pyファイルは、app.pyファイルからアプリケーションオブジェクトをインポートし、Gunicornサーバー用のアプリケーションオブジェクトを作成します。

これでToDoアプリが配置されたので、コンテナーでアプリケーションの実行を開始する準備が整いました。

ステップ5—コンテナの構築と実行

docker-compose.ymlファイル内のすべてのサービスとその構成を定義したので、コンテナーを開始できます。

サービスは単一のファイルで定義されているため、単一のコマンドを発行してコンテナーを開始し、ボリュームを作成し、ネットワークをセットアップする必要があります。 このコマンドは、FlaskアプリケーションとNginxWebサーバーのイメージも構築します。 次のコマンドを実行して、コンテナーを作成します。

docker-compose up -d

コマンドを初めて実行すると、必要なすべてのDockerイメージがダウンロードされます。これには時間がかかる場合があります。 イメージがダウンロードされてローカルマシンに保存されると、docker-composeによってコンテナが作成されます。 -dフラグはプロセスをデーモン化し、バックグラウンドプロセスとして実行できるようにします。

ビルドプロセスが完了したら、次のコマンドを使用して実行中のコンテナを一覧表示します。

docker ps

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

OutputCONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                      NAMES
f20e9a7fd2b9        digitalocean.com/webserver:latest   "nginx -g 'daemon of…"   2 weeks ago         Up 2 weeks          0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   webserver
3d53ea054517        digitalocean.com/flask-python:3.6   "gunicorn -w 4 --bin…"   2 weeks ago         Up 2 weeks          5000/tcp                                   flask
96f5a91fc0db        mongo:4.0.8                     "docker-entrypoint.s…"   2 weeks ago         Up 2 weeks          27017/tcp                                  mongodb

CONTAINER IDは、コンテナーへのアクセスに使用される一意の識別子です。 IMAGEは、指定されたコンテナーのイメージ名を定義します。 NAMESフィールドは、コンテナーが作成されるサービス名です。CONTAINER IDと同様に、コンテナーへのアクセスに使用できます。 最後に、STATUSは、コンテナーが実行中、再起動中、または停止中かどうかに関するコンテナーの状態に関する情報を提供します。

docker-composeコマンドを使用して、構成ファイルからコンテナーをビルドしました。 次のステップでは、アプリケーションのMongoDBユーザーを作成します。

ステップ6—MongoDBデータベースのユーザーを作成する

デフォルトでは、MongoDBはユーザーが資格情報なしでログインすることを許可し、無制限の特権を付与します。 このステップでは、MongoDBデータベースにアクセスするための専用ユーザーを作成して、MongoDBデータベースを保護します。

これを行うには、mongodbサービスのdocker-compose.ymlファイル環境変数MONGO_INITDB_ROOT_USERNAMEおよびMONGO_INITDB_ROOT_PASSWORDで設定したrootユーザー名とパスワードが必要です。 一般に、データベースを操作するときは、root管理者アカウントの使用を避けることをお勧めします。 代わりに、Flaskアプリケーション専用のデータベースユーザーと、Flaskアプリがアクセスできる新しいデータベースを作成します。

新しいユーザーを作成するには、最初にmongodbコンテナでインタラクティブシェルを起動します。

docker exec -it mongodb bash

docker execコマンドを使用して、実行中のコンテナー内でコマンドを実行し、-itフラグを使用して、コンテナー内で対話型シェルを実行します。

コンテナ内に入ると、MongoDB root管理者アカウントにログインします。

mongo -u mongodbuser -p

docker-compose.ymlファイルのMONGO_INITDB_ROOT_PASSWORD変数の値として入力したパスワードの入力を求められます。 mongodbサービスでMONGO_INITDB_ROOT_PASSWORDに新しい値を設定することでパスワードを変更できます。その場合は、docker-compose up -dコマンドを再実行する必要があります。

show dbs;コマンドを実行して、すべてのデータベースを一覧表示します。

show dbs;

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

Outputadmin    0.000GB
config   0.000GB
local    0.000GB
5 rows in set (0.00 sec)

adminデータベースは、ユーザーに管理者権限を付与する特別なデータベースです。 ユーザーがadminデータベースへの読み取りアクセス権を持っている場合、他のすべてのデータベースへの読み取りおよび書き込み権限があります。 出力にはadminデータベースがリストされているため、ユーザーはこのデータベースにアクセスでき、他のすべてのデータベースに対して読み取りと書き込みを行うことができます。

最初のやることメモを保存すると、自動的にMongoDBデータベースが作成されます。 MongoDBでは、use databaseコマンドを使用して、存在しないデータベースに切り替えることができます。 ドキュメントがコレクションに保存されるときにデータベースを作成します。 したがって、データベースはここでは作成されません。 これは、APIからデータベースに最初のやることメモを保存するときに発生します。 useコマンドを実行して、flaskdbデータベースに切り替えます。

use flaskdb

次に、このデータベースへのアクセスを許可する新しいユーザーを作成します。

db.createUser({user: 'flaskuser', pwd: 'your password', roles: [{role: 'readWrite', db: 'flaskdb'}]})
exit

このコマンドは、flaskdbデータベースへのreadWriteアクセス権を持つflaskuserという名前のユーザーを作成します。 pwdフィールドには必ず安全なパスワードを使用してください。 userおよびpwdは、flaskサービスの環境変数セクションのdocker-compose.ymlファイルで定義した値です。

次のコマンドを使用して、認証されたデータベースにログインします。

mongo -u flaskuser -p your password --authenticationDatabase flaskdb

ユーザーを追加したので、データベースからログアウトします。

exit

そして最後に、コンテナを終了します。

exit

これで、Flaskアプリケーション専用のデータベースとユーザーアカウントが構成されました。 データベースコンポーネントの準備ができたので、Flaskto-doアプリの実行に進むことができます。

ステップ7—FlaskTo-doアプリを実行する

サービスが構成されて実行されたので、ブラウザーでhttp://your_server_ipに移動してアプリケーションをテストできます。 さらに、curlを実行して、FlaskからのJSON応答を確認できます。

curl -i http://your_server_ip

次の応答が返されます。

Output{"message":"Welcome to the Dockerized Flask MongoDB app!","status":true}

Flaskアプリケーションの構成は、docker-compose.ymlファイルからアプリケーションに渡されます。 データベース接続に関する設定は、flaskサービスのenvironmentセクションで定義されているMONGODB_*変数を使用して設定されます。

すべてをテストするには、FlaskAPIを使用してやることメモを作成します。 これは、/todoルートへのPOSTcurlリクエストを使用して行うことができます。

curl -i -H "Content-Type: application/json" -X POST -d '{"todo": "Dockerize Flask application with MongoDB backend"}' http://your_server_ip/todo

このリクエストにより、To DoアイテムがMongoDBに保存されると、ステータスコード201 CREATEDの応答が返されます。

Output{"message":"To-do saved successfully!","status":true}

/todoルートへのGETリクエストを使用して、MongoDBからのすべてのToDoノートを一覧表示できます。

curl -i http://your_server_ip/todo
Output{"data":[{"id":"5c9fa25591cb7b000a180b60","todo":"Dockerize Flask application with MongoDB backend"}],"status":true}

これにより、サーバーにデプロイされたリバースプロキシとしてNginxを使用してMongoDBバックエンドを実行するFlaskAPIをDocker化できました。 実稼働環境の場合、sudo systemctl enable dockerを使用して、Dockerサービスが実行時に自動的に開始されるようにすることができます。

結論

このチュートリアルでは、Docker、MongoDB、Nginx、およびGunicornを使用してFlaskアプリケーションをデプロイしました。 これで、スケーリング可能な最新のステートレスAPIアプリケーションが機能するようになりました。 docker container runのようなコマンドを使用してこの結果を達成できますが、docker-compose.ymlは、このスタックをバージョン管理に入れて必要に応じて更新できるため、作業が簡素化されます。

ここから、さらにPythonFrameworkチュートリアルを見ることができます。