MongoDBとDockerでFlaskをセットアップする方法

著者は、Write for DOnationsプログラムの一部として寄付を受け取るためにInternet Archiveを選択しました。

前書き

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

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

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

前提条件

このチュートリアルを実行するには、次のものが必要です。

[[step-1 -—- writing-the-stack-configuration-in-docker-compose]] ==ステップ1— DockerComposeでスタック構成を書き込む

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

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

ボリュームは、Dockerでデータを永続化するための最良の方法です。ボリューム内のデータは、他のアプリケーションとエクスポートまたは共有できるためです。 Dockerでのデータ共有の詳細については、How To Share Data Between the Docker Container and the Hostを参照してください。

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

mkdir flaskapp

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

cd flaskapp

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

nano docker-compose.yml

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

docker-compose.yml

version: '3'
services:

ここで、docker-compose.ymlファイルの最初のサービスとしてflaskを定義します。 次のコードを追加して、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

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

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

次に、bridge networksを作成して、コンテナーが相互に通信できるようにします。 ファイルの最後に次の行を追加します。

docker-compose.yml

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

接続するサービス用に2つのネットワーク(frontendbackend)を定義しました。 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データベースやNginx Webサーバーログなどのデータが失われる可能性があるため、ボリュームはこのデータを永続化するために使用されます。

完全な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構成をdocker-compose.ymlファイルで定義しました。 次に、FlaskとWebサーバーのDockerfileの作成に進みます。

[[step-2 -—- writing-the-flask-and-web-server-dockerfiles]] ==ステップ2—フラスコとWebサーバーのDockerfilesの書き込み

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

この手順では、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 "

ENV GROUP_ID=1000 \
    USER_ID=1000

WORKDIR /var/www/

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

ENVディレクティブは、グループとユーザーIDの環境変数を定義するために使用されます。
Linux Standard Base (LSB)は、UIDs and GIDs 0-99がシステムによって静的に割り当てられることを指定します。 UIDs 100-999は、システムユーザーとグループに動的に割り当てられることになっています。 UIDs 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変数で定義されているように、ユーザーIDが1000wwwユーザーを作成します。 -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 "

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ディレクトリにNginx WebサーバーのDockerfileを作成します。

nano nginx/Dockerfile

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

nginx/Dockerfile

FROM digitalocean.com/alpine:latest

LABEL MAINTAINER="FirstName LastName "

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)と出力(/dev/stdout)にアクセスするためのシンボリックリンクを作成します。 コンテナは一時的なものであるため、エラーを標準エラーと標準出力に公開することをお勧めします。これにより、ログがdockerログに送信され、そこからログをElasticスタックなどのロギングサービスに転送して永続化できます。 これが完了したら、コマンドを実行してdefault.conf/var/cache/apk/*を削除し、結果の画像のサイズを縮小します。 これらすべてのコマンドを単一のRUNで実行すると、画像内のレイヤーの数が減り、結果の画像のサイズも小さくなります。

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

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

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

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

[[step-3 -—- configuring-the-nginx-reverse-proxy]] ==ステップ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;
    }
}

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

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

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

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

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

[[step-4 -—- creating-the-flask-to-do-api]] ==ステップ4— Flask To-doAPIの作成

環境を構築したら、アプリケーションを構築する準備が整いました。 このステップでは、POST要求から送信されたTo Doノートを保存および表示するTo Do APIアプリケーションを作成します。

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

nano app/requirements.txt

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

app/requirements.txt

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

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

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

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オブジェクトが得られ、これにより、mongo.dbから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を反復処理して、idtodoのみを含むitemを構築し、それらをdata配列に追加して最終的に戻ります。それらを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サーバーのアプリケーションオブジェクトを作成します。

To Doアプリが準備できたので、コンテナでアプリケーションを実行する準備が整いました。

[[ステップ-5 ---コンテナーの構築と実行]] ==ステップ5—コンテナーの構築と実行

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

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

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ユーザーを作成します。

[[step-6 -—- creating-a-user-for-your-mongodb-database]] ==ステップ6—MongoDBデータベースのユーザーを作成する

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

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

新しいユーザーを作成するには、最初にmongodbコンテナーで対話型シェルを開始します。

docker exec -it mongodb bash

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

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

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データベースがリストされているため、ユーザーはこのデータベースにアクセスでき、他のすべてのデータベースに対して読み取りと書き込みを行うことができます。

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

use flaskdb

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

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

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

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

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

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

exit

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

exit

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

[[step-7 -—- running-the-flask-to-do-app]] ==ステップ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_*変数を使用して設定されます。

すべてをテストするには、Flask APIを使用してTo Doノートを作成します。 これは、/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バックエンドを実行するFlask APIをDocker化できました。 実稼働環境では、sudo systemctl enable dockerを使用して、Dockerサービスが実行時に自動的に開始されるようにすることができます。

結論

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

ここから、さらにPython Framework tutorialsを確認することもできます。

Related