S3からJavaを起動するLambda Function

id:winebarrelさんのエントリ や西谷さんのエントリに書かれている通り、Nodeからプロセスを起動すれば、任意のコマンドが実行できるようです。

exports.handler = function(event, context) {
  var child_process = require('child_process');
  child_process.exec("java -version", function(err, stdout, stderr) {
    console.log(stderr)
    context.done();
  })
};

結果

Logs
----
START RequestId: 63c680fd-757e-11e4-b378-096fa7a78a98
2014-11-26T15:10:31.563Z	63c680fd-757e-11e4-b378-096fa7a78a98	java version "1.7.0_71"
OpenJDK Runtime Environment (amzn-2.5.3.0.51.amzn1-x86_64 u71-b14)
OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)

END RequestId: 63c680fd-757e-11e4-b378-096fa7a78a98
REPORT RequestId: 63c680fd-757e-11e4-b378-096fa7a78a98	Duration: 1619.27 ms	Billed Duration: 1700 ms 	Memory Size: 128 MB	Max Memory Used: 16 MB	

java version "1.7.0_71"、素晴らしい。Eric先生のエントリにも細かく書いていますね。


Lambdaは/tmpに512MBまでのファイルが置けるので、これを使って、S3上に置いたJarファイルからJavaを起動するwrapperを書きました。
適当なS3 bucket上にjarを並べて、バケット名、リージョン、起動クラスを指定すればブートします。
プロセス起動はこちらを参考にしました。

var aws = require('aws-sdk');
var fs = require('fs');
var javaBucket = "lambda-java";
var javaBucketRegion = "us-west-2";
var mainClass = "Main2";

exports.handler = function(event, context) {
  loadAllFiles(function() {
    var spawn = require('child_process').spawn;
    var child = spawn('java', [ "-cp", "/tmp:/tmp/*", mainClass,
        JSON.stringify(event,null,2) ]);
    child.stdout.on('data', function(data) {
      console.log("stdout:" + data);
    });
    child.stderr.on('data', function(data) {
      console.log("stderr:" + data);
    });
    child.on('close', function(code) {
      context.done(null, 'end');
    });
  });
};
function loadAllFiles(callback) {
  var s3 = new aws.S3({
    region : javaBucketRegion
  });
  s3.listObjects({
    Bucket : javaBucket
  }, function(err, data) {
    if (err) {
      throw err;
    } else {
      var cb = createCounter(data.Contents.length, callback);
      for (var i = 0; i < data.Contents.length; i++) {
        loadObject(cb, data.Contents[i].Key);
      }
    }
  });
}
function loadObject(callback, key) {
  var s3 = new aws.S3({
    region : javaBucketRegion
  });
  s3.getObject({
    Bucket : javaBucket,
    Key : key
  }, function(err, data) {
    if (err) {
      throw err;
    } else {
      console.log("load:" + key);
      fs.writeFileSync('/tmp/' + key, data.Body);
      callback();
    }
  });
}
function createCounter(length, callback) {
  var counter = 0;
  return function() {
    if (++counter == length) {
      callback();
    }
  }
}

ためしにTomcatをS3上に置いて起動してみます。

結果

Logs
----
START RequestId: 026ef527-757f-11e4-8cf1-c193c778b689
2014-11-26T15:14:55.796Z	026ef527-757f-11e4-8cf1-c193c778b689	load:tomcat-embed-jasper.jar
2014-11-26T15:14:55.842Z	026ef527-757f-11e4-8cf1-c193c778b689	load:tomcat-dbcp.jar
2014-11-26T15:14:55.880Z	026ef527-757f-11e4-8cf1-c193c778b689	load:tomcat-embed-el.jar
2014-11-26T15:14:55.921Z	026ef527-757f-11e4-8cf1-c193c778b689	load:hoge2.jar
2014-11-26T15:14:55.940Z	026ef527-757f-11e4-8cf1-c193c778b689	load:tomcat-embed-logging-juli.jar
2014-11-26T15:14:55.979Z	026ef527-757f-11e4-8cf1-c193c778b689	load:tomcat7-embed-websocket.jar
2014-11-26T15:14:56.019Z	026ef527-757f-11e4-8cf1-c193c778b689	load:tomcat-embed-logging-log4j.jar
2014-11-26T15:14:56.456Z	026ef527-757f-11e4-8cf1-c193c778b689	load:tomcat-embed-core.jar
2014-11-26T15:15:05.480Z	026ef527-757f-11e4-8cf1-c193c778b689	stderr:Nov 26, 2014 7:15:05 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
2014-11-26T15:15:05.601Z	026ef527-757f-11e4-8cf1-c193c778b689	stderr:Nov 26, 2014 7:15:05 AM org.apache.coyote.AbstractProtocol init
SEVERE: Failed to initialize end point associated with ProtocolHandler ["http-bio-8080"]
java.net.BindException: Operation not permitted <null>:8080
	at org.apache.tomcat.util.net.JIoEndpoint.bind(JIoEndpoint.java:411)
	at org.apache.tomcat.util.net.AbstractEndpoint.init(AbstractEndpoint.java:646)

8080へのbind処理が失敗しましたが、ブートすることが確認できました。headlessオプション付ければ描画もいけると思うので、次はサーバサイド描画を試してみます。