最終更新日:
公開日:
WEB制作
DockerでFlaskを実行するコンテナを作る
PythonのWebフレームワーク「Flask」を実行するDockerコンテナを構築する方法について解説します。
この記事のポイント
- Python公式のコンテナイメージをベースにして開発向けのFlaskが動くコンテナを作成する
- テンプレートシステム「Jinja2」を使う
目次
Flaskを実行するコンテナに必要なもの
PythonのWebフレームワークとして広く活用されている「Flask」を動かすことができる開発向けのDockerコンテナを構築していきます。
Pythonのバージョンは「3.9.4」を使い、コンテナイメージはDocker HubのPython公式イメージからAlpine Linux(python:alpine)をベースにします。
Flaskを動かすために、以下の2つのファイルが最低限必要になります。
- Dockerfile
- app/index.py
コンテナイメージの設計図となるDockerfileと、Flaskを実行するPythonコードが書かれたindex.pyを用意します。
index.pyはコンテナ内のディレクトリにあわせて、「app」というディレクトリ内に作成します。
まずは最低限のコードを使ってFlaskを動かしながら、続いてもう一歩進めてFlaskのルーティングとテンプレート「Jinja2」を使った簡単な出力まで行っていきます。
今回は以下の流れでコンテナを作成、実行します。
- Dockerfileを作成する
- index.pyを作成する
- Dockerコンテナイメージをビルドする
- Dockerコンテナを起動
- ブラウザからコンテナにアクセス
- Jinja2のテンプレートファイルを作成
- ルーティング設定
なお、Dockerはすでにインストールされていることを前提としています。
これからインストールする場合は、「Dockerをインストールする」よりインストールを行なってください。
Dockerfileを作成する
まずはコンテナの設計図にあたる「Dockerfile」というテキストファイルを作成します。
今回はローカル環境の「/Users/test/docker/flask/」というパスにファイルを配置していきます。
ディレクトリ名は任意のもので大丈夫なので、パスはご自身の環境に置き換えて進めてください。
「/Users/test/docker/flask/」の中に「Dockerfile」というファイルを作成して、以下のコードを書いてください。
Dockerfile
FROM python:alpine
WORKDIR /app
COPY ./app /app
RUN pip install Flask
CMD ["python", "index.py"]
記述したコードを先頭から順に解説していきます。
FROM
1行目の「FROM python:alpine」は「FROM」を使ってベースになるコンテナイメージを指定しています。
今回は先述の通りDocker HubのPython公式イメージをベースにするため、「python」とタグ「alpine」を「:」で区切って指定します。
WORKDIR
3行目の「WORKDIR」はコンテナの作業ディレクトリを指定します。
今回はコンテナ内の「/app」を作業ディレクトリとして指定します。
COPY
5行目の「COPY」は「docker image build」コマンドでイメージを構築するときに、ローカル環境からコンテナ内にファイルをコピーして設置するための指定です。
「COPY」の1つ目にはコピー元のホスト(ローカル環境)ディレクトリ、2つ目にはコピー先のコンテナのディレクトリを指定します。
今回の例であれば、buildコマンドを実行したときに1つ目に指定したホスト(ローカル環境)ディレクトリ「./app」にあるファイルが、コンテナ内のディレクトリ「/app」に複製して設置されます。
RUN
7行目の「RUN」は「docker image build」コマンドでイメージを構築するときにコンテナ内でシェルコマンドを実行します。
今回はFlaskをインストールするためのpip(Pytyhonのパッケージ管理システム)のコマンド「pip install Flask」を実行する指定になっています。
pip自体はベースのイメージ「python:alpine」に含まれているため、こちらはインストール不要で使用することができます。
CMD
最後の行の「CMD」もコンテナ内でシェルコマンドを実行するためのものですが、こちらは「dockeru run」でコンテナが起動したタイミングに実行するコマンドを指定することができます。
上記の設定はLinuxのCUIに置き換えると「python index.py」を実行する指定です。
CMDではCUIのコマンド入力で使う半角スペースを「,(コンマ)」に置き換え、それぞれのコマンドやパラメータを「"(ダブルクォート)」で囲みます。
index.pyを作成する
続いて、Flaskのアプリケーションを実行するためのPythonコードを作成します。
ローカル環境の「/Users/test/docker/flask/」に「app」という名前でディレクトリを作成して、さらにその中に「index.py」を作成してください。
作成したら、index.pyを開いて以下のコードを書いていきましょう。
index.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "<h1>Hello, Flask!</h1>"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80, debug=True)
とメッセージを出力するコードです。
先頭から順にコードをみてきましょう。
まず1行目の「from flask import Flask」はFlaskクラスを読み込みます。
2行目は読み込んだクラスからインスタンスを作成して、変数appに代入します。
「__name__」は自動的に作成される変数で、今回のように「index.py」単体のファイルで実行したときは値に「__main__」がセットされます。
続く4行目はルーティング(URL)、5〜6行は実行するアクションの定義です。
ここではルートURL(/)にアクセスがあったときに、続く「def index():」の処理を実行する内容になります。
5行目の「def index():」は「def hello():」など自由な名前を設定することができます。
6行目はシンプルにh1要素で「Hello, Flask!」という見出しテキストを出力します。
8、9行目はFlaskのWebサーバーを起動します。
先述の通り変数__name__には値「__main__」がセットされますが、if文で変数の値を確認して9行目のapp.runメソッドで起動します。
app.runメソッドにはパラメータでホスト「0.0.0.0」、ポート番号「80」、デバッグモード(True)指定します。
ここで指定した内容はあくまでコンテナ内で適用されるため、もしローカル環境ですでに「80」を使っていたとしても問題ありません。
コンテナを起動するタイミングでコンテナの公開するポート番号をまた別で指定するため、ここでは「80」のまま進めてください。
Dockerコンテナイメージをビルドする
Dockerコンテナを作成し、シンプルなFlaskのアプリケーションを起動する準備が整いました。
続いて、コンテナイメージを作成しましょう。
まずはコマンドを入力して実行するためにプロンプトを起動します。
Windowsで実行するなら「PowerShell」、Macの場合は「ターミナル」などを開いてください。
(コマンドの入力、実行ができるなら他のアプリケーションでも大丈夫です)
cdコマンドを使って、ローカル環境の「/Users/test/docker/flask/」に移動します。
cdコマンド 入力例
$ cd /Users/test/docker/flask/
続いて、Dockerの「docker image build」コマンドを実行します。
新しく作成するコンテナイメージ名は「flask」としていますが、名前は自由に変えても大丈夫です。
末尾には現在のディレクトリである「.(ドット)」を指定します。
コマンド 入力例
$ docker image build -t flask .
実行すると、以下のようにコンテナのビルドが実行されます。
Stepの表示は実行環境によってダウンロードなどが発生するため異なります。
コマンド 出力例
$ docker image build -t flask .
Sending build context to Docker daemon 13.31kB
Step 1/5 : FROM python:alpine
---> ef8f54a83dcd
Step 2/5 : WORKDIR /app
---> Using cache
---> 2f44d7638389
...省略...
Successfully built 244b24e4330a
Successfully tagged flask:latest
無事にビルド成功すると、最後の2行のような「Successfully built 244b24e4330a」と「Successfully tagged flask:latest」が出力されます。
「Successfully built」に続くランダムな英数字はイメージIDです。
-tオプションは作成するコンテナイメージ名とタグ名を指定できますが、今回のようにイメージ名のみ指定してタグ名を省略すると自動的に「latest」になります。
作成したコンテナイメージを「docker image ls」コマンドで確認してみましょう。
lsコマンド 入力&出力例
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
flask latest 244b24e4330a 44 seconds ago 55.6MB
正常にコンテナイメージが作成できると「flask」が表示されます。
(コンテナイメージ名を変えている場合は指定したイメージ名が表示されます)
「IMAGE ID」には先ほど出力されていた「Successfully built 244b24e4330a」のイメージIDが入ります。
コンテナを起動する
作成したコンテナイメージを使ってコンテナを起動してみましょう。
以下の1行目の「docker run」コマンドを入力して実行してください。
コンテナの起動に成功すると、2行目のようにランダムの英数字からなるコンテナIDが表示されます。
コマンド入力例
$ docker run -p 5000:80 -v ${PWD}/app:/app -d flask
8b4e058bd0ff0bbcc872c594bfba1a5489673e8da7324545ac40ca8190aae883
コンテナの起動はdockerコマンドで「container run」、もしくは「run」を使います(どちらも同じです)。
コマンドはオプションの指定は以下の内容です。
ポート番号を指定する-pオプション
1つ目の「-p 5000:80」はコンテナの外からコンテナ内に接続するポート番号を指定します。
「-p」は「publish」の頭文字です。
ポート番号は「:」で区切って、「開くポート番号:コンテナ内のポート番号」の形式で指定します。
index.pyの最終行でapp.runメソッドを実行するときに「80」を指定しましたが、コンテナの外からアクセスするためには-pオプションで外部のポート番号を別途指定する必要があります。
ここではコンテナ内のポート番号「80」に対して、外部からは「5000」でアクセスできるようにしています。
そのため、ブラウザからアクセスするときは「localhost:5000」を使います。
もしローカル環境でポート番号「5000」をすでに他のDockerコンテナやサーバーが使用している場合は、同じポート番号を使うことができないため以下のようなエラーができます。
コマンド入力例
$ docker run -p 5000:80 -v ${PWD}/app:/app -d flask
55d443f62f7268b72739ab2a0130c1cfa59f7cf6978fa76997b887
docker: Error response from daemon: driver failed programming external connectivity on endpoint zen_mahavira
(1a7aa82afdd184c42a4218b2861df841b6d3d4a3d52d556ce8): Bind for 0.0.0.0:5000 failed: port is already allocated.
その場合は上記の-pオプションで指定するポート番号を「5001」など重複しない番号を指定してください。
ファイルを同期する-vオプション
2つ目の「-v ${PWD}/app:/app」はローカル環境とコンテナのディレクトリを同期するオプションです。
「-v」は「volume」の頭文字です。
このオプションを指定すると、ローカル環境の特定のディレクトリをコンテナ内と同期させて、ファイルの修正をすぐに反映することができるようになります。
同期するディレクトリのパスは「:」で区切って、「ホスト(ローカル環境)ディレクトリ:コンテナ内のディレクトリ」の形式で指定します。
上記のように「${PWD}」を使うと、現在のディレクトリのパスが自動的に入ります。
もちろん絶対パスや相対パスで指定しても大丈夫です。
コンテナをバックグランド処理にする-dオプション
3つ目の「-d」は起動したコンテナをバックグラウンド処理で実行するオプションです。
「-d」は「detach」の頭文字です。
-dオプションを指定しない場合、「docker run」コマンドでコンテナを起動するとコンテナが終了するまではカーソルが入力状態になりません。
もし挙動が気になる方は、後述するコンテナ終了コマンドを実行してから改めて-dオプションを指定しないで実行してみてください。
いよいよFlaskのコンテナが起動しました。
起動中のコンテナは「docker container ls」コマンドより確認することができます。
コマンド入力例
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b4e058bd0ff flask "python in…" 4 seconds ago Up 4 seconds 0.0.0.0:5000->8000/tcp modest_kho
「CONTAINER ID」は「docker run」コマンドで実行した後に表示されたコンテナIDを短くしたものが表示されます。
もしここで先ほど起動したコンテナが表示されていない場合は、起動後にエラーなどでコンテナがすぐに終了してしまった可能性があります。
そのときは「docker container ls」に-aオプションをつけて実行してみてください。
コマンド入力例
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b4e058bd0ff py "python in…" 4 seconds ago Exited (137) 8 seconds ago modest_kho
「STATUS」カラムに「Exited」と表示されていたらコンテナはすでに実行を終了しています。
そのときは上記のDockerfileの記述、パスやコマンドのパラメーターなどを改めて確認してみてください。
ブラウザからコンテナにアクセス
コンテナを起動したら、ブラウザからコンテナ内のWebサーバーにアクセスしてみましょう。
Chromeなどのブラウザを開いて、URLに「c」と入力してアクセスしてみてください。
もし上記「docker run」コマンドの-pで「5000」以外のポート番号を指定している場合は、指定したポート番号に置き換えてください。
ブラウザに「Hello, Flask!」と表示されたでしょうか。
無事に表示されていたら、コンテナ内のindex.pyに書いたFlaskアプリケーションが実行されています。
ここで、-vオプションのファイル同期の挙動を確認してみましょう。
ローカル環境の「/Users/test/docker/flask/app」にあるindex.pyを編集すると、コンテナ内の「/app」にあるindex.pyにも編集が反映されます。
index.pyを開いて、以下の赤字の箇所のようにコードを編集して上書き保存してください。
index.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "<h1>Hi, Flask!</h1>"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80, debug=True)
ブラウザに戻って、再読み込み(リロード)してください。
以下のように編集内容がすぐに反映されたらファイル同期が成功しています。
テンプレートシステム「Jinja2」を使ってみる
シンプルなFlaskアプリケーションを実行できたところで、続いてFlaskが標準で採用しているテンプレートシステム「Jinja2」を動かしてみましょう。
ローカル環境の「/Users/test/docker/flask/app」に新しく「templates」ディレクトリを作成して、さらに中にトップページの「index.html」と2ページ目の「test.html」を作成してください。
まずはindex.htmlのコードを書いていきましょう。
作成したファイルを開いて、以下のコードを入力してください。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Flask</title>
</head>
<body>
<h1>Welcome to Flask!</h1>
<p>Hello, {{ data.name }}</p>
</body>
</html>
見たところ普通のHTMLですが、p要素のところで「{{ data.name }}」の記述があります。
これはFlaskアプリケーションのindex.pyからデータを受け取って出力するためのタグです。
続いて、test.htmlを開いてコードを書いていきましょう。
test.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Test Page</title>
</head>
<body>
<h1>Test Page</h1>
<p>{{ data.message }}</p>
</body>
</html>
こちらではp要素に「{{ data.message }}」の記述をします。
続いて、index.pyを修正します。
赤字の箇所のコードを修正、追記してください。
index.py
from flask import Flask, render_template
app = Flask(__name__)
# TOP Page
@app.route("/")
def index():
values = { "name": "Taro"}
return render_template('index.html', data=values)
# Second Page
@app.route("/test")
def test():
values = { "message":"Hello! This is test page." }
return render_template('test.html', data=values)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80,debug=True)
1行目のモジュールを読み込むところで「render_template」を追記しました。
これで「Jinja2」が有効になり、先ほど作成した2つのHTMLをテンプレートとして使用できるようになります。
まず、「def index():」ではrender_template関数を使って「index.html」を読み込んでいます。
そして2つ目のパラメータは1行前の変数valuesに設定した値をテンプレートの変数dataに渡しています。
これで、index.htmlでは変数dataを通して「{{ data.name }}」のように値を出力することができます。
続いて新しいルーティングのURLとして「/test」を登録します。
これで「localhost:5000/test」にアクセスすると、「def test():」が実行されるになります。
「def test():」では先ほど作成した2つ目のテンプレートファイルtest.htmlを表示するように指定します。
ここでも2つ目のパラメータでテンプレートに変数valuesの値を渡します。
変更を上書き保存したら、ブラウザに戻って再読み込み(リロード)してみてください。
index.htmlに書いたHTMLと、変数data.nameの値が出力されていたら成功です。
続いてブラウザのURLバーに「localhost:5000/test」を入力して、2ページ目を表示してみましょう。
test.htmlに書いたHTMLと、変数data.messageの値が出力されたでしょうか。
やや駆け足になってしまいましたが、以上がFlaskのルーティングとテンプレートシステムになります。
ここではコンテナ内で動かしているFlaskの機能を、簡単にではありますが体感することができたらと思います。
コンテナを停止する
以上でDockerでFlask(Python)を実行できるコンテナを作成、起動することができました。
もし起動したコンテナが不要になったときは、「docker stop」コマンドか「docker container stop」コマンドでコンテナを停止します(どちらも停止する動作は同じです)
コマンド入力例
$ docker stop 8b4e058bd0ff
8b4e058bd0ff
正常に停止すると、停止したコンテナのIDを出力します。
コンテナIDは「docker container ls」コマンドで表示された短いID、起動したときに表示されるフルIDのどちらでも大丈夫です。
また、停止したコンテナは削除したわけはないため、「docker start」コマンドで再度起動することができます。
コマンド入力例
$ docker start 8b4e058bd0ff
8b4e058bd0ff
こちらの記事は役に立ちましたか?
コメントありがとうございます!
運営の参考にさせていただきます。
ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。