Ubuntu18.04でDockerを使用してFlaskアプリケーションをビルドおよびデプロイする方法
著者は、 Write forDOnationsプログラムの一環として寄付を受け取るためにTechEducationFundを選択しました。
序章
Docker は、管理者がコンテナーを使用してアプリケーションを作成、管理、デプロイ、および複製できるようにするオープンソースアプリケーションです。 コンテナは、アプリケーションがオペレーティングシステムレベルで実行するために必要な依存関係を格納するパッケージと考えることができます。 これは、Dockerを使用してデプロイされた各アプリケーションが独自の環境に存在し、その要件が個別に処理されることを意味します。
Flask は、Python上に構築されたWebマイクロフレームワークです。 実行に特定のツールやプラグインを必要としないため、マイクロフレームワークと呼ばれます。 Flaskフレームワークは軽量で柔軟性がありますが、高度に構造化されているため、他のフレームワークよりも優先されます。
Dockerを使用してFlaskアプリケーションをデプロイすると、最小限の再構成で異なるサーバー間でアプリケーションを複製できます。
このチュートリアルでは、Flaskアプリケーションを作成し、Dockerを使用してデプロイします。 このチュートリアルでは、デプロイ後にアプリケーションを更新する方法についても説明します。
前提条件
このチュートリアルに従うには、次のものが必要です。
- Ubuntu 18.04を使用したサーバーの初期設定ガイドに従って構成された、sudo権限を持つroot以外のユーザー。
- Dockerがインストールされた1台のUbuntu18.04サーバー。このチュートリアルに従うか、DigitalOceanワンクリックDockerイメージを使用してセットアップします。
- Nginx は、 Ubuntu18.04チュートリアルにNginxをインストールする方法のステップ1に従ってインストールされます。
ステップ1—Flaskアプリケーションのセットアップ
開始するには、Flaskアプリケーションを保持するディレクトリ構造を作成します。 このチュートリアルでは、/var/www
にTestApp
というディレクトリを作成しますが、コマンドを変更して任意の名前を付けることができます。
- sudo mkdir /var/www/TestApp
新しく作成されたTestApp
ディレクトリに移動します。
- cd /var/www/TestApp
次に、Flaskアプリケーションの基本フォルダー構造を作成します。
- sudo mkdir -p app/static app/templates
-p
フラグは、mkdir
がディレクトリと存在しないすべての親ディレクトリを作成することを示します。 この場合、mkdir
は、static
およびtemplates
ディレクトリを作成する過程でapp
親ディレクトリを作成します。
app
ディレクトリには、ビューやブループリントなど、Flaskアプリケーションに関連するすべてのファイルが含まれます。 Views は、アプリケーションへのリクエストに応答するために作成するコードです。 ブループリントは、アプリケーションコンポーネントを作成し、アプリケーション内または複数のアプリケーション間で共通のパターンをサポートします。
static
ディレクトリは、画像、CSS、JavaScriptファイルなどのアセットが存在する場所です。 templates
ディレクトリは、プロジェクトのHTMLテンプレートを配置する場所です。
基本フォルダー構造が完成したので、Flaskアプリケーションの実行に必要なファイルを作成します。 まず、app
ディレクトリ内に__init__.py
ファイルを作成します。 このファイルは、app
ディレクトリがパッケージであり、そのように扱われる必要があることをPythonインタープリターに通知します。
次のコマンドを実行してファイルを作成します。
- sudo nano app/__init__.py
Pythonのパッケージを使用すると、モジュールを論理的な名前空間または階層にグループ化できます。 このアプローチにより、コードを特定の機能を実行する個別の管理可能なブロックに分割できます。
次に、Flaskインスタンスを作成するコードを__init__.py
に追加し、このファイルの保存後に作成するviews.py
ファイルからロジックをインポートします。 次のコードを新しいファイルに追加します。
from flask import Flask
app = Flask(__name__)
from app import views
そのコードを追加したら、ファイルを保存して閉じます。
__init__.py
ファイルを作成したら、app
ディレクトリにviews.py
ファイルを作成する準備が整います。 このファイルには、ほとんどのアプリケーションロジックが含まれています。
- sudo nano app/views.py
次に、views.py
ファイルにコードを追加します。 このコードは、hello world!
文字列をWebページにアクセスするユーザーに返します。
from app import app
@app.route('/')
def home():
return "hello world!"
関数の上の@app.route
行は、デコレータと呼ばれます。 デコレータは、それに続く関数を変更します。 この場合、デコレータは、どのURLがhome()
関数をトリガーするかをFlaskに通知します。 home
関数によって返されるhello world
テキストは、ブラウザのユーザーに表示されます。
views.py
ファイルを配置したら、uwsgi.ini
ファイルを作成する準備が整います。 このファイルには、アプリケーションのuWSGI構成が含まれます。 uWSGIは、プロトコルとアプリケーションサーバーの両方であるNginxの展開オプションです。 アプリケーションサーバーは、uWSGI、FastCGI、およびHTTPプロトコルを提供できます。
このファイルを作成するには、次のコマンドを実行します。
- sudo nano uwsgi.ini
次に、次のコンテンツをファイルに追加して、uWSGIサーバーを構成します。
[uwsgi]
module = main
callable = app
master = true
このコードは、Flaskアプリケーションが提供されるモジュールを定義します。 この場合、これはmain.py
ファイルであり、ここではmain
と呼ばれます。 callable
オプションは、メインアプリケーションによってエクスポートされたapp
インスタンスを使用するようにuWSGIに指示します。 master
オプションを使用すると、アプリケーションを実行し続けることができるため、アプリケーション全体をリロードする場合でもダウンタイムはほとんどありません。
次に、アプリケーションへのエントリポイントであるmain.py
ファイルを作成します。 エントリポイントは、アプリケーションとの対話方法についてuWSGIに指示します。
- sudo nano main.py
次に、以下をコピーしてファイルに貼り付けます。 これにより、以前に作成されたアプリケーションパッケージからapp
という名前のFlaskインスタンスがインポートされます。
from app import app
最後に、requirements.txt
ファイルを作成して、pip
パッケージマネージャーがDockerデプロイメントにインストールする依存関係を指定します。
- sudo nano requirements.txt
次の行を追加して、Flaskを依存関係として追加します。
Flask==1.0.2
インストールするFlaskのバージョンを指定します。 このチュートリアルを書いている時点では、1.0.2
が最新のFlaskバージョンです。 Flaskの公式Webサイトで更新を確認できます。
ファイルを保存して閉じます。 これでFlaskアプリケーションが正常にセットアップされ、Dockerをセットアップする準備が整いました。
ステップ2—Dockerをセットアップする
このステップでは、Dockerfile
とstart.sh
の2つのファイルを作成して、Dockerデプロイメントを作成します。 Dockerfile
は、イメージのアセンブルに使用されるコマンドを含むテキストドキュメントです。 start.sh
ファイルは、イメージを構築し、Dockerfile
からコンテナーを作成するシェルスクリプトです。
まず、Dockerfile
を作成します。
- sudo nano Dockerfile
次に、必要な構成をDockerfile
に追加します。 これらのコマンドは、イメージの構築方法と、含まれる追加の要件を指定します。
FROM tiangolo/uwsgi-nginx-flask:python3.6-alpine3.7
RUN apk --update add bash nano
ENV STATIC_URL /static
ENV STATIC_PATH /var/www/app/static
COPY ./requirements.txt /var/www/requirements.txt
RUN pip install -r /var/www/requirements.txt
この例では、Dockerイメージは、DockerHubにある既存のイメージtiangolo/uwsgi-nginx-flask
から構築されます。 この特定のDockerイメージは、さまざまなPythonバージョンとOSイメージをサポートしているため、他のイメージよりも優れています。
最初の2行は、アプリケーションを実行し、bashコマンドプロセッサとnano
テキストエディタをインストールするために使用する親イメージを指定します。 また、GitHub、GitLab、Bitbucketなどのバージョン管理ホスティングサービスにプルおよびプッシュするためのgit
クライアントもインストールします。 ENV STATIC_URL /static
は、このDockerイメージに固有の環境変数です。 画像、CSSファイル、JavaScriptファイルなどのすべてのアセットが提供される静的フォルダーを定義します。
最後の2行は、requirements.txt
ファイルをコンテナーにコピーして実行できるようにし、requirements.txt
ファイルを解析して指定された依存関係をインストールします。
構成を追加したら、ファイルを保存して閉じます。
Dockerfile
を配置すると、Dockerコンテナーをビルドするstart.sh
スクリプトを作成する準備がほぼ整います。 start.sh
スクリプトを作成する前に、まず、構成で使用するポートが開いていることを確認してください。 ポートが空いているかどうかを確認するには、次のコマンドを実行します。
- sudo nc localhost 56733 < /dev/null; echo $?
上記のコマンドの出力が1
の場合、ポートは空いていて使用可能です。 それ以外の場合は、start.sh
構成ファイルで使用する別のポートを選択する必要があります。
使用する開いているポートを見つけたら、start.sh
スクリプトを作成します。
sudo nano start.sh
start.sh
スクリプトは、Dockerfile
からイメージを構築し、結果のDockerイメージからコンテナーを作成するシェルスクリプトです。 新しいファイルに構成を追加します。
#!/bin/bash
app="docker.test"
docker build -t ${app} .
docker run -d -p 56733:80 \
--name=${app} \
-v $PWD:/app ${app}
最初の行はシバンと呼ばれます。 これがbashファイルであり、コマンドとして実行されることを指定します。 次の行は、画像とコンテナに付ける名前を指定し、app
という名前の変数として保存します。 次の行は、現在のディレクトリにあるDockerfile
からイメージをビルドするようにDockerに指示します。 これにより、この例ではdocker.test
というイメージが作成されます。
最後の3行は、ポート56733
で公開されるdocker.test
という名前の新しいコンテナーを作成します。 最後に、現在のディレクトリをコンテナの/var/www
ディレクトリにリンクします。
-d
フラグを使用して、デーモンモードで、またはバックグラウンドプロセスとしてコンテナを起動します。 -p
フラグを含めて、サーバーのポートをDockerコンテナーの特定のポートにバインドします。 この場合、ポート56733
をDockerコンテナーのポート80
にバインドしています。 -v
フラグは、コンテナーにマウントするDockerボリュームを指定します。この場合、プロジェクトディレクトリ全体をDockerコンテナーの/var/www
フォルダーにマウントします。
start.sh
スクリプトを実行して、Dockerイメージを作成し、結果のイメージからコンテナーを構築します。
- sudo bash start.sh
スクリプトの実行が終了したら、次のコマンドを使用して、実行中のすべてのコンテナーを一覧表示します。
- sudo docker ps
コンテナを示す出力が表示されます。
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
58b05508f4dd docker.test "/entrypoint.sh /sta…" 12 seconds ago Up 3 seconds 443/tcp, 0.0.0.0:56733->80/tcp docker.test
docker.test
コンテナが実行されていることがわかります。 実行されたので、ブラウザの指定されたポートのIPアドレスにアクセスします:http://ip-address:56733
次のようなページが表示されます。
このステップでは、FlaskアプリケーションをDockerに正常にデプロイしました。 次に、テンプレートを使用してコンテンツをユーザーに表示します。
ステップ3—テンプレートファイルを提供する
テンプレートは、アプリケーションにアクセスするユーザーに静的および動的コンテンツを表示するファイルです。 このステップでは、アプリケーションのホームページを作成するためのHTMLテンプレートを作成します。
app/templates
ディレクトリにhome.html
ファイルを作成することから始めます。
- sudo nano app/templates/home.html
テンプレートのコードを追加します。 このコードは、タイトルといくつかのテキストを含むHTML5ページを作成します。
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Welcome home</title>
</head>
<body>
<h1>Home Page</h1>
<p>This is the home page of our application.</p>
</body>
</html>
テンプレートを追加したら、ファイルを保存して閉じます。
次に、app/views.py
ファイルを変更して、新しく作成されたファイルを提供します。
- sudo nano app/views.py
まず、ファイルの先頭に次の行を追加して、Flaskからrender_template
メソッドをインポートします。 このメソッドは、HTMLファイルを解析して、ユーザーにWebページをレンダリングします。
from flask import render_template
...
ファイルの最後に、テンプレートファイルをレンダリングするための新しいルートも追加します。 このコードは、ユーザーがアプリケーションの/template
ルートにアクセスするたびに、home.html
ファイルのコンテンツが提供されることを指定します。
...
@app.route('/template')
def template():
return render_template('home.html')
更新されたapp/views.py
ファイルは次のようになります。
from flask import render_template
from app import app
@app.route('/')
def home():
return "Hello world!"
@app.route('/template')
def template():
return render_template('home.html')
完了したら、ファイルを保存して閉じます。
これらの変更を有効にするには、Dockerコンテナを停止して再起動する必要があります。 次のコマンドを実行して、コンテナを再構築します。
- sudo docker stop docker.test && sudo docker start docker.test
http://your-ip-address:56733/template
でアプリケーションにアクセスして、提供されている新しいテンプレートを確認してください。
これで、アプリケーションの訪問者にサービスを提供するDockerテンプレートファイルを作成しました。 次のステップでは、Dockerコンテナを再起動しなくても、アプリケーションに加えた変更をどのように有効にできるかを確認します。
ステップ4—アプリケーションを更新する
新しい要件のインストール、Dockerコンテナーの更新、HTMLとロジックの変更など、アプリケーションに変更を加える必要がある場合があります。 このセクションでは、Dockerコンテナーを再起動せずにこれらの変更を行うようにtouch-reload
を構成します。
Python autoreloading は、ファイルシステム全体の変更を監視し、変更を検出するとアプリケーションを更新します。 自動リロードは、リソースをすぐに消費する可能性があるため、本番環境では推奨されません。 このステップでは、touch-reload
を使用して特定のファイルへの変更を監視し、ファイルが更新または置換されたときにリロードします。
これを実装するには、uwsgi.ini
ファイルを開くことから始めます。
- sudo nano uwsgi.ini
次に、強調表示された行をファイルの最後に追加します。
module = main
callable = app
master = true
touch-reload = /app/uwsgi.ini
これは、アプリケーション全体のリロードをトリガーするように変更されるファイルを指定します。 変更を加えたら、ファイルを保存して閉じます。
これを実証するために、アプリケーションに小さな変更を加えます。 app/views.py
ファイルを開くことから始めます。
- sudo nano app/views.py
home
関数によって返された文字列を置き換えます。
from flask import render_template
from app import app
@app.route('/')
def home():
return "<b>There has been a change</b>"
@app.route('/template')
def template():
return render_template('home.html')
変更を加えたら、ファイルを保存して閉じます。
次に、アプリケーションのホームページをhttp://ip-address:56733
で開くと、変更が反映されていないことがわかります。 これは、リロードの条件がuwsgi.ini
ファイルへの変更であるためです。 アプリケーションをリロードするには、touch
を使用して条件をアクティブにします。
- sudo touch uwsgi.ini
アプリケーションのホームページをブラウザに再度ロードします。 アプリケーションに変更が組み込まれていることがわかります。
この手順では、touch-reload
条件を設定して、変更を加えた後にアプリケーションを更新します。
結論
このチュートリアルでは、Flaskアプリケーションを作成してDockerコンテナーにデプロイしました。 また、touch-reload
を構成して、コンテナーを再起動せずにアプリケーションを更新しました。
Docker上の新しいアプリケーションを使用すると、簡単にスケーリングできるようになります。 Dockerの使用の詳細については、Dockerの公式ドキュメントを確認してください。