fbpx

Cloud Foundryを使ってみよう[4]

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

node.jsとMongoDBの連携

node.jsは、「サーバサイドJavaScript」と形容されることが多いですが、スケーラブルなネットワークアプリケーションのためのJavaScriptで作成されたプラットフォームです。イベント駆動の非同期I/Oモデルを用いています。

MongoDBは、NoSQL (Not only SQL)の一種で、ドキュメント指向のデータベースです。BSON (Binary JSON)というJSON (JavaScript Object Notation) をバイナリ化したしたような形式でドキュメントを表現しています。

本項では、Using MongoDB with Node.jsを参考に、MongoDBに接続するnode.jsで作成されたアプリケーションを、Cloud Foundry上にデプロイします。

MongoDBとnode.jsのインストール

MongoDB クライアントとサーバをローカルにインストールします。

root@debian:~# aptitude install mongodb
	:
Setting up libpcre3 (8.02-1.1) ...
Setting up libboost-system1.42.0 (1.42.0-4) ...
Setting up libboost-filesystem1.42.0 (1.42.0-4) ...
Setting up libboost-program-options1.42.0 (1.42.0-4) ...
Setting up libboost-thread1.42.0 (1.42.0-4) ...
Setting up libnspr4-0d (4.8.6-1) ...
Setting up libmozjs2d (1.9.1.16-11) ...
Setting up libpcrecpp0 (8.02-1.1) ...
Setting up mongodb-clients (1:1.4.4-3) ...
Setting up mongodb-server (1:1.4.4-3) ...
Adding system user `mongodb' (UID 102) ...
Adding new user `mongodb' (UID 102) with group `nogroup' ...
Not creating home directory `/home/mongodb'.
Adding group `mongodb' (GID 104) ...
Done.
Adding user `mongodb' to group `mongodb' ...
Adding user mongodb to group mongodb
Done.
Starting database: mongodb.
Setting up mongodb-dev (1:1.4.4-3) ...
Setting up mongodb (1:1.4.4-3) ...

root@debian:~#

MongoDBサーバが起動していることを確認します。

cf@debian:~$ ps auxwwwf | grep "[ m]ongodb"
mongodb   6566  0.0  0.8  80176  4260 ?        Sl   21:04   0:00 /usr/bin/mongod --dbpath /var/lib/mongodb --logpath /var/log/mongodb/mongodb.log --config /etc/mongodb.conf run
cf@debian:~$

MongoDBクライアントがインストールされていることを確認します。

cf@debian:~$ mongo --version
MongoDB shell version: 1.4.4
cf@debian:~$

node.jsをローカルにインストールします。

root@debian:~# aptitude install nodejs
	:
Setting up libssl1.0.0 (1.0.0f-1) ...
Setting up libicu48 (4.8.1.1-2) ...
Setting up libv8-3.6.6.14 (3.6.6.14-2) ...
Setting up libc-ares2 (1.7.3-1) ...
Setting up libev4 (1:4.04-1) ...
Setting up nodejs (0.4.12-3) ...
update-alternatives: using /usr/bin/node to provide /usr/bin/js (js) in auto mode.

root@debian:~#

node.jsがインストールされていることを確認します。

cf@debian:~$ node -v
v0.4.12
cf@debian:~$

node.jsのパッケージマネージャであるnpmをローカルにインストールします。

root@debian:~# aptitude install npm
	:
Setting up mime-support (3.48-1) ...
Setting up python2.6-minimal (2.6.6-8+b1) ...
Linking and byte-compiling packages for runtime python2.6...
Setting up python2.6 (2.6.6-8+b1) ...
Setting up python-minimal (2.6.6-3+squeeze6) ...
Setting up python (2.6.6-3+squeeze6) ...
Setting up binutils (2.20.1-16) ...
Setting up libgmp3c2 (2:4.3.2+dfsg-1) ...
Setting up libmpfr4 (3.0.0-2) ...
Setting up cpp-4.4 (4.4.5-8) ...
Setting up cpp (4:4.4.5-1) ...
Setting up libgomp1 (4.4.5-8) ...
Setting up gcc-4.4 (4.4.5-8) ...
Setting up gcc (4:4.4.5-1) ...
Setting up libc-dev-bin (2.11.2-10) ...
Setting up linux-libc-dev (2.6.32-39squeeze1) ...
Setting up libc6-dev (2.11.2-10) ...
Setting up zlib1g-dev (1:1.2.3.4.dfsg-3) ...
Setting up libssl-dev (0.9.8o-4squeeze5) ...
Setting up libv8-dev (3.6.6.14-2) ...
Setting up manpages-dev (3.27-1) ...
Setting up libc-ares-dev (1.7.3-1) ...
Setting up libev3 (1:3.9-1) ...
Setting up libev-dev (1:3.9-1) ...
Setting up nodejs-dev (0.4.12-3) ...
Setting up npm (0.2.19-1) ...

