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"}
以上のように、今度は正常に動作することが確認できました。