use RA-GRS;

メリークリスマス!(まだ早い)

この記事は Windows Azure Advent Calendar 2013 の23日目です。

さて。今年は @dankogai さんにお会いできたり、YAPC::Asia Tokyo 2013で話す機会を頂いたり したために、実に久しぶりにPerl熱が(自分の中で)再燃した年でした。また、 @junnama さんが Net::Azure::StorageClient なるモジュールをCPANに登録するという嬉しい出来事もあったのです。これは、 Perl 版の Azure ストレージクライアントライブラリです。私もさっそくこれを使って、「ページBLOBの割り当て済みサイズを取得するツール」という小さなスクリプトを書いてみたりしました。

RA-GRS is here!

ところで、Azureのストレージといえば、「読み取りアクセス地理冗長」(RA-GRS) のプレビューが始まりましたね。今までは、地理冗長(同一ジオ内への別リージョンへの自動的な複製)がデフォルトで有効になっていたものの、セカンダリ側のデータに普段はアクセスできず、ジオフェールオーバーが発生しない限り複製は役に立たなかったわけですが、RA-GRS を使うとセカンダリ側のデータをいつでも読み取ることができます。(セカンダリ側には書き込めません。それ故 ”RA”-GRS)

プレビュー機能ページからの申し込みが有効になっていれば、ストレージアカウントの「構成」タブでこんな風に設定できます↓

image

すると、セカンダリエンドポイントが使えるようになります。ストレージアカウント名に ”-secondary” を付けたホスト名となります。

また、「最終の同期時刻」(LastSyncTime)という項目も増えます。セカンダリへの同期は非同期ですから、「どの時点の内容までは伝わっているのか」をこれで示しているわけです。

image

# 23日の内容なのにギリギリになってから慌てて書いているのがバレバレですね。

There is more than one way to do it.

となれば、業界標準かつクロスプラットフォームでアワードウィニングなスクリプト言語であるところの Perl で RA-GRS してみようと思うのは極めて自然であると言えましょう。もちろん、.NET版のストレージクライアントライブラリ 等を使えば簡単にアクセスできるわけですが、それではオモシロミに欠けるというものです。

そこでまずは、前述の「ページBLOBの割り当て済みサイズを取得するツール」である get-usedsize.pl を使って、自分のストレージアカウントのセカンダリ側を調べてみることにしました。なお、@junnama さんのストレージクライアントモジュールの名前が、当初の WindowsAzure::Blob から Net::Azure::StorageClient::Blob へ変わっているので、それに合わせて get-usedsize.pl も直しました。

さて、試してみます。get-usedsize.pl は第1引数としてストレージアカウント名をとるのですが、ここに ”-secondary” を付けた名前を指定してみます。

C:\>perl get-usedsize.pl ksasiaeast02c -secondary アクセスキー vhds
ERROR: 400: ・ソ<?xml version="1.0" encoding="utf-8"?><Error><Code>OutOfRangeInput</Code><Message>One of the request inputs is out of range.
RequestId:xxxxxxxx-e22f-43a2-xxxx-8df6717a4e7e
Time:2013-12-23T05:08:57.9803639Z</Message></Error> at get-usedsize.pl line 22.

あれ、エラー・・・

最初に疑ったのはプロトコルバージョンです。RA-GRS はリクエストの x-ms-version フィールドに ”2013-08-15” を指定しなければ使えません。そこで、Net::Azure::StorageClient::Blob のコンストラクタで api_version を指定してみました。

my $client = Net::Azure::StorageClient::Blob->new(
account_name => $account_name,
primary_access_key => $primary_access_key,
container_name => $container_name,

api_version => '2013-08-15', );

が、同じエラー・・・

しかし ”-secondary” を付けずにいつも通りプライマリ側へアクセスする場合は何の問題も起きないので、やはり ”-secondary” の指定が関係していると思わざるを得ません。はてどこを直せば、、、もしかしてセカンダリ側へアクセスするときには何か追加の設定が必要なのかも、とストレージサービスのREST APIリファレンスを調べてみたりしたのですが、特にそういうものはなさそうです…

接続先としては ”-secondary” を指定しなければ意味が無い、しかし、どこかに ”-secondary" を指定してはいけない場所がある気がする…

あ、もしかして?

ストレージの認証ヘッダ

Windows Azureストレージ の REST API を呼び出す際は、Authorization ヘッダフィールドに複雑怪奇な呪文を唱えながら生成した署名をくっつけてやる必要があるのですが、これは以下のような形をしています。

Authorization: SharedKey ストレージアカウント名:署名

この「ストレージアカウント名」というのは、ひょっとして “-secondary” 付きの名前では無く、List Storage Accountsが返してくるアカウント名そのものなのでは、と推測したわけです。現状のNet::Azure::StorageClientは account.blob.core.windows.net のようなFQDNから、「最初のピリオドより前」を切り取ってストレージアカウント名としているので、”-secondary” がくっついてきちゃうのです。

試しに、ストレージアカウント名を取り出している部分のコードを、ちょびっといじってみました。

image

赤い部分を緑部分のように変えたわけです。文字クラス \w に ’-‘ (ハイフン) は含まれませんので、\w+ は ’-‘ の直前までを消費して $1 には ”-secondary” を除いたストレージアカウント名が入ります。

