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

2015-11-09

Rundeck を Cloud Foundry で動かす

「Cloud Foundry 百日行」第95日目,今日は最近注目されているジョブ・スケジューラー Rundeck です。Webベースのジョブ・スケジューラーといえば, 92日目に webcrontab を紹介 しましたが,Rundeck はこれと比べるとずっと本格派で,最近はインフラ周りの管理に使われることも増えているようです。

基本情報

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

  • 1) Buildpack の修正
  • 2) 各種設定ファイル/スクリプトの作成
  • 3) サービスの作成とバインド及び接続設定
  • 4) アプリのデプロイ
  • 5) 動作確認

1. Buildpack の修正

JAR ファイルを使っていることからもわかるように,このアプリは Java で書かれているのですが,

という理由から, Cloud Foundry 標準の java-buildpack は使えないことがわかりました。

そこで JAR ファイルを直接起動できる buildpack を探した結果, energizedwork/heroku-buildpack-runnable-jar を見つけました。

この buildpack は,アプリケーションの JAR ファイルをステージング時にダウンロードすることが前提なので,今回はコードの取得も不要になりました。

ただ,1つだけ問題が。

この buildpack にはデプロイの事前処理を行う機能があり,さらにそれをオプションでスキップできるようになっているのですが,バグ(と思われる問題)があってスキップが正常に行えず,Rundeck の場合ステージングが正常終了しません。

そこで,スキップが正常に行えるようにコードの修正を行いました。オリジナルのバージョンは検証時点の master ブランチのヘッド ( https://github.com/energizedwork/heroku-buildpack-runnable-jar/tree/a736a0571ab050815c287cef5bdf74a3a6e59152 ) で,それとの差分は以下の(実質)1行です。

$ git diff a736a05
diff --git a/bin/compile b/bin/compile
index f729d59..fa327fb 100755
--- a/bin/compile
+++ b/bin/compile
@@ -17,6 +17,8 @@ BUILD_DIR=$1
 CACHE_DIR=$2
 ENV_DIR=$3

+source ${BUILD_DIR}/manifest.sh
+
 GRADLE_TASK=${GRADLE_TASK-stage}

 export_env_dir $ENV_DIR

この修正を施した buildpack を,

に push しました。

2. 各種設定ファイル/スクリプトの作成

2.1. Buildpack用設定ファイルの作成

前節で述べた通り,今回の buildpack では

  • アプリの JAR ファイルをダウンロードするので,その URL を指定
  • デプロイの事前処理をスキップするように設定

する必要があります。

これらの設定は manifest.sh という shell スクリプト・ファイルに書くことになっているので,最終的に以下の内容の manifest.sh ファイルを作成しました(この辺りの説明は README.md には書かれていなかったので, bin/ 下のスクリプト から直接読み解きました)。

$ cat manifest.sh
export ARTIFACT_URL=http://download.rundeck.org/jar/rundeck-launcher-2.6.1.jar
export NO_PRE_DEPLOY=true

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

今回使う buildpack はデフォルトの起動スクリプトを持たないので,Procfile もしくはコマンドで起動方法を指定する必要があります。

単に java コマンドに -jar で JAR ファイルを渡せば済む,のであればよかったのですが,残念ながらそれではメモリ設定が固定値になってしまうので,割り当てられたメモリに合わせて起動スクリプト側でメモリ設定を計算するようにしました。

計算は Cloud Foundry 標準の java-buildpack のやりかたをベースにしました。

最終的に出来上がった起動スクリプトは以下の通りです。

$ cat start.sh
#!/bin/bash

set -x

## Calculate memory actually assigned to Java process
## '* 75 / 110' comes from:
##   - https://github.com/cloudfoundry/java-buildpack-memory-calculator/blob/6a7690ba8d6ae97b0c87209f22a2ad09d6098504/memory/allocator.go#L179-L181
##   - https://github.com/cloudfoundry/java-buildpack/blob/485c4552f9465b7e4dcf0acb0bdab43087e4637b/config/open_jdk_jre.yml#L32-L37
mem=$(echo $MEMORY_LIMIT | perl -ne '/(\d+)([gmk])/; $mx = $1; $factor = $2; if ($factor eq "g") { $mx = $mx * 1024 * 1024 } elsif ($factor eq "m") { $mx = $mx * 1024 }; print (int($mx))')
heap=$(($mem * 75 / 110))
stack=$(($mem * 5 / 110))

export JAVA_TOOL_OPTIONS="-Xmx${heap} -Xss${stack} -Dfile.encoding=UTF-8"

java -Dserver.http.port=$PORT -jar application.jar

なお,このメモリ設定の計算は,本来なら buildpack 側で行うべき処理ですが,試してみたものの環境変数の引き渡しがうまくいかなかったため,今回は諦めて起動スクリプト側で計算/設定を行うことにしました。

2.3. アプリケーション・マニフェストの作成

必須ではありませんが,push 時の指定オプションが多いので,アプリケーションの各種設定をまとめたマニフェスト・ファイルも作りました。内容は以下の通りです。

$ cat manifest.yml
applications:
  - name: rundeck
    command: "./start.sh"
    buildpack: "https://github.com/nota-ja/heroku-buildpack-runnable-jar.git#cf-100-day-challenge-095"
    memory: 1536m
    services:
      - mysql-rundeck

Buildpack として1節で修正したものを,起動スクリプトとして2.2節で作成したものを指定しています。またメモリ容量は試行錯誤の結果1.5GBとしました。サービスについては,次の節で述べます。

3. サービスの作成とバインド及び接続設定

本アプリはデータの永続化に DBMS を使います。DBMS については, 公式のマニュアル で MySQL について詳しく述べてあることから,今回は MySQL を使うことにしました。

3.1. MySQLサービスの作成

manifest.yml に指定したのと同じ名前でサービスを作成します。

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

3.2. アプリのアップロード及びバインド

アプリを非起動状態で push します。manifest.yml があるので,自動的に bind まで行われます。

$ cf push --no-start
Using manifest file /home/nota-ja/repos/rundecks/rundeck-jar/manifest.yml

Creating app rundeck in org nota-ja / space 100 as nota-ja...
OK

Using route rundeck.10.244.0.34.xip.io
Binding rundeck.10.244.0.34.xip.io to rundeck...
OK

Uploading rundeck...
Uploading app files from: /home/nota-ja/repos/rundecks/rundeck-jar
Uploading 2K, 7 files
Done uploading
OK
Binding service mysql-rundeck to app rundeck in org nota-ja / space 100 as nota-ja...
OK

3.3. DBMS接続及びその他の設定ファイルの作成

本アプリは DBMS の接続情報を環境変数から取得する機能がないため,通常どおり設定ファイルから読み込めるよう,ファイルを作成する必要があります。 公式サイトのマニュアル の記述に沿って,server/config/rundeck-config.properties というファイルを作成し,そこに設定を記述します。

接続情報は cf env を使って取得します。

$ cf env rundeck
..
 "VCAP_SERVICES": {
  "p-mysql": [
   {
    "credentials": {
     "hostname": "10.244.7.6",
     "jdbcUrl": "jdbc:mysql://10.244.7.6:3306/cf_fefc58cd_f912_4324_9117_caa3aefb5fe1?user=32KOFnad6F1BmBJU\u0026password=77Fi15F6RNbggaKj",
     "name": "cf_fefc58cd_f912_4324_9117_caa3aefb5fe1",
     "password": "77Fi15F6RNbggaKj",
     "port": 3306,
     "uri": "mysql://32KOFnad6F1BmBJU:77Fi15F6RNbggaKj@10.244.7.6:3306/cf_fefc58cd_f912_4324_9117_caa3aefb5fe1?reconnect=true",
     "username": "32KOFnad6F1BmBJU"
    },
    "label": "p-mysql",
    "name": "mysql-rundeck",
    "plan": "100mb",
    "tags": [
     "mysql"
    ]
   }
  ]
 }
}
..

併せて,アプリの正常な動作に必要と思われるサーバーURL (grails.serverURL) の設定もこのファイルで行います。

