とりあえずすぐ動く!Dockerを使い、Python3, Flask, MySQL開発環境を構築する!

More than 1 year has passed since last update.

はじめに

「優しいIT」という理念の基、ITコンサルタントをしている亀井亮介と申します!
主な仕事は要求分析・要件定義の上流工程から、開発のマネジメントまでしていますが、プライベートでウェブシステム開発をしています!

目的

細かい説明は抜きにして、Git cloneし、"docker-compose up -d"するだけで、Python3, Flask, MySQL開発環境を構築します!
Dockerをインストールしていることが、前提となります!
「とりあえず動くところが見たい!」というせっかちな(私のような)方は、「1. 動作するまでの手順」を見てくださいね!
2章以降は、各項目の説明をしています!

Githubにファイルを上げているので、興味のある方は使ってくださいね!

目次

  1. 動作するまでの手順
  2. Dockerfile
  3. docker-compose.yml
  4. Flask(mysql-connector-python)
  5. Jinja2 テンプレートエンジン
  6. ローカルでファイル操作し、コンテナに即時反映

1. 動作するまでの手順

1-1. Git cloneする

Gitが使えることが前提です。

Gitクローン
$ git clone git@github.com:RyosukeKamei/python-flask-mysql.git
移動
$ cd python-flask-mysql

フォルダ構成は次のようになります。
フォルダ構成.png

1-2. docker-compose up -d

docker-composeを使い、データコンテナ、データベースコンテナ、アプリケーションコンテナを立ち上げます。
アプリケーションコンテナは、Dockerfileを使いビルドしてから、コンテナの起動をします。

docker-composeでビルドし、起動
$ docker-compose up -d

※アプリケーションコンテナをビルドするのに時間がかかります。

1-3. 動作確認

http://{IPアドレス}:5000/hello
にアクセスします。
IPアドレスは環境によって異なるかもしれません。私の環境では、Dockerのデフォルトで"192.168.99.100"です。
Flaskのデフォルトのポート番号は5000番です。

スクリーンショット 2016-09-23 13.40.23.png

MySQLに登録したデータをそのまま表示しています。
テンプレートエンジンにJinja2を利用しています!

2. Dockerfile

Dockerfileを使いアプリケーションコンテナをビルドします。

2-1. Dockerfile全体

Dockerfile
# Pythonは公式イメージ
FROM python:3.5.2

# オリジナルはJoshua Conner氏
# MAINTAINER Joshua Conner <joshua.conner@gmail.com>
MAINTAINER Ryosuke Kamei <sr2smail@gmail.com>

# 各ライブラリインストール
# Pythonがパッケージ依存するものもインストール
# Pythonプロフェッショナルプログラミング第2版P9より
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y  vim \
                        sudo \
                        python3-dev \
                        zlib1g-dev \
                        libsqlite3-dev \
                        libreadline6-dev \
                        libgdbm-dev \
                        libbz2-dev \
                        tk-dev

# ユーザ作成
RUN groupadd web
RUN useradd -d /home/python -m python

# pipでインストール
# virtualenv Pythonの仮想環境構築コマンド
# flake8 コーディングスタイル/シンタックスのチェック
# ipython Pythonのインタラクティブモード拡張
# Flask Pytrhonの軽量フレームワーク
RUN pip install virtualenv \
                ipython \
                flake8 \
                Flask

# MySQLドライバ"mysql-connector-python"をインストール
# pipのを使うとうまくいかない。
# git cloneしてビルド、インストール
RUN git clone https://github.com/mysql/mysql-connector-python.git
WORKDIR mysql-connector-python
RUN python ./setup.py build
RUN python ./setup.py install

# ユーザを変更
USER python

# vim の設定ファイル
ADD ./vim/.vimrc /home/python/
WORKDIR /home/python
RUN mkdir /home/python/.vim
RUN mkdir /home/python/.vim/ftplugin
ADD ./vim/python.vim /home/python/.vim/ftplugin/
RUN mkdir /home/python/.vim/bundle
RUN git clone https://github.com/Shougo/neobundle.vim /home/python/.vim/bundle/neobundle.vim

# Flaskの場合
# ポートを解放(Flaskのデフォルトのポート番号:5000)
EXPOSE 5000
# サーバ起動
ENTRYPOINT ["/usr/local/bin/python", "/home/python/flask_sample.py"]

# フレームワークを指定しない時や、サーバにログインしてから実行したい場合
# ENTRYPOINT ["/usr/bin/tail", "-f", "/dev/null"]

