Heroku に代わって OpenShift で Flask アプリケーションを立ち上げてみる

# 登録
https://www.openshift.com/ からメールアドレスとパスワードを入力、確認メールで登録手続き完了。

# 準備
openshift の CLI として、Ruby の rhc という gem を入れる。グローバルに入れるのも嫌なので、gemset を作って入れてみる（Ruby は詳しくないのであまり分かっていない）。

```shell-session
$ mkdir openshift
$ cd openshift
$ rbenv install 2.2.1
$ rbenv rehash
$ rbenv local 2.2.1
$ rbenv gemset create 2.2.1 openshift
$ echo openshift > .rbenv-gemsets
$ rbenv gemset active
$ gem install rhc
```

rhc のインストール中に「最初は rhc setup しろよ」みたいに言われるので言う通りにする。

```shell-session
$ rhc setup
OpenShift Client Tools (RHC) Setup Wizard

This wizard will help you upload your SSH keys, set your application namespace, and check that other programs like Git are properly installed.

If you have your own OpenShift server, you can specify it now. Just hit enter to use the server for OpenShift Online: openshift.redhat.com.
Enter the server hostname: |openshift.redhat.com| 

You can add more servers later using 'rhc server'.

Login to openshift.redhat.com: ＜メールアドレス＞
Password: ********

OpenShift can create and store a token on disk which allows to you to access the server without using your password. The key is stored in your
home directory and should be kept secret.  You can delete the key at any time by running 'rhc logout'.
Generate a token now? (yes|no) yes
Generating an authorization token for this client ... lasts about 1 month

Saving configuration to /Users/kounoike/.openshift/express.conf ... done

Checking for git ... found git version 2.3.2

Checking common problems .. done

Checking for a domain ... typemiss

Checking for applications ... none

Run 'rhc create-app' to create your first application.

  Do-It-Yourself 0.1                      rhc create-app <app name> diy-0.1
  JBoss Application Server 7              rhc create-app <app name> jbossas-7
  JBoss Data Virtualization 6             rhc create-app <app name> jboss-dv-6.1.0
  JBoss Data Virtualization 6             rhc create-app <app name> jboss-dv-6.0.0
  JBoss Enterprise Application Platform 6 rhc create-app <app name> jbosseap-6
  JBoss Unified Push Server 1.0.0.Beta1   rhc create-app <app name> jboss-unified-push-1
  Jenkins Server                          rhc create-app <app name> jenkins-1
  Node.js 0.10                            rhc create-app <app name> nodejs-0.10
  PHP 5.3                                 rhc create-app <app name> php-5.3
  PHP 5.4                                 rhc create-app <app name> php-5.4
  PHP 5.4 with Zend Server 6.1            rhc create-app <app name> zend-6.1
  Perl 5.10                               rhc create-app <app name> perl-5.10
  Python 2.6                              rhc create-app <app name> python-2.6
  Python 2.7                              rhc create-app <app name> python-2.7
  Python 3.3                              rhc create-app <app name> python-3.3
  Ruby 1.8                                rhc create-app <app name> ruby-1.8
  Ruby 1.9                                rhc create-app <app name> ruby-1.9
  Ruby 2.0                                rhc create-app <app name> ruby-2.0
  Tomcat 6 (JBoss EWS 1.0)                rhc create-app <app name> jbossews-1.0
  Tomcat 7 (JBoss EWS 2.0)                rhc create-app <app name> jbossews-2.0
  Vert.x 2.1                              rhc create-app <app name> jboss-vertx-2.1
  WildFly Application Server 8.2.0.Final  rhc create-app <app name> jboss-wildfly-8

  You are using 0 of 3 total gears
  The following gear sizes are available to you: small

Your client tools are now configured.
```

Jenkins サーバとか独特ですね。どんな感じなのか気になるところではあります。


# Flask アプリのひな形の作成

```shell-session
$ rhc app create myflaskapp python-2.7 --from-code=https://github.com/openshift-quickstart/flask-base.git
Application Options
-------------------
Domain:      typemiss
Cartridges:  python-2.7
Source Code: https://github.com/openshift-quickstart/flask-base.git
Gear Size:   default
Scaling:     no

Creating application 'myflaskapp' ... done


Waiting for your DNS name to be available ... done

Cloning into 'myflaskapp'...
The authenticity of host 'myflaskapp-typemiss.rhcloud.com (23.20.119.74)' can't be established.
RSA key fingerprint is ＜RSAキー指紋＞.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'myflaskapp-typemiss.rhcloud.com,23.20.119.74' (RSA) to the list of known hosts.

Your application 'myflaskapp' is now available.

  URL:        http://myflaskapp-typemiss.rhcloud.com/
  SSH to:     ＜SSHユーザ名？＞@myflaskapp-typemiss.rhcloud.com
  Git remote: ssh://＜SSHユーザ名？＞@myflaskapp-typemiss.rhcloud.com/~/git/myflaskapp.git/
  Cloned to:  /Users/kounoike/openshift/pythontest/myflaskapp

Run 'rhc show-app myflaskapp' for more details about your app.
```

git 指定していることからも分かる通り、`myflaskapp`というディレクトリを作ってその中にアプリケーションのひな形が置かれます。git 指定しないときも同じで、アプリ名でディレクトリを作った上でその中に git で管理されたアプリケーションのひな形が作られます。

また、flask ひな形でもひな形を指定しなかった場合でも、index ページに OpenShift の案内みたいなページが作成されます。

