Azure の詳細

Microsoft Azure とオープンソース パワー グリッド コンピューティング

Bruno Terkaly
Ricardo Villalobos

Bruno Terkaly and Ricardo VillalobosMicrosoft Azure と接続型のデバイスをたくさん使って、独自のグリッド コンピューティング プラットフォームを構築するとします。それぞれのクライアントに少量の JavaScript コードとデータを送信し、各クライアントが最新ブラウザーの強力な計算能力を活用して、1 つの計算ジョブを実行することが目標です。このグリッドに接続された各デバイスでの計算が完了すると、結果が Azure に常駐する中央サーバーに送り返されます。

このグリッド コンピューティング プラットフォームは、「地球外知的生命体探査 (SETI)」プロジェクトで実際に稼働しています。このプロジェクトは、インターネット経由の大規模なグリッド (分散) コンピューティングを使用します。地球外生命体探査は宇宙空間を監視して、マイクロ波スペクトルの電磁波を分析することで、エイリアン文明からの通信の兆候を探します。これはグリッド コンピューティングの能力を生かすのに適した例です。

汎用グリッド

今月のコラムでは、汎用性の高いグリッド コンピューティング システムを作成します。このシステムでは、グリッドの各ノードで実行する特定のコードとデータを送信します。今回のプロジェクトの場合は、各クライアント ブラウザーが少量の JavaScript のブロックとそのスクリプトが処理する情報を受け取ります。そのため、ブラウザーで実行するタスクを正確に制御できるようになります。今回紹介する例では、グリッド コンピューティングのコンテキストで発生すると考えられる汎用コンピューティングの多くの問題を解決していきます。

このプロジェクトは、世界最大のハッカーソン「Tech Crunch Disrupt 2013」にマイクロソフトが参加したのが発端です。マイクロソフトは 280 チームの中で 3 位になりました。このソリューションの全容については、tcrn.ch/OkIchx(英語) を参照してください。

この大会の難所は、審査員の審査を受けるまでのたった 2 日間でプロジェクトを完成しなければならないことです。睡眠不足に耐えるだけでなく、できる限りビルド済みのコンポーネントを多用して、プロジェクトを期日どおりに完成する必要があります。全部ではありませんが、大会で使用したテクノロジの多くは、Azure で稼働しているオープン ソース ソフトウェアを基盤としました。使用したオープン ソースのテクノロジは、Jade、Express、Socket.io、Bootstrap、jQuery、Node.js などです。

Web ソケット

プロジェクトは、至るところで使われるようになった Web ソケット標準に大きく依存しています。Web ソケットは HTML5 イニシアチブの一部で、全二重双方向通信によって、クライアントとサーバーの間でメッセージを送信できるようにします。Web ソケットでは、サーバー用に標準化されたアプローチによって、クライアントから明示的な要求を受けなくてもブラウザーにコンテンツを送信できるようになります。

その結果、完全な通信とオーケストレーションを作成して、接続を開いた状態を維持してメッセージを送受信できます。これは、グリッド コンピューティング システムに必須の機能です。Firefox 6、Safari 6、Google Chrome 14、Opera 12.10、Internet Explorer 10 (以降) などの最新ブラウザーは、例外なく Web ソケットをサポートします。

Web ソケットの役割

Web ソケットは、クライアントからサーバーに HTTP GET 要求の形式で Web ソケット ハンドシェイク要求を送信すると、動作を開始します。Web ソケットは、ハンドシェイクの確立後は標準の HTTP プロトコルには従いません。データ テキスト フレームは全二重で送受信されます。ペイロードを表す各テキスト フレームには小さなヘッダーが付属します。大きなメッセージについては、複数のデータ フレームに分割します。

Web ソケットのコーディングでは、構成済みのユーザー エージェントがあるかどうか検出を試みます。ユーザー エージェントがあれば、永続的な通信トンネルを確立できるようになります。今回の実装では、ユーザー エージェントは、基本的には「Web ソケットに切り替える」という特別な HTTP 要求を送信するために使用する HTTP ヘッダーの単純なフィールドです。このコラムでは、Web ソケットを使用して、実行可能な JavaScript とデータを各 Web クライアントに送信します。ジョブが完了したら、Web ソケットを使用して計算結果を Node.js サーバーに送り返します。これは、後ほど説明するアーキテクチャの重要な部分です。

