オープンソースのPaaSソフトウェア CloudFoundry の技術情報やイベント告知などを掲載します

2015-07-31

jqplay を Cloud Foundry で動かす

「Cloud Foundry 百日行」第41日目は,軽量 JSON パーサーである「jq」をブラウザ上で実行できる jqplay です。
ブラウザ上で簡単にjsonから必要な情報を抜き出すことができるため、例えば長大なjsonを整形したいが、パスワード等の秘匿情報が入っているので、他者が管理するサービスは使いたくないような場合などに、お持ちのCloud Foundry環境へPushして頂くと便利です。

基本情報

手順の概要は以下の通りです。

  • 1) ソースコードの取得
  • 2) アプリの起動
  • 3) 動作確認

1. ソースコードの取得

$ git clone https://github.com/jingweno/jqplay.git
$ cd jqplay/
jqplay$ ls
assets  bower.json  Gruntfile.js  LICENSE.md  package.json  public     script  tasks
bin     Godeps      jq            main.go     Procfile      README.md  server

README.md を確認すると、 GO と Node の環境が必要だよ、と書いてあるので、今回は heroku-buildpack-multi を使ってデプロイを行っていきます。

2. アプリの起動

デプロイ用ファイルの作成

デプロイのために manifest.yml と .buildpacks (heroku-buildpack-multi用の設定ファイル) を作成します。

まずは manifest.yml 。
buildpack: には、 heroku-buildpack-multi を指定します。
command: には、 jqplay が用意する構築用スクリプトから必要な処理を抜き出して指定しています。
また、アプリの起動に時間がかかる場合があるため、 timeout: を最大の180秒に設定しておきます。

jqplay$ vi manifest.yml

---
applications:
- name: jqplay-100
  buildpack: https://github.com/ddollar/heroku-buildpack-multi.git
  command: ./script/bootstrap;grunt build;./bin/jqplay
  timeout: 180

次に、 .buildpacks に、GO と Node の buildpack を設定します。

jqplay$ vi .buildpacks
https://github.com/cloudfoundry/nodejs-buildpack.git
https://github.com/cloudfoundry/go-buildpack.git

アプリのプッシュ

ファイルの作成が完了したら、アプリをデプロイします。

jqplay$ cf push
:
1 of 1 instances running

App started


OK

App jqplay-100 was started using this command `./script/bootstrap;grunt build;./bin/jqplay`

Showing health and status for app jqplay-100 in org horiu-jn / space 100nichi as horiu-jn...
OK

requested state: started
instances: 1/1
usage: 512M x 1 instances
urls: jqplay-100.10.244.0.34.xip.io
last uploaded: Wed Jul 22 07:48:52 UTC 2015
stack: cflinuxfs2
buildpack: https://github.com/ddollar/heroku-buildpack-multi.git

     state     since                    cpu    memory         disk      details
#0   running   2015-07-22 04:50:54 PM   0.0%   5.7M of 512M   0 of 1G

成功しました。

3. 動作確認

ブラウザからアプリにアクセスします。

動作確認のため、
“Filter” フィールドに

.color[1]

“Jason” フィールドに

{
  "color":[
    {"red":156,"green":167,"red":22,"name":"sky blue"},
    {"red":231,"green":225,"red":143,"name":"cream"},
    {"red":99,"green":124,"red":53,"name":"olive"}
  ]
}

を設定すると、 “Result” フィールドに cream のレコードだけ表示されます。

今回使用したソフトウェア



投稿者:NTTソフトウェア株式会社 堀内 純

2015-07-30

あいまいランチ を Cloud Foundry で動かす

「Cloud Foundry 百日行」第40日目は,新たな切り口でランチのお店を紹介してくれるアプリあいまいランチです。
このアプリは “Ruby on Rails”&”PostgreSQL” で構成されています。またアプリのデプロイ時にぐるなびWebサービスを利用しているようです。

基本情報

手順の概要は以下の通りです。

  • 1) ソースコードの取得
  • 2) 事前準備
  • 3) デプロイ
  • 4) 動作確認

1. ソースコードの取得

$ git clone https://github.com/esodov/wassermann.git
$ cd wassermann
$ ls
app  config     db       Gemfile.lock  log     Rakefile   README.rdoc  tmp
bin  config.ru  Gemfile  lib           public  README.md  spec         vendor

2. 事前準備

Cloud Foundryでアプリを動作させる為に変更します。

  • Gemfileruby version を削除
$ vi Gemfile

(変更内容)
@@ -1,7 +1,7 @@
 source 'https://rubygems.org'
  
 gem 'rails_12factor', group: :production
-ruby '2.1.2'
+#ruby '2.1.2'
  
 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
 gem 'rails', '4.1.6'
 

3. デプロイ

今回は ServiceBrokerPostgreSQL を利用します。
詳細はこちらを確認して下さい。

$ cf create-service PostgreSQL "Basic PostgreSQL Plan" testdb1

次にアプリを --no-startcf push します。
またデプロイ時にDB初期設定、初期データのインポートも行うことからタイムアウトにならないように念の為、 -t 180 を付けます。

$ cf push was -c 'bundle exec rake db:migrate && bundle exec rake db:seed && bundle exec rails server --port=$PORT' -t 180 --no-start

cf push が終われば次に ServiceBrokerPostgreSQL にバインドします。

$ cf bind-service was testdb1

そして rake db:seed を実行する際に必要となる環境変数を設定します。

$ cf set-env was GNAVI_KEYID <ID>

ここで指定する <ID> はぐるなびWebサービスに登録し発行される keyID が必要になります。
今回は動作確認だけなのでぐるなびが提供しているAPI テストツールのページで日々変更される keyID を使って試します。(正式に利用される方は登録したものを利用してください。)

あとはアプリを起動します。

$ cf start was

:

OK
 
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: was.10.244.0.34.xip.io
last uploaded: Tue Jul 28 03:23:36 UTC 2015
stack: cflinuxfs2
buildpack: Ruby
 
     state     since                    cpu    memory           disk      details   
#0   running   2015-07-28 12:24:55 PM   0.0%   144.5M of 256M   0 of 1G    

アプリが起動しました。

4. 動作確認

ブラウザからアプリにアクセスします。

駅と決定方法を選ぶと、

お店が表示されます。(本来の結果にはお店の写真と店名が表示されます)

おまけ

今回のアプリは山手線上の駅名しかないのですが、「山手線って何処だ!」って方は以下の2ファイルに最寄りの駅名をそれぞれ追加して下さい。

  • app/controllers/welcome_controller.rb
  • db/seeds/locations.rb

これであなたの近くのお店も検索出来るようになるかも・・・。

今回使用したソフトウェア

2015-07-29

Feedbin を Cloud Foundry で動かす

「Cloud Foundry 百日行」第39日目は,Ruby-on-Rails ベースのフィード・リーダー Feedbin です。今個人的にフィード・リーダーを探していて,その一環で試してみたプロダクトです。 Alternativeto.net においても,同種のオープンソース・ソフトウェアの中ではかなり人気が高く,実際動作確認時にさわってみた範囲ではかなり使えそうな感じでした。なお Alternativeto.net ではライセンスが Commercial に分類されていて,実際公式サイトで商用サービスも提供されていますが, MIT ライセンスのオープンソース・ソフトウェア としても提供されています。

基本情報

このアプリのデプロイは,これまで私が百日行で扱ってきた中では最も難敵でした。以下に示す手順も一見複雑ですが,個々のステップはシンプルで,一度わかってしまえばそれほど難しくはないので,恐れずに試してみていただきたいと思います。

  • 1) ソースコードの取得
  • 2) ソースコードの修正
  • 3) デプロイ準備
  • 4) サービスの作成とアプリとのバインド
  • 5) 起動スクリプトの作成
  • 6) manifest.yml の作成
  • 7) アプリの更新・起動
  • 8) 動作確認

1. ソースコードの取得

GitHub からソースコードをクローンします。この辺はいつも通りの手順です。

$ git clone https://github.com/feedbin/feedbin.git
$ cd feedbin/

2. ソースコードの修正

本アプリを Cloud Foundry 上で動かすには,以下の3点の修正が必要でした。

  1. 環境変数 MEMCACHED_HOSTS の条件付き読み込み
  2. 環境変数 FORCE_SSL の追加
  3. 環境変数 SERVE_STATIC_ASSETS の追加

いずれも,環境変数が関わる修正になります。このあたりは PaaS 上でアプリを動かす上での基本原則ですね。

2.1. 環境変数 MEMCACHED_HOSTS の条件付き読み込み

本アプリは Memcached も使うことができるようなのですが,

などから,必須ではないと考えられます。

しかし, ソースコード的には,プロダクション環境では環境変数 MEMCACHED_HOSTS が設定されていることが必須になっていた ので,これを条件付き読み込み (環境変数 MEMCACHED_HOSTS が設定されていたらそれを読み込む) に改めました。

$ git show 94d7fc6
commit 94d7fc64781d47b3474ed97a8e240ede5f1c9f48
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Tue Mar 10 23:44:10 2015 +0900

    Runnable without memcached

diff --git a/config/environments/production.rb b/config/environments/production.rb
index 320ab21..259d46b 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -68,7 +68,7 @@ Feedbin::Application.configure do
   # Disable automatic flushing of the log to improve performance.
   # config.autoflush_log = false

-  config.cache_store = :dalli_store, ENV['MEMCACHED_HOSTS'].split(',')
+  config.cache_store = :dalli_store, ENV['MEMCACHED_HOSTS'].split(',') if ENV['MEMCACHED_HOSTS']

   # Enable serving of images, stylesheets, and JavaScripts from an asset server.
   config.action_controller.asset_host = ENV['ASSET_HOST']

2.2. 環境変数 FORCE_SSL の追加

オリジナルのソースコードは, プロダクション環境では HTTPS の利用が必須 になっていました。しかし Cloud Foundry のような PaaS 環境では,リクエストがアプリに届く前に HTTPS が終端されることが多いので,これを設定で変更できるようにしました。

$ git show 77550ee
commit 77550eefb722f5cb0a778b4d2f247d7e0a474593
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Wed Mar 18 20:01:32 2015 +0900

    Make force_ssl configurable

    SSL might be terminated before the applicaton receives request.

