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ファイルからロジックをインポートします。 次のコードを新しいファイルに追加します。
/var/www/TestApp/app/__init__.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ページにアクセスするユーザーに返します。
/var/www/TestApp/app/views.py
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サーバーを構成します。
/var/www/TestApp/uwsgi.ini
[uwsgi] module = main callable = app master = true
このコードは、Flaskアプリケーションが提供されるモジュールを定義します。 この場合、これはmain.pyファイルであり、ここではmainと呼ばれます。 callableオプションは、メインアプリケーションによってエクスポートされたappインスタンスを使用するようにuWSGIに指示します。 masterオプションを使用すると、アプリケーションを実行し続けることができるため、アプリケーション全体をリロードする場合でもダウンタイムはほとんどありません。
次に、アプリケーションへのエントリポイントであるmain.pyファイルを作成します。 エントリポイントは、アプリケーションとの対話方法についてuWSGIに指示します。
sudo nano main.py
次に、以下をコピーしてファイルに貼り付けます。 これにより、以前に作成されたアプリケーションパッケージからappという名前のFlaskインスタンスがインポートされます。
/var/www/TestApp/main.py
from app import app
最後に、requirements.txtファイルを作成して、pipパッケージマネージャーがDockerデプロイメントにインストールする依存関係を指定します。
sudo nano requirements.txt
次の行を追加して、Flaskを依存関係として追加します。
/var/www/TestApp/requirements.txt
Flask==1.0.2
インストールするFlaskのバージョンを指定します。 このチュートリアルを書いている時点では、1.0.2が最新のFlaskバージョンです。 Flaskの公式ウェブサイトでアップデートを確認できます。
ファイルを保存して閉じます。 これでFlaskアプリケーションが正常にセットアップされ、Dockerをセットアップする準備が整いました。
ステップ2—Dockerをセットアップする
このステップでは、Dockerfileとstart.shの2つのファイルを作成して、Dockerデプロイメントを作成します。 Dockerfileは、画像の組み立てに使用されるコマンドを含むテキストドキュメントです。 start.shファイルは、イメージを構築し、Dockerfileからコンテナーを作成するシェルスクリプトです。
まず、Dockerfileを作成します。
sudo nano Dockerfile
次に、必要な構成をDockerfileに追加します。 これらのコマンドは、イメージの構築方法と、含まれる追加の要件を指定します。
/ var / www / TestApp / 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イメージからコンテナーを作成するシェルスクリプトです。 新しいファイルに構成を追加します。
/var/www/TestApp/start.sh
#!/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ページを作成します。
/var/www/TestApp/app/templates/home.html
<!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ページをレンダリングします。
/var/www/TestApp/app/views.py
from flask import render_template ...
ファイルの最後に、テンプレートファイルをレンダリングするための新しいルートも追加します。 このコードは、ユーザーがアプリケーションの/templateルートにアクセスするたびに、home.htmlファイルのコンテンツが提供されることを指定します。
/var/www/TestApp/app/views.py
...
@app.route('/template')
def template():
return render_template('home.html')
更新されたapp/views.pyファイルは次のようになります。
/var/www/TestApp/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
次に、強調表示された行をファイルの最後に追加します。
/var/www/TestApp/uwsgi.ini
module = main callable = app master = true touch-reload = /app/uwsgi.ini
これは、アプリケーション全体のリロードをトリガーするように変更されるファイルを指定します。 変更を加えたら、ファイルを保存して閉じます。
これを実証するために、アプリケーションに小さな変更を加えます。 app/views.pyファイルを開くことから始めます。
sudo nano app/views.py
home関数によって返された文字列を置き換えます。
/var/www/TestApp/app/views.py
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の公式ドキュメントを確認してください。