2.1. Flaskアプリの作成

目的

Flaskアプリケーションを作成します。

対象

  • ローカル環境

パラメータ

パラメータの指定

0.1. 開発用ディレクトリの指定

開発用ディレクトリ名を指定します。

変数の設定:

DIR_DEVELOP="${HOME}/environment"

0.2. プロジェクト名の指定

変数の設定:

PRJ_NAME="handson-cli-repository"

0.3. Python環境名の指定

Python環境名を指定します。

変数の設定:

ENV_NAME="venv"

パラメータの確認

変数の確認:

cat << ECHO

  # 0.1. DIR_DEVELOP:"${HOME}/environment"
         DIR_DEVELOP="${DIR_DEVELOP}"
  # 0.2. PRJ_NAME:"handson-cli-repository"
         PRJ_NAME="${PRJ_NAME}"
  # 0.3. ENV_NAME:"venv"
         ENV_NAME="${ENV_NAME}"

ECHO

実施

venv環境の作成

venv環境の作成

変数の設定:

DIR_PRJ="${DIR_DEVELOP}/${PRJ_NAME}" \
  && echo ${DIR_PRJ}

結果(例):

${HOME}/environment/handson-cli-repository

コマンド:

mkdir -p ${DIR_PRJ} \
  && cd ${DIR_PRJ}

コマンド:

python3 -m venv ${ENV_NAME}

結果(例):

(出力なし)

venv環境のアクティブ化

コマンド:

. ${ENV_NAME}/bin/activate

結果(例):

シェルプロンプトの先頭に"(venv) "が追加されます。

Flaskのインストール

コマンド:

pip install Flask

結果(例):

Successfully installed Flask-1.0.2 Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 itsdangerous-0.24

requirementsファイルの作成

コマンド:

pip freeze > requirements.txt \
  && cat requirements.txt

結果(例):

click==6.7
Flask==1.0.2
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
Werkzeug==0.14.1

アプリケーションの作成

変数の設定:

DIR_APP="${DIR_PRJ}" \
  && echo ${DIR_APP}

結果(例):

${HOME}/environment/handson-cli-repository

コマンド:

mkdir -p ${DIR_APP}/helloworld

helloworld/__init__.py

コマンド:

cat << EOF > ${DIR_APP}/helloworld/__init__.py
EOF

cat ${DIR_APP}/helloworld/__init__.py

結果(例):


helloworld/application.py

コマンド:

cat << EOF > ${DIR_APP}/helloworld/application.py
#!flask/bin/python
import json
from flask import Flask, Response
from helloworld.flaskrun import flaskrun

application = Flask(__name__)

@application.route('/', methods=['GET'])
def get():
    return Response(json.dumps({'Output': 'Hello World'}), mimetype='application/json', status=200)

@application.route('/', methods=['POST'])
def post():
    return Response(json.dumps({'Output': 'Hello World'}), mimetype='application/json', status=200)

if __name__ == '__main__':
    flaskrun(application)
EOF

cat ${DIR_APP}/helloworld/application.py

結果(例):

#!flask/bin/python
import json
from flask import Flask, Response
from helloworld.flaskrun import flaskrun

application = Flask(__name__)

@application.route('/', methods=['GET'])
def get():
    return Response(json.dumps({'Output': 'Hello World'}), mimetype='application/json', status=200)

@application.route('/', methods=['POST'])
def post():
    return Response(json.dumps({'Output': 'Hello World'}), mimetype='application/json', status=200)

if __name__ == '__main__':
    flaskrun(application)

helloworld/flaskrun.py

コマンド:

cat << EOF > ${DIR_APP}/helloworld/flaskrun.py
import optparse


def flaskrun(app, default_host="0.0.0.0", default_port="80"):
    """
    Takes a flask.Flask instance and runs it. Parses
    command-line flags to configure the app.
    """

    # Set up the command-line options
    parser = optparse.OptionParser()
    msg = 'Hostname of Flask app [{}]'.format(default_host)
    parser.add_option("-H", "--host",
                      help=msg,
                      default=default_host)
    msg = 'Port for Flask app [{}]'.format(default_port)
    parser.add_option("-P", "--port",
                      help=msg,
                      default=default_port)
    parser.add_option("-d", "--debug",
                      action="store_true", dest="debug",
                      help=optparse.SUPPRESS_HELP)

    options, _ = parser.parse_args()

    app.run(
        debug=options.debug,
        host=options.host,
        port=int(options.port)
    )
EOF

cat ${DIR_APP}/helloworld/flaskrun.py

結果(例):

import optparse


def flaskrun(app, default_host="0.0.0.0", default_port="80"):
    """
    Takes a flask.Flask instance and runs it. Parses
    command-line flags to configure the app.
    """

    # Set up the command-line options
    parser = optparse.OptionParser()
    msg = 'Hostname of Flask app [{}]'.format(default_host)
    parser.add_option("-H", "--host",
                      help=msg,
                      default=default_host)
    msg = 'Port for Flask app [{}]'.format(default_port)
    parser.add_option("-P", "--port",
                      help=msg,
                      default=default_port)
    parser.add_option("-d", "--debug",
                      action="store_true", dest="debug",
                      help=optparse.SUPPRESS_HELP)

    options, _ = parser.parse_args()

    app.run(
        debug=options.debug,
        host=options.host,
        port=int(options.port)
    )

スクリプトの作成

コマンド:

mkdir -p ${DIR_APP}/scripts

scripts/install_dependencies

コマンド:

cat << EOF > ${DIR_APP}/scripts/install_dependencies
#!/bin/bash
easy_install pip
pip install virtualenv
cd /home/ec2-user/python-flask-service
virtualenv environment
source environment/bin/activate
pip install -r requirements.txt
pip install supervisor
python setup.py install
EOF

cat ${DIR_APP}/scripts/install_dependencies

結果(例):

#!/bin/bash
easy_install pip
pip install virtualenv
cd /home/ec2-user/python-flask-service
virtualenv environment
source environment/bin/activate
pip install -r requirements.txt
pip install supervisor
python setup.py install

scripts/start_server

コマンド:

cat << EOF > ${DIR_APP}/scripts/start_server
cd /home/ec2-user/python-flask-service/
source environment/bin/activate
supervisord -c scripts/supervisord.conf
EOF

cat ${DIR_APP}/scripts/start_server

結果(例):

cd /home/ec2-user/python-flask-service/
source environment/bin/activate
supervisord -c scripts/supervisord.conf

scripts/stop_server

コマンド:

cat << EOF > ${DIR_APP}/scripts/stop_server
pkill supervisord
EOF

cat ${DIR_APP}/scripts/stop_server

結果(例):

pkill supervisord

scripts/supervisord.conf

コマンド:

cat << EOF > ${DIR_APP}/scripts/supervisord.conf
;For a sample configuration file, refer to https://github.com/Supervisor/supervisor/blob/master/supervisor/skel/sample.conf
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false               ; (start in foreground if true;default false)
minfds=1024                  ; (min. avail startup file descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

[program:flaskapplication]
command = /home/ec2-user/python-flask-service/environment/bin/python /home/ec2-user/python-flask-service/helloworld/application.py
autostart=true
stdout_logfile = /var/log/flask-application-stdout.log
stderr_logfile = /var/log/flask-application-stderr.log
EOF

cat ${DIR_APP}/scripts/supervisord.conf

結果(例):

;For a sample configuration file, refer to https://github.com/Supervisor/supervisor/blob/master/supervisor/skel/sample.conf
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false               ; (start in foreground if true;default false)
minfds=1024                  ; (min. avail startup file descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

[program:flaskapplication]
command = /home/ec2-user/python-flask-service/environment/bin/python /home/ec2-user/python-flask-service/helloworld/application.py
autostart=true
stdout_logfile = /var/log/flask-application-stdout.log
stderr_logfile = /var/log/flask-application-stderr.log

テストの作成

コマンド:

mkdir -p ${DIR_APP}/tests

tests/test_application.py

コマンド:

cat << EOF > ${DIR_APP}/tests/test_application.py
import json
import pytest
from helloworld.application import application

@pytest.fixture
def client():
    return application.test_client()

def test_response(client):
    result = client.get()
    response_body = json.loads(result.get_data())
    assert result.status_code == 200
    assert result.headers['Content-Type'] == 'application/json'
    assert response_body['Output'] == 'Hello World'
EOF

cat ${DIR_APP}/tests/test_application.py

結果(例):

import json
import pytest
from helloworld.application import application

@pytest.fixture
def client():
    return application.test_client()

def test_response(client):
    result = client.get()
    response_body = json.loads(result.get_data())
    assert result.status_code == 200
    assert result.headers['Content-Type'] == 'application/json'
    assert response_body['Output'] == 'Hello World'

setupスクリプトの作成

setup.py

コマンド:

cat << EOF > ${DIR_APP}/setup.py
from setuptools import setup, find_packages

setup(
    name='helloworld',
    packages=find_packages(),
    include_package_data=True,
    install_requires=[
        'flask',
    ],
    setup_requires=[
        'pytest-runner',
    ],
    tests_require=[
        'pytest',
    ],
)
EOF

cat ${DIR_APP}/setup.py

結果(例):

from setuptools import setup, find_packages

setup(
    name='helloworld',
    packages=find_packages(),
    include_package_data=True,
    install_requires=[
        'flask',
    ],
    setup_requires=[
        'pytest-runner',
    ],
    tests_require=[
        'pytest',
    ],
)

備考