python dashDBをアクセスするコードの作り方 on Bluemix or local

[python から dashDB をアクセスしたメモ](http://qiita.com/MahoTakara/items/b6fb1d7bd3947b3dcce9) のつづきで、local と Bluemix の CFアプリとして実行するコード開発のメモです。

Bluemix の CloudFoundry アプリ (以下 BMX-CFAとする) のサンプルコード [IBM-Bluemix/get-started-python] (https://github.com/IBM-Bluemix/get-started-python) (1)を読んでいたら、やはり ローカルで実行するケースと Bluemix 上で実行する二つのケースを前提としたコードになっている事が解りました。 そこで、このコードの書き方を参考にしながら、dashDB をアクセスするコードを考えます。

そして、dashDB を Python からアクセスするコードのサンプル [dashDB and Python]
(https://hub.jazz.net/project/ibmdatabase/dashDB/overview#https://hub.jazz.net/git/ibmdatabase%252FdashDB/list/master/samples/dashDBPython) (2)は、参考にするには課題が多く、平文通信している、また重複コードを含む無駄なコードがあるなど、あまり参考にならないので、横目で見ながら、洗練されたコードを作る事にしました。

この記事に対応する GitHub リポジトリは、 https://github.com/takara9/python-dashDB です。インストール手順は、このURLの README.md を参照してください。 

## ローカル環境 と Bluemix で dashDB サービスを利用する
基本的なプログラムの動き確認するために、スニペット コード作成して、書き方を考えて、本体のプログラムへ組み込むといったことを日常的に実施しています。 ところが、Bluemix 上でスニペットを書いて動作を確認するといった行為をするには、bx cf push を繰り返して、ウェブ画面からログを確認する必要があり、数倍の労力を要します。 このため、開発の初期段階や保守の段階でも、ローカルでも動作でき、サービスはBluemixの PaaS基盤から動作できるというのが理想的です。

BMX-CFAの動作環境では、プログラムのランタイム環境と連携するサービスを結び付ける方法として、環境変数 VCAP_SERVICES にアクセス資格情報などをセットして、プログラムへ渡すという構造になっています。 BMX-CFA環境で実行する場合には、環境変数を参考にすれば良いのですが、ローカル環境で実行する場合は、JSONファイルなどを読み込んでサービス資格情報を取得する必要があります。

Bluemix CLI には、サービス資格情報を取得するためのコマンドがありますから、これらを利用するのが良いです。次のコマンドは、dasDBの一つのインスタンスに定義されているサービス資格情報の名前のリストを表示するものです。ダブルクォーテーションで囲まれた部分は、dashDBのインスタンス名です。

~~~
bx cf service-keys "dashDB for Analytics-iq"
~~~
次のコマンドで、リストの中から選択して、JSON形式の情報を取得する事ができます。最後のパラメータ Credentials-1 は前述のコマンドで得られた サービス資格情報の名前です。

~~~
bx cf service-key "dashDB for Analytics-iq" Credentials-1
~~~

次のコードは、環境変数 VCAP_SERVICES と JSON形式のファイルから必要な情報をセットするスニペットです。このコードが実行されると dashDB のサービス資格情報が creds にセットされてきます。 いろいろなアクセス方法に対応する情報が提供されるのですが、最後の行の ssldsn にODBCドライバに与える暗号化通信で接続する文字列がセットされます。

~~~
    creds = None
    # 環境変数からセットする
    if 'VCAP_SERVICES' in os.environ:
	vcap = json.loads(os.getenv('VCAP_SERVICES'))
	print('Found VCAP_SERVICES')
	if 'dashDB' in vcap:
            creds = vcap['dashDB'][0]['credentials']
    # ローカルのファイルからセットする
    elif os.path.isfile('vcap-local.json'):
	with open('vcap-local.json') as f:
            print('Found local VCAP_SERVICES')
            vcap = json.load(f)
            creds = vcap['services']['dashDB'][0]['credentials']
    # 暗号化通信の文字列
    url = creds["ssldsn"]
~~~
変数 url を ODBCドライバ(3) ibm_db の関数に与えてコールすることで、dashDBと接続します。 

~~~
    conn = ibm_db.connect(url, '', '')
~~~

## ローカル環境 と Bluemix でのポート番号
BMX-CFAの実行環境では PaaS がアプリが利用できるポート番号を指定します。 このポート番号もランタイムの環境変数としてプロビジョニング時に動的にアサインされたポート番号が伝えられます。 一方、ローカル環境で実行する場合は 自己管理で利用可能なポート番号を把握しておき、手動で割り当てます。 下記のスニペットは、その両方に対応するものです。

~~~
    port = int(os.getenv('PORT', 8080))
    app.run(host='0.0.0.0', port=port, debug=True)
~~~

## Python の Flask フレームワーク
本アプリでは、ウェブ画面を表示するために、Flask（フラスク）利用しています。このFlaskとは、プログラミング言語Python用の、軽量なウェブアプリケーションフレームワークである。標準で提供する機能を最小限に保っているため、自身を「マイクロフレームワーク」と呼んでいる。Werkzeug WSGIツールキットとJinja2テンプレートエンジンを基に作られている。BSDライセンスで公開されている。(6),(5)

最小のアプリ(5)の例を以下に提示して解説します。 以下は アクセスすると "Hello, World!" の文字列を返すものです。 @app.route('/') で ルートが GETされた際に、hello_world()がコールバックされます。

~~~
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!\n'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9080, debug=True)
~~~
ウェブサーバーは コマンドラインから起動するだけです。 

~~~
ubuntu@ubuntu-xenial:~/dashDB$ python test7.py
 * Running on http://0.0.0.0:9080/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger pin code: 195-308-734
~~~~
クライアントとして curl コマンドで実行しています。 

~~~
buntu@ubuntu-xenial:~/dashDB/python-dashDB$ curl http://localhost:9080/
Hello, World!
~~~
このアプリのコードでは、/ への HTTP GET をトリガーとして、hello() がコールバックされ、ODBCによる dashDBへのアクセスが実行されます。

~~~
# アクセス時のコールバック
@app.route('/')
def hello():
    conn = ibm_db.connect(url, '', '')
    stmt = ibm_db.prepare(conn, 'SELECT RACE_CODE, RACE_DESC from SAMPLES.RACE_CODE FETCH FIRST 5 ROWS ONLY')
    ibm_db.execute(stmt)
~~~

## アプリの実行結果
Bluemix に デプロイされた後の実行結果です。
![スクリーンショット 2017-06-03 16.15.07.png](https://qiita-image-store.s3.amazonaws.com/0/42829/56b64333-6580-47ed-3e20-a3aa04add3f4.png)

## ファイルセットの説明
bx cf push 前のフォルダとファイルを 以下にリストして、内容を説明します。

~~~
ubuntu@ubuntu-xenial:~/dashDB$ tree python-dashDB
python-dashDB/
├── LICENSE
├── main.py
├── manifest.yml
├── Procfile
├── README.md
├── requirements.txt
└── vcap-local.json
~~~
デプロイ時には、manifest.yml を読み取ってBluemix ClooudFoundry ランタイムの資源を確保して、requirements.txt からパッケージをインストールして、Procfile のコマンド実行してアプリを開始します。

* LICENSE, README.md ライセンスと利用説明
* main.py アプリの本体で、この中からdashDBへアクセスしてWeb画面を送出します。
* manifest.yml BMX-CFAとしてデプロイに必要な情報
* requirements.txt 必要なパッケージのリスト
* Procfile デプロイ後に実行するコマンド

マニフェストの項目の説明は、参考資料(7)が助けになります。 また、Procfile の解説は、参考資料(8)にあります。

## まとめ
Bluemix は、PaaS 環境だけで開発するというコンセプトではなく、ローカルの開発環境もCFアプリも両方での動作をサポートし、開発者の使い慣れたツールで効率良く、そして、面倒なサーバー運用はPaaSに任せることで、開発者にとって、もっとも幸せな環境を提供する事を目指している。という事が解った。


## 参考資料
(1) IBM-Bluemix/get-started-python https://github.com/IBM-Bluemix/get-started-python
(2) dashDB and Python https://hub.jazz.net/project/ibmdatabase/dashDB/overview#https://hub.jazz.net/git/ibmdatabase%252FdashDB/list/master/samples/dashDBPython
(3) Python DBI driver for DB2 (LUW, zOS, i5) and IDS https://pypi.python.org/pypi/ibm_db/2.0.7
(4) Python Packaging User Guide https://packaging.python.org/
(5) Flask Home page http://flask.pocoo.org/
(6) ウィッキペディア Flask https://ja.wikipedia.org/wiki/Flask
(7) Application manifestn https://console.ng.bluemix.net/docs/manageapps/depapps.html#appmanifest
(8) CloudFoundry Application about Procfile https://docs.cloudfoundry.org/buildpacks/prod-server.html#procfile