最終的に server/config/rundeck-config.properties は以下のようになりました。

$ cat server/config/rundeck-config.properties
grails.serverURL = http://rundeck.10.244.0.34.xip.io

dataSource.url = jdbc:mysql://10.244.7.6:3306/cf_fefc58cd_f912_4324_9117_caa3aefb5fe1?user=32KOFnad6F1BmBJU&password=77Fi15F6RNbggaKj
dataSource.username = 32KOFnad6F1BmBJU
dataSource.password = 77Fi15F6RNbggaKj
dataSource.dbCreate = update
rundeck.projectsStorageType = db

VCAP_SERVICESjdbcUrl 内にあった \u0026 は,設定ファイルの dataSource.url では & に置き換えています。

4. アプリのデプロイ

準備が整ったので,アプリをデプロイします。アプリケーション・マニフェストがあるので, cf push のみでOKです。

$ cf push
Using manifest file /home/nota-ja/repos/rundecks/rundeck-jar/manifest.yml

Updating app rundeck in org nota-ja / space 100 as nota-ja...
OK
..
0 of 1 instances running, 1 starting
1 of 1 instances running

App started


OK

App rundeck was started using this command `./start.sh`

Showing health and status for app rundeck in org nota-ja / space 100 as nota-ja...
OK

requested state: started
instances: 1/1
usage: 1.5G x 1 instances
urls: rundeck.10.244.0.34.xip.io
last uploaded: Mon Nov 9 10:34:42 UTC 2015
stack: cflinuxfs2
buildpack: https://github.com/nota-ja/heroku-buildpack-runnable-jar.git#cf-100-day-challenge-095

     state     since                    cpu    memory           disk      details
#0   running   2015-11-09 07:36:04 PM   1.1%   920.2M of 1.5G   0 of 1G

起動しました。

5. 動作確認

アプリ起動後,ブラウザーでアプリのURLにアクセスすると,ログイン画面に遷移するので,ユーザー名とパスワードを入力してログインします。デフォルトのユーザー名/パスワードは, 公式マニュアル によると admin / admin です:

ログインに成功するとホーム画面に遷移します:

【New Project+】をクリックして,プロジェクトを作成してみます。

とりあえず【Project Name】と【Description】以外はデフォルトのままです:

【Create】ボタンをクリックするとプロジェクトが作成され,プロジェクトのジョブ一覧画面に遷移します:

【Create a new Job…】をクリックして,ジョブを作成してみます。

【Job Name】と【Description】を適当に入力します:

【Add a Step】で【Command】を選択し,

echodate を使ったコマンドを入力して【Save】します:

残りの設定は全てスキップして【Create】ボタンをクリックします:

ジョブが作成されました:

【Run Job Now】をクリックしてジョブを実行してみます。

成功しました:

【Log Output】タブをクリックすると,出力が表示されます:

想定通りの出力になっていました。

以上で動作確認は終わりです。多機能すぎるのでこれ以上の確認は時間的に無理でしたが,試した範囲での動作に問題はありませんでした。

このアプリは,基本的にデータを DBMS に格納するようなので, Jenkins と違って Cloud Foundry 上でも本格利用ができるかもしれません。また当然ながら,Jenkins と同様に,簡単に試して捨てられるジョブの試験環境としても使うことができると思います。

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

2015-11-06

OpenPNE を Cloud Foundry で動かす

「Cloud Foundry 百日行」第94日目、本日は大学や企業、ファンクラブなど様々な組織にあわせたサイトを作ることを可能にするSNSエンジン OpenPNE です。

公式サイトでは「OpenPNEは誰でも設置・運営ができ、豊富な機能を用いて用途に合わせ自由にカスタマイズすることができるSNS」と紹介されており、プラグインや他のアプリと連携することで様々な形態で運用することが出来るアプリのようです。 このアプリはPHP+MySQLで動作させることが出来ます。

基本情報

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

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

1. ソースコードの取得

ソースコードを取得します。
その後、 OpenPNE3.8 の最新安定版の 3.8.17.1checkout します。

$ git clone https://github.com/openpne/OpenPNE3.git
$ cd OpenPNE3/
OpenPNE3$ git checkout release-3.8.17.1
OpenPNE3$ ls
apps  bin  config  data  doc  i18n  lib  LICENSE  log  NOTICE  plugins  README  symfony  templates  test  web

2. アプリのデプロイ

2.1 設定ファイルの準備

セットアップガイド のとおり設定ファイルをコピーします。

OpenPNE3$ cp config/OpenPNE.yml.sample config/OpenPNE.yml
OpenPNE3$ cp config/ProjectConfiguration.class.php.sample config/ProjectConfiguration.class.php

コピーを実施後、 config/OpenPNE.yml を編集します。今回はSMTPサーバにはGmailを利用します。
なおGmailには自身のGmailアカウント情報が必要となります。

OpenPNE3$ vi config/OpenPNE.yml
OpenPNE3$ diff config/OpenPNE.yml.sample config/OpenPNE.yml
7c7
< base_url: "http://example.com"
---
> base_url: "http://openpne3.10.244.0.34.xip.io/"
15c15
< mail_domain: "example.com"
---
> mail_domain: "10.244.0.34.xip.io"
31c31
< #mail_smtp_host: "smtp.example.com"
---
> mail_smtp_host: "smtp.gmail.com"
35,40c35,40
< #mail_smtp_config:
< #  auth:     "login"
< #  username: "myusername"
< #  password: "password"
< #  ssl:      "tls"
< #  port:     587
---
> mail_smtp_config:
>   auth:     "login"
>   username: "<username>@gmail.com"
>   password: "<password>"
>   ssl:      "tls"
>   port:     587

2.2 PHP拡張モジュールの準備

PHPの拡張モジュールがインストールされるように .bp-config/options.json を作成します。
インストールが必要な拡張モジュールは セットアップガイド に記載されていますが、一部の動作(招待メール送信、jpegファイルの表示)に不足が生じたため opensslexif を加えています。

OpenPNE3$ mkdir .bp-config
OpenPNE3$ vi .bp-config/options.json
{
    "PHP_EXTENSIONS": ["mbstring","xml","pdo","pdo_mysql","pcre","json","gd","mcrypt","apc","openssl","exif"],
    "PHP_MODULES": ["fpm","cli"],
    "WEBDIR": "web"
}

2.3 MySQLのDB準備

利用するMySQLのDBを準備します。
手順はいつも通りアプリを --no-start オプションで cf push し、その後、MySQLのDBを作成します。最後にアプリと作成したDBをバインドして環境変数を取得出来るようにしておきます。

OpenPNE3$ cf push openpne3 --no-start
OpenPNE3$ cf create-service p-mysql 100mb opdb
OpenPNE3$ cf bind-service openpne3 opdb

2.4 DB初期化

まずは環境変数から作成したDBへの接続情報を確認します。

OpenPNE3$ cf env openpne3
:
System-Provided:
{
 "VCAP_SERVICES": {
  "p-mysql": [
   {
    "credentials": {
     "hostname": "10.244.7.6",
     "jdbcUrl": "jdbc:mysql://10.244.7.6:3306/cf_ca449fee_2168_4848_9309_8ddefb9a7fdc?user=ZDwelosGrjrqzpAL\u0026password=484s4mJZjWbkjI2W",
     "name": "cf_ca449fee_2168_4848_9309_8ddefb9a7fdc",
     "password": "484s4mJZjWbkjI2W",
     "port": 3306,
     "uri": "mysql://ZDwelosGrjrqzpAL:484s4mJZjWbkjI2W@10.244.7.6:3306/cf_ca449fee_2168_4848_9309_8ddefb9a7fdc?reconnect=true",
     "username": "ZDwelosGrjrqzpAL"
    },
    "label": "p-mysql",
    "name": "opdb",
    "plan": "100mb",
    "tags": [
     "mysql"
    ]
   }
  ]
 }
}
:

DBへの接続情報を取得後に、DB初期化の為に cf push にて一旦アプリを動作させます。

