オープンソースの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-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

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

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

2015-07-17

Markdown Previewer を Cloud Foundry で動かす

「Cloud Foundry 百日行」第32日目は,Markdown Previewerです。
最近はブラウザ上でMarkdownのプレビューを見ながら編集したりといったエディタアプリがたくさんあるようですが、今回のアプリは手元で作成したMarkdownFileをブラウザにドラッグするとプレビューとHTMLの出力が得ることが出来ます。

基本情報

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

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

1. ソースコードの取得

$ git clone https://github.com/komiya-atsushi/markdown-previewer.git
$ cd markdown-previewer
$ ls
css  img  index.html  js  README.md  swf

アプリ作者の方が Node.js の環境やら Ruby の環境やらを整えたくないといったモチベーションで作られたそうなので Staticfile_buildpack のみで行けます。

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

そのままではBuildpackの選択が出来ずにエラーになってしまうので Staticfile を作成します。

$ touch Staticfile

では cf push しましょう。

$ cf push mark -m 64m  
:
requested state: started  
instances: 1/1
usage: 64M x 1 instances
urls: mark.10.244.0.34.xip.io
last uploaded: Fri Jul 17 00:49:50 UTC 2015
stack: cflinuxfs2
buildpack: Static file
 
     state     since                    cpu    memory        disk      details   
#0   running   2015-07-17 09:49:58 AM   0.0%   5.1M of 64M   0 of 1G      

アプリが起動しました。

3. 動作確認

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

アクセスしたら手元で作成したMarkdownFileを Drop your markdown file here に投げ込みます。
表示されました。

画面左に目次も自動作成されています。
では次に HTMLタブ を開いて見ましょう。

HTMLに変換されたソースが表示されます。
右上の CopyHTML ボタンをクリックするとクリップボードにソースがコピーされます。
問題なく動作していそうですね。

余談

この百日行を通して初めてMarkdownで文章を書くことになった為、なんとか簡単にプレビューを見れないかといろいろ探していて見つけたのがこのアプリです。いろんな機能を持ったアプリはあるのですが、プレビューを簡単に見れてHTML出力してくれて、そして何と言ってもブラウザ上でページを誤って移動していちからやり直しの危険性が無いのが気に入っています。

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

2015-07-16

GoBB を Cloud Foundry で動かす

「Cloud Foundry 百日行」第31日目は,Go 言語で書かれた BBS アプリ GoBB です。私の記憶に間違いがなければ,百日行シリーズ初の Golang アプリです。

基本情報

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

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

1. ソースコードの取得

$ git clone https://github.com/stevenleeg/gobb.git
..
$ cd gobb/

2. ソースコードの変更

このアプリは,少し古い(最終更新が2014年8月)ということもあって,けっこう手を加えないと Cloud Foundry 上で動作しませんでした。

ソースコード変更の概要は以下の通りです。

  • a) .godir の追加
  • b) 待ち受けポートを環境変数からも設定できるよう変更
  • c) データベース接続情報を環境変数からも設定できるよう変更
  • d) Go 言語のコンパイル環境がなくても動作するよう変更
  • e) admin_topbar の復活 (Cloud Foundry とは無関係な変更)
  • f) .cfignore の追加

ステップ数は多いのですが,一つ一つのステップの内容は割とシンプルです。

手順を辿るのが面倒な方は, 修正後のコード を GitHub 上に置いたので,そちらをご利用ください。

a) .godir の追加

Cloud Foundry の go-buildpack では,アップロードされたソースコードを依存関係も含めてコンパイルするための方法として,.godir と Godep の2つが用意されています。

https://github.com/cloudfoundry/go-buildpack/tree/v1.3.1#godir-and-godeps

上記 README に書かれている通り,.godir は obsolete な方法で,Godep を使うのが新しい&正しい方法なのですが,本アプリは Godep を使って書かれていないため,今回は .godir を使う事にしました。

$ git show 1af161d
commit 1af161d4d27685aab467be1c182177bb2ab186ab
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Thu Jul 16 11:43:55 2015 +0900

    Set .godir to use with Cloud Foundry go-buildpack

diff --git a/.godir b/.godir
new file mode 100644
index 0000000..68848f4
--- /dev/null
+++ b/.godir
@@ -0,0 +1 @@
+github.com/stevenleeg/gobb