2-2. 元のイメージを指定 FROM

元となるイメージは公式イメージのpython:3.5.2です。

イメージ
FROM python:3.5.2

2-3. 作成者情報 MAINTAINER

誰が作ったかを記載します。筆者の情報です。

作成者情報
MAINTAINER Ryosuke Kamei <sr2smail@gmail.com>

2-4. 必要なライブラリのインストール

ライブラリをインストールする前に、既存ライブラリをアッデート・アップグレードします。インストールする前のおまじないと思ってください。
その後、必要なライブラリをインストールします。
vimやsudoもよく使うのでインストールします。

ライブラリアップデート・アップグレード・インストール
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y  vim \
                        sudo \
                        python3-dev \
                        zlib1g-dev \
                        libsqlite3-dev \
                        libreadline6-dev \
                        libgdbm-dev \
                        libbz2-dev \
                        tk-dev

2-5. サーバのユーザを作成

サーバのユーザを作成します。
ユーザ名は"python"にしています。ここはお好みで変えてください。
変更する場合、後述のフォルダ名なども変更する必要があります。

ユーザ作成
RUN groupadd web
RUN useradd -d /home/python -m python

2-6. Python開発で必要なライブラリをpipでインストール

Pythonのパッケージマネージャpipで必要なライブラリをインストールします。

  • virtualenv Pythonの仮想環境構築コマンド
  • flake8 コーディングスタイル/シンタックスのチェック
  • ipython Pythonのインタラクティブモード拡張
  • Flask Pythonの軽量フレームワーク
virtualenv,flake8,ipython,Flaskをインストール
RUN pip install virtualenv \
                ipython \
                flake8 \
                Flask

MySQLと接続するドライバは"mysql-connector-python"を利用します。
pipでインストールできるはずが、上手くいかない(2016年9月23日現在)ので、gitクローンして、ビルドし、インストールします。

mysql-connector-pythonインストール
RUN git clone https://github.com/mysql/mysql-connector-python.git
WORKDIR mysql-connector-python
RUN python ./setup.py build
RUN python ./setup.py install

2-7. vimの設定ファイル

vimを使いPythonのファイルを操作する際に、便利な設定をしておきます。
深くは説明しません。

vimの設定
ADD ./vim/.vimrc /home/python/
WORKDIR /home/python
RUN mkdir /home/python/.vim
RUN mkdir /home/python/.vim/ftplugin
ADD ./vim/python.vim /home/python/.vim/ftplugin/
RUN mkdir /home/python/.vim/bundle
RUN git clone https://github.com/Shougo/neobundle.vim /home/python/.vim/bundle/neobundle.vim

2-8. Flaskのサンプルファイルを起動

Flaskのデフォルトのポートは"5000"なので開放します。
サンプルファイルを起動します。

サンプルのファイル"flask_sample.py"は3.で説明する"docker-compose.yml"ファイルで同期を取っています。

このファイルを実行すると、サーバが起動するので、"docker-compose up -d"実行後に、即動作を確認できます!

EXPOSE 5000
ENTRYPOINT ["/usr/local/bin/python", "/home/python/flask_sample.py"]

3. docker-compose.yml

3-1. docker-compose.yml全体

docker-compose.yml全体
data:
  container_name: python-flask-data
  image: busybox
  stdin_open: true
  tty: false
  volumes:
    - ./docker/mysql:/etc/mysql/conf.d:ro
    - ./app:/home/python
  command: /bin/sh

mysql:
  container_name: python-flask-db
  image: mysql
  volumes:
   - ./initdb.d:/docker-entrypoint-initdb.d
  environment:
    MYSQL_ROOT_PASSWORD: password
    MYSQL_USER: python
    MYSQL_PASSWORD: python
    MYSQL_DATABASE: sample
  ports:
    - "3306:3306"
  volumes_from:
    - data

application:
  container_name: python-flask-app
  build: .
  # image: pythonbottle_application
  tty: false
  volumes_from:
    - data
  ports:
    - "5000:5000"
  links:
    - mysql:python-flask-db

3-2. データコンテナ

データを永続化するためにデータコンテナを用意します。
データベースコンテナを削除しても中身は残るので、分けると便利です。

container_nameでコンテナ名指定します。任意です。
イメージは、busyboxを使っています。

dataコンテナ
  container_name: python-flask-data
  image: busybox

ボリュームで、mysqlの設定ファイル指定します。
conf.dに日本語設定があり、自動的に適用されます。