この際、コマンドオプションには3つのコマンドを指定します。
セットアップガイド を参考に

  • 入力事項をコマンドの引数としてデータベースの設定値を指定してインストールできる ./symfony openpne:fast-install
  • 開発環境用ファイルを削除を行う ./symfony project:clear-controllers

を指定します。
そしてプロセスを起動する為に

  • PHP Buildpack を利用した際の detected_start_command で設定される python /home/vcap/app/.bp/bin/start

を指定します。

OpenPNE3$ cf push openpne3 -c './symfony openpne:fast-install --dbms=mysql --dbuser=ZDwelosGrjrqzpAL --dbpassword=484s4mJZjWbkjI2W --dbhost=10.244.7.6 --dbport=3306 --dbname=cf_ca449fee_2168_4848_9309_8ddefb9a7fdc --internet && ./symfony project:clear-controllers && python /home/vcap/app/.bp/bin/start'

2.5 databases.yml の書出し

ここで再デプロイに向けて作業を実施します。上記 cf push によりアプリ内にDBとの接続情報が保存された databases.ymlconfig フォルダに作成されています。このファイルをローカルに書き出し保存しておきます。
これで次回 cf push しても既存DBへアクセス可能となります。

OpenPNE3$ cf files openpne3 app/config/databases.yml | sed -e '1,3d' > config/databases.yml

なおこの際,先頭3行に不要な情報が入ってしまうので | sed -e '1,3d' を使ってその3行を削除しています。

Getting files for app openpne3 in org k-nagai / space work as k-nagai...
OK

all:
  doctrine:
    class: sfDoctrineDatabase
:

2.6 manifest.ymlの作成

デプロイ用の manifest.yml を作成します。
ここで command には既に初期化済のDBが準備されているため、バージョンアップガイド に示されているコマンドを抜き出し指定します。

OpenPNE3$ vi manifest.yml
OpenPNE3$ cat manifest.yml
---
 applications:
 - name: openpne3
   buildpack: php_buildpack
   services:
     - opdb
   command: ./symfony doctrine:build --all-classes && ./symfony cc && ./symfony plugin:publish-assets && ./symfony project:clear-controllers && python /home/vcap/app/.bp/bin/start

2.7 デプロイ

cf push でアプリを再デプロイします。

OpenPNE3$ cf push

:

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: openpne3.10.244.0.34.xip.io
last uploaded: Mon Oct 26 04:41:06 UTC 2015
stack: cflinuxfs2
buildpack: php_buildpack
 
     state     since                    cpu    memory          disk      details   
#0   running   2015-10-26 01:41:41 PM   1.5%   55.7M of 256M   0 of 1G 

以上で完了です。

3. 動作確認

管理画面用URL( http://openpne3.10.244.0.34.xip.io/pc_backend.php )にアクセスします。
初期のログインIDは adminpassword になります。


ログイン後にメニューの『メンバー管理』から『招待メール送信』画面を開きます。

メールの送信に成功したら受信したメールからアクティベーションを行います。

自身のプロフィールを設定したら一般サイトURL( http://openpne3.10.244.0.34.xip.io/ ) へログインします。


メニューの『コミュニティ検索』より『コミュニティ作成』のリンクを選択しサイトを作成してみます。


最後にアイコンを設定しましょう。
『写真を編集する』よりアップロード画面に移動し画像をアップロードします。


今回の動作確認は以上です。

なお、このアプリには多数のプラグインが準備されています。
プラグインチャンネルサーバ のサイトにはskype連携や日記などもアップされており、追加インストールは ./symfony opPlugin:install [plugin-name] で出来るようです。
必要な機能がある方は是非、 manifest.yml に追加して試してみてはいかがでしょうか。

おまけ

ここでは動作検証に発生したトラブルの解決手段を紹介します。

今回の検証において数回にわたりデプロイを繰り返していると、DBの初期化で以下のようなエラーが発生するようになりました。

2015-10-24T20:47:59.41+0900 [App/0]      ERR   SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`cf_90f2de74_1d84_4030_838a_4063ee9659ef`.`community_member`, CONSTRAINT `community_member_community_id_community_id` FOREIGN KEY (`community_id`) REFERENCES `community` (`id`) ON DELETE CASCA)
2015-10-24T20:47:59.49+0900 [DEA/0]      ERR Instance (index 0) failed to start accepting connections
2015-10-24T20:47:59.51+0900 [API/0]      OUT App instance exited with guid 73103822-6762-4e3f-9943-9eeada11ee5e payload: {"cc_partition"=>"default", "droplet"=>"73103822-6762-4e3f-9943-9eeada11ee5e", "version"=>"aaaee8c0-5028-48d0-9657-5bfcce40fd68", "instance"=>"7091905666e44c70881356ce35ed7d34", "index"=>0, "reason"=>"CRASHED", "exit_status"=>216, "exit_description"=>"failed to accept connections within health check timeout", "crash_timestamp"=>1445687279}

結論からお伝えすると、初期化の中で data/fixtures/011_import_first_community.yml を実行時に作成されたテーブルのAUTO_INCREMENTの値が1以外から開始されることが原因でした。通常、新規のテーブルの場合は1から始まるので問題ないのですが、繰り返していたためカウントだけが保存されてしまったようです。
data/fixtures/011_import_first_community.yml には固定で id が 1 のレコードを外部キーとして参照している箇所がある為、エラーを解消する為には以下の修正を加えることで対応出来ます。

$ git diff
diff --git a/data/fixtures/002_import_first_member.yml b/data/fixtures/002_import_first_member.yml
index f76e464..a230df3 100644
--- a/data/fixtures/002_import_first_member.yml
+++ b/data/fixtures/002_import_first_member.yml
@@ -1,5 +1,6 @@
 Member:
   first_member:
+    id: 1
     name: "OpenPNE君"
     is_active: 1
  
diff --git a/data/fixtures/011_import_first_community.yml b/data/fixtures/011_import_first_community.yml
index 339e8dc..e88e946 100644
--- a/data/fixtures/011_import_first_community.yml
+++ b/data/fixtures/011_import_first_community.yml
@@ -1,5 +1,6 @@
 Community:
   first_community:
+    id: 1
     name: "all"
  
 CommunityConfig:
@@ -30,6 +31,7 @@ CommunityConfig:
  
 CommunityMember:
   first_member:
+    id: 1
     community_id: 1
     member_id: 1
     is_pre: 0

これで必ず外部キーとなるレコードは id が 1 になるのでエラーは発生しなくなります。

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

2015-11-05

SharePla を Cloud Foundry で動かす

「Cloud Foundry 百日行」第93日目は、旅行のプランを共有しながら立てることのできるRailsアプリ、SharePlaです。Google Mapsと連動して場所やルートを確認しながらプランを立てられるので、友人と遠隔で旅行を計画を立てなければならない場合などに便利かもしれません。
本アプリを動かすには、TwitterアカウントとGoogleアカウントが必要となるので、予め用意しておく必要があります。

基本情報

デプロイ準備から動作確認までの手順は以下の通りです。

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

1. ソースコードの取得

SharePlaのソースを、GitHubから取得します。

$ git clone https://github.com/RyuPiT/SharePla.git
$ cd SharePla
SharePla$ ls -a
.    bin        db       Gemfile.lock  lib      public       .rspec  .travis.yml
..   config     .fonts   .git          LICENSE  Rakefile     spec    vendor
app  config.ru  Gemfile  .gitignore    log      README.rdoc  test

2. サービスの作成

SharePlaは、MongoDBを使用するため、MongDBのサービスを作成しておきます。
もしデプロイする bosh-lite 環境にMongDBサービスブローカがない場合、百日行の「spring-boot-cf-service-broker-mongoを Cloud Foundry で動かす」を参考に、事前に準備をしてください。

/SharePla$ cf create-service 'Mongo DB' 'Default Mongo Plan' sp-mongo
:
OK
SharePla$ cf services
:
name         service      plan                    bound apps   last operation
sp-mongo     Mongo DB     Default Mongo Plan                   create succeeded

3. デプロイ準備

3-1. アプリ連携準備

SharePlaを使うには、TwitterおよびGoogleと連携する必要があります。
まず、Twitter Appsで本アプリを登録し、API KeyAPI Secretを取得します。アプリ登録には、電話番号が登録済みのTwitterアカウントが必要なので予め用意しておく必要があります。
例として、以下に本アプリ検証で登録した内容を記します。

次に、アプリからGoogle MapへアクセスできるようにGoogle Developers Consoleで本アプリ用のプロジェクトを作成し、利用するAPIおよび認証情報を登録することで、API Keyを取得します。
以下に、登録が必要なものを記します。

  • Google Maps APIの中から、Google Places API Web Serviceを選択し、有効にする
  • 認証情報の中からAPIキーを選択し、ブラウザキーを作成する

3-2. MongoDBバインド準備

先に作っておいたMongoDBのCredential情報を確認するため、--no-startで起動したアプリに作成したサービスをバインドし、cf envの情報を確認します。

SharePla$ cf push sp-100 --no-start
:
Uploading 13.2M, 201 files
Done uploading
OK
SharePla$ cf bind-service sp-100 sp-mongo
:
OK

Mongo DBのurl:の情報を控えておきます。

SharePla$ cf env sp-100
:
OK

System-Provided:
{
 "VCAP_SERVICES": {
  "Mongo DB": [
   {
    "credentials": {
     "uri": "mongodb://44cf2e59-cfe3-4d4c-ae6d-85c347a7499a:password@192.168.15.91:27017/31f89550-96bc-42fd-a7c9-cbe7640d1fad"
    },
    "label": "Mongo DB",
    "name": "sp-mongo",
    "plan": "Default Mongo Plan",
    "tags": [
     "mongodb",
     "document"
    ]
   }
  ]
 }
}
:

3-3. デプロイ用マニフェストの作成

デプロイ用マニフェストを作成して行きます。
上記で取得したTwitter、Googleとの連携情報およびMongoDBのCredential情報は、環境変数として指定します。

SharePla$ vi manifest.yml
applications:
- name: sp-100
  command: 'bundle exec rails s --port=$PORT'
  services:
    - sp-mongo
  env:
    MONGOHQ_URL: 'mongodb://44cf2e59-cfe3-4d4c-ae6d-85c347a7499a:password@192.168.15.91:27017/31f89550-96bc-42fd-a7c9-cbe7640d1fad'
    TwitterConsumerKey: '<TwitterのAPI Key>'
    TwitterConsumerSecret: '<TwitterのAPI Secret>'
    GooglePlacesApiKey: '<GoogleのAPI Key>'

4. デプロイ

デプロイ準備が完了したら、cf pushします。

SharePla$ cf push
:
OK

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: sp-100.10.244.0.34.xip.io
last uploaded: Mon Nov 2 07:54:31 UTC 2015
stack: cflinuxfs2
buildpack: Ruby

     state     since                    cpu    memory           disk      details
#0   running   2015-11-02 04:57:51 PM   0.0%   118.3M of 256M   0 of 1G

無事、デプロイは成功しました。

5. 動作確認

ブラウザから払いだされたアプリのURLへアクセスすると、無事トップページであるhosted.htmlの画面が表示されました。

実は、Twitterアカウントがなくても「お試し」ボタンでSharePlaを試すことが出来るのですが、今回は折角Twitterの設定もしたので「Twitterでログイン」ボタンでログインして使ってみます。

ログインするとホーム画面へ移りますので、「新しいプランを作成する」ボタンをクリックし、プラン作成画面へ移動します。

上方にタイトルや概要を記述し、左のエリアで操作をしてプランを組み立てていきます。
例えば、2つ目の「都道府県選択」のタブで「北海道」を選択すると、他のタブに北海道の観光地や北海道限定の検索結果が表示されるようになります。

左のエリアに表示された観光地を右エリアにドラックアンドドロップして、計画を組み立てていきます。
右上の「ルートの表示」ボタンをクリックすると、左した地図に時系列順にルートが表示されます。

ただ、「ホテル検索」については、検索に失敗します。
Heroku上で公開されている公式の同Webアプリのサイトでも同じようにエラーとなるので、これはアプリのバグと思われます。

一通りプランの入力に満足したら、左上の「プラン保存」ボタンを押すことで設定したプランを保存することが出来ます。
保存が完了すると、ホーム画面に保存したプランがタイル状に表示されます。

保存した情報は、MongoDBに保存され、永続化されています。試しにアプリをリスタートしてみましたが、保存したプランは問題なく表示されました。

SharePla$ cf restart sp-100
:
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: sp-100.10.244.0.34.xip.io
last uploaded: Mon Nov 2 10:38:16 UTC 2015
stack: cflinuxfs2
buildpack: Ruby

     state     since                    cpu    memory           disk      details
#0   running   2015-11-02 08:17:46 PM   0.0%   128.7M of 256M   0 of 1G

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



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

2015-11-04

webcrontab を Cloud Foundry で動かす

「Cloud Foundry 百日行」第92日目は、Web UIを持ったcronのwebcrontab です。
webcrontabはGo言語で実装されており、cron機能のみに絞ったシンプルなアプリケーションです。

基本情報

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

  • 1) ソースコードの取得
  • 2) 事前準備
  • 3) アプリの起動
  • 4) 動作確認
  • 5) 補足情報