プロジェクト全体を自分自身で実行するのは実に簡単です。稼働時のプロジェクトの様子を示す概要ビデオについては、1drv.ms/1d79pjo(英語) をご覧ください。ビデオを見る前に、GitHub bit.ly/1mgWWwc(英語) ですべてのコードを取得できます。Node.js を使用すると、簡単にプロジェクトを設定して実行できます。

  1. まず、nodejs.org(英語) から Node.js をインストールします。
  2. Git (git-scm.com、英語) または GitHub (github.com、英語) をインストールします。
  3. Git クローン (bit.ly/1cZ1nZh、英語) で自分用のフォークを複製します。
  4. 複製したディレクトリに Node.js パッケージ マネージャー (NPM) をインストールします。
  5. NPM の実行を開始します。

ここで紹介する Node.js のさまざまなパッケージをインストールする必要があります。パッケージは NPM を使用して npmjs.org でダウンロードできます。また、Visual Studio でパッケージを右クリックしてインストールする方法については、bit.ly/OBbtEF(英語) を参照してください。Node.js と Visual Studio の併用の詳細については、Bruno のブログ記事「Getting Started with Node.js and Visual Studio」(Node.js および Visual Studio 入門。bit.ly/1gzKkbj、英語) を参照してください。

App.js に注目する

作成する最終的なソリューションには、実は 2 つのサーバー側プロセスがあります。最も明確な最初のサーバー側のプロセスでは、大きな計算ジョブを小さく分割して、接続しているクライアント ブラウザーに分割した作業とデータを配布します。このコードは App.js にあります。

2 つの目のサーバー側のプロセスでは、グリッド全体で実行する大きな計算ジョブを管理、表示するためのポータルの処理を提供します。このコードは Server.js にあります。ここでは、ブラウザーでライブ更新されるグラフと数値を備えたリアル タイム ダッシュボードの処理を提供します (図 1 参照)。ここでは、App.js コードに注目します。


図 1 グリッド アーキテクチャの概要

オーケストレーションの詳細

Node.js は、非常に強力な抽象化を提供します。この抽象化により、エレガントな実装を組み立てることができます。まず、大きなグリッド ジョブの一部として実行する JavaScript コードを送信するという課題を解決します。また、その Javascript コードで使用するデータも送信します。

これは、Node.js パッケージの Express と Socket.io を使用して実現します。単に 1 片の JavaScript コードとデータをブラウザーに送信するだけでは不十分です。データに対してコードを実行し、サーバーに結果を送り返す方法も必要です。この問題は Index.jade モジュールを使用して解決します。つまり、グリッド コード自体の実行を管理する 2 つ目の JavaScript コードが必要です。

このアーキテクチャは、3 つのノード パッケージ (および付属するサポート パッケージ) を使用するとかなり簡単に実装されます。たとえば、Express パッケージは、URL ルート、要求処理、および表示に役立つ人気のパッケージです。ペイロードの解析、Cookie、セッションの保存なども簡単になります。

もう 1 つの強力なパッケージは Socket.io です。このパッケージは、Web ソケットを抽象化し、ブロードキャストやマルチキャストといった便利な機能を含みます。Socket.io により、サーバーとブラウザーの両方で同じ構文の JavaScript コードを使用する双方向通信を設定できます。Socket.io は、ブラウザーとサーバーで実行する JavaScript を管理します。これこそ、Node.js が優れていると感じる点です。サーバーとクライアントのペアで実行する JavaScript を記述することに代わる選択肢は今のところ思いつきません。

Node.js は Jade と密接に統合されるため、Web インターフェイスを作成するプロセスが簡潔になります。Jade では、サーバーとブラウザー (クライアント) との通信を管理するオーケストレーション JavaScript コードを含むこと以外に、HTML を作成するためのテンプレート ベースのアプローチが提供されます。

これらをまとめて、図 2に示したすべてのパッケージを使い、記述するコード量を大幅に削減します。優秀な Node.js 開発者とは、言語と組み込みの機能を理解している開発者です。偉大な Node.js 開発者とは、さまざまなパッケージに精通し、それらのパッケージを効率的に使用するスキルを備えた開発者です。npmjs.org サイトにアクセスして、Node.js のパッケージ モジュール ライブラリの詳細を確認してください。


図 2 グリッド アーキテクチャの双方向通信

双方向ロジック

結局のところ、クライアントとサーバーとのオーケストレーションは、状態マシンの双方向ロジックにすぎません。たとえば、クライアントは、JavaScript コードの受信待ち状態またはデータの受信待ち状態になります。

サーバー側は、JavaScript 送信状態またはデータ送信状態など、クライアントに対応する状態になります。Node.js コードには、状態変化をトリガーするキーとなる文字列の受信をサーバーが待っていることを示す "Socket.on("状態")" などのステートメントがあります (図 3参照)。その結果、その事象に適切に応答できます。