.godir には,ソースコードをアップロードしたものと置き換える対象となるパッケージ・パスを記述します。本アプリでは上に示したように github.com/stevenleeg/gobb となっていますが,こう指定すると $GOPATH/src/github.com/stevenleeg/gobb が,アップロードしたソースコードと置き換えられます。

これを指定しない場合, go get で取得された https://github.com/stevenleeg/gobb のソースコードがそのまま使われるため,次節以降の変更が反映されず,アプリが正常に動作しなくなります。

b) 待ち受けポートを環境変数からも設定できるよう変更

次に,待ち受けポートを環境変数からも設定できるようにソースコードを変更します。

オリジナルのコードでは,アプリの待ち受けポートは設定ファイルで指定するようになっているのですが,Cloud Foundry 上ではシステムが指定する待ち受けポートが動的に変わるので,このままでは動かすのが大変です。そこで,環境変数 PORT が設定されている場合はそちらを待ち受けポートとして使うよう,コードを変更しました。

$ git show 1dc5161
commit 1dc516197a827cbfa343ad235984a274324e2807
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Fri Jan 23 01:06:07 2015 +0900

    Respect PORT env

    Making it easy to run on PaaS.

diff --git a/gobb/main.go b/gobb/main.go
index 1440a51..73aba4e 100644
--- a/gobb/main.go
+++ b/gobb/main.go
@@ -10,6 +10,7 @@ import (
        "github.com/stevenleeg/gobb/utils"
        "go/build"
        "net/http"
+       "os"
        "path/filepath"
 )

@@ -82,7 +83,10 @@ func main() {

        http.Handle("/", r)

-       port, err := config.Config.GetString("gobb", "port")
+       port := os.Getenv("PORT")
+       if port == "" {
+               port, _ = config.Config.GetString("gobb", "port")
+       }
        fmt.Println("[notice] Starting server on port " + port)
        http.ListenAndServe(":"+port, nil)
 }

PORT が設定されていない場合は,従来通り設定ファイルで指定されたポートを使うようになっています。

c) データベース接続情報を環境変数からも設定できるよう変更

次は,同様の修正をデータベース接続情報についても行います。

環境変数 DATABASE_URL が設定されている場合はそちらをDB接続情報として使い,設定されていない場合は従来通り設定ファイルの値を使うよう,コードを変更しました。

$ git show 14afe27
commit 14afe276d83d11d5b938525f1605daad8dd89ab5
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Thu Jan 22 21:15:10 2015 +0900

    Respect DATABASE_URL env

    Making it easy to run on PaaS.

diff --git a/models/connection.go b/models/connection.go
index d43a177..9405eb0 100644
--- a/models/connection.go
+++ b/models/connection.go
@@ -37,13 +37,17 @@ func GetDbSession() *gorp.DbMap {
                db_port = "5432"
        }