1. ソースコードの取得

$ git clone https://github.com/codeskyblue/webcron
$ cd webcron
$ ls
assetfs.go    cron.go     Godeps     LICENSE   Procfile  README.md   scripts    web.go
broadcast.go  Dockerfile  keeper.go  Makefile  public    sched.json  templates

2. 事前準備

2.1. MySQLのサービスインスタンス作成

$ cf create-service p-mysql 1gb webcrontab-mysql

2.2. アプリの事前push

アプリを--no-startでpushし、作成したサービスインスタンスをアプリに紐づけます。

$ cf push webcrontab --no-start
$ cf bind-service webcrontab webcrontab-mysql

紐づけたサービスの情報を『cf env』を実行し、credentialsのuri部分をメモします。

$ cf env webcrontab
:
System-Provided:
{
 "VCAP_SERVICES": {
  "p-mysql": [
   {
    "credentials": {
     "hostname": "10.244.7.6",
     "jdbcUrl": "jdbc:mysql://10.244.7.6:3306/cf_a99c856a_3b71_4af8_b5e3_e3e6872b4af6?user=MtKu4dqqjSz0y5ey\u0026password=yFg1sCjoTzkvutKz",
     "name": "cf_a99c856a_3b71_4af8_b5e3_e3e6872b4af6",
     "password": "yFg1sCjoTzkvutKz",
     "port": 3306,
     "uri": "mysql://MtKu4dqqjSz0y5ey:yFg1sCjoTzkvutKz@10.244.7.6:3306/cf_a99c856a_3b71_4af8_b5e3_e3e6872b4af6?reconnect=true",
     "username": "MtKu4dqqjSz0y5ey"
    },
    "label": "p-mysql",
    "name": "webcrontab-mysql",
    "plan": "1gb",
    "tags": [
     "mysql"
    ]
   }
  ]
 }
}

デフォルトではsqlite3のローカルファイルに書き込みを行う実装になっているDB接続のコード部分をmysql用に編集します。

