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

2015-06-09

EtherCalc を Cloud Foundry で動かす

「Cloud Foundry 百日行」第4回目は,前回と同様の spreadsheet アプリ “EtherCalc” です。

基本情報

EtherCalc は web spreadsheet で,複数人で同時編集できる点も前回の EtherSheet と同じです。ソースを見た印象としては,PaaS を含めたクラウド環境で動かすことを意識した造りになっているように感じました。

記事の概要は以下の通りです。

  1. ソースコードの取得
  2. Redis なしでデプロイ
  3. Redis なしで動作確認
  4. Redis ありでデプロイ
    • Redis サービスの作成と結合
    • ソースコードの修正
    • アプリの push
  5. Redis ありで動作確認

では,始めます。

ソースコードの取得

$ git clone https://github.com/audreyt/ethercalc.git

ethercalc ディレクトリーに入ります。

$ cd ethercalc/

Redis なしでデプロイ

ではまず,このアプリをRedis なしでデプロイしてみます。

本アプリはデータを Redis に格納しますが,Redis がない場合ローカルのディスクにデータを保存するようになっているので,とりあえず試してみたいようなだけであればこちらの方が簡単です。

ただし,Cloud Foundry にデプロイした場合ファイルシステムに永続性がないので,アプリを再起動したりするとデータはすべて消えてしまいます。またアプリを複数インスタンス構成で動作させたい場合は,Redis が必須です。

【アプリのpush】

$ cf push ec
Creating app ec in org nota-ja / space 100 as nota-ja...
......
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: ec.10.244.0.34.xip.io
last uploaded: Tue Jun 9 06:24:46 +0000 2015
stack: lucid64

     state     since                    cpu    memory           disk      details
#0   running   2015-06-09 03:25:22 PM   0.0%   133.9M of 256M   0 of 1G

拍子抜けするほど簡単に動きました。

Redis なしで動作確認

初期画面

シートを作成

Create Spreadsheet をクリックしてシートを作ります。

入力/演算

A1 セルに 123, A2 セルに 456, A3 セルに =sum(A1:A2) を入力してみます。

数字が右寄せになってるのがちょっとポイント高い感じです。

表示の変更

【列幅の拡大】

【フォントの拡大】

他にも機能はあるのですが,多すぎるのでこれくらいにしたいと思います。

ともかく,極めて簡単に動かすことができました。

Redis ありでデプロイ

今度は Redis ありでデプロイしてみます。

Redis サービスの作成と結合

本アプリ用に Redis サービスのインスタンスを作成します。

今回, Redis サービスの提供には, cf-redis-release を利用しました。

cf marketplace でサービスの一覧を確認します。

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

service      plans                     description
PostgreSQL   Basic PostgreSQL Plan*    PostgreSQL on shared instance.
p-mysql      100mb-dev, 1gb-dev        A MySQL service for application development and testing
p-redis      shared-vm, dedicated-vm   Redis service to provide a key-value store
......

サービス p-redis, プラン shared-vm を指定して,サービス・インスタンスを作成します。サービス・インスタンス名は redis4ec としました。

$ cf create-service p-redis shared-vm redis4ec
Creating service instance redis4ec in org nota-ja / space 100 as nota-ja...
OK

確認:

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

name        service      plan                    bound apps   last operation
my4es       p-mysql      100mb-dev               es
pg4kandan   PostgreSQL   Basic PostgreSQL Plan   kandan
redis4ec    p-redis      shared-vm

Redis のサービス・インスタンスが作成できました。

次に,これをアプリと結合します。

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

確認:

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

name        service      plan                    bound apps   last operation
my4es       p-mysql      100mb-dev               es
pg4kandan   PostgreSQL   Basic PostgreSQL Plan   kandan
redis4ec    p-redis      shared-vm               ec

ソースコードの修正

この状態でステージングをしなおして (cf restage ec) みたのですが,残念ながら Redis に接続した状態にはなりませんでした。

調査したところ,実はこのアプリは Cloud Foundry を意識した造りになっていた (cf. ソースコード : VCAP_SERVICES という環境変数から Redis への接続情報を取り出すコードが実装されている) のですが,cf-redis-release の仕様とのわずかな差異によって接続に失敗していたことがわかりました。

最終的な差分は以下の通りです。

$ git diff master
 1diff --git a/db.js b/db.js
 2index 9a776f4..c13bae5 100644
 3--- a/db.js
 4+++ b/db.js
 5@@ -12,8 +12,8 @@
 6     services = JSON.parse(process.env.VCAP_SERVICES || '{}');
 7     for (name in services) {
 8       items = services[name];
 9-      if (/^redis/.test(name) && (items != null && items.length)) {
10-        ref1$ = [(ref$ = items[0].credentials)['port'], ref$['hostname'], ref$['password']], redisPort = ref1$[
11+      if (/redis/.test(name) && (items != null && items.length)) {
12+        ref1$ = [(ref$ = items[0].credentials)['port'], ref$['host'], ref$['password']], redisPort = ref1$[0],
13       }
14     }
15     redisHost == null && (redisHost = 'localhost');
16diff --git a/src/db.ls b/src/db.ls
17index 93aaeed..f4f7aa8 100644
18--- a/src/db.ls
19+++ b/src/db.ls
20@@ -9,8 +9,8 @@
21     process.env.VCAP_SERVICES or '{}'
22
23   for name, items of services
24-    | /^redis/.test name and items?length
25-      [redisPort, redisHost, redisPass] = items.0.credentials<[ port hostname password ]>
26+    | /redis/.test name and items?length
27+      [redisPort, redisHost, redisPass] = items.0.credentials<[ port host password ]>
28
29   redisHost ?= \localhost
30   redisPort ?= 6379

修正が2ファイルにわたっているのは,元のソースコードが LiveScript で書かれていて,それをコンパイルした結果の JavaScript ファイルにも(当然)変更が入るためで,実質的に修正したのは src/db.ls の1ファイルのみです。

アプリの push

アプリをpushして更新します。

$ cf push ec
Updating app ec in org nota-ja / space 100 as nota-ja...
......
requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: ec.10.244.0.34.xip.io
last uploaded: Tue Jun 9 07:38:36 +0000 2015
stack: lucid64

     state     since                    cpu    memory           disk      details
#0   running   2015-06-09 04:38:52 PM   0.0%   138.8M of 256M   0 of 1G

この時のログ (cf logs ec で見ることが可能) を見ると,以下のような出力があり,Redis と接続できていることが確認できました。

2015-06-09T16:42:47.13+0900 [App/0]      OUT Connected to Redis Server: 10.244.3.46:50638

Redis ありで動作確認

先ほどのシートを確認

アプリの更新時に再起動がかかったため,先ほどのシートにアクセスするとデータはすべて消えてしまっています。

シートの作成と操作

トップ画面から新しくシートを作り,いろいろ操作してみました。

アプリの再起動

ここでアプリを再起動してみます。

$ cf restart ec

再起動中,ブラウザーの画面はこのようになります。

数十秒ほどで再起動が完了しました。

Stopping app ec in org nota-ja / space 100 as nota-ja...
OK

Starting app ec in org nota-ja / space 100 as nota-ja...

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

App started


OK

App ec was started using this command `npm start`

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

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: ec.10.244.0.34.xip.io
last uploaded: Tue Jun 9 07:42:27 +0000 2015
stack: lucid64

     state     since                    cpu    memory         disk      details
#0   running   2015-06-09 04:57:53 PM   0.0%   133M of 256M   0 of 1G

ブラウザー画面はもとの状態に戻っていて,リロードしてもデータは残っています。

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

今回使用した環境