-       db, err := sql.Open("postgres",
-               "user="+db_username+
-                       " password="+db_password+
-                       " dbname="+db_database+
-                       " host="+db_hostname+
-                       " port="+db_port+
-                       " sslmode=disable")
+       data_source := os.Getenv("DATABASE_URL")
+       if data_source == "" {
+               data_source = "user=" + db_username +
+                       " password=" + db_password +
+                       " dbname=" + db_database +
+                       " host=" + db_hostname +
+                       " port=" + db_port +
+                       " sslmode=disable"
+       }
+
+       db, err := sql.Open("postgres", data_source)

        if err != nil {
                fmt.Printf("Cannot open database! Error: %s\n", err.Error())
diff --git a/utils/migrations.go b/utils/migrations.go
index 122acbb..c0bcf70 100644
--- a/utils/migrations.go
+++ b/utils/migrations.go
@@ -40,12 +40,17 @@ func generateGooseDbConf() *goose.DBConf {
                db_port = "5432"
        }

+       openstr := os.Getenv("DATABASE_URL")
+       if openstr == "" {
+               openstr = fmt.Sprintf("user=%s dbname=%s password=%s port=%s host=%s sslmode=disable", db_userna
+       }
+
        goose_conf = &goose.DBConf{
                MigrationsDir: migrations_path,
                Env:           "development",
                Driver: goose.DBDriver{
                        Name:    "postgres",
-                       OpenStr: fmt.Sprintf("user=%s dbname=%s password=%s port=%s host=%s sslmode=disable", db
+                       OpenStr: openstr,
                        Import:  "github.com/lib/pq",
                        Dialect: &goose.PostgresDialect{},
                },

d) Go 言語のコンパイル環境がなくても動作するよう変更

本アプリのコードは,なぜか Go 言語のコンパイル環境がないと実行できないようになっています。Go 言語のバイナリは静的リンクで生成され,ライブラリーの動的リンクを必要としません。本来,生成されたバイナリ・ファイル1つを持って行けば(バイナリ五感の環境なら)どこでも動く点が Go の売りの一つなので,これはやや本末転倒感があります。

そこで,環境変数 RUN_WITHOUT_GOENV が真であれば,Go 言語のコンパイル環境なしでも動くよう,コードを変更しました。環境変数 RUN_WITHOUT_GOENV が未設定または偽であれば,従来通りの動作となります。

$ git show ec23f4a
commit ec23f4a7730b64a8011f4ab7f27127859fcfb705
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Sun Jan 25 16:24:02 2015 +0900

    Run without Golang environment dependency

diff --git a/gobb/main.go b/gobb/main.go
index 73aba4e..7b6bc63 100644
--- a/gobb/main.go
+++ b/gobb/main.go
@@ -69,6 +69,9 @@ func main() {
        if selected_template == "default" {
                pkg, _ := build.Import("github.com/stevenleeg/gobb/gobb", ".", build.FindOnly)
                static_path := filepath.Join(pkg.SrcRoot, pkg.ImportPath, "../templates")
+               if utils.RunWithoutGoenv {
+                       static_path = filepath.Join(utils.Buildpath, "templates")
+               }
                r.PathPrefix("/static/").Handler(http.FileServer(http.Dir(static_path)))
        } else {
                static_path := filepath.Join(base_path, "templates", selected_template)
diff --git a/utils/migrations.go b/utils/migrations.go
index c0bcf70..173983b 100644
--- a/utils/migrations.go
+++ b/utils/migrations.go
@@ -24,6 +24,9 @@ func generateGooseDbConf() *goose.DBConf {
        db_hostname, _ := config.Config.GetString("database", "hostname")
        db_port, _ := config.Config.GetString("database", "port")
        migrations_path := filepath.Join(pkg.SrcRoot, pkg.ImportPath, "../db/migrations")
+       if RunWithoutGoenv {
+               migrations_path = filepath.Join(Buildpath, "db", "migrations")
+       }

        db_env_hostname, _ := config.Config.GetString("database", "env_hostname")
        db_env_port, _ := config.Config.GetString("database", "env_port")
diff --git a/utils/path.go b/utils/path.go
new file mode 100644
index 0000000..00169fb
--- /dev/null
+++ b/utils/path.go
@@ -0,0 +1,23 @@
+package utils
+
+import (
+       "log"
+       "os"
+       "path/filepath"
+       "strconv"
+)
+
+var RunWithoutGoenv bool = func() bool {
+       run_without_goenv, _ := strconv.ParseBool(os.Getenv("RUN_WITHOUT_GOENV"))
+       return run_without_goenv
+}()
+
+var Buildpath string = func() string {
+       // Assume the executable exists directly under BUILDPATH/bin
+       buildpath, err := filepath.Abs(filepath.Join(filepath.Dir(os.Args[0]), ".."))
+       if err != nil {
+               log.Fatal(err)
+       }
+       log.Printf("BuildPath buildpath='%v'\n", buildpath)
+       return buildpath
+}()
diff --git a/utils/template.go b/utils/template.go
index f7c36f1..4917702 100644
--- a/utils/template.go
+++ b/utils/template.go
@@ -128,6 +128,9 @@ func RenderTemplate(
        if selected_template == "default" {
                pkg, _ := build.Import("github.com/stevenleeg/gobb/gobb", ".", build.FindOnly)
                base_path = filepath.Join(pkg.SrcRoot, pkg.ImportPath, "../templates/")
+               if RunWithoutGoenv {
+                       base_path = filepath.Join(Buildpath, "templates")
+               }
        } else {
                base_path, _ = config.Config.GetString("gobb", "base_path")
                base_path = filepath.Join(base_path, "templates", selected_template)

e) admin_topbar の復活 (Cloud Foundry とは無関係な変更)

次に,これは動作確認中に気づいた問題に対する修正なのですが, admin_topbar というテンプレート要素が削除されてしまったせいでアプリが正常に動作しないため,これを復活させました。

Cloud Foundry とは完全に無関係な修正で,普通はやらないのですが,今回は動作確認に支障を来したので特別に行いました。

$ git show 3ef8584
commit 3ef858494b3a9f1f986218610b5c849bc1d51f5a
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Sun Jan 25 16:57:34 2015 +0900

    Resurrect admin_topbar

diff --git a/templates/base.html b/templates/base.html
index f412540..759cb55 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -1,3 +1,12 @@
+
+<table class="box_tabs">
+    <tr>
+        <td><a href="/admin">General</a></td>
+        <td><a href="/admin/boards">Boards</a></td>
+        <td><a href="/admin/users">Users</a></td>
+    </tr>
+</table>
+
 <!DOCTYPE html>
 <html>
   <head>

f) .cfignore の追加

最後に,(これは必須ではないのですが) Cloud Foundry 上にアプリをプッシュする際,余分なファイルをアップロードしないよう .cfignore というファイルを作ってそこに不要なファイルを指定します。

$ git show 3d7bc1a
commit 3d7bc1a9f0960b8483529fead74af77bfde0d6d6
Author: Noburou TANIGUCHI <dev@nota.m001.jp>
Date:   Sun Jan 25 16:24:27 2015 +0900

    Add .cfignore not to upload unneccesary files to Cloud Foundry

diff --git a/.cfignore b/.cfignore
new file mode 100644
index 0000000..ed4e977
--- /dev/null
+++ b/.cfignore
@@ -0,0 +1,5 @@
+*.swp
+.DS_Store
+dbconf.yml
+templates/static/.sass-cache
+bin/gobb

.cfignore は,.gitignore と似た (同じ?) 記法で不要ファイルを指定する仕組みです。詳しくは

https://docs.cloudfoundry.org/devguide/deploy-apps/prepare-to-deploy.html#exclude

をご覧ください。

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

ここまでで必要なソースコードの修正は終わりましたので,次にアプリを Cloud Foundry 上にプッシュします。

が,実際にプッシュするその前に。

ここまで何度か出てきた「設定ファイル」を作っておきます。サンプルが ./gobb/gobb.sample.conf にあるので,それをコピーして1行だけ書き換えます。

$ cp ./gobb/gobb.sample.conf ./gobb/gobb.conf
$ emacs ./gobb/gobb.conf
..

出来上がったファイルはこちらです:

$ cat gobb/gobb.conf
;; This section deals with various gobb-specific settings
[gobb]
site_name=gobb
cookie_key=encrypt_your_cookies
posts_per_page=15
threads_per_page=30
enable_signatures=true
port=8080

;; Base URL of your site. Don't include the http:// but DO
;; include the trailing slash.
;;
;; Example: example.com/forum/
base_url=gobb.10.244.0.34.xip.io/

;; The base path is a directory that houses any custom
;; gobb things (eg, templates, css modifications, etc.)
;; This field is optional (disabled by default), but we
;; reccommend that you set it to where your config file
;; is located (keep things organized!)
;base_path=/path/to/gobb/directory

;; This section deals with the database connection. It's
;; definitely not optional, so you should fill it in now.
[database]
username=db_username
password=db_password
database=db_name
hostname=localhost
port=5432

;; These options are used for docker port redirection.
;; env_hostname=POSTGRES_PORT_5432_TCP_PORT
;; env_port=POSTGRES_PORT_5432_TCP_ADDR

;; If you have a Google Analytics account, put your information
;; in this section (optional)
[googleanalytics]
tracking_id=
account=

実際には,(上述の通り) 次の1行を書き換えただけです。

$ git diff --no-index gobb/gobb.sample.conf gobb/gobb.conf
diff --git a/gobb/gobb.sample.conf b/gobb/gobb.conf
index c9f3011..7593a07 100644
--- a/gobb/gobb.sample.conf
+++ b/gobb/gobb.conf
@@ -11,7 +11,7 @@ port=8080
 ;; include the trailing slash.
 ;;
 ;; Example: example.com/forum/
-base_url=localhost:8080/
+base_url=gobb.10.244.0.34.xip.io/

 ;; The base path is a directory that houses any custom
 ;; gobb things (eg, templates, css modifications, etc.)

本来このファイルは .gitignore に含まれていて Git 管理の対象外なのですが,一応 こちら に置いてあるので,必要な方は参考にしてください。

準備が整ったので,いよいよ Cloud Foundry の操作に入ります。

まずは,データベース・サービスを作成します。GoBB の README では PostgreSQL を使うよう書いてあるので,PostgreSQL サービスを作成します。

$ cf create-service PostgreSQL "Basic PostgreSQL Plan" pg4gobb
Creating service instance pg4gobb in org nota-ja / space 100 as nota-ja...
OK
..

次にアプリを (データベースを使うので) 非起動状態でプッシュします。

$ cf push gobb --no-start
..
OK

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

$ cf bind-service gobb pg4gobb
Binding service pg4gobb to app gobb in org nota-ja / space 100 as nota-ja...
OK

バインドすると環境変数 VCAP_SERVICES が見えるようになるので,そこから DATABASE_URL に設定する値を取得します。

$ cf env gobb | grep postgres
     "uri": "postgres://a61ab179-f1d3-4daa-9864-3f4ec9f8cc81:g06isuvj22nqng5s9ni2fs9n1@192.168.15.91:5432/a61ab179-f1d3-4daa-9864-3f4ec9f8cc81"

環境変数 DATABASE_URL を設定します。

ここで注意点が一つ。今回使う PostgreSQL は SSL/TLS 接続をサポートしていないので, DATABASE_URL を設定する時は先に取得した値の後ろに ?sslmode=disable を付ける必要があります。

 cf set-env gobb DATABASE_URL "postgres://61bda49d-5172-4938-9584-4f6624a212f6:8lapo7ba10tcv6liia9r6dmb61@192.168.15.91:5432/61bda49d-5172-4938-9584-4f6624a212f6?sslmode=disable"
Setting env variable 'DATABASE_URL' to 'postgres://61bda49d-5172-4938-9584-4f6624a212f6:8lapo7ba10tcv6liia9r6dmb61@192.168.15.91:5432/61bda49d-5172-4938-9584-4f6624a212f6?sslmode=disable' for app gobb in org nota-ja / space 100 as nota-ja...
OK
TIP: Use 'cf restage' to ensure your env variable changes take effect

環境変数 RUN_WITHOUT_GOENV を設定します。

$ cf set-env gobb RUN_WITHOUT_GOENV true
Setting env variable 'RUN_WITHOUT_GOENV' to 'true' for app gobb in org nota-ja / space 100 as nota-ja...
OK
TIP: Use 'cf restage' to ensure your env variable changes take effect

環境変数の設定を確認します。

$ cf env gobb
..
User-Provided:
DATABASE_URL: postgres://a61ab179-f1d3-4daa-9864-3f4ec9f8cc81:g06isuvj22nqng5s9ni2fs9n1@192.168.15.91:5432/a61ab179-f1d3-4daa-9864-3f4ec9f8cc81?sslmode=disable
RUN_WITHOUT_GOENV: true
..

正しく設定されています。

起動コマンドを指定して再プッシュします。--migrate は,データベースのスキーマを設定するためのオプションです。2回目以降の起動では (スキーマに変更がない限り) 不要です。

$ cf push gobb -c "bin/gobb --config gobb/gobb.conf --migrate"
Updating app gobb in org nota-ja / space 100 as nota-ja...
..
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: gobb.10.244.0.34.xip.io
last uploaded: Thu Jul 16 02:06:51 UTC 2015
stack: cflinuxfs2
buildpack: Go

     state     since                    cpu    memory         disk      details
#0   running   2015-07-16 11:08:38 AM   0.0%   4.7M of 256M   0 of 1G

起動しました。

4. 動作確認

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

最初の画面はこのようになっています:

右上の【register】をクリックしてユーザー登録画面に入り,ユーザーを登録します:

登録が済むとログイン画面に遷移するので,今登録したユーザーでログインします:

ログインすると初期画面と似た画面が出てきますが,右上が少し変化しています:

右上の真ん中の【admin】(ユーザー名 “admin” と管理画面の “admin” が被ってしまってちょっとわかりにくくなってしまいました) をクリックして,管理画面に入ります:

左上の【Boards】をクリックしてボード作成画面に入ります:

ボード名と説明を記入して【create】をクリックします:

トップ画面に戻ると,先ほど作った test ボードが見えるので,クリックして入ってみます:

ボードの画面はこんな感じです:

右上の【new thread】を押して,新しいスレッドを作ります:

スレッドが作られ,投稿が表示されました:

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