$ vi web.go
$ git diff
diff --git a/web.go b/web.go
index 58e416d..7c42c5f 100644
--- a/web.go
+++ b/web.go
@@ -223,8 +223,9 @@ func main() {
        flag.Parse()

        var err error
-       xe, err = xorm.NewEngine("sqlite3", "./test.db")
+       //xe, err = xorm.NewEngine("sqlite3", "./test.db")
        //xe, err = xorm.NewEngine("mysql", "cron:cron@tcp(10.246.13.180:3306)/cron?charset=utf8")
+       xe, err = xorm.NewEngine("mysql", "MtKu4dqqjSz0y5ey:yFg1sCjoTzkvutKz@tcp(10.244.7.6:3306)/cf_a99c856a_3b71_4af8_b5e3_e3e6872b4af6?charset=utf8")
        // xe, err = xorm.NewEngine("mysql", "root:@/cron?charset=utf8")
        if err != nil {
                log.Fatal(err)

3. アプリの起動

DBの接続情報を編集した状態のコードをpushします。
なお、Procfileが標準で用意されているので-cの起動コマンドの指定は不要です。

$ cf push webcrontab
:
App started


OK

App webcrontab was started using this command `webcrontab -port $PORT`

Showing health and status for app webcrontab in org morika-t / space morika-t as morika-t...
OK

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: webcrontab.10.244.0.34.xip.io
last uploaded: Wed Oct 28 13:29:26 UTC 2015
stack: cflinuxfs2
buildpack: Go

     state     since                    cpu    memory          disk      details
     #0   running   2015-10-28 10:30:14 PM   0.0%   10.1M of 256M   0 of 1G

成功しました。

4. 動作確認

ブラウザからアプリにアクセスします。
※一部のメニューが中国語ベースとなっています。

デフォルトで”hello”と”hello 2”というジョブが設定されている状態になっていますが、”hello”が失敗しているので歯車の部分を押して設定を修正します。

“Directory”の値が、Mac向けの”/Users”というPATHになっているため、Ubuntu用のPATHに変更します。 本動作確認では、ひとまず”/tmp”と設定しますが、環境に応じて変更してください。

書き換え後”Save”をクリックして保存し、”主页”をクリックしてトップページに戻ります。

ジョブが成功した結果が表示されます。

5. 補足情報

MySQLで管理される情報はジョブの実行履歴のみで、ジョブの設定自体は”sched.json”というファイルで管理されています。

$ cat sched.json
[
    {
        "name": "hello",
        "schedule": "*/20 * * * * *",
        "command": "for i in $(seq 10)\ndo\n    echo hello world: 10-$i $(pwd)\n    sleep 1\ndone",
        "dir": "/Users",
        "description": "你好",
        "environ": {
            "Name": "123"
        },
        "enabled": true
    },
    {
        "name": "hello     2",
        "schedule": "@every 24h",
        "command": "asdf",
        "dir": "",
        "description": "again",
        "environ": {},
        "enabled": true
    }
]

その為、アプリをpushする前に、予め”sched.json”ファイルを直接編集し、ジョブの設定を追加しておくことも可能です。
またCFの特性上、アプリが停止されると”sched.json”ファイルは初期化されてしまい、Web UIから変更した設定も消去されてしまいます。
そのため、以下のコマンドを実行し、ローカルにファイルを保存することで、アプリ再pushにより設定を復活させることができます。

$ cf files webcrontab app/sched.json
Getting files for app webcrontab in org morika-t / space morika-t as morika-t...
OK

[
    {
        "name": "hello",
        "schedule": "*/20 * * * * *",
        "command": "for i in $(seq 10)\ndo\n    echo hello world: 10-$i $(pwd)\n    sleep 1\ndone",
        "dir": "/tmp",
        "description": "你好",
        "environ": {
            "Name": "123"
        },
        "enabled": true
    },
    {
        "name": "hello     2",
        "schedule": "@every 24h",
        "command": "asdf",
        "dir": "",
        "description": "again",
        "environ": {},
        "enabled": true
    },
    {
        "name": "fasdf",
        "schedule": "@every 1h",
        "command": "echo 123",
        "dir": "",
        "description": "asdfa",
        "environ": {},
        "enabled": true
    }
]

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



投稿者:NTTソフトウェア株式会社 森川 健

2015-11-02

shooter.io を Cloud Foundry で動かす

「Cloud Foundry 百日行」第91日目は、多人数で遊べるシューティングゲーム shooter.io です。
Goで書かれたサーバ shooter-server と、HTTPサーバ shooter-html5 が連携して動くアプリです。
なお、今回のアプリはサーバー間の通信にWebSocketが使われているため、Proxy等の設定によってはアプリが正常に動作しない可能性もありますので、ご了承下さい。

基本情報

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

  • 1) shooter-server のデプロイ
    • 1.1) ソースコードの入手
    • 1.2) Serviceの作成
    • 1.3) 事前準備
    • 1.4) Cloud Foundryへのデプロイ
  • 2) shooter-html のデプロイ
    • 2.1) ソースコードの入手
    • 2.2) 事前準備
    • 2.3) Cloud Foundryへのデプロイ
  • 3) 動作確認

1. shooter-server のデプロイ

1.1. ソースコードの入手

$ git clone https://github.com/xiam/shooter-server
$ cd shooter-server/
shooter-server$ ls
LICENSE  Makefile  README.md  shooter.png  src
shooter-server$ ls src/
agent.go  control.go  entity   fn.go  main.go   player.go   scores.go  ship
bullet    diff        fire.go  item   Makefile  powerup.go  sector.go

Go言語で書かれたアプリです。
README によると、以降のサーバ起動手順は、