※ なお、簡単な文字列処理も全部正規表現でやっちゃうのはPerl星人のごく一般的な習慣です。indexofとかsubstringとかめんどくさくて…

さぁ、これでうまく動くようになりました!

C:\>perl get-usedsize.pl ksasiaeast02c-secondary vhds
3637658112/32212255232 3.4/30 (11.3% used) 0nl5kzo5.2cz201308300518360065.vhd
1677476352/68719477248 1.6/64 (2.4% used) centdb01-centdb01-0706-1.vhd
68712944128/68719477248 64.0/64 (100.0% used) centdb01-centdb02-0706-1.vhd
3271582208/32212255232 3.0/30 (10.2% used) keazt1ai.g5h201307031712170126.vhd

“vhds” というコンテナの中にある BLOB 達すべてにつき、BLOB のサイズのうち実際にページが書き込まれているのはどの程度なのか、数え上げるスクリプトです。上記実行例ではセカンダリ側にアクセスしてこの数え上げ処理をやっています。

@junnama さんに pull request 出しておきました。正規表現をちょびっと直すだけなのですが、Gitあまり使ったこと無かったので、pull requestというのをやってみたかったのです…

RA-GRS情報取得API

さて、セカンダリエンドポイント目がけて API を呼び出せるようになったところで、もう一つ何か試してみたいと思います(もちろん Perl でね!)

題材としては、RA-GRSの登場とともに増えたAPI Get Blob Service Stats (REST API) を使って、セカンダリ側の状態を問い合わせてみるというのはどうでしょうか。これを呼び出すと、以下のようなレスポンスが返って来るようです。

<?xml version="1.0" encoding="utf-8"?>
< StorageServiceStats>
<GeoReplication>
<Status>live|bootstrap|unavailable</Status>
<LastSyncTime>sync-time|<empty></LastSyncTime>
</GeoReplication>
< /StorageServiceStats>

この API を呼び出し、返ってくる XML を解析し、Status と LastSyncTime を表示する簡単なスクリプト “get-blobstats.pl” を書いてみましょう。

まず、スクリプト冒頭に以下の2行。これはお約束。

use strict;
use warnings;

次に、必要なモジュールをuseします。

use LWP::UserAgent;
use XML::Simple;
use Net::Azure::StorageClient::Blob;

Get Blob Service Stats (REST API)はかなり新しいAPIなので、まだNet::Azure::StorageClientには対応するメソッドが存在しません。そこで、Net::Azure::StorageClientはその一部の機能を使い、実際のAPI呼び出し処理と結果の解析は自前で行います。とはいえ、HTTP は LWP::UserAgent に、XML 解析は XML::Simple に任せてしまえるので、楽なものです。

では、処理開始、まずはストレージクライアントをインスタンス化します。コマンドライン引数で、対象のストレージアカウント名(“-secondary”を付けて)と、アクセスキーを指定する想定です。

my ($account_name, $access_key) = @ARGV;

my $client = Net::Azure::StorageClient::Blob->new(
account_name => $account_name,
primary_access_key => $access_key,
api_version => '2013-08-15',
);

次に、HTTP::Request オブジェクトを作ります。赤字部分で呼び出し API を指定しているわけです。

my $req = new HTTP::Request("GET", "https://${account_name}.blob.core.windows.net/ ?restype=service&comp=stats");

そして、次に Net::Azure::StorageClient の便利機能を活用します。sign メソッドが面倒な Authorization ヘッダを作ってくれるので楽ちん。

$req = $client->sign($req);

いよいよ API を呼び出します。HTTP レスポンスは $res に返ってきます。

my $res = LWP::UserAgent->new->request($req);

返ってきた $res はいろんな要素が詰まったハッシュ(のリファレンス)なのですが、この中の {_content} キーに HTTP のレスポンスボディが入っています。これは前述の通り XML 形式なので、XML::Simple を使って扱いやすい Perl のデータ構造に変換してやります。

my $result = XML::Simple->new->XMLin($res->{"_content"});

これで、$result には API のレスポンスを表すハッシュリファレンスが入ります。こんなの↓

0 HASH(0x48ac208)
'GeoReplication' => HASH(0x48b01a0)
'LastSyncTime' => 'Mon, 23 Dec 2013 14:46:00 GMT'
'Status' => 'live'

後はこれを整形して表示するだけですね。

print "Status: $result->{GeoReplication}->{Status}\n";
print "LastSyncTime: $result->{GeoReplication}->{LastSyncTime}\n";

では、実行してみましょう。

C:\>perl get-blobstats.pl ksasiaeast02c-secondary アクセスキー
Status: live
LastSyncTime: Mon, 23 Dec 2013 14:47:41 GMT

RA-GRS は有効(live)で、最終同期時刻は日本時間の 23:47 だということがわかります。

いやー!やっぱり Perl で書くと簡単ですね!?

関連情報

Windows Azure ストレージの冗長オプションと読み取りアクセス地理冗長ストレージ

Authentication for the Windows Azure Storage Services

Get Blob Service Stats (REST API)

Net::Azure::StrageClientモジュールを書いてCPANデビューした話。

Strawberry Perl

__END__