root@debian:~#

npmがインストールされていることを確認します。

cf@debian:~$ npm -v
0.2.19
cf@debian:~$

MongoDBを利用しないnode.jsのサンプルアプリケーションの作成

まず、MongoDBを利用しない、node.jsのサンプルアプリケーションを作成します。

cf@debian:~$ mkdir mongo-node
cf@debian:~$ cd mongo-node
cf@debian:~/mongo-node$ 

cf@debian:~/mongo-node$ cat > app.js
var port = (process.env.VMC_APP_PORT || 3000);
var host = (process.env.VCAP_APP_HOST || 'localhost');
var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(port, host);
cf@debian:~/mongo-node$

サンプルアプリケーションをローカルで実行します。

cf@debian:~/mongo-node$ node app.js

別シェルからtelnetコマンドで接続します。

cf@debian:~$ telnet localhost 3000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close

Hello World
Connection closed by foreign host.
cf@debian:~$

以上のように、問題なく動作することが確認できました。

このサンプルアプリケーションをCloud Foundryにデプロイします。このアプリケーションはMongoDBを利用していませんが、現時点でMongoDBとの関連付けを行っておきます。

cf@debian:~/mongo-node$ vmc push
Would you like to deploy from the current directory? [Yn]: y
Application Name: mongo-node
Application Deployed URL [mongo-node.cloudfoundry.com]: mongo-node-creationline.cloudfoundry.com
Detected a Node.js Application, is this correct? [Yn]: y
Memory Reservation (64M, 128M, 256M, 512M, 1G, 2G) [64M]:
Creating Application: OK
Would you like to bind any services to 'mongo-node'? [yN]: y
The following system services are available
1: mongodb
2: mysql
3: postgresql
4: rabbitmq
5: redis
Please select one you wish to provision: 1
Specify the name of the service [mongodb-94ec6]:
Creating Service: OK
Binding Service [mongodb-94ec6]: OK
Uploading Application:
  Checking for available resources: OK
  Packing application: OK
  Uploading (0K): OK
Push Status: OK
Staging Application: OK
Starting Application: OK                                                        

cf@debian:~/mongo-node$

サンプルアプリケーションがMongoDBと関連付けられていることを確認します。

cf@debian:~/mongo-node$ vmc apps

+-------------+----+---------+------------------------------------------+---------------+
| Application | #  | Health  | URLS                                     | Services      |
+-------------+----+---------+------------------------------------------+---------------+
| mongo-node  | 1  | RUNNING | mongo-node-creationline.cloudfoundry.com | mongodb-94ec6 |
+-------------+----+---------+------------------------------------------+---------------+

cf@debian:~/mongo-node$

Webブラウザで http://mongo-node-creationline.cloudfoundry.com/ にアクセスします。以下の表示が得られれば、サンプルアプリケーションは正常に動作しています。

Hello World

MongoDBを利用するnode.jsのサンプルアプリケーションの作成

サンプルアプリケーションの先頭に、MongoDBサービスと接続するためのコードを追加します。接続するだけで、実際にMongoDBに対しては何の操作も行いません。

Cloud Foundry上では、アプリケーションとサービスを接続するための情報は環境変数 VCAP_SERVICES、VMC_APP_PORT、VCAP_APP_HOST に格納されるようになっており、その有無を判定してCloud Foundry上とローカルのどちらでも動作するようになっています。

逆に言うと、Cloud Foundry上でアプリケーションを動かすには、接続情報に関する部分を修正する必要があります。