diff --git a/config/environments/production.rb b/config/environments/production.rb
index 259d46b..36fa004 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -40,7 +40,7 @@ Feedbin::Application.configure do
   config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx

   # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
-  config.force_ssl = true
+  config.force_ssl = true unless (ENV["FORCE_SSL"] && (ENV["FORCE_SSL"].downcase == "false"))

   # Less verbose logs
   config.lograge.enabled = true

2.3. 環境変数 SERVE_STATIC_ASSETS の追加

Ruby-on-Rails では,静的コンテンツの提供をアプリとは別の専用 proxy cache を用いて行うことが一般的なようで, オリジナルのソースコードでもそういう設定 になっています。しかし,今回の Cloud Foundry 上へのデプロイではそうした proxy cache を使わないので,環境変数 SERVE_STATIC_ASSETS が設定されていて,かつ “false” でなければ, config.serve_static_assets を true に設定できるようにしました。

$ git show fdf3c14
commit fdf3c14ac4bf31d2fe5447f61d5e587466951ff2
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Wed Mar 18 19:59:30 2015 +0900

    Make "serving static assets" configurable

    Because we may not use any static content server on Cloud Foundry environment.

diff --git a/config/environments/production.rb b/config/environments/production.rb
index 36fa004..44c7328 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -20,6 +20,9 @@ Feedbin::Application.configure do
   # config.action_dispatch.rack_cache = true

   config.serve_static_assets = false
+  if ENV["SERVE_STATIC_ASSETS"] && (ENV["SERVE_STATIC_ASSETS"].downcase != "false")
+    config.serve_static_assets = true
+  end

   # Compress JavaScripts and CSS
   config.assets.compress = true

3. デプロイ準備

3.1. bundle install と .cfignore の作成

この後何度かローカル環境で bundle exec を実行する場面が出てくるので,その準備として bundle install を実行しておきます。また bundle install によって vendor/bundle に gem ファイルが保存されますが, 以前 Redmine の記事でも書いた ように,Cloud Foundry 上でも bundle install が実行されるためこれらのファイルのアップロードは不要なので,.cfignore ファイルを作成してアップロードを抑止するようにしておきます。

bundle install

$ bundle install --path vendor/bundle

Native extension を含む gem のコンパイル時に必要なライブラリーがないとエラーで止まってしまうことがありますが,これは環境によって異なるので本記事では詳細に記述しません。一応参考情報として,私が使っている検証環境では curb, pg, rmagick の3つの gem のインストール/コンパイル時にエラーが出て,それぞれ以下のページを参考に解決しました。

  • curb: http://stackoverflow.com/questions/16162266/unable-to-install-curb-gem
  • pg: http://stackoverflow.com/questions/3116015/how-to-install-postgresqls-pg-gem-on-ubuntu
  • rmagick: http://stackoverflow.com/questions/3704919/installing-rmagick-on-ubuntu

最終的に,上で示した bundle install --path vendor/bundle コマンドがエラーなしに最後まで走りきればOKです。

.cfignore の作成

先の bundle install でリポジトリー内に作成されたファイルのアップロードを抑制するために,以下の内容で .cfignore ファイルを作ります。

$ cat .cfignore
/.bundle/
/vendor/bundle/

3.2. アプリの事前プッシュ

サービスとのバインドや環境変数の設定を行うために,予めアプリを停止状態でプッシュしておきます。

$ cf push feedbin --no-start

4. サービスの作成とアプリとのバインド

本アプリを動かすには,PostgreSQL と Redis の2つのデータ・サービスが必要 です。

4.1. PostgreSQL

本アプリは,PostgreSQL にプラグインをインストールして使います。通常,プラグインのインストールには PostgreSQL インスタンスの管理者権限が必要なので,Cloud Foundry で提供されている通常の PostgreSQL サービス (例: postgresql-cf-service-broker) は使えません。そこで今回は自分で作成した PostgreSQL インスタンスに直接接続する方法をとります。

Docker で PostgreSQL を起動

まず PostgreSQL インスタンス を Cloud Foundry 内から接続可能な環境に構築します。今回は postgresql-cf-service-broker の時と同様,Docker を使って構築します。手順の詳細は そちら をご覧になってください。ここでは「Docker Image の取得 (docker pull)」までは終わっているものとして,「Docker Image の起動 (docker run)」から記述します。

$ sudo docker run --name feedbin-postgres -e POSTGRES_PASSWORD=xxxxxxxxxx -e POSTGRES_USER=feedbin -e LC_ALL=C.UTF-8 -p 0.0.0.0:5442:5432 -d postgres:9.4.2
3d1db1a8ed6cfc5b4f5d8ab20448194585310f5fff95544ad087ec4c28c8c734

PostgreSQL インスタンスの動作を確認します。

$ psql -U feedbin -W -l -h 192.168.15.91 -p 5442
Password for user feedbin:
                              List of databases
   Name    |  Owner   | Encoding | Collate |  Ctype  |   Access privileges
-----------+----------+----------+---------+---------+-----------------------
 feedbin   | postgres | UTF8     | C.UTF-8 | C.UTF-8 |
 postgres  | postgres | UTF8     | C.UTF-8 | C.UTF-8 |
 template0 | postgres | UTF8     | C.UTF-8 | C.UTF-8 | =c/postgres          +
           |          |          |         |         | postgres=CTc/postgres
 template1 | postgres | UTF8     | C.UTF-8 | C.UTF-8 | =c/postgres          +
           |          |          |         |         | postgres=CTc/postgres
(4 rows)

データベース一覧が取れました。問題ないようです。

データベースのスキーマ作成

次に,今起動した PostgreSQL の feedbin データベースにスキーマを作成します。

普通,Rails アプリのスキーマ作成には db:migrate という rake タスクを使うのですが,このアプリでは db:setup というタスクを使います。このタスクでは SQL をそのまま扱うこともできるので,db:migrate よりも素の DBMS に近い複雑な操作が行えます。前述のプラグインのインストールも,このタスクの中で行われています。

本アプリでは,このタスクは実際には

  • db/structure.sql の実行によるスキーマ作成
  • rake タスク db:seed による初期データ投入

という2段階で構成されています。ここではまず前半の「db/structure.sql の実行によるスキーマ作成」を実施します。

$ psql -U feedbin -W -h 192.168.15.91 -p 5442 -d feedbin -f db/structure.sql

途中でエラーが発生せずに最後まで走りきればOKです。

一応結果を確認してみます。

$ psql -U feedbin -W -h 192.168.15.91 -p 5442 -d feedbin -c '\d'
Password for user feedbin:
                        List of relations
 Schema |               Name                |   Type   |  Owner
--------+-----------------------------------+----------+---------
 public | actions                           | table    | feedbin
 public | actions_id_seq                    | sequence | feedbin
..
 public | users                             | table    | feedbin
 public | users_id_seq                      | sequence | feedbin
(47 rows)

テーブルができていることが確認できました。

初期データの投入

rake タスク db:seed で初期データを投入します。この初期データには 動作確認に使う初期ユーザーが含まれています