コンテナ上の"/home/python"フォルダと、ローカルの"app"フォルダを同期しています。この設定をすることによりローカルで編集した内容をコンテナに反映します。

この設定をすることで、コンテナにファイルをアップロードすることなく、単純に保存をするだけで、サーバに設定を反映することができるので便利です!

dataコンテナ
  volumes:
    - ./docker/mysql:/etc/mysql/conf.d:ro
    - ./app:/home/python

MySQLの日本語設定ファイルです。
このファイルの内容が自動設定されます。

./docker/mysql/my.cnf
[mysqld]
innodb_strict_mode
innodb_file_format = Barracuda
innodb_file_per_table
innodb_large_prefix = 1
character-set-server=utf8mb4
skip-character-set-client-handshake
max_allowed_packet = 32m
skip-networking = 0

[client]
default-character-set=utf8mb4

3-3. データベースコンテナ

コンテナ名は"python-flask-db"
イメージは公式の"mysql"
を使います。

コンテナ名とイメージ
  container_name: python-flask-db
  image: mysql

初期データを指定します。
テーブル作成のCREATE文と、データ登録するINSERT文を"/docker-entrypoint-initdb.d"に置きます。
データベース構築時に、自動的に実行されるので、初期値が入ります。

初期データを設定
  volumes:
   - ./initdb.d:/docker-entrypoint-initdb.d
/initdb.d/create_table.sql
USE sample;

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `users` (`id`, `name`) VALUES (1, 'pyhons');

下記の環境変数を設定すると、ルートパスワードやユーザ設定、データベースを構築することができます。

MYSQL_ROOT_PASSWORD ルートのパスワード
MYSQL_USER データベースのユーザ名
MYSQL_PASSWORD データベースのパスワード
MYSQL_DATABASE データベース

MySQLの環境変数
  environment:
    MYSQL_ROOT_PASSWORD: password
    MYSQL_USER: python
    MYSQL_PASSWORD: python
    MYSQL_DATABASE: sample

ポートを開放します。MySQLは"3306"です。

ポートを開放
  ports:
    - "3306:3306"

データコンテナから、全てのボリュームをマウントします。

ボリュームをマウント
  volumes_from:
    - data

3-4. アプリケーションコンテナ

コンテナ名任意です。
イメージは、dockerfileからビルドしています。

コンテナ名と元のイメージ
container_name: python-flask-db
build: .

データコンテナから、全てのボリュームをマウントします。

ボリュームをマウント
  volumes_from:
    - data

ポートを開放します。Flaskは"5000"です。

ポートを開放
  ports:
    - "5000:5000"

データベースコンテナにリンクを張ります。

データベースコンテナにリンクを張る
  links:
    - mysql:python-flask-db

4. Flask(mysql-connector-python)

4-1. flaskのサンプルファイル

flask_sample.py
# ライブラリをインポート
from flask import Flask, render_template, request, redirect, url_for

# MySQLドライバはmysql.connectorを使う
import mysql.connector
# Dockerを使う場合で、初期設定の場合hostは"192.168.99.100"
# MySQLのユーザやパスワード、データベースはdocker-compose.ymlで設定したもの
connector = mysql.connector.connect(
            user='python',
            password='python',
            host='192.168.99.100',
            database='sample')

cursor = connector.cursor()
cursor.execute("select * from users")

disp = ""
for row in cursor.fetchall():
    disp = "ID:" + str(row[0]) + "  名前:" + row[1]

cursor.close
connector.close

# Flaskはインスタンスを生成する
app = Flask(__name__)
# デバッグを可能とする
app.config.update({'DEBUG': True })

# ここからウェブアプリケーション用のルーティングを記述
# index にアクセスしたときの処理
@app.route('/hello')
def hello():
    # return "Flask DBから取得 "+disp
    # Jinjaを使う
    title = "ようこそ"
    message = "DBから取得 "+disp
    # index.html をレンダリングする
    return render_template('index.html', message=message, title=title)
if __name__ == '__main__':
    app.run(host='0.0.0.0')

4-2. MySQLとの接続

アプリケーションコンテナをビルドするときに、mysql.connectorをインストールしているので、importすれば使えます。

MySQLドライバはmysql.connectorを使う
import mysql.connector

データベースの初期設定をします。
user, password, databaseはdocker-composeで設定したものが入ります。
hostは、DockerでアサインされたIPアドレスを入力します。
筆者の環境の場合は、"192.168.99.100"でした。