cf@debian:~/mongo-node$ cat > app.js
if(process.env.VCAP_SERVICES){
  var env = JSON.parse(process.env.VCAP_SERVICES);
  var mongo = env['mongodb-1.8'][0]['credentials'];
}
else{
  var mongo = {
    "hostname":"localhost",
    "port":27017,
    "username":"",
    "password":"",
    "name":"",
    "db":"db"
  }
}

var generate_mongo_url = function(obj){
  obj.hostname = (obj.hostname || 'localhost');
  obj.port = (obj.port || 27017);
  obj.db = (obj.db || 'test');

  if(obj.username && obj.password){
    return "mongodb://" + obj.username + ":" + obj.password + "@" + obj.hostname + ":" + obj.port + "/" + obj.db;
  }
  else{
    return "mongodb://" + obj.hostname + ":" + obj.port + "/" + obj.db;
  }
}

var mongourl = generate_mongo_url(mongo);

//--------------------------------------

var port = (process.env.VMC_APP_PORT || 3000);
var host = (process.env.VCAP_APP_HOST || 'localhost');
var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(port, host);
cf@debian:~/mongo-node$

サンプルアプリケーションをローカルで実行します。

cf@debian:~/mongo-node$ node app.js

別シェルからtelnetコマンドで接続します。

cf@debian:~$ telnet localhost 3000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close

Hello World
Connection closed by foreign host.
cf@debian:~$

以上のように、問題なく動作することが確認できたら、Cloud Foundry上のサンプルアプリケーションを更新します。

cf@debian:~/mongo-node$ vmc update mongo-node
Uploading Application:
  Checking for available resources: OK
  Packing application: OK
  Uploading (1K): OK
Push Status: OK
Stopping Application: OK
Staging Application: OK
Starting Application: OK                                                        

cf@debian:~/mongo-node$

Webブラウザで http://mongo-node-creationline.cloudfoundry.com/ にアクセスします。以下の表示が得られれば、サンプルアプリケーションは正常に動作しています。

Hello World

では、実際にnode.jsからMongoDBサービスを利用してみます。まず、npmコマンドでnode.jsのMongoDBドライバをローカルにインストールします。

cf@debian:~/mongo-node$ npm install mongodb
================================================================================
=                                                                              =
=  To install with C++ bson parser do    =
=                                                                              =
================================================================================
npm ok
cf@debian:~/mongo-node$

C++ コンパイラなど開発環境が必要となるので、インストールします。

root@debian:~# aptitude install build-essential
	:
Setting up libdb4.7 (4.7.25-9) ...
Setting up patch (2.6-2) ...
Setting up make (3.81-8) ...
Setting up fakeroot (1.14.4-1) ...
update-alternatives: using /usr/bin/fakeroot-sysv to provide /usr/bin/fakeroot (fakeroot) in auto mode.
Setting up perl-modules (5.10.1-17squeeze2) ...
Setting up libstdc++6-4.4-dev (4.4.5-8) ...
Setting up perl (5.10.1-17squeeze2) ...
update-alternatives: using /usr/bin/prename to provide /usr/bin/rename (rename) in auto mode.
Setting up g++-4.4 (4.4.5-8) ...
Setting up g++ (4:4.4.5-1) ...
update-alternatives: using /usr/bin/g++ to provide /usr/bin/c++ (c++) in auto mode.
Setting up libtimedate-perl (1.2000-1) ...
Setting up libdpkg-perl (1.15.8.11) ...
Setting up dpkg-dev (1.15.8.11) ...
Setting up build-essential (11.5) ...
Setting up libalgorithm-diff-perl (1.19.02-2) ...
Setting up libalgorithm-diff-xs-perl (0.04-1) ...
Setting up libalgorithm-merge-perl (0.08-2) ...

root@debian:~#

再度、インストールを行います。