make
cd src
go get -d
make
MONGO_HOST="<MONGODB_HOST>" ./shooter-server -listen <LISTEN_IP>:<LISTEN_PORT>
  • MONGODB_HOST → MongoDBのある場所のIPアドレス。デフォルトの値は “127.0.0.1” 。(参考
  • LISTEN_IP : LISTEN_PORT → HTTPサーバからの接続待ち受けIPアドレス/PORT。デフォルトの値は “127.0.0.1:3223” 。(参考

となっているようです。
さて、恐らくですが、 go-buildpack を使った場合は、2つのディレクトリに渡って make を実行し、かつその間に go get も適切なタイミングで実行し・・・といった処理はできない可能性が高いです。
もちろんGo言語とgo-buildpackの仕様を共にしっかりと理解している方であれば可能であるかもしれませんが、残念ながら私には今回の執筆期間ではそのような手段を編み出すことはできませんでした。
しかし幸いなことにGo言語には、binaryさえ作ってしまえば外部のライブラリ等に依存せずに実行ができるという強みがあります。
ということで、今回はこれを活かし、Goのバイナリを事前にローカルの環境で作ってしまい、その後Cloud Foundryで動作させる、という手段をとってみようと思います。
使うBuildpackは binary-buildpack です。

1.2. Serviceの作成

さて、 shooter-server はゲームスコアなどの保存にMongoDBを使うとのことなので、 こちらの記事 で作成したMongoDB Serviceを使います。

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

service      plans                     description   
Mongo DB     Default Mongo Plan*       A simple mongo implementation   
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.
shooter-server$ cf create-service "Mongo DB" "Default Mongo Plan" shooter-mongo
Creating service instance shooter-mongo in org ukaji / space default as ukaji...
OK

バインドも済ませておきましょう。

shooter-server$ cf push shooter-server --no-start
・・・
shooter-server$ cf bind-service shooter-server shooter-mongo
Binding service shooter-mongo to app shooter-server in org ukaji / space default as ukaji...
OK

1.3. 事前準備

MongoDB接続周りの修正

1.2で作成したMongoDBに接続する箇所の修正を行います。
scores.go を見てみると、このアプリはデータベースのHost名とDatabase名のみで接続を行っています。

shooter-server$ cat src/scores.go
・・・
        settings = db.Settings{
                Host:     host,
                Database: defaultDatabase,
        }

        if sess, err = db.Open("mongo", settings); err != nil {
                log.Fatal("db.Open: ", err)
        }
・・・

しかし、 cf env で見ることができるServiceの環境変数中の uri の値を見て分かる通り、Cloud Foundry上で作成したMongoDBのServiceにはUsername、Passwordが設定されているため、このままでは正しく接続することはできません。

shooter-server$ cf env shooter-server
{
 "VCAP_SERVICES": {
  "Mongo DB": [
   {
    "credentials": {
     "uri": "mongodb://ad55761d-bd50-40f1-ae0f-875cc1c92e96:password@192.168.15.91:27017/7f7607ab-db63-465a-95c9-2b2318c5d68d"
    },
    "label": "Mongo DB",
    "name": "shooter-mongo",
    "plan": "Default Mongo Plan",
    "tags": [
     "mongodb",
     "document"
    ]
   }
  ]
 }
}

そこで今回は、「起動時に環境変数 MONGO_URI でURI( mongodb://<username>:<password>@<host>:<port>/<database> )を指定すればDBとの接続を行う」という方針で修正を行います。もちろん従来の MONGO_HOST が指定されている場合の動作にも影響が及ばないようにしましょう。

shooter-server$ vi src/scores.go
・・・
import (
        "log"
        "os"
        "strings"
        "time"
        "upper.io/db"
        "upper.io/db/mongo"
)
・・・
var settings mongo.ConnectionURL
var sess db.Database
var scores db.Collection
・・・
func init() {
        var err error

        var mongoURI = ""
        host := os.Getenv("MONGO_HOST")
        uri := os.Getenv("MONGO_URI")

        if uri != "" {
                mongoURI = uri
        } else if host != "" {
                mongoURI = host
        } else {
                mongoURI = defaultHost
        }

        parsedURI, _ := mongo.ParseURL(mongoURI)

        settings = mongo.ConnectionURL {
                Address:  parsedURI.Address,
                Database: parsedURI.Database,
                User:     parsedURI.User,
                Password: parsedURI.Password,
        }

        if sess, err = db.Open("mongo", settings); err != nil {
                log.Fatal("db.Open: ", err)
        }

        log.Printf("Connected to mongo://%s.\n", mongoURI)

・・・
shooter-server$ git diff --no-prefix src/scores.go
diff --git src/scores.go src/scores.go
index 1d5b173..bb6666f 100644
--- src/scores.go
+++ src/scores.go
@@ -20,7 +20,7 @@ import (
        "strings"
        "time"
        "upper.io/db"
-       _ "upper.io/db/mongo"
+       "upper.io/db/mongo"
 )
 
 type mark struct {
@@ -29,7 +29,7 @@ type mark struct {
        Created time.Time `json:"-" bson:"created"`
 }
 
-var settings db.Settings
+var settings mongo.ConnectionURL
 var sess db.Database
 var scores db.Collection
 
@@ -41,22 +41,32 @@ const (
 func init() {
        var err error
 
+       var mongoURI = ""
        host := os.Getenv("MONGO_HOST")
+       uri := os.Getenv("MONGO_URI")
 
-       if host == "" {
-               host = defaultHost
-       }
+        if uri != "" {
+                mongoURI = uri
+        } else if host != "" {
+                mongoURI = host
+        } else {
+                mongoURI = defaultHost
+        }
 
-       settings = db.Settings{
-               Host:     host,
-               Database: defaultDatabase,
-       }
+       parsedURI, _ := mongo.ParseURL(mongoURI)
+
+        settings = mongo.ConnectionURL {
+                Address:  parsedURI.Address,
+                Database: parsedURI.Database,
+                User:     parsedURI.User,
+                Password: parsedURI.Password,
+        }
 
        if sess, err = db.Open("mongo", settings); err != nil {
                log.Fatal("db.Open: ", err)
        }
 
-       log.Printf("Connected to mongo://%s/%s.\n", host, defaultDatabase)
+       log.Printf("Connected to mongo://%s.\n", mongoURI)
 
        scores, err = sess.Collection("scores")
        if err != nil {

これでOKです。
環境変数 MONGO_URI にMongoDBのデータベース情報を参照するURIが書かれていればそれを用いるようになりました。
また、従来通り MONGO_HOST にHost名だけを指定している場合はその場所を参照し、MongoDBに関する環境変数が何も与えられていない場合は、localhostを探しに行くという設定も生きています。

バイナリファイルの作成

今回はbinary-buildpackを使うので、ローカル環境でGoのbinaryを作ってしまいます。
手順は README の通りです。

shooter-server$ go version
go version go1.4 linux/amd64

goのversionは1.4を用いています。

shooter-server$ make
mkdir -p $GOPATH/src/
ln -sf $PWD/src $GOPATH/src/shooter.io
shooter-server$ cd src/
shooter-server/src$ go get -d
shooter-server/src$ make
go build -o shooter-server

shooter-serverというbinaryファイルができていればOKです。

shooter-server/src$ ls shooter-server
shooter-server

manifestファイルの作成

今回デプロイに必要なのはこのbinaryファイルだけなので、余分なファイルをアップロードしないためにもbinaryファイルとmanifestファイルのみが入ったディレクトリを別に作っておきましょう。

go-server$ ls
manifest.yml  shooter-server

今回はgo-serverという別の適当なディレクトリを用意しました。
なお、manifestファイルの中身はこのようになっています。

go-server$ vi manifest.yml
---
applications:
- name: shooter-server
  domain: 192.168.15.91.xip.io
  buildpack: binary_buildpack
  memory: 16M
  command: ./shooter-server -listen :$PORT
  env:
    MONGO_URI: "mongodb://ad55761d-bd50-40f1-ae0f-875cc1c92e96:password@192.168.15.91:27017/7f7607ab-db63-465a-95c9-2b2318c5d68d"
  services:
  - shooter-mongo

command ではbinaryの起動とあわせて、ListenするPORT番号を環境変数から取得しています。
環境変数 MONGO_URI には cf env で見ることができるMongoDB接続のためのuriをそのまま記述しました。
なお、今回検証を行った環境では 192.168.15.91.xip.io を指定した場合にWebSocketが通るようになるので、manifest中で domain を設定しています。

1.4. Cloud Foundryへのデプロイ

では、Cloud Foundryへのデプロイを行います。

go-server$ cf push
・・・
-----> Uploading droplet (2.2M)

1 of 1 instances running

App started


OK

App shooter-server was started using this command `./shooter-server -listen :$PORT`

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

requested state: started
instances: 1/1
usage: 16M x 1 instances
urls: shooter-server.192.168.15.91.xip.io
last uploaded: Thu Oct 29 09:45:48 UTC 2015
stack: cflinuxfs2
buildpack: binary_buildpack

     state     since                    cpu     memory      disk      details   
#0   running   2015-10-29 06:46:01 PM   28.3%   5M of 16M   0 of 1G 

OKのようです。
発行されたURLは後ほど使います。

2. shooter-html5 のデプロイ

2.1. ソースコードの入手

さて、続いてHTTPのサーバ、 shooter-html5 のデプロイに移ります。
まずはソースコードの入手から。

$ git clone https://github.com/xiam/shooter-html5
$ cd shooter-html5/src/
shooter-html5/src$ ls
assets  css  index.html  js

HTML+JavaScriptのアプリです。

2.2. 事前準備

shooter-server への接続先設定

まずは先ほどデプロイした shooter-server のURLを、WebSocketの接続先として登録します。

shooter-html5/src$ vi js/main.js 
// Websocker server address.
var WEBSOCKET_SERVICE = 'ws://shooter-server.192.168.15.91.xip.io/w/';
・・・
shooter-html5/src$ git diff --no-prefix js/main.js
diff --git src/js/main.js src/js/main.js
index b114c65..5e4cbbd 100644
--- src/js/main.js
+++ src/js/main.js
@@ -1,5 +1,5 @@
 // Websocker server address.
-var WEBSOCKET_SERVICE = 'ws://shooter.io/w/';
+var WEBSOCKET_SERVICE = 'ws://shooter-server.192.168.15.91.xip.io/w/';
 
 // Frames configuration.
 var FRAMES_PER_SECOND = 24;

今回のアプリのソースコードのファイル構成を眺めてみると、2箇所程Symbolic Linkが張られている所があります。

shooter-html5/src$ tree js
js
├── controller.js
├── entity.js
├── fire.js
├── game.js
├── isMobile.js -> isMobile.min.js
├── isMobile.min.js
├── jquery.js -> jquery.min.js
├── jquery.min.js
├── json2.js
├── layer.js
├── license-sm2.txt
├── lifebar.js
├── main.js
├── powerup.js
├── radar.js
├── require.js
├── score.js
├── screen.js
├── ship.js
├── sm2.js
├── sound.js
├── swf
│   └── soundmanager2.swf
├── util.js
└── ws.js

ローカルで起動させている分にはこのままでも良いのですが、実は、Cloud FoundryのCLIは、pushの際にSymbolic Linkを無視する仕様になっています。

※参考

一時凌ぎな解決策ではありますが、ここではSymbolic Linkを外してHard Linkに変更しておきましょう。
もちろん中身をCopyしたファイルを用意しておくのもOKです。

shooter-html5/src$ unlink js/isMobile.js 
shooter-html5/src$ ln js/isMobile.min.js js/isMobile.js
shooter-html5/src$ unlink js/jquery.js 
shooter-html5/src$ ln js/jquery.min.js js/jquery.js 

manifestファイルの作成

こちらもmanifestファイルを作っておきます。

shooter-html5/src$ vi manifest.yml 
---
applications:
- name: shooter
  domain: 192.168.15.91.xip.io
  buildpack: staticfile_buildpack
  memory: 16M

2.3. Cloud Foundryへのデプロイ

cf push でデプロイします。

shooter-html5/src$ cf push
・・・
-----> Uploading droplet (4.3M)

1 of 1 instances running

App started


OK

App shooter was started using this command `sh boot.sh`

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

requested state: started
instances: 1/1
usage: 16M x 1 instances
urls: shooter.192.168.15.91.xip.io
last uploaded: Thu Oct 29 07:01:49 UTC 2015
stack: cflinuxfs2
buildpack: staticfile_buildpack

     state     since                    cpu    memory        disk      details   
#0   running   2015-10-29 04:01:59 PM   0.0%   5.2M of 16M   0 of 1G  

OKですね。

3. 動作確認

発行されたURLにアクセスします。

名前を登録して START >> を押すとゲーム開始です。

操作方法はシンプルで、

  • 上下左右キーで移動
  • SPACEキーで射撃

これだけです。
自機のライフが0になるまで敵を撃墜し続け最終スコアを競う、というゲームのようです。

おまけ

さて、今回は2つのCloud Foundryアプリケーションが連携して動くものをご紹介しました。
折角なので、一回の cf push で両方のデプロイが終わるような設定にしてみましょう。
方法としては簡単で、1つのmanifestファイルから2つのアプリケーションを呼び出す、という方法を取ります。

適当なディレクトリ配下に、 shooter-serverのbinaryが入ったgo-serverのディレクトリと shooter-html5 のディレクトリを配置します。

$ tree -L 3
.
├── go-server
│   ├── manifest.yml
│   └── shooter-server
└── shooter-html5
    ├── LICENSE
    ├── README.md
    └── src
        ├── assets
        ├── css
        ├── index.html
        ├── js
        └── manifest.yml

必要なmanifestファイルは以下の通りです。

$ vi manifest.yml
---
applications:
- name: shooter-server
  domain: 192.168.15.91.xip.io
  path: go-server/
  buildpack: binary_buildpack
  memory: 16M
  command: ./shooter-server -listen :$PORT
  env:
    MONGO_URI: "mongodb://ad55761d-bd50-40f1-ae0f-875cc1c92e96:password@192.168.15.91:27017/7f7607ab-db63-465a-95c9-2b2318c5d68d"
  services:
  - shooter-mongo
- name: shooter
  domain: 192.168.15.91.xip.io
  path: shooter-html5/src/
  buildpack: staticfile_buildpack
  memory: 16M

path という項目を使い、デプロイを行うソースコードが入ったディレクトリを相対パスで指定しています。
もう個別のmanifestファイルは特に必要ないので消しておきましょう。

$ rm go-server/manifest.yml
$ rm shooter-html5/src/manifest.yml 

ファイル構成は以下のようになっているはずです。

$ tree -L 3
.
├── go-server
│   └── shooter-server
├── manifest.yml
└── shooter-html5
    ├── LICENSE
    ├── README.md
    └── src
        ├── assets
        ├── css
        ├── index.html
        └── js

この状態でデプロイを行います。

$ cf push
・・・
-----> Uploading droplet (2.2M)

1 of 1 instances running

App started


OK

App shooter-server was started using this command `./shooter-server -listen :$PORT`

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

requested state: started
instances: 1/1
usage: 16M x 1 instances
urls: shooter-server.192.168.15.91.xip.io
last uploaded: Fri Oct 30 02:28:48 UTC 2015
stack: cflinuxfs2
buildpack: binary_buildpack

     state     since                    cpu     memory        disk      details   
#0   running   2015-10-30 11:29:01 AM   19.3%   4.6M of 16M   0 of 1G      
・・・
-----> Uploading droplet (4.3M)

1 of 1 instances running

App started


OK

App shooter was started using this command `sh boot.sh`

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

requested state: started
instances: 1/1
usage: 16M x 1 instances
urls: shooter.192.168.15.91.xip.io
last uploaded: Fri Oct 30 02:29:23 UTC 2015
stack: cflinuxfs2
buildpack: staticfile_buildpack

     state     since                    cpu    memory        disk      details   
#0   running   2015-10-30 11:29:35 AM   0.0%   5.2M of 16M   0 of 1G

cf push コマンド1回で、2つのデプロイが実行されるようになりました。

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

2015-10-30

Firepoker を Cloud Foundry で動かす

「Cloud Foundry 百日行」第90日目、本日はアジャイル開発等のストーリーポイントを決定する際に利用する Planning poker を ブラウザ上で実行できる Firepoker です。

アジャイル開発のチケットのポイント決めも開発メンバーが1箇所に揃っていれば、その場で「せーの!」でカードを見せ合ってポイント決定ということが出来ます。しかし最近の開発スタイルでは1箇所に全員集合というのは少ないかもしれません。
そんな時にこのツールを利用すればメンバーが何処にいても実施できます。またコメントや条件を入れ合って再度実施も可能です。一度試してみてはいかがでしょうか。

基本情報

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

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

1. ソースコードの取得

まずはソースコードを取得します。

$ git clone https://github.com/Wizehive/Firepoker
$ cd Firepoker
Firepoker$ ls
app  bower.json  CNAME  Gruntfile.js  karma.conf.js  karma-e2e.conf.js  LICENSE  package.json  README.md  test

2. アプリのデプロイ

README にも書かれていますがソースコードダウンロード後の手順は以下の3つです。

  • Install with NPM: npm install
  • Install with Bower: bower install
  • To run the local server: grunt server

では手順通りすすめていきましょう。

2.1 npm installとbower install

出来れば手元の環境でのインストール作業は極力さけたいことろですが、 cf push-c オプションで実施するにはいろいろと修正が必要そうなので今回はあきらめて手元の環境で実施します。
その為、インストール前に node -v , npm -v , bower -v でそれぞれのコマンドが利用出来ることを確認しておいて下さい。

Firepoker$ npm install
Firepoker$ bower install

それぞれ準備された package.jsonbower.json に沿ってインストールが実行されます。
その後フォルダを確認すると node_modulesapp/components が作成されています。

2.2 index.htmlの修正

app/index.html の中で bower でインストールされたファイル名の指定に誤りがあるので修正します。

Firepoker$ vi app/index.html
Firepoker$ git diff
: 
diff --git a/app/index.html b/app/index.html
index a1d574d..bc46819 100644
--- a/app/index.html
+++ b/app/index.html
@@ -54,7 +54,7 @@
     <script src="components/angular-sanitize/angular-sanitize.js"></script>
     <script src="components/angular-truncate/src/truncate.js"></script>
     <script src="components/firebase/firebase.js"></script>
-    <script src="components/angularfire/angularfire.js"></script>
+    <script src="components/angularfire/angularFire.js"></script>
     <!-- endbuild -->
  
     <!-- build:js scripts/scripts.js -->

2.3 Gruntfiles.jsへの編集

今回もデフォルトではポート番号が9000固定になっています。環境変数からポート番号を取得する必要があるので Gruntfiles.js に修正を加えます。
また grunt で起動する際に --force を加えてもいいのですが compassopen のエラーによる起動の中断をさける為に compassopen をコメントアウトします。そして外部からアクセス出来るように hostname の修正もあわせて実施します。

Firepoker$ vi Gruntfile.js 
Firepoker$ git diff
diff --git a/Gruntfile.js b/Gruntfile.js
index 7a5ca60..475cc23 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -45,9 +45,9 @@ module.exports = function (grunt) {
     },
     connect: {
       options: {
-        port: 9000,
+        port: (process.env.VCAP_APP_PORT || 9000),
         // Change this to '0.0.0.0' to access the server from outside.
-        hostname: 'localhost'
+        hostname: '0.0.0.0'
       },
       livereload: {
         options: {
@@ -270,10 +270,10 @@ module.exports = function (grunt) {
   grunt.registerTask('server', [
     'clean:server',
     'coffee:dist',
-    'compass:server',
+//    'compass:server',
     'livereload-start',
     'connect:livereload',
-    'open',
+//    'open',
     'watch'
   ]);

2.4 マニフェストの作成

最後に manifest.yml を作成します。指定するのは -c のコマンドオプションと time です。

Firepoker$ vi manifest.yml
Firepoker$ cat manifest.yml 
---
applications:
- name: firepoker
  command: npm install grunt-cli -g && cd /home/vcap/app/ && grunt server 
  time: 180

grunt コマンドを実行する為に必要となる grunt-cli を追加インストールして grunt server コマンドで起動します。
なお cdGruntfile.js が存在するディレクトリに念の為、移動しています。

2.5 デプロイの実行

では cf push しましょう。

Firepoker$ cf push
Using manifest file /home/k-nagai/work/Firepoker/manifest.yml
 
Creating app firepoker in org k-nagai / space work as k-nagai...
OK
 
Creating route firepoker.10.244.0.34.xip.io...
OK
 
Binding firepoker.10.244.0.34.xip.io to firepoker...
OK
 
Uploading firepoker...
Uploading app files from: /home/k-nagai/work/Firepoker
Uploading 28.8M, 9760 files
Done uploading               
OK
 
Starting app firepoker in org k-nagai / space work as k-nagai...
-----> Downloaded app package (26M)
-------> Buildpack version 1.3.1
       Node.js Buildpack v64
-----> Reading application state
       package.json...
 
:
:
       WARNING: Avoid semver ranges starting with '>' in engines.node
       WARNING: Avoid checking node_modules into source control
       WARNING: No Procfile, package.json start script, or server.js file found
-----> Uploading droplet (31M)
 
1 of 1 instances running
 
App started
 
 
OK
 
App firepoker was started using this command `npm install grunt-cli -g && cd /home/vcap/app/ && grunt server`
 
Showing health and status for app firepoker in org k-nagai / space work as k-nagai...
OK
 
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: firepoker.10.244.0.34.xip.io
last uploaded: Tue Oct 20 04:32:14 UTC 2015
stack: cflinuxfs2
buildpack: Node.js
 
     state     since                    cpu    memory           disk      details   
#0   running   2015-10-20 01:33:09 PM   0.0%   130.7M of 256M   0 of 1G     

動作確認

ブラウザでURLへアクセスします。

Play now をクリックして Create a new game でストーリー等を作って行きましょう。

その後に取得したURLをメンバーに共有します。

メンバー各々が join してストーリーに対してポイントを選択し終えるとカードの値が表示されます。

未選択のユーザがいる場合は相手のカードは裏返した状態で見えません。

動作は問題ないようです。

おまけ

今回のアプリでステータスはスタート状態になったのにブラウザでアクセスしたら画面が真っ白という方へ。
まず1つは bower install 。単純なことなのですがインストール途中にインストールするバージョンを聞いてきたりします。アプリによっては新しいバージョンを入れる場合が正しいこともあるのですが、今回は bower.json に書かれたバージョンを選択して下さい。
もう1つは手順の index.html の修正。初歩的な問題として Cloud Foundry の実行環境は Ubuntu がベースになっているので、Windows がベースのアプリを動かしたい場合は大文字・小文字に注意しましょう。特に今回のアプリのように読み込みファイルのアドレスがエラーになってしまうと元も子もありません。
最後に別のブラウザでもアクセスを試して下さい。検証中に「別のブラウザだったら見えた」ということもありました。

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

2015-10-29

Sphinx を Cloud Foundry で動かす

「Cloud Foundry 百日行」第89日目,今日は3回シリーズ「静的サイト・ジェネレーター」の最終回,Python ベースのドキュメンテーション生成ツール Sphinx です。前2回の Jekyll, Hugo がブログ寄りであったのに対し,Sphinx はマニュアル等のドキュメンテーションを意識したツールということで,若干毛色は異なる印象です。

基本情報

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

  • 1) 静的サイトのサンプルの取得
  • 2) Cloud Foundry 向けのカスタマイズ
  • 3) Cloud Foundry へのデプロイ
  • 4) 動作確認
  • 5) まとめ

1. 静的サイトのサンプルの取得

前々回, 前回 同様,Cloud Foundry にデプロイするサイトのサンプルとして,百日行シリーズの最初の2つの記事を使います。元のフォーマットは Markdown で,それを reStructuredText に手動で書き直しましたが,内容的には同等です。

サンプルは GitHub に置いてあるので,試してみられる方は以下の手順で clone してください。

$ git clone https://github.com/nota-ja/cf-sphinx-example.git
..
$ cd cf-sphinx-example/

2. Cloud Foundry 向けのカスタマイズ

今回はカスタム buildpack を使わず,Cloud Foundry 標準の buildpack である python-buildpack だけを使ってサイト作成 / serve を行います。作業としては以下の3つのファイルを追加するだけです。

  1. pip を使って sphinx をインストールするための requirements.txt を用意する
  2. 起動時にサイトのビルドと Web サーバーの起動を行うための Procfile を用意する
  3. (optional) Cloud Foundry 環境に余分なファイルをアップロードしないために,.cfignore ファイルを用意する

2.1. requirements.txt

以下の内容の requirements.txt をリポジトリーのトップ・ディレクトリーに置きます。

$ cat requirements.txt
sphinx

2.2. Procfile

make html でサイトをビルドしたあと,build ディレクトリーに移動して Python の Web サーバーを起動する Procfile を作成し,リポジトリーのトップ・ディレクトリーに置きます。

$ cat Procfile
web: make html && cd build/html && python -m SimpleHTTPServer $PORT

2.3. .cfignore

不要なファイルが Cloud Foundry にアップロードされるのを避けるため,cfignore を追加します。Sphinx のデフォルトのビルド・ディレクトリーは build なので,build を含めておきます。

$ cat .cfignore
*~
.*~
/build

3. Cloud Foundry へのデプロイ

以上で準備が整ったので,Cloud Foundry にデプロイします。

$ cf push sphinx-example
Creating app sphinx-example in org nota-ja / space prod as nota-ja...
OK
..
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: sphinx-example.10.244.0.34.xip.io
last uploaded: Wed Oct 28 11:16:54 UTC 2015
stack: cflinuxfs2

     state     since                    cpu    memory          disk      details
#0   running   2015-10-28 08:17:36 PM   0.0%   63.3M of 256M   0 of 1G

起動しました。これもメモリはデフォルトの 256MB のままにしていますが,もう少し減らしても大丈夫そうです。

4. 動作確認

ブラウザーでアクセスしてみた時のトップ画面はこんな感じです:

各ページを表示させてみます:

画像の表示も確認しました:

以上で動作確認は終わりです。

5. まとめ

「静的サイト・ジェネレーター」シリーズ第3弾,最終回は Sphinx を試してみました。本記事はカスタマイズを極力減らす方針での試行だったのですが,いかがだったでしょうか?

今回「静的サイト・ジェネレーター」という同種のアプリのくくりで,さまざまなデプロイのパターンを試してみましたが,まとめるとおおよそ以下のようになると思います。

Generator Buildpack(s) Web server site generation
Jekyll custom, multi custom(*1) on staging
Hugo custom built-in(*2) on startup
Sphinx standard built-in on startup

(*1) メインの buildpack に含まれない Web サーバーを使用
(*2) メインの buildpack 内で調達できる Web サーバーを使用

各サイト・ジェネレーターは今回のシリーズで試したパターンでしかデプロイできないわけではなく,上記のさまざまな組み合わせでデプロイできると思います。どのパターンが良いかは,使っているツールや,サイトの更新頻度,負荷の状況等によって変わってくると思いますので,ご自分のサイトに適したパターンを試してみてください。

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