データベース設定
# MySQLのユーザやパスワード、データベースはdocker-compose.ymlで設定したもの
connector = mysql.connector.connect(
            user='python',
            password='python',
            host='192.168.99.100',
            database='sample')

コネクションを開きます。

コネクションを開く
cursor = connector.cursor()

SELECT文を実行します。

SELECT文を実行
cursor.execute("select * from users")

データを取得します。

データを取得し整形
disp = ""
for row in cursor.fetchall():
    disp = "ID:" + str(row[0]) + "  名前:" + row[1]

後処理をします。

後処理
cursor.close
connector.close

4-3. Flask

ライブラリをインポート

Flaskをインポート
from flask import Flask
Flaskのインスタンスを生成
app = Flask(__name__)
デバッグの設定
app.config.update({'DEBUG': True })

http://{IPアドレス}:5000/hello
でアクセスできるようにルーティングします。

helloにルーティング
@app.route('/hello')

データベースから取得した値を、テンプレートにアサインします。
render_templateでテンプレートファイルを指定し、第2引数以降にテンプレートに渡す値を指定します。

helloの中身
def hello():
    # return "Flask DBから取得 "+disp
    # Jinjaを使う
    title = "ようこそ"
    message = "DBから取得 "+disp
    # index.html をレンダリングする
    return render_template('index.html', message=message, title=title)

Dockerの場合、host='0.0.0.0'を明示的に設定します。

サーバ起動
if __name__ == '__main__':
    app.run(host='0.0.0.0')

5. Jinja2 テンプレートエンジン

5-1. index.html

/templates/index.html
{% extends "layout.html" %}
{% block content %}
    <h3>Jinja2</h3>
    <p>
        {% if message %}
            {{ message }}
        {% else %}
            特に何もありません。
        {% endif %}
    </p>
{% endblock %}

5-2. extends

HTMLの共通部分を読み込みます。
ヘッダー部分や、CSSファイル、jQueryファイルなど、共通で利用する部分を読み込みます。

/templates/index.html
{% extends "layout.html" %}

5-3. layout.html

layout.htmlは次の通りです。
CSSにbootstrapを使っているので、bootstrapのサンプルをカスタマイズしています。

/templates/layout.html
<!DOCTYPE html>
<!-- http://getbootstrap.com/getting-started/ をカスタマイズ -->
<!-- 全画面共通部分 -->
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- タイトルが入る。タイトルがない場合はBootstrap 101 Templateと表示(仮) -->
    {% if title %}
      <title>{{ title }}</title>
    {% else %}
      <title>Bootstrap 101 Template</title>
    {% endif %}
    <!-- Bootstrap -->
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <!-- ここに本体が入る -->
    {% block content %}{% endblock %}
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="/static/js/bootstrap.min.js"></script>
  </body>
</html>

5-4. block

index.htmlのblock部分に埋め込むHTMLを
{% block content %}
から
{% endblock %}
に埋め込みます。

block
{% block content %}
    <h3>Jinja2</h3>
    <p>
        {% if message %}
            {{ message }}
        {% else %}
            特に何もありません。
        {% endif %}
    </p>
{% endblock %}

5-5. if文

if文は次のように書きます。
elseやここにはありませんが、elseifも書けます。

jinjaのif文
        {% if message %}
            {{ message }}
        {% else %}
            特に何もありません。
        {% endif %}

6. ローカルでファイル操作し、コンテナに即時反映

このdocker-composeで起動すると、ローカルの"app"フォルダと、コンテナの"/home/python"が同期されます。

このことにより、ローカルからコンテナにファイルアップロードすることなく、保存するだけで、プログラムを反映することができて、非常に効率的です。

サイトマップ

Raspberry Pi 3 にDockerを乗せてPython+MySQL環境を構築!

RaspberryPi3(ラズパイ)にDockerをインストール
RaspberryPi3(ラズパイ)にDockerでPython+bottle+MySQL環境構築する!【簡単構築編】
RaspberryPi3(ラズパイ)にDockerでPython+bottle+MySQL環境構築する!【試行錯誤編】

テスト駆動開発を重点においた規則

コーディング規則「優しいコードを書こう」(FuelPHP)
命名規則「3ヶ月後の自分自身に優しく、チームに優しく、まだ見ぬメンバーに優しく」

開発しやすい環境構築(Docker+PHP)

Dockerを利用しApacheにPHP環境 + Eclipseを連携
Dockerを利用したFuelPHP開発環境構築
Docker利用したFuelPHP開発環境の初期設定とscaffoldを利用してCRUDスケルトン作成
FuelPHPのデータベースマイグレーション