図 3 App.js で Node.js パッケージを設定する部分のリスト

// Setup libraries.
var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');
var socketio = require('socket.io');
var app = express();
var azure = require('azure');
var fs = require('fs')
// Code omitted for brevity.
// Let Jade handle the client-side JavaScript and HTML
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

Node.js サーバー側プロセスの設定コードを調べることから始めます。ワークフローは、サーバーがポートを開いて接続を待機する時点から始まります。Express と Socket.io の両方によって、サーバーはポート 3,000 でブラウザーの着信接続をリッスンできます。

// Create a Web server, allowing the Express package to
// handle the requests.
var server = http.createServer(app);
// Socket.io injects itself into HTTP server, handling Socket.io
// requests, not handled by Express itself.
var io = socketio.listen(server);

ブラウザーへの JavaScript の送信

接続を確立したら、サーバー側は、クライアント側が Web ソケット接続経由で JavaScript コードを受信する準備が完了したことを示すメッセージを待機します。図 4 に示す 9 行目までのコードでは、サーバーが接続の確立と、クライアントから送信される "ready for job" という文字列を待機しています。この文字列は、サーバーが JavaScript をクライアントに送り返すことができることを示します。

図 4 グリッドのブラウザーに JavaScript とデータを配布するサーバー側コードのリストの一部

