DockerでFlaskが動き、簡単なテストが通る状態を作る(Flaskで簡単なWebサービスを作ってみる[第3回])

はじめに

PythonのWebアプリケーションフレームワークFlaskを使って、「消耗品買い物リスト」を作ってみます。
ついにFlaskのコードを書き始めました。

前回まで

環境構築(Docker+Flask)

本記事のテーマは環境構築です。
本プロジェクトでは、アプリケーションフレームワークとしてFlaskを使いますが、開発環境にDockerを使ってみることにしました。

Dockerとは

一言でいうと手軽に仮想環境を構築できるサービスです。
VitrualBoxなどのVMとの違いとしては、VMが環境をまるごとホストOSの上に構築して動かすのに対し、Dockerでは、OSはホストOSのものを使いつつ、その上に必要な部分だけ構築する点です。

メリットとしては環境の構築・破棄が非常に手軽に行える点、設定ファイルを共有することで同一環境の再現ができる点です。「環境構築に半日かかる」や「環境が違うからバグが再現できない」のようなめんどくさい課題を解決してくれるサービスです。

上記メリットを認識しつつも、これまでちゃんと使ったことは無かったため、この機会にガッツリと触ってみようと思います。

Flaskとは

Pythonで動く軽量なWebアプリケーションフレームワークです。
Web開発で必要な機能群から、最小限のものを提供してくれています。
同じPythonのWebアプリケーションフレームワークであるDjangoと比較し、機能を絞った分軽量で、セットアップが楽だという利点があります。

本プロジェクトでは、素早くサービスを構築したい点と作るものがシンプルな買い物リストである点から、Flaskを採用します。

環境構築の進め方

本記事のゴールは、「ローカルにDockerコンテナを作成し、コンテナ上でFlaskを動かしてWebページを表示すること」とします。

Dockerのインストール

Docker公式サイトから。

Dockerfileの作成

最小構成の設定にしておきます。プロジェクトディレクトリ直下に保存。

Dockerfile

# ベースイメージ作成
FROM python:3.7

# pipアップグレード
RUN pip install --upgrade pip
RUN pip install flask

# 作業ディレクトリ指定
WORKDIR /workdir

# 公開ポート指定
EXPOSE 8080

Dockerイメージの作成

ログインしてビルドします。コマンド実行はプロジェクトディレクトリ直下にて。

$ docker login

$ docker build -t flask-app .

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
flask-app           latest              619c347dc0dd        10 seconds ago      929MB
python              3.7                 22c70bba8283        2 days ago          920MB

Dockerコンテナの作成

共有用フォルダ作成

コンテナで共有するための作業ディレクトリを作っておきます。

$ mkdir src

コンテナを起動して入る

$ docker container run -it -p [Host Port]:[Guest Port] -v [共有用フォルダのパス]:[コンテナ内の絶対パス] --name [コンテナ名] [イメージ名] /bin/bash

例
$ docker container run -it -p 8080:5000 -v ~/Developments/to-buy-list/src:/workdir --name to-buy-list flask-app /bin/bash

root@a25d6b02fc8b:/workdir# 
root@a25d6b02fc8b:/workdir# pwd
/workdir
-pオプション

ホストOSのポートをコンテナのポートにマッピングします。
これで、ホストOSでlocalhost:8080にアクセスしたときに、コンテナの5000(Flaskのデフォルトポート)につながります。

マッピングはrun時にしか設定できないらしく、
設定を忘れるとrunし直しになりますので注意してください。(参考)

-vオプション

コンテナ内の/workdirの中身と、ホストOSの作業用ディレクトリの中身を同期しています。

コンテナを抜けるとき

コンテナを抜けるときは以下のコマンドを使い分けます。

  • control + d : コンテナを停止して抜ける(exit)
  • control + p -> control + q : コンテナを起動したまま抜ける(detach)

次回以降は

$ docker start to-buy-list # 起動
$ docker exec -it to-buy-list bash # 入る

でコンテナに入ります。

エラー

runコマンド実行時にこんなエラーが。

docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"bin/bash\": stat bin/bash: no such file or directory": unknown.

ただのタイポでした… : bin/bash => /bin/bash

ちなみにこのとき、コンテナ自体は作られているので、$ docker rm [container_id]で一度コンテナを削除してやり直すか、execコマンドで入りましょう。

ファイルの同期・配置(VSCode : Remote Containerのインストール)

コンテナ内のファイルを編集するのに、VSCodeのRemote Containerを使います。

大まかな流れとしては:

  • VSCodeのプラグインでRemote – Containersをインストール
  • コンテナと接続
  • 必要な拡張機能をインストール
    • Python
    • Linter など
  • デバッグ→実行ができることを確認
    • ブレイクポイントの設定なども

詳しくは以下の記事をご参照ください。
DockerでPythonの開発環境を作成してみる その2 – ITエンジニア日記 ~NO SKILL, NO LIFE~

Flaskの起動・表示確認

アプリケーションの作成

Hello, worldするだけの簡単なアプリを書きます。
一旦/workdir配下に配置します。

app.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Hello world!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

テストの作成

ついでにただ通るだけのテストも書いておきます。

test_app.py

import os
import app
import unittest
import tempfile

class AppTestCase(unittest.TestCase):
    def test_do(self):
        self.assertEqual(1,1)

if __name__ == '__main__':
    unittest.main()

動作確認

先にテストを動かしてみます。

root@1caa75e54efb:/workdir# python -m unittest test_app.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

当然通ります。

次にFlaskを起動してWebページで表示します。

root@1caa75e54efb:/workdir# python app.py

 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

ブラウザでhttp://localhost:8080にアクセスすると、無事「Hello world!」が表示されました。

未解決 : ソースコードの変更がリアルタイムに反映されない問題

現状では、Flaskを動かしたまま、ソースコードの内容を書き換えても反映されず、Flaskを起動し直す必要があります。

コンテナを使わずローカルでFlaskを実行したときも同じ挙動のため、コンテナではなくFlask側の仕様のようです…

終わりに

本記事では、「Dockerイメージを作成し、コンテナを起動してその上でFlaskを動かし、Webページで表示されることを確認する」までを行いました。

次回は本番環境へのデプロイを行います。

参考