cf@debian:~/mongo-node$ npm install mongodb --mongodb:native
================================================================================
=                                                                              =
=  To install with C++ bson parser do    =
=                                                                              =
================================================================================
make -C ./external-libs/bson all
make[1]: Entering directory `/home/cf/.node_libraries/.npm/mongodb/0.9.7-3-5/package/external-libs/bson'
rm -rf build .lock-wscript bson.node
node-waf configure build
Checking for program g++ or c++          : /usr/bin/g++
Checking for program cpp                 : /usr/bin/cpp
Checking for program ar                  : /usr/bin/ar
Checking for program ranlib              : /usr/bin/ranlib
Checking for g++                         : ok
Checking for node path                   : ok /home/cf/.node_libraries
Checking for node prefix                 : ok /usr
'configure' finished successfully (0.152s)
Waf: Entering directory `/home/cf/.node_libraries/.npm/mongodb/0.9.7-3-5/package/external-libs/bson/build'
[1/2] cxx: bson.cc -> build/default/bson_1.o
[2/2] cxx_link: build/default/bson_1.o -> build/default/bson.node
Waf: Leaving directory `/home/cf/.node_libraries/.npm/mongodb/0.9.7-3-5/package/external-libs/bson/build'
'build' finished successfully (2.313s)
cp -R ./build/Release/bson.node . || true
cp: cannot stat `./build/Release/bson.node': No such file or directory
# @node --expose-gc test/test_bson.js
# @node --expose-gc test/test_full_bson.js
# @node --expose-gc test/test_stackless_bson.js
# @node --expose-gc test/test_shared_objects.js
make[1]: Leaving directory `/home/cf/.node_libraries/.npm/mongodb/0.9.7-3-5/package/external-libs/bson'
child process exited with code 0
npm ok
cf@debian:~/mongo-node$

MongoDBドライバがインストールできたら、サンプルアプリケーションにさらにコードを追加します。

cf@debian:~/mongo-node$ cat > app.js
if(process.env.VCAP_SERVICES){
  var env = JSON.parse(process.env.VCAP_SERVICES);
  var mongo = env['mongodb-1.8'][0]['credentials'];
}
else{
  var mongo = {
    "hostname":"localhost",
    "port":27017,
    "username":"",
    "password":"",
    "name":"",
    "db":"db"
  }
}

var generate_mongo_url = function(obj){
  obj.hostname = (obj.hostname || 'localhost');
  obj.port = (obj.port || 27017);
  obj.db = (obj.db || 'test');

  if(obj.username && obj.password){
    return "mongodb://" + obj.username + ":" + obj.password + "@" + obj.hostname + ":" + obj.port + "/" + obj.db;
  }
  else{
    return "mongodb://" + obj.hostname + ":" + obj.port + "/" + obj.db;
  }
}

var mongourl = generate_mongo_url(mongo);

//--------------------------------------

var record_visit = function(req, res){
  /* Connect to the DB and auth */
  require('mongodb').connect(mongourl, function(err, conn){
    conn.collection('ips', function(err, coll){
      /* Simple object to insert: ip address and date */
      object_to_insert = { 'ip': req.connection.remoteAddress, 'ts': new Date() };

      /* Insert the object then print in response */
      /* Note the _id has been created */
      coll.insert( object_to_insert, {safe:true}, function(err){
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.write(JSON.stringify(object_to_insert));
        res.end('\n');
      });
    });
  });
}

//--------------------------------------

var port = (process.env.VMC_APP_PORT || 3000);
var host = (process.env.VCAP_APP_HOST || 'localhost');
var http = require('http');

http.createServer(function (req, res) {
  record_visit(req, res);
}).listen(port, host);
cf@debian:~/mongo-node$

サンプルアプリケーションをローカルで実行します。

cf@debian:~/mongo-node$ node app.js

別シェルからtelnetコマンドで接続します。

cf@debian:~$ telnet localhost 3000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close

{"ip":"127.0.0.1","ts":"2012-01-16T12:56:57.226Z","_id":"4f141e99e3b2f2d420000001"}
Connection closed by foreign host.
cf@debian:~$

以上のように、MongoDBサーバに投入されたデータが表示されます。MongoDBサーバのログを確認します。

cf@debian:~$ tail /var/log/mongodb/mongodb.log
Mon Jan 16 21:56:57 allocating new datafile /var/lib/mongodb/db.ns, filling with zeroes...
Mon Jan 16 21:56:57 done allocating datafile /var/lib/mongodb/db.ns, size: 16MB, took 0.011 secs
Mon Jan 16 21:56:57 allocating new datafile /var/lib/mongodb/db.0, filling with zeroes...
Mon Jan 16 21:56:57 done allocating datafile /var/lib/mongodb/db.0, size: 64MB, took 0.094 secs
Mon Jan 16 21:56:57 building new index on { _id: 1 } for db.ips
Mon Jan 16 21:56:57 Buildindex db.ips idxNo:0 { name: "_id_", ns: "db.ips", key: { _id: 1 } }
Mon Jan 16 21:56:57 done for 0 records 0secs
Mon Jan 16 21:56:57 insert db.ips 136ms
Mon Jan 16 21:56:57 allocating new datafile /var/lib/mongodb/db.1, filling with zeroes...
Mon Jan 16 21:57:03 done allocating datafile /var/lib/mongodb/db.1, size: 128MB, took 6.154 secs
cf@debian:~$

MongoDBに新しいDBが作成されたことが確認できます。

Cloud Foundry上のサンプルアプリケーションを更新します。

cf@debian:~/mongo-node$ vmc update mongo-node
Uploading Application:
  Checking for available resources: OK
  Packing application: OK
  Uploading (1K): OK
Push Status: OK
Stopping Application: OK
Staging Application: OK
Starting Application: OK                                                        

cf@debian:~/mongo-node$

Webブラウザで http://mongo-node-creationline.cloudfoundry.com/ にアクセスします。

502 Bad Gateway

しかし、以上のようにエラーとなってしまいました。ログを確認します。

cf@debian:~/mongo-node$ vmc crashlogs mongo-node
====> logs/stderr.log <====

module.js:326
    throw new Error("Cannot find module '" + request + "'");
          ^
Error: Cannot find module 'mongodb'
    at Function._resolveFilename (module.js:326:11)
    at Function._load (module.js:271:25)
    at require (module.js:355:19)
    at /var/vcap/data/dea/apps/mongo-node-0-e096b2ff2028e697bc888f5f1274295b/app/app.js:35:3
    at Server.<anonymous> (/var/vcap/data/dea/apps/mongo-node-0-e096b2ff2028e697bc888f5f1274295b/app/app.js:58:2)
    at Server.emit (events.js:67:17)
    at HTTPParser.onIncoming (http.js:1134:12)
    at HTTPParser.onHeadersComplete (http.js:108:31)
    at Socket.ondata (http.js:1029:22)
    at Socket._onReadable (net.js:677:27)

cf@debian:~/mongo-node$

Cloud Foundry上にはnode.jsのMongoDBドライバがないようです。

Cloud Foundryのナレッジベース Deploying a Node.js app with NPM dependencies によると、

When deploying any application to vCloudLabs, the directory from which you push the app via vmc push, needs to include all packages and dependencies that are needed to run your application, and your application itself. Node.JS applications are no different.

とあり、アプリケーションに依存したすべてのパッケージを一緒にデプロイしなければいけないようです。

そこで、サンプルアプリケーションのあるディレクトリにnode_modulesディレクトリを作成し、その中にMongoDBドライバをコピーします。

cf@debian:~/mongo-node$ mkdir node_modules
cf@debian:~/mongo-node$ cp -a ~/.node_libraries/.npm/mongodb/active/package/lib/mongodb/ node_modules
cf@debian:~/mongo-node$

再度、サンプルアプリケーションを更新します。これによって、一緒にMongoDBドライバもCloud Foundry上に転送されます。

cf@debian:~/mongo-node$ vmc update mongo-node
Uploading Application:
  Checking for available resources: OK
  Processing resources: OK
  Packing application: OK
  Uploading (1K): OK
Push Status: OK
Stopping Application: OK
Staging Application: OK
Starting Application: OK                                                        

cf@debian:~/mongo-node$

再び、Webブラウザで http://mongo-node-creationline.cloudfoundry.com/ に
アクセスします。

{"ip":"172.30.49.41","ts":"2012-01-16T13:18:30.767Z","_id":"4f1423a6852cd13470000001"}

以上のように、今度は正常に動作することが確認できました。

Author

Chef・Docker・Mirantis製品などの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA