あけてました。おめでとうございます。
本年もまったりとよろしくお願い致します。
さて、すっかり忘れていましたが昨年末に読み取り地理冗長(RA-GeoRep)が有効になっていたので、試してみたいと思います。
読み取り地理冗長について
さておさらいですが、Windows Azure Storageには同じデータセンター内での冗長化(3重/ LRS)に追加で同一リージョン内の他のサブリージョン(データセンター)に対しても冗長化を行うことができます。(=地理冗長ストレージ/GRS)
たとえば、東アジア(香港)にWindows Azure Storageを作り、地理冗長も設定すると東南アジア(シンガポール)にもデータが複製され、合計6重のコピーが保持されます。(3重+3重)
以前は複製先の(冗長化された)データセンターのストレージはアクセスができなかったのですが、昨年末のUpdateによりこちらに対してアクセスできるオプションが追加されました。
※今はPreview機能なので申し込み後アクティブにならないと使えません。
では実際にコードなりをもとに動作を見てみたいと思います。
サンプルコード
using System;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.RetryPolicies;
using Microsoft.WindowsAzure.Storage.Table;
namespace AzureRAGeoRepSample
{
class Program
{
private static string AccountName = "name";
private static string AccountKey = "key";
private static string BlobName = "TestBlob.txt";
static void Main(string[] args)
{
try
{
var timestamp = DateTimeOffset.UtcNow.ToString();
var cred = new StorageCredentials(AccountName, AccountKey);
//Primary
Console.WriteLine("Primary ---------------");
var container = CreateContainer(cred);
Console.WriteLine("Primary Uri: {0}\r\nSecondary Uri: {1}", container.StorageUri.PrimaryUri, container.StorageUri.SecondaryUri);
Console.WriteLine("Primary Write --");
WriteBlob(container, timestamp);
Console.WriteLine("Primary Read --");
ReadBlob(container);
Console.ReadLine();
Console.WriteLine("Secondary ---------------");
var container2 = CreateContainer(cred, true);
Console.WriteLine("Primary Uri: {0}\r\nSecondary Uri: {1}", container2.StorageUri.PrimaryUri, container2.StorageUri.SecondaryUri);
Console.WriteLine("Secondary Read --");
ReadBlob(container2);
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}",ex.Message);
}
Console.ReadLine();
}
static CloudBlobContainer CreateContainer(StorageCredentials cred, bool isSecondary = false)
{
var storageAccount = new CloudStorageAccount(cred, false);
var client = storageAccount.CreateCloudBlobClient();
var container = client.GetContainerReference("test");
if (isSecondary)
{
client.LocationMode = LocationMode.SecondaryOnly;
var georep = client.GetServiceStats().GeoReplication;
Console.WriteLine("GeoRep Status: {0}\r\nGeoRep LastSyncTime: {1}", georep.Status, georep.LastSyncTime);
}
else
{
container.CreateIfNotExists();
container.SetPermissions(new BlobContainerPermissions()
{
PublicAccess = BlobContainerPublicAccessType.Blob,
});
}
return container;
}
static void WriteBlob(CloudBlobContainer container, string text)
{
var blob = container.GetBlockBlobReference(BlobName);
Console.WriteLine("Write body: " + text);
blob.UploadText(text);
blob.Properties.ContentType = "text/plain";
blob.SetProperties();
}
static void ReadBlob(CloudBlobContainer container)
{
var blob = container.GetBlobReferenceFromServer(BlobName);
blob.FetchAttributes();
Console.WriteLine("\tPrimary Blob Uri: {0}\r\n\tSecondary Blob Uri: {1}", blob.StorageUri.PrimaryUri, blob.StorageUri.SecondaryUri);
Console.WriteLine("\tBlob last modified: {0}", blob.Properties.LastModified);
using (var s = new MemoryStream())
{
blob.DownloadToStream(s);
Console.WriteLine("\tRead body: {0}", Encoding.UTF8.GetString(s.ToArray()));
}
}
}
}
※REST APIだとあまり気にならないですが、.NETのStorage Client Libraryを使う場合は3.0以上を使用しましょう
最初にプライマリのStorageに対してBlobの追加と参照を行っています。その後、セカンダリのStorage(地理冗長されたストレージ)に対して参照を行います。
Storage Client Libraryでセカンダリにアクセスする際のポイントですが、62行目で行っているLocationModeの設定部分ぐらいです。
BlobやTable、Queueのクライアント(または各アイテムに対してのアクセス時のリクエストオプション)がアクセスする際のロケーションをここで設定しますが、このプロパティをLocationMode.SecondaryOnlyなどにすればセカンダリにアクセスすることができます。(URIなど基本的にはプライマリのものを使えばよい)
※ちなみに既定はPrimaryOnlyのようです
LocationModeは以下の4つから指定できます。
LocationMode.PrimaryOnly LocationMode.PrimaryThenSecondary LocationMode.SecondaryOnly LocationMode.SecondaryThenPrimary
PrimaryThenSecondary はプライマリにアクセスして再試行可能なエラーで失敗した場合はセカンダリにアクセスします。
SecondaryThenPrimary は逆に先にセカンダリにアクセスし、失敗した場合はプライマリにアクセスします。
※書き込み操作はセカンダリは基本不可なので、この指定にかかわらずプライマリに行くようです。
Thenほげほげを指定した場合、2つ目に試行した内容が404(Not Found)だと次回以降優先のように戻るようです。
さてさて、こんな感じのコードで 1: 地理冗長なし 2: 地理冗長のみ設定 3: 読み取りアクセス地理冗長 の3パターンで結果を見てみたいと思います。
1: 地理冗長なし
地理冗長がない場合は当然ながらアクセスできないようです。一応エラーとしてはセカンダリの名前解決に失敗ですが。
※まぁストレージアカウント名の後ろに –secondary がつくだけなので。
2: 地理冗長のみ
次は地理冗長のみの場合です。
こっちも地理冗長なしと変わらない結果です。内部的に冗長化されてても、外部からアクセスする手段がないということですね。ただ、Azure側でたとえばプライマリのストレージがダウンするなどの障害があった場合、プライマリのホスト名がセカンダリのほうに割り当てられます。
なので利用者としては(DNSの変更が反映されるまでの間はダウンしますが)プライマリのURLに対してアクセスしておけばよい感じになります。
3: 読み取りアクセス地理冗長
こっちが本命。明示的に冗長化されたセカンダリにアクセスしたいと思います。
無事アクセスできました。ブラウザでもよいのでセカンダリのURLにアクセスしてみると、ちゃんとアクセスできていることがわかります。
※ただセカンダリに対しての書き込み操作系は失敗します。
地理冗長の複製ステータスについて
RA-GRSの複製ステータスはBlobなどのストレージクライアントに対してGetServiceStatsメソッド(もしくはREST APIのGet Service Stats)を呼べば確認することができます。(サンプルの63行目)
※ただしセカンダリ側でのみ且つ読み取りアクセス地理冗長が有効な場合のみ
ステータスは Live、 Bootstrap、 Unavailableの3つのようです。
Bootstrapは地理冗長なし(LRS)から地理冗長あり(GRS)へ切り替えた後のブートストラップの初期段階となります。この時はまだセカンダリへのアクセスはできません。
カスタムドメイン使用時について
Windows Azure Storageは既定のFQDNに対してCNAMEを設定してカスタムドメイン名を追加することができます。
ただ、ここで設定することができるカスタムドメイン名はあくまでプライマリに対してだけなので、セカンダリ(GRS)に対して明示的にカスタムドメイン名でアクセスすることはできません。(設定も今は無理)
セカンダリもユーザーに開放しようと考えている場合は注意ください。
ストレージ分析について
Windows Azure ストレージの分析を有効にしている場合、以下のメトリックを参照することができます。
- $MetricsHourPrimaryTransactionsBlob
- $MetricsHourPrimaryTransactionsTable
- $MetricsHourPrimaryTransactionsQueue
- $MetricsMinutePrimaryTransactionsBlob
- $MetricsMinutePrimaryTransactionsTable
- $MetricsMinutePrimaryTransactionsQueue
またRA-GRSが有効な場合は以下のセカンダリ用の指標も参照することができます。
- $MetricsHourSecondaryTransactionsBlob
- $MetricsHourSecondaryTransactionsTable
- $MetricsHourSecondaryTransactionsQueue
- $MetricsMinuteSecondaryTransactionsBlob
- $MetricsMinuteSecondaryTransactionsTable
- $MetricsMinuteSecondaryTransactionsQueue
- コード例:
var t = storageAccount.CreateCloudTableClient();
var tt = t.GetTableReference("$MetricsHourPrimaryTransactionsBlob");
foreach (var l in tt.CreateQuery<DynamicTableEntity>().Select(x => x).Take(2).ToList())
{
Console.WriteLine(l.PartitionKey, l.RowKey);
l.Properties
.Where(x => x.Value.PropertyType == EdmType.Int64)
.Take(5)
.ToList()
.ForEach(pair => Console.WriteLine("\t\tkey: {0} value: {1}", pair.Key, pair.Value.Int64Value));
}
tt = t.GetTableReference("$MetricsHourSecondaryTransactionsBlob");
foreach (var l in tt.CreateQuery<DynamicTableEntity>().Select(x => x).Take(2).ToList())
{
Console.WriteLine(l.PartitionKey, l.RowKey);
l.Properties
.Where(x => x.Value.PropertyType == EdmType.Int64)
.Take(5)
.ToList()
.ForEach(pair => Console.WriteLine("\t\tkey: {0} value: {1}", pair.Key, pair.Value.Int64Value));
}
ちなみにセカンダリ側のトランザクションログについてはPreviewなのでまだ無いようです。
※RA-GRSでない場合(LRS/GRS)、セカンダリの指標は空です。
まとめ
FQDNと読み取り専用、というところを考慮すれば面白い使い方ができそうですね。
また他の制約や細かい点については Windows Azure ストレージの冗長オプションと読み取りアクセス地理冗長ストレージ を参照ください。