$ DATABASE_URL=postgres://feedbin:xxxxxxxxxx@192.168.15.91:5442/feedbin RAILS_ENV=production bundle exec rake db:seed
W, [2015-07-27T23:16:34.421389 #20297]  WARN -- : ** [Honeybadger] Unable to start Honeybadger -- api_key is missing or invalid. level=2 pid=20297
Skipping index creation, cannot connect to Elasticsearch
(The original exception was: #<Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 9200>)

今回 Honeybadger と Elasticsearch を使わない=未設定のため警告が出ますが,無視して問題ありません。

4.2. Redis

cf-redis-release を使って Redis サービスを作成し,アプリにバインドします。

$ cf marketplace
Getting services from marketplace in org nota-ja / space 100 as nota-ja...
OK

service      plans                     description
PostgreSQL   Basic PostgreSQL Plan*    PostgreSQL on shared instance.
p-mysql      100mb, 1gb                MySQL databases on demand
p-redis      shared-vm, dedicated-vm   Redis service to provide a key-value store

* These service plans have an associated cost. Creating a service instance will incur this cost.

TIP:  Use 'cf marketplace -s SERVICE' to view descriptions of individual plans of a given service.
$ cf create-service p-redis shared-vm redis4feedbin
Creating service instance redis4feedbin in org nota-ja / space 100 as nota-ja...
OK
$ cf bind-service feedbin redis4feedbin
Binding service redis4feedbin to app feedbin in org nota-ja / space 100 as nota-ja...
OK
TIP: Use 'cf restage feedbin' to ensure your env variable changes take effect

VCAP_SERVICES 環境変数はこうなります。

$ cf env feedbin
..
 "VCAP_SERVICES": {
  "p-redis": [
   {
    "credentials": {
     "host": "10.244.3.46",
     "password": "c227fdd7-ed24-4e62-aa27-0a11765edf54",
     "port": 39039
    },
    "label": "p-redis",
    "name": "redis4feedbin",
    "plan": "shared-vm",
    "tags": [
     "pivotal",
     "redis"
    ]
   }
  ],
..

5. 起動スクリプトの作成

本アプリは foreman を使って worker プロセスを起動するのですが,Cloud Foundry の起動コマンドでは foreground プロセスは同時に1つしか許されないため,foreman で起動されるプロセスを background に持っていく必要があります。1-liner でそれができないか試みたのですが難しかったので,Cloud Foundry 用の起動スクリプトを作成して,その中で起動するプロセスの制御を行うことにしました。

$ cat cf-startup.sh
#!/usr/bin/env bash

set -x

bundle exec foreman start &
sleep 10
bundle exec rackup --port $PORT

ファイルに実行可能フラグをつけるのを忘れないようにしてください。

$ ls -l cf-startup.sh
-rwxr----- 1 nota-ja nota-ja 98 Jul 29 04:20 cf-startup.sh

6. manifest.yml の作成

最後に,このアプリでは設定する環境変数が多くて cf set-env コマンドでいちいち設定するのが大変なので,manifest.yml ファイルを作りました。

$ cat manifest.yml
---
applications:
- name: feedbin
  memory: 1344m
  command: './startup.sh'
  env:
    DATABASE_URL: postgres://feedbin:xxxxxxxxxx@192.168.15.91:5442/feedbin
    REDIS_URL: redis://redis:c227fdd7-ed24-4e62-aa27-0a11765edf54@10.244.3.46:39039
    FORCE_SSL: false
    SERVE_STATIC_ASSETS: true
    PUSH_URL: https://feedbin.10.244.0.34.xip.io
    SECRET_KEY_BASE: 3ccac264399989430f06deafaf796faf1a357fcb505125c9c783b200ef70c02451e250a54d288edb4340b6af4a688b65e04fdb39f4b9318199ec4c0a34569ad6

以下簡単に説明します。

- name: feedbin

↑ Cloud Foundry 上でのアプリ名。当然ながら, 3.2. アプリの事前プッシュ の時に指定した名前と一致していなければなりません。

  memory: 1344m

↑ worker プロセスもいるので,多めに指定しておいたほうが無難です。

  command: './startup.sh'

↑ 起動コマンドとして,先ほど作成したスクリプトを指定します。

env: 階層の下は環境変数です。

    DATABASE_URL: postgres://feedbin:xxxxxxxxxx@192.168.15.91:5442/feedbin

↑ PostgreSQL の接続情報を,DATABASE_URL 形式で記述したものです。

    REDIS_URL: redis://redis:c227fdd7-ed24-4e62-aa27-0a11765edf54@10.244.3.46:39039

↑ Redis の接続情報です。 cf env feedbin で取得した VCAP_SERVICES 環境変数を元に作成して設定します。

    FORCE_SSL: false

2.2. 環境変数 FORCE_SSL の追加 で定義した環境変数です。今回は SSL/TLS を強制しません。

    SERVE_STATIC_ASSETS: true

2.3. 環境変数 SERVE_STATIC_ASSETS の追加 で定義した環境変数です。今回は Rails プロセス自身で静的コンテンツも提供します。

    PUSH_URL: https://feedbin.10.244.0.34.xip.io

↑ worker プロセスがフィードの更新を POST する URL のようです (参考) 。Cloud Foundry 上でのアプリの URL を設定します。

    SECRET_KEY_BASE: 3ccac264399989430f06deafaf796faf1a357fcb505125c9c783b200ef70c02451e250a54d288edb4340b6af4a688b65e04fdb39f4b9318199ec4c0a34569ad6

↑ 秘密鍵の元です。 bundle exec rake secret で作ることができます。

7. アプリの更新・起動

必要な設定は全て終わったので,アプリを更新&起動します。

$ cf push
..
requested state: started
instances: 1/1
usage: 1.3G x 1 instances
urls: feedbin.10.244.0.34.xip.io
last uploaded: Mon Jul 27 21:28:40 UTC 2015
stack: cflinuxfs2
buildpack: Ruby

     state     since                    cpu    memory           disk      details
#0   running   2015-07-28 06:32:40 AM   0.0%   642.8M of 1.3G   0 of 1G

この後述べる Ruby のバージョン問題がなければ,正常に起動するはずです。

なお,使用する Cloud Foundry のリリースによっては,以下のようなエラーが出て起動に失敗することがあります。

..
-----> Compiling Ruby/Rails
       Could not get translated url, exited with: DEPENDENCY_MISSING_IN_MANIFEST: https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/ruby-2.1.4.tgz
 !
 !     exit
 !
Staging failed: Buildpack compilation step failed


FAILED
BuildpackCompileFailed
..

これは,アプリが要求している Ruby のバージョン (2.1.4) が,使用している Cloud Foundry の Ruby buildpack にないために起きる現象です。古い Ruby buildpack を指定するなどの方法で回避することもできますが,今回私はアプリが要求している Ruby のバージョンを書き換えることで対処しました。

$ git show abb8ec5
commit abb8ec5625bee615861991d7677c076690704618
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Tue Jul 28 08:38:16 2015 +0900

    Use Ruby 2.1.6

    Because the recent Cloud Foundry Ruby buildpack doesn't have 2.1.4.

diff --git a/.ruby-version b/.ruby-version
index c346e7a..b6da512 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.1.4
\ No newline at end of file
+2.1.6
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
index f99e7b9..166053a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,5 +1,5 @@
 source 'https://rubygems.org'
-ruby '2.1.4'
+ruby '2.1.6'

 gem 'rails', '= 4.1.11'
 gem 'rest-client', '= 1.6.7'

8. 動作確認

ブラウザーでアクセスしてみます。

起動直後の画面:

db/seeds.rb に書かれた初期ユーザーでログインしてみます:

ログイン直後の画面:

試しに 本ブログのAtom を登録してみます:

Atomが読み込まれ,フィード一覧が表示されました:

フィードを選択すると,中身も読めます:

画像もインラインで表示されます:

背景やフォントも変えられますし,普通に使えそうです。

今回使用したソフトウェア

2015-07-28

Smalruby を Cloud Foundry で動かす

「Cloud Foundry 百日行」第38日目は、Web上でRubyのビジュアルプログラミングができるツール Smalruby です。

基本情報

手順の概要は以下の通りです。

  • 1) ソースコードの取得
  • 2) データベースの準備
  • 3) コードの修正
  • 4) Cloud Foundry 環境へのプッシュ
  • 5) 動作確認

1. ソースコードの取得

$ git clone https://github.com/smalruby/smalruby-editor/
$ cd smalruby-editor/
smalruby-editor$ ls
app     config.ru  Gemfile       LEGAL    log       Rakefile                 spec
bin     db         Gemfile.lock  lib      Procfile  README.rdoc              vendor
config  demos      Guardfile     LICENSE  public    smalruby-editor.gemspec

Rubyのアプリです。
公式ドキュメントにも インストール手順 が記載されているので、これを参考に進めていきましょう。

2. データベースの準備

まずはデータベースの準備です。

アプリを起動無しモードでPush。

smalruby-editor$ cf push smalruby --no-start
Creating app smalruby in org ukaji / space default as ukaji...
OK

Creating route smalruby.10.244.0.34.xip.io...
OK

Binding smalruby.10.244.0.34.xip.io to smalruby...
OK

Uploading smalruby...
Uploading app files from: /home/ukaji/workspace/smalruby-editor
Uploading 1.5M, 519 files
Done uploading               
OK

MySQLのサービスを作成し、アプリとバインド。

smalruby-editor$ cf marketplace
Getting services from marketplace in org ukaji / space default as ukaji...
OK

service      plans                     description
PostgreSQL   Basic PostgreSQL Plan*    PostgreSQL on shared instance.
p-mysql      100mb, 1gb                MySQL databases on demand
p-redis      shared-vm, dedicated-vm   Redis service to provide a key-value store

* These service plans have an associated cost. Creating a service instance will incur this cost.

TIP:  Use 'cf marketplace -s SERVICE' to view descriptions of individual plans of a given service.
smalruby-editor$ cf create-service p-mysql 100mb smalruby-db
Creating service instance smalruby-db in org ukaji / space default as ukaji...
OK
smalruby-editor$ cf bind-service smalruby smalruby-db
Binding service smalruby-db to app smalruby in org ukaji / space default as ukaji...
OK
TIP: Use 'cf restage smalruby' to ensure your env variable changes take effect

3. コードの修正

Cloud Foundryへデプロイするにあたり、少々コードを修正します。
まずはデータベースとの接続部分から。

先ほどバインドしたサービスへの接続情報を $ cf env smalruby で参照しつつ記載します。

smalruby-editor$ cf env smalruby
Getting env variables for app smalruby in org ukaji / space default as ukaji...
OK

System-Provided:
{
 "VCAP_SERVICES": {
  "p-mysql": [
   {
    "credentials": {
     "hostname": "10.244.7.6",
     "jdbcUrl": "jdbc:mysql://10.244.7.6:3306/cf_0e3058cc_91dd_4738_91a1_21f93e41ad0d?user=eYUkO8eGA2neWOKm\u0026password=hHCDD1A5vLFsXzAO",
     "name": "cf_0e3058cc_91dd_4738_91a1_21f93e41ad0d",
     "password": "hHCDD1A5vLFsXzAO",
     "port": 3306,
     "uri": "mysql://eYUkO8eGA2neWOKm:hHCDD1A5vLFsXzAO@10.244.7.6:3306/cf_0e3058cc_91dd_4738_91a1_21f93e41ad0d?reconnect=true",
     "username": "eYUkO8eGA2neWOKm"
    },
    "label": "p-mysql",
    "name": "smalruby-db",
    "plan": "100mb",
    "tags": [
     "mysql"
    ]
   }
  ]
 }
}

{
 "VCAP_APPLICATION": {
  "application_name": "smalruby",
  "application_uris": [
   "smalruby.10.244.0.34.xip.io"
  ],
  "application_version": "6040512d-4671-4101-89a7-73d36c5732b4",
  "limits": {
   "disk": 1024,
   "fds": 16384,
   "mem": 256
  },
  "name": "smalruby",
  "space_id": "03bf316f-df9e-442e-b127-589e673a5652",
  "space_name": "default",
  "uris": [
   "smalruby.10.244.0.34.xip.io"
  ],
  "users": null,
  "version": "6040512d-4671-4101-89a7-73d36c5732b4"
 }
}

No user-defined env variables have been set

No running env variables have been set

No staging env variables have been set

書き換え箇所はこちら。

smalruby-editor$ cp config/database.yml.mysql2 config/database.yml
smalruby-editor$ vi config/database.yml
# MySQL.  Versions 4.1 and 5.0 are recommended.
#
# Install the MYSQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   http://dev.mysql.com/doc/refman/5.0/en/old-client.html
development:
  adapter: mysql2
  encoding: utf8
  database: smalruby-editor_development
  pool: 5
  username: eYUkO8eGA2neWOKm
  password: hHCDD1A5vLFsXzAO
  host: 10.244.7.6

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  adapter: mysql2
  encoding: utf8
  database: smalruby-editor_test
  pool: 5
  username: eYUkO8eGA2neWOKm
  password: hHCDD1A5vLFsXzAO
  host: 10.244.7.6

production:
  adapter: mysql2
  encoding: utf8
  database: smalruby-editor_production
  pool: 5
  username: eYUkO8eGA2neWOKm
  password: hHCDD1A5vLFsXzAO
  host: 10.244.7.6

standalone:
  adapter: mysql2
  encoding: utf8
  database: smalruby-editor_standalone
  pool: 5
  username: eYUkO8eGA2neWOKm
  password: hHCDD1A5vLFsXzAO
  host: 10.244.7.6
smalruby-editor$ diff config/database.yml.mysql2 config/database.yml
16,18c16,18
<   username: root
<   password:
<   host: localhost
---
>   username: eYUkO8eGA2neWOKm
>   password: hHCDD1A5vLFsXzAO
>   host: 10.244.7.6
28,30c28,30
<   username: root
<   password:
<   host: localhost
---
>   username: eYUkO8eGA2neWOKm
>   password: hHCDD1A5vLFsXzAO
>   host: 10.244.7.6
37,39c37,39
<   username: root
<   password:
<   host: localhost
---
>   username: eYUkO8eGA2neWOKm
>   password: hHCDD1A5vLFsXzAO
>   host: 10.244.7.6
46,48c46,48
<   username: root
<   password:
<   host: localhost
---
>   username: eYUkO8eGA2neWOKm
>   password: hHCDD1A5vLFsXzAO
>   host: 10.244.7.6

ちなみにこのように記述してもOKです。

# MySQL.  Versions 4.1 and 5.0 are recommended.
#
# Install the MYSQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   http://dev.mysql.com/doc/refman/5.0/en/old-client.html

# common
common: &common
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: eYUkO8eGA2neWOKm
  password: hHCDD1A5vLFsXzAO
  host: 10.244.7.6

# development
development:
  <<: *common
  database: smalruby-editor_development
 
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *common
  database: smalruby-editor_test
 
production:
  <<: *common
  database: smalruby-editor_production
 
standalone:
  <<: *common
  database: smalruby-editor_standalone

次に、デプロイ時のコマンドが少々複雑になるのでmanifestファイルを作ってしまいましょう。

smalruby-editor$ vi manifest.yml
---
applications:
- name: smalruby
  buildpack: ruby_buildpack
  command: rake db:migrate; rake; touch tmp/standalone; rails server -p $PORT;
  env:
    RAILS_ENV: production
    SECRET_KEY_BASE: atemporarysecretkeyforsmalruby

SECRET_KEY_BASE は30文字以上の適当な文字列でOKです。

4. Cloud Foundry 環境へのプッシュ

準備は整ったのでPushを行いましょう。

smalruby-editor$ cf push
(一部略)
-----> Uploading droplet (47M)

0 of 1 instances running, 1 starting
1 of 1 instances running

App started


OK

App smalruby was started using this command `rake db:migrate; rake; touch tmp/standalone; rails server -p $PORT;`

Showing health and status for app smalruby in org ukaji / space default as ukaji...
OK

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: smalruby.10.244.0.34.xip.io
last uploaded: Tue Jul 28 01:06:34 UTC 2015
stack: cflinuxfs2
buildpack: ruby_buildpack

     state     since                    cpu    memory           disk      details   
#0   running   2015-07-28 10:08:37 AM   0.0%   143.3M of 256M   0 of 1G 

起動しました。

5. 動作確認

ブラウザーからアプリにアクセスします。

このようにパズルを組み合わせる感覚でプログラミングをすることができ、

裏では自動的にRubyのコードを生成してくれています。

ソースコードは右上のダウンロードボタンからローカルに保存し、実行することも可能です。
小学校3年生でも使えるとだけあって、プログラミングを学んだことのない人でもお手軽にRubyのコードを生成できそうな面白いツールでした。

おまけ

Railsのアプリをデプロイする際などによく登場する rails server コマンド。
Cloud Foundryではよく起動コマンド rails server -p $PORT として登場します。
Cloud Foundryでアプリが動作する際のポート番号を環境変数から取得してRailsサーバを起動するコマンドですね。

さて、このようなRailsアプリをmanifestファイルを使わずに cf push 一行でデプロイを試みようとすると、

$ cf push appname -b ruby_buildpack -c "rails server -p $PORT"

とできそうですが、実はこのコマンド、失敗します。

普段からターミナルをよく操作する方はお分かりかもしれませんが、実は上のケースでダブルクォーテーション " で括った内部にある $PORT は手元のローカル環境で解釈されてしまいます。
ログ等で見える事象としては、 $PORT に想定通りの値が入ってくれない、そもそも値が入らない、などになりますね。

というわけで正解はこちら。

$ cf push appname -b ruby_buildpack -c 'rails server -p $PORT'

シングルクォーテーション ' で括るだけです。
これなら $PORT はローカルで解釈されることなく、Cloud Foundry上での環境変数を読んでくれます。

今回使用したソフトウェア

2015-07-27

OpenDocMan を Cloud Foundry で動かす

本日の第37回「Cloud Foundry 百日行」は、PHP 製のドキュメント管理アプリ OpenDocMan です。

基本情報

はじめに

本日のアウトラインは以下の通りです。

  1. 手順の確認とソースコードの取得
  2. MySQL DB の作成
  3. SQL Buddy を使ったデータ登録(および編集)
  4. config.php の作成
  5. その他デプロイ前の準備
  6. Cloud Foundry へデプロイ
  7. 動作確認
  8. まとめ

1. 手順の確認とソースコードの取得

こちらの手順 を踏まえてデプロイしていきます。
Requirements については、まずは Cloud Foundry 環境にお任せとして、 5 Minute Install のポイントを確認してみると、以下のようになっています。

  1. ソースコードの取得
  2. MySQL DB の作成
  3. データ格納用フォルダの作成
  4. データ格納用フォルダへのアクセス権限設定
  5. 必要に応じて Web サーバの権限設定
  6. ソフトウェアのデプロイ
    • OpenDocMan をアクセスする URL のルートで使いたい場合は、ソフトウェアを Web サーバのルートディレクトリに配置
    • サブディレクトリを切って使いたい場合は、Web サーバのルートディレクトリ直下にそのディレクトリを作成して、ソフトウェアを配置
    • templates_c ディレクトリが書き込み可能になっているか確認
  7. ブラウザから OpenDocMan にアクセスし、インストールスクリプトを実行
  8. インストールスクリプト実行の中で、必要情報をフォームに入力
  9. 最後のページにて、”New installation…” のリンクをクリックして初期設定実施

5番の Web サーバ設定については、Cloud Foundry 環境にお任せとしましょう。
6番のデプロイについては、アプリのルートディレクトリから直接 Cloud Foundry へプッシュ、といういつもの手順を考えます。

では何はともあれ、まずは最初の手順、ソースコードを取得し、中身を確認していきましょう。

~$ wget http://sourceforge.net/projects/opendocman/files/opendocman/1.3.2/opendocman-1.3.2.tar.gz
~$ tar xzvf opendocman-1.3.2.tar.gz 
~$ cd opendocman-1.3.2/
~/opendocman-1.3.2$ ls
AccessLog_class.php  classHeaders.php        Department_class.php  file_ops.php         includes         odm-header.php    reports             udf_help.html
access_log.php       class.tree              department.php        FileTypes_class.php  index.php        odm-init.php      Reviewer_class.php  udf.php
add.php              config-sample.php       Dept_Perms_class.php  filetypes.php        in.php           odm-load.php      search.php          User_class.php
admin.php            CONTRIBUTING.md         details.php           forgot_password.php  install          out.php           Settings_class.php  UserPermission_class.php
ajax_udf.php         COPYING                 docs                  FormCheck.js         ldap.inc         Plugin_class.php  settings.php        User_Perms_class.php
Category_class.php   CREDITS.txt             edit.php              functions.js         LICENSE.txt      plug-ins          signup.php          user.php
category.php         crumb.php               Email_class.php       functions.php        linkcontrol.css  profile.php       templates           version.php
check_exp.php        databaseData_class.php  error.php             help.html            logout.php       README            templates_c         view_file.php
check-in.php         database.sql            File_class.php        history.php          magic            README.md         toBePublished.php   view.php
check-out.php        delete.php              FileData_class.php    images               mimetypes.php    rejects.php       udf_functions.php

index.php を開いてみると、以下のようにあります。

~/opendocman-1.3.2$ cat index.php

:

if(!file_exists('config.php'))
{
    if (
        !extension_loaded('pdo')
        || !extension_loaded('pdo_mysql')
    ) {
        echo "<p>PHP pdo Extensions not loaded. <a href='./'>try again</a>.</p>";
        exit;
    }
    // A config file doesn't exist
    ?>
    <html><head><link rel="stylesheet" href="templates/common/css/install.css" type="text/css" /></head>
        <body>Looks like this is a new installation because we did not find a config.php file. We need to create a config.php file now: <p><a href="install/setup-config.php" class="button">Create a Configuration File</a></p></body>
    </html>
    <?php
    exit;
}

:

上のソースファイルで参照されている config.php ですが、本来は上記の手順の7〜9で作成されるようです。
今回はあらかじめ config.php ファイルを作成しておき、デプロイが完了した時点でアプリが利用可能になるような修正を行いました。
また、後々余分な初期設定スクリプト等を走らせないために、 install ディレクトリ自体は削除 or リネームしておきます。

また上記から、 pdopdo_mysql の PHP 拡張モジュールのロードが必要になるようにコーディングされているので、デプロイ前にこれらの設定もしておきましょう。

database.sql は SQL のスクリプトで、DB 対して実行して、データを流し込む必要があるようです。
DB へのデータの初期登録の方法はいくつかありますが、今回は、 SQL Buddy を使うことにします。

上記の手順にあった templates_c ディレクトリの権限設定もそのままで大丈夫そうです。

これらを加味して、Cloud Foundry へデプロイするための計画手順を今一度整理すると、以下のようになります。

(Cloud Foundryへデプロイする際の手順)

  1. ソースコードの取得(済み)
  2. MySQL DB の作成
  3. SQL Buddy を使ったデータ登録(および編集)
  4. config.php の作成
  5. install ディレクトリの削除(リネーム)
  6. データ格納用フォルダの作成とアクセス権限設定
  7. PHP 拡張モジュール設定
  8. Cloud Foundry へデプロイ

2. MySQL DB の作成

上記の手順の2番から進めていきましょう。

まずはアプリを --no-start で push しておいてから、MySQL サービスを作成し、そして、アプリとそのサービスをバインド、といつもの手順で進めていきます。

~/opendocman-1.3.2$ cf push opendocman --no-start
~/opendocman-1.3.2$ cf create-service p-mysql 100mb odm-msql
~/opendocman-1.3.2$ cf bind-service opendocman odm-msql

3. SQL Buddy を使ったデータ登録(および編集)

