※ その 2 のエントリからの続きです。(エントリが長すぎてポストできなかったため、分割しています。)
[③ Windows Azure ストレージサービス]
Windows Azure ストレージサービスとは、大規模・大容量の、高信頼性データストレージサービスです。内部的には、データを複数のサーバで分散・冗長化して保持するようになっているのですが、外から見た場合にはこれが巨大な一つのストレージシステムに見えるようになっている、というのが、この Windows Azure ストレージサービスです。SQL Azure データベースサービスと同様、実際のデータは少なくとも 3 つのノードに複製格納されているため、ひとつのサーバがクラッシュしても、データは生き残ることが可能になっています。
※ (注意) ”Windows Azure” という名を冠していますが、前述の Windows Azure コンピュートサービスとは全くの別物なので注意してください。
※ (参考) 現状では、Windows Azure ストレージサービス、SQL Azure データベースサービスのいずれも、Geo-Replication (データセンタまたがりのデータ複製)には対応していません。
通常、こうしたストレージシステムに対しては、ローカル HDD であれば普通の Windows エクスプローラで、リモート HDD であればファイル共有を通してアクセスすることになります。しかし、この Windows Azure ストレージサービスでは、HTTP REST プロトコルと呼ばれる、特殊な HTTP プロトコルでデータの読み書きを行います。
この HTTP REST プロトコルを直接ハンドリングするのは大変ですが、幸い、C# や VB などでアプリケーションを開発する場合には、Windows Azure ストレージサービスへアクセスするためのライブラリを使うことができます。このため、このライブラリを利用すれば、HTTP REST プロトコルを知らなくても、Windows Azure ストレージサービスへのデータの読み書きを実施することができます。例えば、コンソールアプリケーションから、Windows Azure ストレージサービスを読み書きするには、以下のようなコードを利用します。(※ このサンプルコードは特に理解しなくても大丈夫です。興味がある方は読んでください。本論からは逸れるため、今回はコードの詳細は説明しません。)
1: CloudStorageAccount storageAccount = new CloudStorageAccount(
2: new StorageCredentialsAccountAndKey("nakama000",
3: "sLyGGvOHJszS9wABrog4HhrxN+8ICH0A/diyMp.....JS/6Cm4S9c3TDH+CRRo8Tj5vIpgYfB7yArq1+xWjDSg=="),
4: new Uri("http://nakama000.blob.core.windows.net"),
5: new Uri("http://nakama000.queue.core.windows.net"),
6: new Uri("http://nakama000.table.core.windows.net"));
7:
8: CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
9: CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
10: CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
11:
12: // コンテナへの接続
13: CloudBlobContainer blobContainer = blobClient.GetContainerReference("pictures");
14: // コンテナの作成
15: bool created = blobContainer.CreateIfNotExist();
16: Console.WriteLine((created ? "コンテナを作成しました。" : "コンテナはすでに存在します。"));
17: // コンテナに対して、public アクセスを認めるように設定(http://.../Container/BlobName でアクセス可に)
18: var permissions = blobContainer.GetPermissions();
19: permissions.PublicAccess = BlobContainerPublicAccessType.Container;
20: blobContainer.SetPermissions(permissions);
21: Console.WriteLine("権限設定しました。");
22:
23: // ファイルのアップロード
24: string path = @"C:\Users\Public\Pictures\Sample Pictures";
25: foreach (string fullPathFilename in Directory.GetFiles(path, "*.jpg"))
26: {
27: string filename = fullPathFilename.Substring(fullPathFilename.LastIndexOf("\\") + 1);
28: byte[] data = File.ReadAllBytes(fullPathFilename);
29:
30: // まずファイルポインタを作成
31: CloudBlob blob = blobContainer.GetBlobReference(filename);
32: // すでに存在していたら削除
33: bool delete = blob.DeleteIfExists();
34: if (delete) Console.WriteLine("すでにデータがあったため、いったん削除しました。");
35: // そこに書き込みを行う ※ アップロード時にエラーがあってもちゃんと報告してくれないので注意
36: blob.UploadByteArray(data);
37: // クライアントに送り返すヘッダー情報を設定
38: blob.Properties.ContentType = "image/jpeg";
39: blob.SetProperties();
40: // サーバ側で保持するファイルのメタデータを記録
41: blob.Metadata["OriginalFilename"] = fullPathFilename;
42: blob.SetMetadata();
43: Console.WriteLine("ファイルを書きこみました。" + filename);
44: }
45:
46: // ファイルの一覧
47: var blobs = blobContainer.ListBlobs();
48: int i = 0;
49: foreach (var b in blobs)
50: {
51: // 各ファイルのメタデータを取得
52: CloudBlob cb = blobContainer.GetBlobReference(b.Uri.AbsoluteUri);
53:
54: // 属性データをサーバから取得
55: cb.FetchAttributes();
56: Console.WriteLine("{0} {1} {2}",
57: cb.Uri.AbsoluteUri,
58: cb.Properties.LastModifiedUtc,
59: cb.Attributes.Metadata["OriginalFilename"]);
60:
61: // データの読み取り(直接ファイルにダウンロードすることも可能)
62: cb.DownloadToFile(@"C:\temp\" + (i++).ToString() + ".jpg");
63: }
前述した Windows Azure コンピュートサービスを利用する場合、この Windows Azure ストレージサービスは、ログデータの転送先として非常に重要なものになります。これは以下のような理由によります。
Windows Azure コンピュートサービスを利用する場合、各仮想マシンは、インスタンス数の増減やサービスのフェイルオーバなどによって、簡単に起動したりシャットダウンさせたりすることができます(というより、それが Windows Azure コンピュートサービスの大きなウリです)。この結果、ローカルマシンに記録した内容(イベントログやパフォーマンスログ、ローカル HDD 上に記録したデータなど)は簡単に消え去ったり初期化されたりすることになります。このため、Windows Azure コンピュートサービスでは、基本的にはローカルマシンやローカルリソース(例えばローカル HDD など)にデータを記録・保存すべきではありません。……が、これではログファイルなどの記録や保存を行うことが全くできなくなってしまうため、困ってしまいます。
このため、各種のデータを残しておく目的で、以下の 2 つのストレージシステムを使います。
このようにしていただければ、Windows Azure コンピュートサービスの仮想マシンインスタンスが増減したとしても、業務データやログデータが消失することはありません。
とはいえ、正直なところ、各種のログデータの出力先をすべて Windows Azure ストレージサービスにするようにアプリケーションを作ったり書き換えたりするのはなかなか大変です。この問題についても以下のような対処方法が用意されています。
応用的な内容になるため今回の一連のエントリでは解説しませんが、Windows Azure コンピュートサービス内の各仮想マシンには、Diagnostic Monitor ランタイムと呼ばれるサービスがインストールされています。このサービスは、イベントログやパフォーマンスログ、フラットファイルなどを定期的に監視・データ収集し、Windows Azure ストレージサービスへとデータを転送するようになっています。このサービスが存在するため、各アプリケーションから直接 Windows Azure ストレージサービスへデータを書き込む必要はなく、従来通り、イベントログやパフォーマンスログなどにデータを出力しているだけで済むようになっています。
ただし、既定ではこれらのサービスは動作していません。このため、構成設定や起動処理を行うことによって、このデータ転送機能を有効化する必要があります。Diagnostics Monitor が既定で転送できるデータの種類は以下の通りです。(転送処理を自力で作り込むこともできるようになっています)
※ (参考) Diagnostic Monitor ランタイムの使い方についてはここでは紹介しませんが、このサイトが非常に詳しいので、興味がある方は読んでみてくださ���。(おそらく Diagnostic Monitor ランタイムを作っているチームの人だと思います)
※ (参考) なお、残念ながら現時点では Windows Azure ストレージサービスに転送されたログデータを簡単に見る方法(ビュアー)が提供されていないため、それらについては自作する必要があります。というか RTM したらログビュアーが提供される、という話だったような気がするんですがまだ提供されていないような……;
※ (注意) Diagnostic Monitor ランタイムによるログデータの転送機能は、遅延転送での動作になっています。このため、仮想マシンのクラッシュが発生した場合には、ログのとりこぼしが発生する危険性があります(通常の Web サーバでもマシンがクラッシュした際にはデータロストが発生するので当たり前のことですが;)。欠損してはならないデータの場合には、このログ転送の仕組みを使うのではなく、業務ログとして、SQL Azure データベースサービスなどに書き込むように設計してください。
さて、Windows Azure ストレージサービスでは、予め 4 種類のデータ構造が定義されており、それぞれの構造を持つデータを簡単に出し入れすることができるように設計されています。
これらについては比較的誤解があったり、わかりにくい資料も多いため、どんなものなのかを簡単に解説しておきたいと思います。
A. BLOB (巨大なバイナリデータ)
.wmv, .wav, .mp3, .jpg, .zip, .vhd などなど、主にマルチメディア系のファイルや巨大なデータファイルなどを格納するのに適したストレージ形式です。IIS ログファイルのようなただのテキストファイルに関してももちろん保存可能で、外から見た場合には 1 階層のファイルシステムであるかのように取り扱うことが可能です。
通常の Web サーバの場合には、FTP プロトコルでファイルをアップロードすることが多いと思いますが、この Windows Azure BLOB ストレージの場合には、HTTP REST プロトコルでファイルをアップロードすることになります。以下に概念図を示します。
なお、重要なポイントとして、各フォルダには public / private の設定を行うことができ、public 設定にした場合には、読み取りに関しては通常の HTTP-GET によるデータ取得ができるようになっています。(ファイル書き込みに関しては、HTTP-REST プロトコルでしか行うことができません。)
※ (参考) アクセス権限設定については、public / private の 2 択で、細かいアクセス可否設定はできません。残念;。
B. Table (ハッシュテーブル的なデータ)
Table ストレージは、プレインオブジェクト(POCO、プロパティ値のみを持つようなオブジェクト)を、お皿のようなものに入れて分散格納することができるストレージです。下図のようなイメージでとらえていただくとわかりやすいでしょう。
Windows Azure Table ストレージを利用するためには、各オブジェクトに対して、必ず以下の 2 つのキーを付与する必要があります。(正確にはこの 2 つに加えてデータ更新時の楽観同時実行制御用の Timestamp フィールドも必要になるのですが、まあとりあえずそれは置いておくとすると。)
同一の PartitionKey を持つオブジェクトは必ず同一ノード(同一物理サーバ)で保持されるようになっていますが、異なる PartitionKey を持つオブジェクトは別ノードに保持されます。このため、データ検索処理を行うと、下図のように、複数のノードで分散検索処理が行われるようになります。
このことからもわかるように、Windows Azure Table ストレージを利用する場合には、PartitionKey の設計が極めて重要になります。うかつな PartitionKey を利用すると、検索速度などが極端に劣化することがあるため、注意してください。
なお、Windows Azure Table ストレージは、「テーブル」という名前がついているものの、いわゆる RDBMS のテーブルとは全くといっていいほど異なるものです。確かに、オブジェクトインスタンスを「行」、プロパティを「列」と捉えれば、確かにテーブル的な構造を持っているといえなくもありません。
しかし、以下のような点は全く異なります。
中でも最後の特徴は極めて重要です。Windows Azure Table ストレージでは、ストレージに対してスキーマの指定ができません。これは、Azure Table ストレージでは、(RowKey さえ異なれば)同一のテーブルに異なる構造を持ったオブジェクトを格納することができるためです。例えば、Author オブジェクトと Title オブジェクトという、異なる構造を持ったデータを同一テーブルに格納した場合を以下に示します。
この様子を無理矢理に表形式に書き直すと「穴空き表」になりますが、ストレージ内部で隙間だらけのデータとして格納しているわけではありません。実際には、左の図のように、「異なるオブジェクトがひとつのお皿の上に乗っているだけ」という状態になります。このような状態は、RDBMS のテーブルでは考え難いことですが、Windows Azure Table ストレージではごく当たり前のこととして扱われます。「テーブル」という名前にあまり惑わされないようにしていただければと思います。
C. Queue (メッセージキュー)
Windows Azure Queue ストレージは、その名の通り、メッセージキューシステムになります。以下のようなシンプルな機能を持ったメッセージキューシステムを提供します。
機能は限定的ですが、その分、プログラミングは非常に単純です。
この Windows Azure Queue ストレージは、現在では利用シナリオが非常に限定的です。これは次のような理由によります。
すなわち、Windows Azure Queue ストレージを利用するのは、Web Role サーバと Worker Role サーバ間をキュー型接続(=非同期接続)しなければならない場合に限られる、ということになります(と書くとやや言いすぎですが、とりあえずはそう理解しておいても差し支えないでしょう)。実際にはそのようなケースはほとんどない(キュー型トランザクション処理が求められるケースは少ない)でしょうから、簡単なアプリケーションやシステムではほとんど使われることがない、と思っていただいてよいと思います。
D. Drive (NTFS ドライブ)
これは、中身としては Windows Azure BLOB ストレージなのですが、それをあたかも NTFS ドライブであるかのようにして使うことができる、という特殊な形式になります。アプリケーションから見た場合にはただの NTFS ボリュームであるかのように見えるため、通常のファイル I/O の API を使ってアクセスすることができる(ただし通常のローカル HDD に比べると若干アクセスが遅い)というものになります。現時点ではまだ未リリースのため詳細は不明ですが、今後、明らかになってくることでしょう。
というわけで、4 種類のWindows Azure ストレージサービスについて概要を解説してきましたが、重要なのは、どのようなケースでどのストレージサービスを使うのか、という点です。それぞれに適切な使い分けが必要になりますので、よく覚えておいていただければと思います。
[開発上の注意点]
さて、ここまで Windows Azure Platform の提供する主要サービスの概要を解説してきたわけですが、Windows Azure Platform の大きなメリットは、なんといっても
という点でしょう。しかしこのことは、オンプレミス型の Web アプリケーションをそのまま Azure プラットフォーム上に移植できるということではありませんし、Azure プラットフォーム上での開発を、���ンプレミス型の Web アプリケーションと完全に同じように捉えられる、ということでもありません。やはり、それ相応に相違点や注意点があります。
基本的に、Windows Azure Platform は PaaS プラットフォームです。このため、オンプレミス型の開発と異なり、ハード/ミドルなどのインフラに関わる設定や運用をほとんど考えなくて済むという利点があります。半面、Web サーバや DB サーバの設定やバージョンを自由に決められない・変更できないという注意点もあります。メリットとデメリットを比較する形で書くと、以下のようになります。
主だった注意点を以下にまとめます。中でも、特に日本語まわりの注意点は、日本ローカルでアプリケーションを開発していたときとは大きく異なる部分になります。十分に注意して取り組むようにしてください。
このため、実際に Windows Azure Platform 上で動作するアプリケーションを開発する場合には、Azure のインフラ特性などをきちんと理解し、Windows Azure Platform の特性に併せた形でアプリケーション開発を行う必要があります。以上のような背景を考えると、Azure プラットフォームに適したアプリケーションと、適していないアプリケーションとがあると言えます。具体的には以下のようになります。
Windows Azure Platform に適したアプリケーション(クラウド化が適しているもの)
Windows Azure Platform に不向きなアプリケーション(オンプレミス型が適しているもの)
Windows Azure Platform は、一般的な PaaS サービスに比べると敷居が低く、またハイブリッド型(オンプレミスとクラウドを併用する形態)などを取りやすいといったメリットがあります。しかし、あらゆるアプリケーションが Windows Azure Platform 上での動作に適しているというわけではありません。適切なシステム及び適切な部分に対して、上手にWindows Azure Platform を適用するようにしてください。
[本エントリのまとめ]
というわけで、本エントリをまとめる目的で、もう一度、Windows Azure Platform の主要構成要素を俯瞰図的にまとめておきたいと思います。
また、これらを利用した典型的な Web-DB アプリケーションの構成は、下図のようになります(コンピュートサービスは主に Web Role サーバを利用、ストレージサービスは主に BLOB, Table を利用)。
また、Windows Azure Platform は PaaS 型プラットフォームであるが故に、オンプレミス型と比べて、ランタイムやミドルウェアの設定を自由に変更できないという問題点があります。このため、ASP.NET Web アプリケーションであれば、どのようなアプリケーションでも Windows Azure Platform 上に移植できる、というわけでもありません。Part 3. のエントリでは実装の詳細に触れていきたいと思いますが、「なんでもかんでもクラウド化」といった考え方をしないように、十分注意してください。