![index のスクリーンショット](https://qiita-image-store.s3.amazonaws.com/0/53147/56ebf837-5c23-32aa-5ea0-0a67afc2f899.png)

初期の app.py は結構ややこしいことをしています。

```py:app.py
#!/usr/bin/python

# This file may be used instead of Apache mod_wsgi to run your python
# web application in a different framework.  A few examples are
# provided (cherrypi, gevent), but this file may be altered to run
# whatever framework is desired - or a completely customized service.
#
import imp
import os
import sys

try:
  virtenv = os.path.join(os.environ.get('OPENSHIFT_PYTHON_DIR','.'), 'virtenv')
  python_version = "python"+str(sys.version_info[0])+"."+str(sys.version_info[1]) 
  os.environ['PYTHON_EGG_CACHE'] = os.path.join(virtenv, 'lib', python_version, 'site-packages')
  virtualenv = os.path.join(virtenv, 'bin','activate_this.py')
  if(sys.version_info[0] < 3):
    execfile(virtualenv, dict(__file__=virtualenv))
  else:
    exec(open(virtualenv).read(), dict(__file__=virtualenv))
    
except IOError:
  pass

#
# IMPORTANT: Put any additional includes below this line.  If placed above this
# line, it's possible required libraries won't be in your searchable path
#


#
#  main():
#
if __name__ == '__main__':
  application = imp.load_source('app', 'flaskapp.py')
  port = application.app.config['PORT']
  ip = application.app.config['IP']
  app_name = application.app.config['APP_NAME']
  host_name = application.app.config['HOST_NAME']

  fwtype="wsgiref"
  for fw in ("gevent", "cherrypy", "flask"):
    try:
      imp.find_module(fw)
      fwtype = fw
    except ImportError:
      pass

  print('Starting WSGIServer type %s on %s:%d ... ' % (fwtype, ip, port))
  if fwtype == "gevent":
    from gevent.pywsgi import WSGIServer
    WSGIServer((ip, port), application.app).serve_forever()

  elif fwtype == "cherrypy":
    from cherrypy import wsgiserver
    server = wsgiserver.CherryPyWSGIServer(
      (ip, port), application.app, server_name=host_name)
    server.start()

  elif fwtype == "flask":
    from flask import Flask
    server = Flask(__name__)
    server.wsgi_app = application.app
    server.run(host=ip, port=port)

  else:
    from wsgiref.simple_server import make_server
    make_server(ip, port, application.app).serve_forever()
```

一方、flaskapp.py は単純な flask アプリケーションになっていますね。


```py:flaskapp.py
import os
from datetime import datetime
from flask import Flask, request, flash, url_for, redirect, \
     render_template, abort, send_from_directory

app = Flask(__name__)
app.config.from_pyfile('flaskapp.cfg')

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/<path:resource>')
def serveStaticResource(resource):
    return send_from_directory('static/', resource)

@app.route("/test")
def test():
    return "<strong>It's Alive!</strong>"

if __name__ == '__main__':
    app.run()
```

app.route で指定している通り、ルートにアクセスすると鬱陶しい index が表示されます。/test にアクセスすると It's Alive! と表示されます。


ローカルでデバッグできるようにしましょう。まずは今後のライブラリモジュール追加に備えて virtualenv です。

```shell-session
$ pyenv virtualenv 2.7.9 openshift-myflaskapp
$ pyenv local openshift-myflaskapp
$ echo .python-version >> .gitignore
```

続いては flask を virtualenv 環境にインストールです。

```shell-session
$ pip install flask
```

ローカルで動かしてみましょう。

```shell-session
$ python app.py
```

8080 ポートが使用中の場合は flastkapp.cfg を編集して別のポートになるようにしましょう。

# 追加ライブラリの入れ方

ドキュメントを見ていると setup.py に書くやり方と requirements.txt に書くやり方の両方があるみたい。ここでは Heroku でなじんだ requirements.txt を使うやり方を用いる。

requiments.txt が使えるので、`pip install matplotlib` して `pip freeze > requirements.txt` すれば良い・・・のかと思ったら落とし穴が。

どうやら OpenShift で使う pip は若干古いリポジトリのクローンを参照しているらしい。まず matplotlib==1.4.3 が無いと文句を言われる（git push したときに）。こんな感じのエラーが出る。

```shell-session
remote:   Could not find a version that satisfies the requirement matplotlib==1.4.3 (from -r /var/lib/openshift/＜なんか意味ない文字列＞/app-root/runtime/repo/requirements.txt (line 5)) (from versions: 0.86.1, 0.86.2, 0.86, 0.91.0, 0.91.1, 1.0.1, 1.1.0, 1.1.1, 1.3.1, 1.4.0, 1.4.1, 1.4.1rc1, 1.4.2, 1.2.0, 1.2.1, 1.3.0, 1.3.1, 1.4.0, 1.4.1, 1.4.1rc1, 1.4.2, dev)
remote: Cleaning up...
remote: No distributions matching the version for matplotlib==1.4.3 (from -r /var/lib/openshift/＜なんか意味ない文字列＞/app-root/runtime/repo/requirements.txt (line 5))
```

そして nose や pytz、python-dateutil だの Werkzeug だのも古いのしか無い。１個１個これを解決していくのは骨が折れる・・・

幸い SSH が出来ることだし、リモートマシン上に もう１個 virtualenv を作ってそこで pip install / freeze した方が早いかも。

# 雑感

上の pip のパッケージバージョンの問題は割と深刻で、開発のストレスにしかならない。それと、git push してデプロイするときに、Heroku と違ってダウンタイムが発生する様子だ。

# 結論

Heroku の方が楽だと思う。