先ほどアプリとバインドした DB にアクセスして操作するために、別アプリとして SQL Buddy をデプロイします。

デプロイ手順は、 百日行第17回の記事 に詳しいので、そちらをご参照ください。ただし、SQL Buddy と MySQL サービスとのバインドは、今回は必要ありませんので、実行不要です。

SQL Buddy がデプロイできたら、それを使って、前章で作成した MySQL DB にアクセスしますが、そのためには、その DB に関する “Host”、”Username”、”Password” の各情報が必要です。
その情報取得のため、 opendocman アプリの環境変数を確認します。

~/opendocman-1.3.2$ cf env opendocman
Getting env variables for app opendocman in org ueno / space test1 as ueno...

:

System-Provided:
{
 "VCAP_SERVICES": {
  "p-mysql": [
   {
    "credentials": {
     "hostname": "10.244.7.6",
     "jdbcUrl": "jdbc:mysql://10.244.7.6:3306/cf_d044277f_8a28_49ce_b13e_271929e7af23?user=OoCb8001U9wpFegv\u0026password=Cn6Wl14YTlpFQk6Z",
     "name": "cf_d044277f_8a28_49ce_b13e_271929e7af23",
     "password": "Cn6Wl14YTlpFQk6Z",
     "port": 3306,
     "uri": "mysql://OoCb8001U9wpFegv:Cn6Wl14YTlpFQk6Z@10.244.7.6:3306/cf_d044277f_8a28_49ce_b13e_271929e7af23?reconnect=true",
     "username": "OoCb8001U9wpFegv"
    },
    "label": "p-mysql",
    "name": "odm-msql",
    "plan": "100mb",
    "tags": [
     "mysql"
    ]
   }
  ]
 }
}

:

上記の "hostname""username""password" の各値を用いて、SQL Buddy にログインします。

database.sql をこの SQL Buddy を使って読み込ませ、DB に対して実行させますが、 SQL Buddy は、ブラウザが動いている環境のローカルファイルしか読んでくれないため、 database.sql をあらかじめその環境にコピーしておきましょう。

ログインしたら、初期画面の左側に当該の DB 名(上記の "name" の値)が表示されているはずですから、それをクリックします。
そして、画面上段にある “Import” のリンクをクリックし、「ファイルを選択」をクリックし、 database.sql を読み込ませます。

“Submit” をクリックすることで、スクリプトが実行されて、DB 内に各テーブルが作成され、値が入っていきます。
実行後に DB の画面を確認すると、以下のようになっています。

続いて、アプリ起動のために必要な登録データの修正を、この SQL Buddy 上で行います。
上記画面の中に表示されている表の中の odm_settings の右側に付いている “→” をクリックします。
以下の画面となりました。

上記画面に表示されている設定項目の中で、 dataDirbase_url の値を今回の環境に合わせて修正する必要があります。
それぞれ、 /home/vcap/app/htdocs/datahttp://opendocman.10.244.0.34.xip.io を登録し、以下のようになりました。

4. config.php の作成

続いての手順は、 config.php の作成です。
元のファイルは、 config-sample.php として用意されていますので、それをコピーして編集します。

~/opendocman-1.3.2$ cp -p config-sample.php config.php

このファイル内の DB 設定について、VCAP_SERVICES 環境変数から DB 接続情報を取得して、動的に DB 接続させるため、以下の修正を加えました。

~/opendocman-1.3.2$ vi config.php
~/opendocman-1.3.2$ diff config-sample.php config.php 
20a21,24
> $vcap = getenv("VCAP_SERVICES");
> $vcap_json = json_decode($vcap,true);
> $mysql_config = $vcap_json["p-mysql"][0]["credentials"];
> 
30c34
< define('DB_NAME', 'database_name_here');
---
> define('DB_NAME', $mysql_config["name"]);
33c37
< define('DB_USER', 'username_here');
---
> define('DB_USER', $mysql_config["username"]);
36c40
< define('DB_PASS', 'password_here');
---
> define('DB_PASS', $mysql_config["password"]);
41c45
< define('DB_HOST', 'localhost');
---
> define('DB_HOST', $mysql_config["hostname"]);

5. その他デプロイ前の準備

Cloud Foundry へデプロイする前に残りの必要な手順 5〜7番を片付けてしまいましょう。

まず、 install ディレクトリをリネームし、アプリへのアクセス時に参照されないようにします。

~/opendocman-1.3.2$ mv install install.org

次に、データ格納用フォルダ data を作成し、手順 通りに chmod しておきます。

~/opendocman-1.3.2$ mkdir data
~/opendocman-1.3.2$ ls -ld data
drwxrwxr-x 2 ueno ueno 4096 Jul 23 11:52 data
~/opendocman-1.3.2$ chmod 770 data
~/opendocman-1.3.2$ ls -ld data
drwxrwx--- 2 ueno ueno 4096 Jul 23 11:52 data

そしてこのフォルダ、まだこのままではダメなのです。
実は中身が空っぽなので、Cloud Foundry にプッシュする際にフォルダが消されてしまいます。
なので、このフォルダ内にダミーファイルを作って置いておきましょう。

~/opendocman-1.3.2$ touch data/.keep

そして最後の準備として、PHP 拡張モジュールの設定をして、 pdo , pdo_mysql がロードされるようにしておきます。
PHP 拡張モジュールの設定については、これまでの百日行記事でも何度か出てきたので、既におなじみになっていると思いますが、最近では、 第33日目 に出てきてますので、参考にしてください。

~/opendocman-1.3.2$ mkdir .bp-config
~/opendocman-1.3.2$ vi .bp-config/options.json
~/opendocman-1.3.2$ cat .bp-config/options.json
{
        "PHP_EXTENSIONS": ["pdo", "pdo_mysql"]
}

6. Cloud Foundry へデプロイ

ようやく準備が整ったので、いよいよデプロイ(再度のプッシュによるアプリ起動)です。

~/opendocman-1.3.2$ cf push opendocman
Updating app opendocman in org ueno / space test1 as ueno...

:

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: opendocman.10.244.0.34.xip.io
last uploaded: Thu Jul 23 04:44:39 UTC 2015
stack: cflinuxfs2
buildpack: PHP

     state     since                    cpu    memory          disk      details   
#0   running   2015-07-23 01:45:00 PM   1.6%   30.1M of 256M   0 of 1G  

デプロイ成功し、ついにアプリが起動しました!

7. 動作確認

http://opendocman.10.244.0.34.xip.io にアクセス。

“admin” でログインします。

画面上段の “Admin” リンクを辿り、Admin 機能によって、一般ユーザ “taro” を作成して一旦ログアウト。

今後はその “taro” でログインし、このアプリを使って、何かローカルのファイルをアップロードしてみます。
適当なファイルを選択し、フォーム入力し、

そして、”Submit” をクリックすると、

ファイル追加完了のメッセージが出ました。

“Home” に戻って、追加したファイルのリストを確認してみると、

追加したファイルが格納されていることが確認できました。

8. まとめ

今回のアプリのデプロイ手順は、デプロイする前の準備作業が多いことが特徴でした。
それらを大別すると、以下の種類のものでした。
(1) DB の作成とデータ登録
(2) DB 接続設定
(3) フォルダ作成や PHP 拡張モジュール対応等の環境設定

(2) の DB 接続設定は、これまでの記事にもよく出てきましたが、今回も、VCAP_SERVICES 環境変数から情報を読み取ることで、動的な DB 接続を実現させました。
これは、アプリを運用する環境が変わっても、それに動的に対応し、スケールさせるものであり、クラウドネイティブアプリの開発における基本的なテクニックです(クラウドネイティブアプリ開発の方法論として、 Twelve-Factor App をご参照のこと)。

今回使用したソフトウェア

2015-07-24

Redmine を Cloud Foundry で動かす

「Cloud Foundry 百日行」第36日目は,Ruby-on-Rails ベースの project management ソフト Redmine です。今さら説明の必要のない超有名アプリケーションだと思いますが,これも Cloud Foundry の上で動かすことができます。

基本情報

手順の概要は以下の通りです。

  • 1) ソースコードの取得
  • 2) セッション・ストアの秘密鍵の生成
  • 3) データベース・サービスの作成とアプリとのバインド
  • 4) 初期データ投入に必要な環境変数の設定
  • 5) アプリの更新・起動
  • 6) 動作確認

1. ソースコードの取得

公式サイトのインストール手順 では, http://www.redmine.org/projects/redmine/wiki/Download から tarball をダウンロードするか,リポジトリーからコードをチェックアウトすると書いてあるので,ここでは GitHub の公式ミラーからソースコードをチェックアウトします。

$ git clone https://github.com/redmine/redmine.git
$ cd redmine

今回は最新の master ではなく,最新安定版 (検証時点では 3.0.4) を使うことにします。

$ git checkout 3.0.4

2. セッション・ストアの秘密鍵の生成

公式サイトのインストール手順には, セッション・ストアの秘密鍵の生成 が含まれています。これはコマンド的には bundle exec rake generate_secret_token を実行すれば良いのですが,このコマンドを正常に実行する前提条件として,

  • Ruby と bundler gem のインストール
  • bundle install の実行
  • config/database.yml が存在すること

が必要になります。

今回は1番目の「Ruby と bundler gem のインストール」は既に終わっているものとして,2番目以降について順次述べていきます。

bundle install の実行

$ bundle install --path vendor/bundle --without rmagick

--path vendor/bundle は,ローカルの vendor/bundle ディレクトリーに Ruby の gem をインストールするというオプションです。必須ではありませんが,これを付けないと,この Redmine だけで使う gem が Ruby 全体の gem を入れるディレクトリーにインストールされ,「ライブラリー汚染」の原因になるので,付けたほうが無難です。

--without rmagick は,rmagick gem のインストールをスキップするためのオプションです。これも必須ではないのですが,これを付けないと rmagick をインストールするために追加のパッケージ (Ubuntu では libmagickwand-dev, imagemagick 等) のインストールが必要になります。今回 Redmine はローカルの環境ではなく Cloud Foundry 上で実行するので,ローカルにこれらのパッケージを入れる必要はありません。つまり bundle exec rake generate_secret_token を実行できる環境を整えるだけのための bundle install なので,rmagick のインストールはスキップすることにしました。

config/database.yml の作成

これも,ただ単に形式の整ったファイルが存在すればいいので, config/database.yml.example をコピーするだけでOKです。

