AWS re:Invent会議が開催されてから2週間も経っていませんが、来年ここに戻ってくることを楽しみにしています。Amazonから多くのエキサイティングな発表があり、非常に興味深い一週間となりました。技術的な面でたくさんの新しいことを学んだだけでなく、(ここでブラックジャックを読んで)楽しみもたくさんありました:)

それでは、今年のre:Inventから私の好きなテーマの一つであるAWS Lambdaサービスとニュートレンドのサーバレスアーキテクチャに焦点を当ててみましょう。「サーバレス」とは、特定のインフラストラクチャ、展開、またはサーバがネイティブAWSのイベントに応答して既存のAWSサービスと対話するように顧客のコード(ラムダ関数としてラップされる)を実行する必要がないことをいいます。魔法のようですね!AWS LambdaがJavaScript、Java、Pythonで記述されているコードをサポートし、これらの機能がステートレスであることは言うまでもありません。つまり、これらの機能はすぐに拡大縮小でき、同じ機能の多くのコピーを同時に実行することができます。

このサービスの性能と、このサービスやNuxeo Platformでできる素晴らしい例を示していきましょう。一般的な展開は、EC2インスタンスで実行するNuxeoサーバで、バイナリマネージャーとしてAmazon S3で構成されています。

この構成では、ドキュメントはNuxeo Platformで作成され、関連するブロブ(該当する場合)はS3に格納されます。ファイルをアップロードする場合、デフォルトでは、最初にNuxeo Platformにアップロードしてから、プラットフォームがそのファイルをS3にアップロードします。しかし、AWS Lambdaサービスを使用することによって、ファイルをS3に直接アップロードすることができ、それから、このブロブを参照するドキュメントがNuxeo Platformに自動的に作成されます。

使用事例


Nuxeo Platformの使用を開始したばかりで、旧システムに保有しているドキュメントをアップロードするために大量のインポートを実行したい場合を考えてみます。

1つずつ取り上げていきましょう。これは概念実証であり、機能性に焦点を合わせているので、Nuxeo Platformで文書を作成する際に、デフォルト設定を使用し、S3で暗号化やマルチパートアップロードを使用しないで、デフォルトユーザの基本認証を使用します。

これが私の現在の展開です(S3BinaryManagerマーケットプレイスがインストールされ、設定されています)。

NuxeoとS3

Configure the bucket

デフォルトでは、オブジェクトが作成、変更またはバケットから削除された場合に、S3バケットイベントが通知されます。ラムダ関数の便利なところは、これらのイベントがその通知とネイティブに一体化していることです。よって、これらの動作のいずれかを実行するようにラムダ関数を構成しておくことができます。この関数が関係するのは、Nuxeo Platformで文書を作成することだけです。

基本的に、これが必要としているものです。ラムダ関数とは、オブジェクトがバケットにアップロードされ、ラムダ関数がこの既存のブロブを指してNuxeo Platformで文書を作成するためのオペレーションを呼び出す時に通知するものです。

ラムダ関数

ラムダ関数の作成


AWS Consoleで、既存のNode.jsのブループリントから新しいラムダ関数を作成します(ブループリントは、Amazonが提供する関数テンプレートで、既存のものがすでにサンプルAmazon S3のオブジェクト作成機能を提供しています)。ただ考慮すべき重要なことは、その機能がS3バケットおよびEC2インスタンスと同じAWS領域に存在するかどうかです。

その手順は以下のとおりです。AWS Lambdaへ進み、新しい機能を作成します。


  1. ブループリントを選択する際に、s3-get-objectテンプレートを選びます。

  2. イベントソースの構成では、S3があらかじめ選択されています。自分のバケット(私の場合は、「mariana」という名前)を選び、イベントタイプとしてObject Created (All)を選択します。

  3. 機能の構成において、ランタイムがすでにnode.jsとなっています。メモリを128MBに設定し、タイムアウトを5秒まで増加することができます。IAMロールの場合、既存のlambda_s3_exec_roleを選択します(このロールは、機能が実行するAWSアクションに必要な権限を持っているので)。


これで全部です!あとやるべきなのは、Nuxeo Platformで文書を作成するためにカスタムコードを追加することだけです。既存のコードで分かるように、一部のログはすでに有効化されています。これらのログのアウトプットは、CloudWatchで見ることができます。Monitoringサブタブ(呼び出し回数、期間など、有用な統計情報が見つけられます)へ移動し、CloudWatchリンクでView logsをクリックします。最後はこのような状態になります:

Lambda関数の作成

Save and test Lambda function

Nuxeo Platformで文書を作成するためにカスタムコードを追加

Nuxeoカスタムオペレーション:


これはちょっとコツが必要な部分です。Create.DocumentまたはFileManager.Importのオペレーションについては、両方ともファイルをパラメータとして想定しているために、起動することはできません。そのため、既存のブロブを指している文書を作成するカスタムオペレーションを記述する必要があります。S3BinaryManagerは、ファイルのダイジェスト(デフォルトのアルゴリズムはMD5です)を必要とし、これは、タイトル、コンテンツタイプ、およびブロブの長さとともに、オペレーションによって想定されるパラメータの一つでなければなりません。

つまり、ダイジェストをS3にアップロードするときに、オブジェクトのキーとしてダイジェストに注目しそれを使用し、必ずそのファイル名を渡す必要があります。

コードは以下のとおりです。