(001) // Code Part 1
(003) // Wait for the browser to say it’s ready for the job.
(005) // If it is, send the JavaScript to the grid node for execution.
(007) // Do the same thing for the data being sent to the browser.
(009) io.on('connection', function(socket) {
(011)   socket.on('ready for job', function() {
(013)     clients++;
(015)     socket.emit('job', 'function process(message){function isInRange(origin,target,range){function toRad(deg){return deg*Math.PI/180}function getDistance(origin,target){var R=6371;var delta={lat:toRad(target.lat-origin.lat),lon:toRad(target.lon-origin.lon)};var start=toRad(origin.lat);var end=toRad(target.lat);var a=Math.sin(delta.lat/2)*Math.sin(delta.lat/2)+Math.sin(delta.lon/2)*Math.sin(delta.lon/2)*Math.cos(start)*Math.cos(end);var c=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));return R*c}return getDistance(origin,target)<range}function parseData(data){var parts=data.split(",");return{lat:parts[parts.length-1],lon:parts[parts.length-2]}}var target=parseData(message.body);var origin={lat:37.769578,lon:-122.403663};var range=5;return isInRange(origin,target,range)?1:0}');
(017)   });
(018)
(021) // Code Part 2  Sending data to the browser for processing
(023) // when 'ready for data event' fires off.
(025)   socket.on('ready for data', function() {
(027)     socket.isClient = true;
(029)     sendDataToSocket(socket);
(031)   });
(032)
(035) // Code Part 3 - retrieving the results of the computation.
(037) // A more thorough implementation will aggregate all the
      // results from all the browsers to solve the large computational
(039) // problem that has been broken into small chunks for each browser.
(041)   socket.on('results', function(message, results) {
(043)     messageCount++;
(045)     crimesInRange += results;
(047)   });
(048)
(051) // Code Part 4 - A basic method to send data to a connected
      // client with a timeout of 77 ms.
(053) function sendDataToSocket(socket) {
(055)   var data = lines.shift();
(057)   lines.push(data);
(059)   setTimeout(function() {
(061)     // To one client, singular
(063)     socket.emit('process', {
(065)                                        body: data
(067)     });
(069)   }, 77);
(071) }

この時点で、完成まであと半分です。クライアントは、処理するデータを要求する必要があります。このコードは、GPS 座標を使用して 2 地点間の距離を計算する基本的な三角関数コードです。このコードは任意の JavaScript コードに置き換えることができます。

図 4 に示す 25 行目からのパート 2 のコードは、サーバーが "ready for data" という文字列を待機している状態を示しています。ここでは、データに対するブラウザーの要求を送信します。前述のパート 1 で送信された JavaScript がこのデータを処理します。パート 3 のコードは、クライアント ブラウザーで送信データの計算が完了した状態を示しています。サーバーが "results" という文字列を受け取る (41 行目) と、ブラウザーの計算ジョブの最終結果をまとめる準備が完了します。この時点で、ブラウザーは、新たなジョブを受信できるようになり、このサイクルが繰り返されます。

Jade エンジン

Jade は、Node.js に統合される生産性の高い HTML ビューおよびテンプレート エンジンです。Jade を使用すると、ブラウザー用に記述するマークアップおよび JavaScript が簡潔になります。図 5 に UI を定義する Jade マークアップ言語を示します。

図 5 Jade による UI 定義

// Part 1
// This UI markup gets translated into real HTML
// before running on the client.
block content
  h1= title
  p This is an example client Web site. Imagine a beautiful Web site without any advertisements!
  p This page is processing
    span#items
    |&nbsp; jobs per second.
// Part 2
// This is the client-side JavaScript code.
  script.
    var socket = io.connect();
    var job = function(id, data) {  };
    var createFunction = function(string) {
      return (new Function( 'return (' + string + ')' )());
    }
    var items = 0;
    function calculateWork() {
      $('#items').text(items);
      items = 0;
    }
    setInterval(calculateWork, 1000);
    socket.on('connect', function() {
      socket.emit('ready for job');
    });
    socket.on('job', function(fn) {
      job = createFunction(fn);
      console.log(fn);
      console.log(job);
      socket.emit('ready for data');
    });
    socket.on('process', function(message) {
      var results = job(message);
      items++;
      socket.emit('results', message, results);
      socket.emit('ready for data');
    });

まず、ジョブの進捗状況を簡単にブラウザーに表示します。次に、サーバーから送信された JavaScript とデータを受け取ります。このコードは、実行する必要がある計算ジョブを表します。ジョブを実行し、サーバーに結果を返します。

ブラウザーに JavaScript を送信して実行を依頼する方法については、図 5 で確認してください。Jade が動作する方法の詳細については、jade-lang.com(英語) のシンプルで優れた解説がお勧めです。要約すると、やっかいな HTML タグ、山かっこなどをまったく使用しないでビジュアル インターフェイスを記述できます。

このコラムで説明できなかったプロジェクトの側面は他にもあります。説明できなかった最大の部分は Server.js です。Server.js にはポータルのエクスペリエンスが存在し、すべてのグリッド ジョブのプロセスを追跡できるようにしています。ポータルのエクスペリエンスは、100 % Web ベースの美しい UI を含む、表とグラフを備えた継続的かつリアルタイムに更新されるダッシュボードです。また、セキュリティの実践的な側面や、クライアントに送信された JavaScript がハイジャックされ改ざんされて被害を受ける脅威についても説明していません。

まとめ

ここで紹介した内容は、別のグリッド コンピューティングの問題にも応用できます。このコラムで説明できなかった重要な内容は、Node.js の能力と柔軟性です。GitHub にある Node.js のリポジトリが jQuery のリポジトリの数を超えているという事実は、Node.js がいかに最近の開発者の共感を得ているかを示す有力な根拠です。

マイクロソフト製品および (多くはオープン ソースの) テクノロジを理解して利用できるようにサポートする仕事に従事する起業家やパートナー エバンジェリストに心より感謝いたします。米西部州の起業マネージャーである Warren Wilbee は、Felix Rieseberg、Helen Zeng、Steve Seow、Timothy Strimple、および Will Tschumy を含む、最も優秀な部下数名で編成した Tech Crunch Disrupt チームの基礎を築きました。

Bruno Terkalyは、マイクロソフトの開発者エバンジェリストです。彼の豊富な知識は、数多くのプラットフォーム、言語、フレームワーク、SDK および API を使用してコードを記述してきた、何年にも上る業界での経験に基づいています。コードの作成、ブログ、クラウド ベースのアプリケーション構築 (特に、Microsoft Azure プラットフォームの使用) に関するライブ プレゼンテーションに携わっています。ブログは、blogs.msdn.com/b/brunoterkaly(英語) で公開されています。

Ricardo Villalobosは、経験豊かなソフトウェア アーキテクトとして、多くの業界の企業用アプリケーションを 15 年以上にわたって設計および作成しています。さまざまな技術認定資格の保持者であり、ダラス大学で経営管理の修士号を取得しています。彼はマイクロソフトの DPE Globally Engaged Partners チームのクラウド アーキテクトを務め、世界中の企業の Microsoft Azure ソリューション実装を支援しています。ブログは blog.ricardovillalobos.com(英語) で公開されています。

Terkaly と Villalobos は、大規模な業界カンファレンスで共同講演を行っています。彼らは、Microsoft Azure の詳細に取り組む読者の皆さんからの講演依頼をお待ちしています。Terkaly の連絡先は bterkaly@microsoft.com(英語のみ) で、Villalobos の連絡先は Ricardo.Villalobos@microsoft.com(英語のみ) です。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Gert Drapers、Cort Fritz、および Tim Park に心より感謝いたします。