$ cp config/database.yml.example config/database.yml

セッション・ストアの秘密鍵の生成

前提が整ったので,本来の目的であるセッション・ストアの秘密鍵生成を行います。

$ bundle exec rake generate_secret_token

config/initializers/secret_token.rb というファイルができていて,内容が以下のようになっていればOKです (当然ですが代入式の右辺値はこの値とは異なるはずです)。

$ cat config/initializers/secret_token.rb
# This file was generated by 'rake generate_secret_token', and should
# not be made visible to public.
# If you have a load-balancing Redmine cluster, you will need to use the
# same version of this file on each machine. And be sure to restart your
# server when you modify this file.
#
# Your secret key for verifying cookie session data integrity. If you
# change this key, all old sessions will become invalid! Make sure the
# secret is at least 30 characters and all random, no regular words or
# you'll be exposed to dictionary attacks.
RedmineApp::Application.config.secret_key_base = '431137b97013042fb346ce3f5517c773d2e7dca562651ab389e81768e52bd9507710f8144437159a'

これでセッション・ストア秘密鍵が生成できました。

3. データベース・サービスの作成とアプリとのバインド

次に,データベース・サービスを作成し,アプリにバインドします。

データベース・サービスの作成

MySQL サービスを作成します。

$ cf create-service p-mysql 100mb my4red
Creating service instance my4red in org nota-ja / space 100 as nota-ja...
OK

アプリを停止状態でプッシュ

サービスとバインドするために,アプリを停止状態でプッシュするのですが,このままプッシュすると vendor/bundle 内のファイルも全てアップロードされてしまいます。Cloud Foundry 側で bundle install が実行されるため,これらは実際にはアップロード不要なので, .cfignore ファイルを予め作成して,vendor/bundle をアップロード対象から除外します。

$ emacs .cfignore
$ cat .cfignore
vendor/bundle/

アプリを停止状態でプッシュします。

$ cf push redmine --no-start
Creating app redmine in org nota-ja / space 100 as nota-ja...
OK
..
Done uploading
OK

アプリとサービスのバインド

$ cf bind-service redmine my4red
Binding service my4red to app redmine in org nota-ja / space 100 as nota-ja...
OK
TIP: Use 'cf restage redmine' to ensure your env variable changes take effect

4. 初期データ投入に必要な環境変数の設定

公式サイトのインストール手順 に従って,初期データの投入を自動で行うために環境変数 REDMINE_LANG を予め設定しておきます。これが未設定だと,初期データ投入時に対話的に入力を求められてしまい,Cloud Foundry では入力のすべがないためエラーが発生してしまいます。

$ cf set-env redmine REDMINE_LANG ja

日本語を使用したいので, ja に設定しました。

Setting env variable 'REDMINE_LANG' to 'ja' for app redmine in org nota-ja / space 100 as nota-ja...
OK
TIP: Use 'cf restage' to ensure your env variable changes take effect
$ cf env redmine
Getting env variables for app redmine in org nota-ja / space 100 as nota-ja...
OK
..
User-Provided:
REDMINE_LANG: ja
..

5. アプリの更新・起動

起動コマンドを設定してアプリを更新・起動します。

$ cf push redmine -c 'bundle exec rake db:migrate && bundle exec rake redmine:load_default_data && bundle exec rails server --port=$PORT' -t 180

ポイントは2つ。

1つは起動コマンドでデータベース・スキーマの設定 (bundle exec rake db:migrate) と初期データの投入 (bundle exec rake redmine:load_default_data) を行ったのちに,環境変数で指定された待ち受けポートでアプリを起動する (bundle exec rails server) こと。

もう1つは,データベース・スキーマの設定にけっこう時間がかかるので,タイムアウトを長めに設定する (-t 180) ことです。

Updating app redmine in org nota-ja / space 100 as nota-ja...
OK
..
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: redmine.10.244.0.34.xip.io
last uploaded: Thu Jul 23 03:59:09 UTC 2015
stack: cflinuxfs2
buildpack: Ruby

     state     since                    cpu    memory           disk      details
#0   running   2015-07-23 01:01:16 PM   0.0%   143.5M of 256M   0 of 1G

アプリが起動しました。

6. 動作確認

ブラウザーでアプリにアクセスしてみます。

初期画面はこんな感じです:

公式サイトの情報 に従い,初期ユーザー admin でログインします:

ログインできました:

新しいプロジェクトを作ってみます:

作成が成功し,プロジェクト一覧に表示されました:

参考情報

今回 Cloud Foundry 上に構築した Redmine はほぼ問題なく使えるのですが,添付ファイル等がローカル・ファイルシステムに保存されるため,再起動したりスケールアウトした時に添付ファイル等へのアクセスに問題が生じます。これを回避するために,添付ファイル等をオブジェクト・ストレージに保存するという方法があります。

今回は検証環境にオブジェクト・ストレージが用意できなかったため試してみることができませんでしたが,ご要望があれば別途試してみたいと思います。

今回使用したソフトウェア

2015-07-23

Stackedit を Cloud Foundry で動かす

本日の第35回「Cloud Foundry百日行」は Stackedit です。

最近、Markdown 記述でドキュメントを作成する機会が増えてきていますね。第32日目でも紹介しているのですが、今回は別のソフトウェアを試してみます。

ある製品のドキュメント作成プロジェクトに短期間参加した時にも、ドキュメントは Markdownで書くから宜しくね!と言われて、とうとう製品開発にも利用するようになったかと思うほど普及してきているのを実感しました。普段は、Orion Editor を使って書くのですが、Stackedit も簡単に使えるのでよいと思っています。

基本情報

 

デプロイ

1. ソースコードの入手

GitHub からソースコードを入手し、該当ディレクトリに移動します。

$ git clone https://github.com/benweet/stackedit.git
$ cd stackedit

2.Cloud Foundry 上で動かすための準備

ドキュメントをみると、Node.js のパッケージの取得、UI パッケージ (bower) の取得、および実行コマンドの起動という手順になっていました。Node.js パッケージの取得は Cloud Foundry のビルドパックが実行するので、UI パッケージだけダウンロードしておきます。

$ bower install 

ポートは PORT 環境変数を読み取ってくれるので、Cloud Foundry で動かすためにありがちなPORT設定は必要なさそうです。それでは Cloud Foundry にデプロイしてみます。今回は Bluemix を利用しました。

3.Cloud Foundry にデプロイ

Cloud Foundry へのデプロイも簡単です。

$ cf push stackedit -n amntstackedit -c "node server.js" -m 256M
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: amntstackedit.mybluemix.net
last uploaded: Fri Jul 17 02:51:53 UTC 2015
stack: lucid64

state     since                    cpu    memory           disk           details
#0   running   2015-07-17 11:53:48 AM   0.0%   184.3M of 256M   281.7M of 1G

正常に起動しました。

動作確認

デプロイしたアプリケーションにアクセスするとかっこいい画面が出てきます。同時にアプリケーションの使い方ガイドも含まれています。親切設計ですね!


一番上に “Start writing Now!” ボタンがあるので、それをクリックします。するとテンプレートが展開されるようです。すべて削除し、簡単なドキュメントを作成してみます。



Stackedit は MathJax を取り入れているので、LaTex で数式をかけます。簡単なシーケンス図もかけるようです。




このアプリケーションはWEBブラウザのローカルストレージを利用しているようなので、オフラインでも動作するのが特徴です。ためしに、ネットワークを切っても作業ができました。

また、Google Drive / Dropbox 連携などができるようです。キーを取得すればできそうですが執筆時点では動作検証ができませんでした。

おまけ

おそらく皆さんも経験があると思いますが、クラウド事業者が提供するブログを編集していると思ったとおりのレイアウトにならない場合が多いです。そこで HTML ファイルを直接いじることになるのですが、下手をするとレイアウトが破壊されてしまうことがあります。悪いことに、編集モードに戻って、再度 HTML ファイルを見てみると余計なコードが入っていたります。フォントが小さくなったり、文書の部分に <code> タグが入ってしまったりします。

この百日行に参加中に、どのように記事を書いているか?という質問をしたところ、Markdown で記述して、html に変換しているという回答を得ました。残念ながら、普段使っている Orion Editor では .html ファイルへの export 機能がないので、export 機能を探っていたところ Stackedit ができそうだとたどり着きました。使ってみると便利そうなので、Orion / Stackedit の両者を有効活用して開発生産性があがるのではないかと期待しています。ちなみにこの記事も Stackedit で書いてみました。

今回使用した環境

2015-07-22

Shaarli を Cloud Foundry で動かす

「Cloud Foundry 百日行」第34日目は,シンプルなソーシャルブックマーク Shaarli です。

基本情報

手順の概要は以下の通りです。

  • 1) ソースコードの取得
  • 2) Cloud Foundry 環境へのプッシュ
  • 3) 動作確認
  • 4) 補足

1. ソースコードの取得

$ git clone https://github.com/sebsauvage/Shaarli/
$ cd Shaarli/
Shaarli$ ls
COPYING  images  inc  index.php  README.md  tpl

いつも通りです。PHPのアプリですね。

2. Cloud Foundry 環境へのプッシュ

公式サイトのドキュメントに従って起動に必要な設定を追加していきます。

Shaarli$ mkdir sessions
Shaarli$ vi sessions/.htaccess
php 1
SetEnv PHP_VER 5

モジュールの追加などは必要なさそうなのでこのままPushしても大丈夫そうです。

Shaarli$ cf push shaarli
(一部略)
-----> Uploading droplet (17M)


1 of 1 instances running

App started


OK

App shaarli was started using this command `$HOME/.bp/bin/start`

Showing health and status for app shaarli in org ukaji / space default as ukaji...
OK

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: shaarli.10.244.0.34.xip.io
last uploaded: Tue Jul 21 06:23:57 UTC 2015
stack: cflinuxfs2
buildpack: PHP

     state     since                    cpu    memory        disk      details   
#0   running   2015-07-21 03:24:16 PM   1.5%   28M of 256M   0 of 1G

起動しました。

3. 動作確認

ブラウザーからアプリにアクセスします。
ユーザの登録を行いログインをしましょう。

メインのページはこちら。

ブックマークリンクの追加は、上部メニューのAdd linkから行います。

登録をしたいページのURLをそのまま貼り付け、