@Operation(id = CreateDocumentFromS3Blob.ID, category = Constants.CAT_DOCUMENT, label = "Create", description = "")
public class CreateDocumentFromS3Blob {

public static final String ID = "CreateDocumentFromS3Blob";

@Context
protected CoreSession session;

@Param(name = "filename")
protected String filename;

@Param(name = "mimeType")
protected String mimeType;

@Param(name = "digest")
protected String digest;

@Param(name = "length")
protected Long length;

@OperationMethod(collector = DocumentModelCollector.class)
public DocumentModel run(DocumentModel doc) throws Exception {
    if (filename == null) {
        filename = "Untitled";
    }
    DocumentModel newDoc = session.createDocumentModel(doc.getPathAsString(), filename, "File");
    newDoc = session.createDocument(newDoc);
    StorageBlob sb = new StorageBlob(new LazyBinary(digest, Framework.getLocalService(RepositoryManager.class).getDefaultRepositoryName(),
            (CachingBinaryManager) Framework.getLocalService(BinaryManagerService.class).getBinaryManager(                     Framework.getLocalService(RepositoryManager.class).getDefaultRepositoryName())), filename,mimeType, null, digest, length);
    newDoc.setPropertyValue("file:content", sb);
    newDoc.setPropertyValue("dc:title", filename);
    return session.saveDocument(newDoc);

}
}


このコードの面白いところは、所定のダイジェストでLazyBinaryを作成し、それをファイル:コンテンツプロパティとして設定する点です。

ラムダ関数にカスタムコードを追加


それでは、EC2インスタンスで実行されている当社のNuxeo Platform上でこのカスタムオペレーションを展開していると仮定しましょう。ラムダ関数からそれを呼び出すためにカスタムコードを追加する必要があります。

デモの目的上、このオペレーションを起動するユーザとしてAdministrator/Administratorを使用し、個人的なワークスペースで文書を作成することにします(操作の入力はこの文書のIDにハードコード化されています)。

前述したように、S3 BinaryManagerは、ブロブのダイジェストがバケット内のオブジェクトのキーとして使用されることを想定しているため、このキーを使用してオブジェクトをアップロードする必要があります。また、ドキュメントのタイトルを必要とするので、それを渡すためにカスタムS3メタデータを使用することができます。

ラムダ関数:

var aws = require('aws-sdk');
var s3 = new aws.S3({
apiVersion : '2006-03-01'
});
var http = require('http');
var crypto = require('crypto');

var options = {
host : '52.26.252.66',
port : '8080',
method : 'POST',
path : '/nuxeo/site/automation/CreateDocumentFromS3Blob',
headers : {
'Accept' : 'application/json',
'Content-Type' : 'application/json+nxrequest'
},
auth : 'Administrator:Administrator'
};

exports.handler = function(event, context) {
console.log('Received event:', JSON.stringify(event, null, 2));

var bucket = event.Records[0].s3.bucket.name;
var key = event.Records[0].s3.object.key;

var params = {
Bucket : bucket,
Key : key
};

s3.getObject(params, function(err, data) {
    if (err) {
        console.log(err);
        var message = "Error getting object " + key + " from bucket " + bucket + ". これらが存在し、あなたのバケットがこの関数と同じ領域にあるようにします.";
        console.log(message);
        context.fail(message);
    } else {

        //Nuxeoは、キーがファイルのダイジェストであると想定しています
        // var digest = crypto.createHash('md5').update(data.Body).digest("hex");
        var title = data.Metadata.title !== undefined ? data.Metadata.title : key;
        //console.log('title :', data.Metadata.title);

        //インプットは、親ドキュメントのIDです
        var postData = JSON.stringify({
        "input" : "f04453f9-de1c-4a8d-9956-add074069813",
        "params" : {
        "filename" : title,
        "mimeType" : data.ContentType,
        "digest" : key,
        "length" : data.ContentLength
        }
        });

        var req = http.request(options, function(res) {
            res.on('data', function(response) {
                console.log('Nuxeo response:' + response);
                context.succeed('succeed');
            });

            res.on('end', function(response) {
                context.succeed('end');
            });

        });
        req.write(postData);
        req.end();
    }
});

};


以上です!

実演ビデオを見る:


1.コマンドラインから、私は、カスタムメタデータとしてそのタイトルを渡すことによって、S3バケット内のフー・ファイターズのチケット(ともかく素晴らしいコンサート!)をアップロードします。

Marianas-MacBook-Pro:opt mariana$ md5 /Users/mariana/Downloads/FooFigthers.pdf
MD5 (/Users/mariana/Downloads/FooFigthers.pdf) = 1a29c592b09ee7725415efa354907426
Marianas-MacBook-Pro:opt mariana$ aws s3api put-object --bucket mariana --key 1a29c592b09ee7725415efa354907426 --body /Users/mariana/Downloads/FooFigthers.pdf --content-type application/pdf --metadata title=FooFighters.pdf

帰ってくるのは次の通り:

{
"ETag": "\"1a29c592b09ee7725415efa354907426\""
}

2.私のcreateDocInNuxeoラムダ関数が自動的に呼び出されました。

createDocInNuxeoラムダ関数が自動的に呼び出されました

3.Nuxeo Platformでドキュメントを見ることができます。

Nuxeo Platformで見られるドキュメント

メインファイルはFooFighters.pdfであり、それをダウンロードして、ファイルが実際に私がアップロードしたものであることを確認することができます。

これで全部です!ここでソースコードを見ることができます(プラグインには、オペレーションコードとラムダ関数コードが含まれます)。