ページの説明やタグ等を追記すれば完了です。

ちなみに上部メニューのToolsにあるShaare linkボタンをブラウザのツールバーなどに配置し、お気に入り登録ボタン感覚でページを追加することも可能です。

登録後のメニュー画面はこのようになります。

Shaarliではブックマークしたページを、画像として一覧することができます。
ブックマークが集まってきたところで上部メニューのPicture wallを押すと・・・

このようにページ内の画像や動画のサムネイルなどを見ることができます。(自力でこれだけの数を登録するのは骨だったためこちらの画像は公式サイトのサンプル画像をお借りしました。)

タイトルやタグだけでなく画像情報からもリンクを探すことができるのは中々に便利そうです。

4. 補足

さて、このアプリ、起動がお手軽なのは良いのですが、DBを使わない仕様となっているためデータの永続性がありません。
つまり再起動をかけると登録したブックマークなどのデータが消えてしまいます。
公式でもその点は意識をしているようで、 公式サイトのドキュメント にもバックアップの取り方が記載されています。

1つ目は、アプリケーションの中にある data/datastore.php というファイルをバックアップしておき、そのファイルを再配置することで復元を図る方法です。
Cloud Foundry環境にあるファイルを取得してくる方法については、 第24日目の記事 でも触れた cf files というコマンドが有効です。

Shaarli$ cf files shaarli app/htdocs/data/datastore.php
Getting files for app shaarli in org ukaji / space default as ukaji...
OK

<?php /* lZNbTxNBFMe/yrA++CLb3W4vdAghgcSERFOwJYIvZLo7Zcdud5adXWoxJOzWNzAajBijPhgTqtx80igvfpgRUr+FZ1pASyDGyWZ2LufMOef3nyHYwo8FNvNYyxqmaZTM3JJZMqBp4wQX1B5sRSzyqDYucDaHtYpLSOgxNIoErQkSr5Jlqvs0UvsW1uLQU6O8gTU3igKcyQybZVqswTIOb8R64AaTzJmAHxaDQ5WnaWLNocIOWRAx7qulMQh7n3o2b1IUcXSewQiqukwg+Aiqcd5okrChoypH1GER4iFyqEcjipr0FmrzGDVjEaE6C6H3+DLzdXV2EWtByFZJBAUybKhoWPOY33D6S9ewERhSisiy6FuUsMYD6gsehzZFgtejFgnBef2St1HM5bPZK8lagO5uG5DaIY2QiOJ6Xdd1gDxLRERrkCxUP4y4dC3iQPlkJsdyVq6WLZasgm2UCrnSjdq94oJYcHmV3bFXpoO5WrMcTDErXMmurc3NT+fdqZnig6pZth6NTVwjRQFqrVRcaCMjCM3cbAL7M35IUUPc99posTyPbOJDOVRHi4BeTS7UAA35FezNf7A/pzfMHlIcQOsz+5t53ihmzSWzYBrm1bc5B/GnPR476DaPfSdsy6R7crgrk69yI5klpCKTI5m8kskzmfzoHbw5Oe5euuTZCwVarZYuPAa0XJC+r8JDYqtbAWoQMVrn4WiNwqXzaSiuYWuqRyM7O7LzSXY6Mv0sO2/PBun3k60dmbyU6ZZMIMMnqt9ITl8fnR596e1uynRTJi9k8lEmYNCVyVOZ7IHZWRU/vx32jvdlug0+5YpaGWyAYQquwxDS7V/vt07fPZfJfq/7obd3IDfS/3kpw9yH1LJgbKtg9UEs1J8gRQj9oQcirv8G */ ?>

再配置については所定の位置に上記のファイルを置き、デプロイを行えばOKです。

2つ目は、アプリのToolメニュー内にあるExport機能、Import機能を使うものです。ワンボタンで状態の保存及び復元ができるため、ブラウザでアプリを閲覧可能な環境であればこちらの方が便利そうです。

さて、このようにDBを使わずファイルベースで状態を保持しているアプリケーションをCloud Foundryで動かす時には、もう一つ重要な事を意識しておく必要があります。
それは、アプリケーションのスケールアウトが正しく行われない、という事です。
スケールアウトを行えばその時の状態ごと複製してくれるように思えますが、実は複製したアプリケーションは初期状態のものとなってしまいます。つまりアプリの各インスタンスによって保持している状態が異なってしまうため、アプリを正しく使うことはできません。

このアプリケーションに限っては、スケールアウトをする必要に迫られないように、負荷が増えすぎない状態で利用をするべきでしょう。

おまけ

さて、PHPのbuildpackはユーザが設定可能なオプションがかなり豊富に用意されており、それに応じて様々な環境を用意することができます。
モジュールの追加、パッケージ管理ツールの適用、バイナリファイルの実行などラインナップは色々で、過去の記事で触れているものもいくつか存在します。

今回はおまけとしてあまり記事の中では触れられなさそうなオプションを試してみます。

A. WebサーバをApacheからNginxにしてみる

PHPのBuildpackで起動しているWebサーバはデフォルトの状態だとApacheなのですが、実はNginxに切り換えることも可能です。
設定ファイルを調整する必要が出た時などは自分の得意な方を選択するとスムーズかもしれません。

設定方法はこちら。

Shaarli$ mkdir .bp-config
Shaarli$ vi .bp-config/options.json
{
        "WEB_SERVER": "nginx"
}

たったこれだけです。後はいつも通りpushをするだけ。お手軽ですね。

B. アプリ起動前にコマンドを動かしてみる

タイトルそのままですがアプリケーション起動前に指定のコマンドを動かすオプションです。
設定方法はこちら。先の設定に追記します。

Shaarli$ mkdir .bp-config
Shaarli$ vi .bp-config/options.json
{
        "WEB_SERVER": "nginx",
        "ADDITIONAL_PREPROCESS_CMDS": "echo -----; printenv; echo -----;"
}

アプリの起動前に環境変数を出力するというだけのコマンドを動かしてみます。

先程のNginxを設定も込みでデプロイを行いましょう。

Shaarli$ cf push shaarli-nginx
(一部略)
App started


OK

App shaarli-nginx was started using this command `$HOME/.bp/bin/start`

Showing health and status for app shaarli-nginx in org ukaji / space default as ukaji...
OK

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: shaarli-nginx.10.244.0.34.xip.io
last uploaded: Tue Jul 21 07:57:45 UTC 2015
stack: cflinuxfs2
buildpack: PHP

     state     since                    cpu    memory          disk      details   
#0   running   2015-07-21 04:58:00 PM   1.3%   27.4M of 256M   0 of 1G 

起動しました。
ログを見てみます。

(一部略)
2015-07-21T16:57:53.87+0900 [STG/0]      OUT Finished: [2015-07-21 07:57:53.872908]
2015-07-21T16:57:56.24+0900 [STG/0]      OUT -----> Uploading droplet (17M)
2015-07-21T16:57:57.84+0900 [DEA/0]      OUT Starting app instance (index 0) with guid d13a2847-c762-48bf-8990-fc87fb816b4c
2015-07-21T16:57:59.85+0900 [App/0]      OUT -----
2015-07-21T16:57:59.85+0900 [App/0]      OUT CF_INSTANCE_ADDR=10.244.0.26:61516
2015-07-21T16:57:59.85+0900 [App/0]      OUT TMPDIR=/home/vcap/tmp
2015-07-21T16:57:59.85+0900 [App/0]      OUT PHPRC=/home/vcap/app/php/etc
2015-07-21T16:57:59.85+0900 [App/0]      OUT VCAP_APP_PORT=61516
2015-07-21T16:57:59.85+0900 [App/0]      OUT USER=vcap
2015-07-21T16:57:59.85+0900 [App/0]      OUT LD_LIBRARY_PATH=:/home/vcap/app/php/lib
2015-07-21T16:57:59.85+0900 [App/0]      OUT PATH=/bin:/usr/bin:/home/vcap/app/php/bin:/home/vcap/app/php/sbin
2015-07-21T16:57:59.85+0900 [App/0]      OUT PWD=/home/vcap
2015-07-21T16:57:59.85+0900 [App/0]      OUT LANG=en_US.UTF-8
2015-07-21T16:57:59.85+0900 [App/0]      OUT CF_INSTANCE_PORT=61516
2015-07-21T16:57:59.85+0900 [App/0]      OUT CF_INSTANCE_IP=10.244.0.26
2015-07-21T16:57:59.85+0900 [App/0]      OUT VCAP_SERVICES={}
2015-07-21T16:57:59.85+0900 [App/0]      OUT CF_INSTANCE_INDEX=0
2015-07-21T16:57:59.86+0900 [App/0]      OUT SHLVL=1
2015-07-21T16:57:59.86+0900 [App/0]      OUT HOME=/home/vcap/app
2015-07-21T16:57:59.86+0900 [App/0]      OUT PYTHONPATH=/home/vcap/app/.bp/lib
2015-07-21T16:57:59.86+0900 [App/0]      OUT CF_INSTANCE_PORTS=[{"external":61516,"internal":61516}]
2015-07-21T16:57:59.86+0900 [App/0]      OUT PORT=61516
2015-07-21T16:57:59.86+0900 [App/0]      OUT VCAP_APP_HOST=0.0.0.0
2015-07-21T16:57:59.86+0900 [App/0]      OUT CF_PROCESS_TYPE=web
2015-07-21T16:57:59.86+0900 [App/0]      OUT MEMORY_LIMIT=256m
2015-07-21T16:57:59.86+0900 [App/0]      OUT _=/usr/bin/printenv
2015-07-21T16:57:59.86+0900 [App/0]      OUT -----
2015-07-21T16:57:59.92+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: using the "epoll" event method
2015-07-21T16:57:59.92+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: nginx/1.6.2
2015-07-21T16:57:59.92+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: built by gcc 4.8.2 (Ubuntu 4.8.2-19ubuntu1) 
2015-07-21T16:57:59.93+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: OS: Linux 3.16.0-41-generic
2015-07-21T16:57:59.93+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: getrlimit(RLIMIT_NOFILE): 16384:16384
2015-07-21T16:57:59.93+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: start worker processes
2015-07-21T16:57:59.93+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: start worker process 39
2015-07-21T16:57:59.93+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: start worker process 40
2015-07-21T16:57:59.93+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: start worker process 41
2015-07-21T16:57:59.93+0900 [App/0]      OUT 07:57:59 nginx   | 2015/07/21 07:57:59 [notice] 35#0: start worker process 42
2015-07-21T16:57:59.95+0900 [App/0]      OUT 07:57:59 php-fpm | [21-Jul-2015 07:57:59] NOTICE: fpm is running, pid 36
2015-07-21T16:57:59.95+0900 [App/0]      OUT 07:57:59 php-fpm | [21-Jul-2015 07:57:59] NOTICE: ready to handle connections
(一部略)

アプリの起動前に printenv が動いているのがわかります。難しいアプリケーションを手探りで動かす時には心強い味方になってくれそうです。
よく見るとWebサーバもNginxが動いてくれていますね。

今回使用したソフトウェア

2015-07-21

Simple Machines Forum を Cloud Foundry で動かす

「Cloud Foundry 百日行」第33日目は,PHP 製の BBS アプリ Simple Machines Forum です。 AlternativeTo で見ると,同種の OSS の中では比較的人気が高いもののようです。開発も活発に継続しているようで,最新安定版が今年(2015年)の4月に出ています。

基本情報

手順の概要は以下の通りです。

  • 1) ソースコードの取得・展開
  • 2) PHP 拡張の設定
  • 3) データベース・サービスの作成・バインドとアプリのプッシュ
  • 4) インストール・ウィザード
  • 5) 動作確認
  • 6) 初期設定の保存&動作確認

1. ソースコードの取得・展開

$ wget http://download.simplemachines.org/index.php/smf_2-0-10_install.tar.bz2
$ mkdir smf
$ cd smf
$ tar xjf ../smf_2-0-10_install.tar.bz2

落としてきた tarball は,展開すると今居るディレクトリーにファイルが展開されるので要注意です。あらかじめ空のディレクトリーを作り,そこに移動してから展開するほうが良いと思います。

2. PHP 拡張の設定

http://wiki.simplemachines.org/smf/SMF2.0:Requirements_and_recommendations を参考に,必要な PHP 拡張を .bp-config/options.json に記していきます。この作業については,以前 別の PHP アプリ の記事でも書いたので,詳細はそちらをご覧ください。

今回の options.json は,けっきょく以下のようになりました。

$ cat .bp-config/options.json
{
    "PHP_EXTENSIONS": ["mysql", "mysqli", "gd", "mbstring"]
}

データベースとして MySQL を使うのでそれ関係の拡張を追加し,上記のドキュメントに GD が必要と書かれていたのでそれも追加,さらに日本語の入力にたぶん必要だろうということで Multibyte String の拡張も追加しました。

3. データベース・サービスの作成・バインドとアプリのプッシュ

いよいよ Cloud Foundry 上の操作に入っていきます。

まずはデータベース・サービスの作成です。 この文書 によると,本アプリは MySQL, PostgreSQL, SQLite に対応しているということですが,PHP と組み合わせるなら安定の MySQL だろうということで MySQL にします。

$ cf create-service p-mysql 100mb my4smf
Creating service instance my4smf in org nota-ja / space 100 as nota-ja...
OK

次に,アプリを停止状態で Cloud Foundry にプッシュします。

$ cf push smf --no-start
Creating app smf in org nota-ja / space 100 as nota-ja...
OK
..
Done uploading
OK

アプリと MySQL サービスをバインドします。

$ cf bind-service smf my4smf
Binding service my4smf to app smf in org nota-ja / space 100 as nota-ja...
OK
TIP: Use 'cf restage smf' to ensure your env variable changes take effect

アプリを起動すると,ステージングが行われてアプリが起動します。

$ cf start smf
Starting app smf in org nota-ja / space 100 as nota-ja...
..
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: smf.10.244.0.34.xip.io
last uploaded: Thu Jul 16 12:01:44 UTC 2015
stack: cflinuxfs2
buildpack: PHP

     state     since                    cpu    memory          disk      details
#0   running   2015-07-16 09:02:34 PM   1.5%   31.8M of 256M   0 of 1G

無事起動しました。

4. インストール・ウィザード

起動後の最初の画面はこうなっています。

PHP のアプリではおなじみのインストール・ウィザードです。
以下,全て説明するのは面倒なので,キーになる画面だけ解説します。

【Step 3】で,データベース接続情報の入力を求められますので,先ほどバインドしたサービスの情報を cf env で取得して入力します。

$ cf env smf
Getting env variables for app smf in org nota-ja / space 100 as nota-ja...
OK

System-Provided:
{
 "VCAP_SERVICES": {
  "p-mysql": [
   {
    "credentials": {
     "hostname": "10.244.7.6",
     "jdbcUrl": "jdbc:mysql://10.244.7.6:3306/cf_06aed91b_25f5_46b8_a13b_5b683325cec9?user=8kV9FdQJsJ6pX60R\u0026password=woXGzdBNGf7mQGhg",
     "name": "cf_06aed91b_25f5_46b8_a13b_5b683325cec9",
     "password": "woXGzdBNGf7mQGhg",
     "port": 3306,
     "uri": "mysql://8kV9FdQJsJ6pX60R:woXGzdBNGf7mQGhg@10.244.7.6:3306/cf_06aed91b_25f5_46b8_a13b_5b683325cec9?reconnect=true",
     "username": "8kV9FdQJsJ6pX60R"
    },
    "label": "p-mysql",
    "name": "my4smf",
    "plan": "100mb",
    "tags": [
     "mysql"
    ]
   }
  ]
 }
}
..

【Step 4】の【Forum URL】には,Cloud Foundry 上にデプロイしたアプリのURLを入力してください。

【Step 6】でもデータベース・パスワードの入力を求められますが,【Step 3】と同じ値を入れればOKです。

5. 動作確認

インストール・ウィザードの設定が正常に終わると,そこで入力した admin account でログインした状態でトップ画面に遷移します。

【General Discussion】をクリックすると,最初の投稿として Welcome メッセージが投稿されている状態になっています。

最初の投稿を読んで,返信を書いてみました。

日本語も問題ありません。

6. 初期設定の保存&動作確認

単に動かしてみるだけならここまでで良いのですが,使い続けることを考えて, SugarCRM の時 と同様,初期設定ファイルをダウンロードして保存し,プッシュ時に一緒にアップロードすることで設定の再初期化を避けることを試みます。

今回は app/htdocs/Settings.php に設定が保存されているようなので,これをダウンロードして保存します。

$ cf files smf app/htdocs/Settings.php

内容をファイルに保存して差分を取ると,先ほど入力した値が見えるので,これで間違いないようです。

$ git diff
diff --git a/Settings.php b/Settings.php
index 7a8ae98..2bc786c 100644
--- a/Settings.php
+++ b/Settings.php
@@ -18,18 +18,18 @@ $mtitle = 'Maintenance Mode';               # Title for the Maintenance Mode message.
 $mmessage = 'Okay faithful users...we\'re attempting to restore an older backup of the database...news will be

 ########## Forum Info ##########
-$mbname = 'My Community';              # The name of your forum.
+$mbname = 'Cloud Foundry';             # The name of your forum.
 $language = 'english';         # The default language file set for the forum.
-$boardurl = 'http://127.0.0.1/smf';            # URL to your forum's folder. (without the trailing /!)
-$webmaster_email = 'noreply@myserver.com';             # Email address to send emails from. (like noreply@yourd
-$cookiename = 'SMFCookie11';           # Name of the cookie to set for authentication.
+$boardurl = 'http://smf.10.244.0.34.xip.io';           # URL to your forum's folder. (without the trailing /!)
+$webmaster_email = '3bgmhc@ahk.jp';            # Email address to send emails from. (like noreply@yourdomain.co
+$cookiename = 'SMFCookie67';           # Name of the cookie to set for authentication.

 ########## Database Info ##########
 $db_type = 'mysql';
-$db_server = 'localhost';
-$db_name = 'smf';
-$db_user = 'root';
-$db_passwd = '';
+$db_server = '10.244.7.6';
+$db_name = 'cf_06aed91b_25f5_46b8_a13b_5b683325cec9';
+$db_user = '8kV9FdQJsJ6pX60R';
+$db_passwd = 'woXGzdBNGf7mQGhg';
 $ssi_db_user = '';
 $ssi_db_passwd = '';
 $db_prefix = 'smf_';
@@ -38,18 +38,14 @@ $db_error_send = 1;

 ########## Directories/Files ##########
 # Note: These directories do not have to be changed unless you move things.
-$boarddir = dirname(__FILE__);         # The absolute path to the forum's folder. (not just '.'!)
-$sourcedir = dirname(__FILE__) . '/Sources';           # Path to the Sources directory.
-$cachedir = dirname(__FILE__) . '/cache';              # Path to the cache directory.
+$boarddir = '/home/vcap/app/htdocs';           # The absolute path to the forum's folder. (not just '.'!)
+$sourcedir = '/home/vcap/app/htdocs/Sources';          # Path to the Sources directory.
+$cachedir = '/home/vcap/app/htdocs/cache';             # Path to the cache directory.

 ########## Error-Catching ##########
 # Note: You shouldn't touch these settings.
 $db_last_error = 0;

-if (file_exists(dirname(__FILE__) . '/install.php'))
-{
-       header('Location: http' . (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 's' : ''
-}

 # Make sure the paths are correct... at least try to fix them.
 if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . '/agreement.txt'))
@@ -59,4 +55,5 @@ if (!file_exists($sourcedir) && file_exists($boarddir . '/Sources'))
 if (!file_exists($cachedir) && file_exists($boarddir . '/cache'))
        $cachedir = $boarddir . '/cache';

-?>
\ No newline at end of file
+$db_character_set = 'utf8';
+?>

さらに, install.php を削除します。

$ git rm install.php
rm 'install.php'

これを削除しないと,再起動後にアプリにアクセスした後,警告付きで再初期化しようとしたり,トップ画面に警告が表示されたりすることがあります。

この状態でアプリを更新します。

$ cf push smf
Updating app smf in org nota-ja / space 100 as nota-ja...
OK
..
     state     since                    cpu    memory          disk      details
#0   running   2015-07-17 10:25:43 PM   1.6%   31.7M of 256M   0 of 1G

再ステージング・再起動が行われますが,ブラウザーのトップ画面をリロードしても,インストール・ウィザードは実行されませんでした。

今回使用したソフトウェア