しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Web Apps にひっそりと Go がインストールされていた

久しぶりに Azure Websites から名前が変わった Web Apps の話です。何となく Web Apps のシステムドライブを眺めていると、64 ビット版の Go がインストールされていることに気が付きました。

f:id:shiba-yan:20150412235958p:plain

バージョンは 1.4.2 で、GOROOT をポータルから設定すればちゃんと動作します。

f:id:shiba-yan:20150413000028p:plain

ランタイムがインストールされていれば、基本的に HttpPlatformHandler を使って HTTP リクエストを流し込むことが出来るので、Azure Web Apps で公式に Go がサポートされる日は近い気がします。

同じことになるので、以前に Go を動かした時の記事をいくつか紹介しておきます。

これまでは Site Extension Gallery から Go Lang for Azure Websites をインストールすれば楽でしたが、一番めんどくさいランタイムが標準でインストールされたというのは喜ばしい感じです。

てか、どう考えても Wade Wegner が暗躍していると思います。

Azure Search のプレビュー API に追加された ja.microsoft アナライザーとは何者なのか

先日公開された Azure Search の 2015-02-28-Preview API には、More Like This の他にアナライザーの追加が行われています。基本的には Microsoft が開発して Office や Bing で使われているものらしいです。

  • 50 analyzers backed by proprietary Microsoft natural language processing technology used in Office and Bing.
Azure Search Service REST API Version 2015-02-28-Preview

日本語のアナライザーも Lucene と Microsoft の 2 種類が用意されるようになりました。

  • ja.lucene
    • Uses morphological analysis
    • Normalizes common katakana spelling variations
    • Light stopwords/stoptags removal
    • Character width-normalization
    • Lemmatization - reduces inflected adjectives and verbs to their base form
  • ja.microsoft
    • Uses morphological analysis

とりあえず試してみようと思ったので、まずは RedDot.Search を前回と同様に改造しました。一応、対応したバージョンはブランチ切って GitHub にプッシュしてあります。

このフォークした RedDog.Search を使えば、以下のようなコードで新しいアナライザーを指定できます。

var connection = ApiConnection.Create("SEARCH_NAME", "ADMIN_KEY");

var client = new IndexManagementClient(connection);

await client.CreateIndexAsync(new Index("entries")
    .WithStringField("id", x => x.IsKey().IsRetrievable())
    .WithStringField("title", x => x.IsSearchable().IsRetrievable().Analyzer("ja.microsoft"))
    .WithStringField("body", x => x.IsSearchable().IsRetrievable().Analyzer("ja.microsoft")));

プレビューポータルで確認すると、以下のように表示されます。でも設定が出来ないのは残念ですね。

f:id:shiba-yan:20150307135924p:plain

インデックスへのドキュメントの追加は、前回と同様にこのブログの記事を全て投入しました。検索キーワードは何時ものアレを使って、まずは ja.lucene の方から試します。

f:id:shiba-yan:20150307140553p:plain

ハイライトを有効にすると、ある程度はどのように形態素解析されたか確認出来ます。

f:id:shiba-yan:20150307140212p:plain

キーワード的にはいい感じに認識されている感じです。

次は ja.microsoft を指定したインデックスで検索してみます。

f:id:shiba-yan:20150307140748p:plain

いい感じに見えますが、最後に全く関係なさそうな記事が混ざってきています。ハイライトの結果は以下の通りになりました。

f:id:shiba-yan:20150307140235p:plain

Lucene に比べると、やたら細かくハイライトされてます。細切れにしすぎた結果、関係ない記事が検索結果に混ざってきたと考えるのが妥当な気がします。*1

ちなみに ja.lucene と ja.microsoft だと処理速度がかなり違ってきました。自分が試した範囲だと ja.microsoft が桁違いに遅かったので、基本的には ja.lucene 使っておけば良いと思います。

*1:検索結果が結構 Bing っぽい、悪い意味で

Azure Search に More Like This API が追加されたので類似ドキュメント検索を実装して遊んでみた

Azure Search が今日 GA になりましたが、GA になったニュースよりも新しい API に追加された機能に注目します。GA になってどう変わるかは、ブチザッキを見ておけば全て理解できます。

2015-02-28-Preview: This is the new “prototype” version where we bring in new experimental features. In this version you will find the enhancements to multilanguage support as well as the new “more like this” feature.

Azure Search is now Generally Available | Blog | Microsoft Azure

the new “more like this” feature と言う部分がとても気になりました。

2015-02-28-Preview API のドキュメントに、もう少し細かく説明が書いてます。

  • Natural language processors from Microsoft (the same ones used by Office and Bing) offer greater precision over query results and more languages.
  • moreLikeThis is a a query parameter used in Search operations that finds other documents that are relevant to another specific document.
Azure Search Service REST API Version 2017-11-11-Preview | Microsoft Docs

Microsoft が Office や Bing で使っている NLP が Azure Search のアナライザーとして使えるようになりました。まあ、それはインデックスを作るときに ja.microsoft とか指定すればいいので、とりあえず放置します。形態素解析の精度ぐらいの差でしょう。

それよりも注目したいのが moreLikeThis パラメータが検索処理に追加されたことです。moreLikeThis パラメータは、Azure Search のバックエンドの Elasticsearch に実装されている類似ドキュメントの検索を行うための API で、これがついに解放されました。

Elasticsearch の more like this に関しては、クックパッドさんのブログが分かりやすかったです。

つまり Elasticsearch を自分で用意しなくても Azure Search を使えば、同じように関連するドキュメントの検索が簡単に出来るようなりました!素晴らしい!*1

実際に Azure Search を使って、このブログの類似ドキュメント検索を試してみました。クックパッドさんのブログをめっちゃ参考にしました。

Azure Search で類似ドキュメントの検索を試す

まずはインデックスを作る必要があるわけですが、構造は全く同じ形で用意しました。

f:id:shiba-yan:20150306174421p:plain

英語にしてあるのは SORTABLE が日本語では検索可能と翻訳されていたからです。まだまだプレビュー感があってたまりませんね。

Azure Search が GA したおかげか、アナライザーの設定までプレビューポータルから行えるようになっていてとても便利です。毎回 REST API を叩く必要もなく最高です。

f:id:shiba-yan:20150306165500p:plain

まだプレビューポータルでは ja.microsoft は選べなかったので、Lucene を選びました。

インデックスが出来たので、実際に Azure Search へドキュメントを追加していくのですが、はてなブログのエクスポートは MT 形式でとてもめんどくさいので、Ruby なオリジナルのコードを C# に移植しました。

var content = File.ReadAllText(@"blog.shibayan.jp.export.txt");

var matches = Regex.Matches(content, @"TITLE:\s(.*?)\n.*?STATUS:\s(.*?)\n.*?DATE:\s(.*?)\n.*?BODY:\n(.*?)----\n", RegexOptions.Singleline);

var entries = matches.Cast<Match>().Select(x => new Entry
{
    Id = Regex.Replace(x.Groups[3].Value, @"\D", ""),
    Title = x.Groups[1].Value,
    Body = StripHtml(x.Groups[4].Value)
}).ToArray();

地味に扱いにくいので、XML とか JSON で吐き出して貰いたいものです。

これで全エントリのデータを配列に詰め込めたので、後は今回新しくリリースされた Azure Search の .NET SDK を使って投入します。

var serviceClient = new SearchServiceClient("SEARCH_NAME", new SearchCredentials("ADMIN_KEY"));

var indexClient = serviceClient.Indexes.GetClient("entries");

// ストレージみたいに 1000 件ずつしか入らないので工夫する
for (int i = 0; i < (entries.Length / 1000) + 1; i++)
{
    indexClient.Documents.Index(IndexBatch.Create(entries.Skip(1000 * i).Take(1000).Select(IndexAction.Create)));
}

API の呼び出し自体は結構素早いですが、インデックスへの投入は非同期で行っているみたいなので、全件処理されるまで少しかかった気がします。

これで、このはてなブログの全エントリデータを Azure Search に投入できました。

f:id:shiba-yan:20150306173508p:plain

とりあえず、ちゃんとインデックスが出来ているか検索して確認しておきます。

var serviceClient = new SearchServiceClient("SEARCH_NAME", new SearchCredentials("QUERY_KEY"));

var indexClient = serviceClient.Indexes.GetClient("entries");

var response = indexClient.Documents.Search<Entry>("抱かれたい男", new SearchParameters
{
    Top = 5
});

foreach (var item in response)
{
    Console.WriteLine("{0} : {1}", item.Score, item.Document.Title);
}

地味に型付きで検索が出来るのが良いです。いい感じにインデックス周りの処理が書けます。

実行してみた結果は以下の通りです。スコアはあまり高くないですが、まずまずの検索が出来ています。

f:id:shiba-yan:20150306174919p:plain

それでは本題の More Like This API を使って、類似ドキュメントの検索を行ってみます。残念ながらプレビュー API に SDK が対応していないので、例によって RedDog.Search を改造して試します。

類似するドキュメントを見つけたい記事は、以下の Knockout.js について書いたものにしました。

RedDog.Search を改造して moreLikeThis パラメータを取れるようにしただけなので、コードとしては以下のような形になります。moreLikeThis には類似ドキュメント検索をしたい元のキーを渡します。なので、今回の場合は日付ベースの値です。

var connection = ApiConnection.Create("SEARCH_NAME", "QUERY_KEY");

var client = new IndexQueryClient(connection);

var results = await client.SearchAsync("entries", new SearchQuery()
    .MoreLikeThis("08192014002348")
    .Top(10));

foreach (var item in results.Body.Records)
{
    Console.WriteLine("{0} : {1}", item.Score, item.Properties["title"]);
}

実行してみた結果は以下の通りです。ちゃんと Knockout.js に関係する記事を取得出来ています。

f:id:shiba-yan:20150306180725p:plain

今度は別の記事で試してみました。ASP.NET MVC について書いた記事です。

これも同様に moreLikeThis にキーを渡して実行してみました。

f:id:shiba-yan:20150306181432p:plain

ASP.NET に関する記事は多いためスコアは思ったほど高くはないですが、ちゃんと関係する記事のみを取得出来ていることが確認出来ました。

本来なら複雑な処理が必要なところ、とりあえずインデックスにデータを入れておけば使える点、かなり便利だと感じました。是非ともお仕事で使ってみたいので、日本リージョンにデプロイお願いします。

*1:最初から Elasticseach の API を全て公開しておけと言う意見は置いておく。

Azure Blob Storage へのアップロード時にファイルの MD5 を同時に計算して保存する

Azure Blob へファイルなどをアップロードしたタイミングで、MD5 を計算してくれる便利な設定があるようです。アップロード前に自前でハッシュ値の計算をしなくても、この値を使って楽が出来そうです。

BlobRequestOptions.StoreBlobContentMD5 Property (Microsoft.WindowsAzure.Storage.Blob)

最初は出来ないと思ってましたが、この設定を Azure Storage の神ことおーみさんが教えてくれました。

アップロード時の処理と、保存された Blob から MD5 を取得する方法を試しました。

MD5 を保存する設定でアップロード

少し見た感じだと、コンテナ単位で設定出来そうな気がしないこともないですが、今回は BlobClient のデフォルト設定を書き換えてしまいます。

// ストレージアカウントを用意
var storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=**;AccountKey=**");

var blobClient = storageAccount.CreateCloudBlobClient();

// デフォルトの設定を書き換える
blobClient.DefaultRequestOptions = new BlobRequestOptions
{
    StoreBlobContentMD5 = true
};

// コンテナが無ければ作る
var container = blobClient.GetContainerReference("md5sample");
container.CreateIfNotExists();

// Blob へファイルをアップロード
var blob = container.GetBlockBlobReference("sample.txt");

blob.UploadFromFile(@"****.txt", FileMode.Open);

// ファイルの MD5 を表示
Console.WriteLine(blob.Properties.ContentMD5);

極めて普通の Blob へアップロードするコードですが、StoreBlobContentMD5 の設定だけ変えてます。

実際にアップロードして CloudBerry Explorer でメタデータを確認してみます。

f:id:shiba-yan:20150301183954p:plain

Content-MD5 にファイルの MD5 が Base64 で保存されています。これを使えばいいですね。

保存した MD5 を取得

アップロード直後には ContentMD5 プロパティで取れますが、後から取得したい場合には FetchAttributes メソッドを呼び出してから ContentMD5 プロパティを参照するようにします。

var container = blobClient.GetContainerReference("md5sample");
var blob = container.GetBlockBlobReference("sample.txt");

// メタデータだけを取得
blob.FetchAttributes();

// ファイルの MD5 を表示
Console.WriteLine(blob.Properties.ContentMD5);

これで ContentMD5 にちゃんとファイルのハッシュ値が入ってきます。

f:id:shiba-yan:20150301184208p:plain

おまけ

Azure Blob のメタデータは HTTP ヘッダーとして返されるので、ブラウザで直接アクセスして Content-MD5 を取得することも出来ます。実際に Chrome で確認してみました。

f:id:shiba-yan:20150301184309p:plain

全ての方法で同じ値が返ってきているので安心できます。

ちなみに Content-MD5 の値は Base64 されているので、よく見る MD5 のハッシュ値とは見た目が異なってます。なので、変換するコードを最後に載せておきます。

// Base64 からバイト配列に戻す
var buffer = Convert.FromBase64String(blob.Properties.ContentMD5);

// 16 進数の表記に変換
var hash = BitConverter.ToString(buffer).Replace("-", "").ToLower();

Console.WriteLine(hash);

これを実行すると、よく見る形式で表示されます。

f:id:shiba-yan:20150301185813p:plain

勝手に MD5 の計算をしてくれるのは、アップロード時にストリームを使う場合に便利だと思うので、これからは使っていきたいですね。MD5 で良いのかという気はしないこともないですが。

Visual Studio と Azure Websites の発行周り小ネタまとめ

最近は 2 つの Websites へ同時にデプロイしたい欲が高まってきたので、コマンドラインからデプロイする方法を簡単に調べました。MSBuild 使えば出来るようです。

それ以外にも調べている途中に見つけた小ネタを忘れないようにメモしておきます。

ちなみに基本的な pubxml の中身はこんな感じです。発行ダイアログの設定から一部は変更可能です。

コマンドラインからデプロイする

MSBuild を使うと Visual Studio から行うデプロイとほぼ同じことが出来ます。ただしパスワードだけは Visual Studio が暗号化したものを参照できないため、指定する必要があるようです。

pubxml を直接弄ってパスワードを書く手もあるみたいですが、間違って Git にプッシュするのも怖いですし、妥協する部分なのかもしれません。

コマンドラインからのデプロイについては、ASP.NET 公式サイトのドキュメントで説明されていました。

実際に実行するコマンドは以下のような感じになるかと思います。

msbuild WebApplication.sln /p:DeployOnBuild=true /p:PublishProfiles=hoge /p:Configuration=Release /p:Password=*****

パスワードは Azure Websites なら発行プロファイルや Get-AzureWebsite で取得出来るやつです。

実際に確認はしていないですが、Azure Websites からダウンロード可能な .publishsettings ファイルを直接指定することも出来るみたいです。

.wpp.targets ファイルを用意する必要はありますが、割とサクッとデプロイ出来そうな感じです。

msbuild WebApplication.sln /p:DeployOnBuild=true /p:PublishSettingsFile=hoge.publishsettings /p:Configuration=Release

パスワードの指定が必要なくていい感じがしますが、Web.config の変換周りが動かない気がします。

デプロイ後にサイトを表示しない

Visual Studio で publishsettings ファイルをインポートすると作成される pubxml を開くと、LaunchSiteAfterPublish と言う名前の要素が存在しているはずなので、値を False に変更します。

<PropertyGroup>
  <LaunchSiteAfterPublish>False</LaunchSiteAfterPublish>
</PropertyGroup>

これでデプロイ完了後に毎回ブラウザが立ち上がることが無くなります。

不要なファイルをデプロイしない

対象 の pubxml に ExcludeFilesFromDeployment や ExcludeFoldersFromDeployment という要素を追加すると、ファイルやフォルダ単位でデプロイから除外することが出来るようです。

デフォルトではこの要素は含まれていないので、自分で追加する必要があります。

<PropertyGroup>
  <ExcludeFilesFromDeployment>
    foo.txt;bar.txt;baz.txt
  </ExcludeFilesFromDeployment>
  <ExcludeFoldersFromDeployment>
    hogehoge;hauhau
  </ExcludeFoldersFromDeployment>
</PropertyGroup> 

Web Deployment FAQ に情報がまとまっていました。思っていた以上に色々なことが出来るようです。

ちなみに pubxml を直接編集する以外にも .wpp.targets ファイルを作成しておけば、それの内容を自動的に反映してくれるみたいですね。

基本的に Visual Studio からデプロイしていただけのゆとりなので、もうちょっと MSBuild と MSDeploy 周りは勉強しておきたいと思いました。*1

*1:Websites なら Git な気がしないこともないけど

プレビューポータルで Azure Websites の Slot Sticky な設定を追加出来るようになっていた

これまでは Azure PowerShell でしか行えなかった Slot Sticky な設定を、プレビューポータルから簡単に追加出来るようになっていました。チェックボックスなのでとても簡単です。

スロットの設定にチェックを入れて保存すると、その設定はスロットに固有な設定になります。

f:id:shiba-yan:20150213004314p:plain

当然ながらスワップした場合に変更されなくなります。

ちなみに Azure Websites のスワップ時に変更される項目についてはドキュメントに記載があります。

スロットのスワップ時に変更される構成:

  • 全般設定
  • Connection strings
  • ハンドラー マッピング
  • 監視と診断の設定
Microsoft Azure Websites でのステージングされたデプロイメント

Slot Sticky な設定を使えば、ステージングスロットの場合には別の DB を見て動作といったことが簡単に実現出来るようになります。4 つまでスロットを作成できるので、使い道が増えていい感じです。

折角なので、実際に Slot Sticky な設定を追加して Azure PowerShell を叩いて確認してみました。

f:id:shiba-yan:20150213005957p:plain

Azure PowerShell で Websites の情報を見るためには Get-AzureWebsites コマンドレットを叩くだけです。

f:id:shiba-yan:20150216213009p:plain

SlotSkickyAppSettingNames と SlotStickyConnectionStringNames にポータルで設定したキー名が入っていることが確認出来ました。値自体はこれまで通り AppSettings と ConnectionStrings から取れます。

最後にスロットを作成して、本当に固有の設定になるのか確認しておきました。設定は以下のようにチェックを入れたものと入れてないものを用意します。

f:id:shiba-yan:20150216214405p:plain

これでまずはステージングスロットに反映されていないことを確認しておきます。

Get-AzureWebsite daruyanagi -Slot staging | Select Name, AppSettings, ConnectionStrings, SlotSticky*

分かりやすいように必要な情報だけを表示するようにしました。設定は空っぽですね。

f:id:shiba-yan:20150216215549p:plain

そしてスワップを行って、もう一度同じコマンドを実行してみます。

f:id:shiba-yan:20150216215439p:plain

スロットの設定にチェックを入れていない設定に関しては、スワップで変更されています。思っていた通りの挙動をしてくれたので、とても満足しています。

Azure Websites のディレクトリ構造が変わっていた件と HttpPlatformHandler がアップデートされていた話

今日 Websites の Kudu を見た時に変更点を見つけたので、そのついでに色々確認しておきました。

個人的には HttpPlatformHandler のアップデートが熱いです。

ディレクトリ構造が少し変更

これまでは C:\DWASFiles\Sites というパスに各サイト用のファイルが保存されていたのが、今日見てみると D:\local というパスに変わっていました。

f:id:shiba-yan:20150207181656p:plain

これまでは Temp ディレクトリのパスに ~ や # が混ざっていましたが、すっきりしていい感じですね。

環境変数に LOCAL_EXPANDED が追加された気がしました。これを使えば本当のパスを得ることが出来ますが、まあ使う場面はほとんどないでしょう。

f:id:shiba-yan:20150207184739p:plain

D ドライブにはマシン単位で永続化された部分、ローカルマシンに繋がれた揮発性の部分、ファイルサーバーに繋がったサイト全体で永続化されている部分の 3 つが混ざることになったので、マニアックな使い方している人は注意が必要かもしれません。

HttpPlatformHandler がアップデート

既に IIS 公式サイトで HttpPlatformHandler の新しいバージョンが配布されていましたが、やっと Websites でもアップデートが行われたようです。HttpPlatformHandler については以下を参照してください。

確認したところ、IIS 公式サイトで配布されているバージョンと同じものがインストールされたみたいなので、新機能の processesPerApplication を使って遊べるようになりました。

自分で Java の環境を作るのは心が折れたので、Websites でサクッと試してみました。

f:id:shiba-yan:20150207182815p:plain

processesPerApplication = 5 を指定しているので、Java のインスタンスが 5 つ立ち上がっています。ApplicationInitialization モジュールと組み合わせると、いい感じにプリロード出来るのではないでしょうか。

プロセスが立ち上がってからは HttpPlatformHandler がラウンドロビンでリクエストを振ってくれるので、不意にクラッシュしたとしてもちょっと安心です。

独自ドメインでホストしているサイトを Azure Websites へスムーズに移行する方法

Twitter で以下のように適当なことを言ってしまったので、懺悔を兼ねて書きます。

今までは Websites をリージョン間での移行しかやったことが無かったので、既存サイトから移行する方法について検証しました。結論としては以下の手順で問題無いです。

  1. awverify を指す CNAME レコードを DNS に追加
  2. 移行先の Websites にドメインを追加
  3. 既存の A / CNAME レコードが指す IP アドレス、ドメインを変更

実際に手持ちのドメインで検証したので順を追って見ていきます。まずは DNS に awverify を指す CNAME レコードを追加して、少しの時間待ちます。

f:id:shiba-yan:20150204162240p:plain

DNS の設定画面は使っているレジストラによって異なりますが、作業はほぼ同じになるはずです。

CNAME レコードが追加出来たら Azure の管理ポータルから Websites を選んでドメインの追加を行います。タイミングが良ければ、すぐに登録可能な状態になります。

f:id:shiba-yan:20150204162109p:plain

ドメイン名を入力して赤いアイコンが表示された場合には、少し時間を空けて再度試してください。今までの経験上、長くても 1 日以内には登録可能な状態になります。

確認のために既存のドメインに対して nslookup を叩いてみると、今までの IP アドレスが返ってきます。

f:id:shiba-yan:20150204162324p:plain

この時点で Websites 側で正しい IP アドレスやホスト名なのかチェックしているのかと思ってましたが、実際には awverify を使った場合にはそっちだけ見ているようです。

ドメインの登録が完了したら、後は好きなタイミングで既存の A / CNAME レコードが指している IP アドレスやドメインを Websites に書き換えれば完了です。

f:id:shiba-yan:20150204162445p:plain

これで DNS のキャッシュが更新されたタイミングで、新しい Websites にアクセスされるようになります。キャッシュが完全に新しくなるまで古いサイトを動かしておけば、ダウンタイムは発生しないでしょう。

Azure 上の Ubuntu に Nginx をインストールしてロードバランサーを構築してみた

IIS 上の Application Request Routing 3.0 と比較したかったので、その準備のために Ubuntu 上に Nginx でロードバランサーを設定してみました。

思ったよりあっさりと設定が終わったので、ちょっと拍子抜けという感じです。ちなみに手順は以下の通り。

  1. 仮想マシン(と仮想ネットワーク)を作成
  2. apt-get で Nginx をインストール
  3. nginx.conf にロードバランサーの設定を追加

大体、数十分で終わるような作業です。

仮想マシンを作成

ロードバランサーから実際の Web サーバまでは内部 IP で分かりやすくアクセスしたかったので、とりあえず仮想ネットワークを作成して全てのインスタンスを入れておくことに。

f:id:shiba-yan:20150203204509p:plain

難しく考えていましたが、割とあっさり出来ました。S2S とか P2S だと追加の設定が必要になるとか。

仮想マシンはおーみさんにおススメされたので、今回は Ubuntu 14.04 LTS を使いました。

f:id:shiba-yan:20150203205347p:plain

実際にインスタンスを作る方法は特に変わらないので省略します。

こんな感じで Ubuntu 14.04 LTS なインスタンスを 4 つ用意しました。1 つをロードバランサーにするので、そのインスタンスはエンドポイントとして 80 番を開けておきます。*1

Nginx をインストール

Nginx は apt-get を使ってサクッとインストールしました。

sudo apt-get install nginx

バージョンを確認すると 1.4.6 がインストールされたようです。ちょっと古めのバージョンですね。

$ nginx -v
nginx version: nginx/1.4.6 (Ubuntu)

既にサービスは起動しているので、ブラウザから URL を叩くとデフォルトのページが表示されます。

f:id:shiba-yan:20150203210043p:plain

後はロードバランサーの設定を行えば終わりです。

ロードバランサーの設定を行う

nginx.conf に upstream と proxy_pass を書けば、大体設定は終わったも同然のようです。設定ファイルのパスは /etc/nginx/nginx.conf なので http ブロック内に設定を追加しました。

upstream shibayan_nginx {
        server 10.0.0.5:80;
        server 10.0.0.6:80;
        server 10.0.0.7:80;

        keepalive 128;
}

server {
        listen 80;
        server_name localhost;

        location / {
                proxy_pass http://shibayan_nginx;

                proxy_http_version 1.1;
                proxy_set_header Connection "";
        }
}

設定に関しては、あくまでも 1 つの例と捉えてください。*2

これで nginx を再起動すれば設定が反映されて、ロードバランサーとして動作するようになります。

sudo nginx -s reload

ちなみに upstream で特に指定をしなければラウンドロビンになるようです。あと HTTP Keep-Alive を使わない場合、パフォーマンスがかなり悪化したので設定を追加しました。必須の設定のようです。

*1:本当なら Nginx インストール後にイメージ作成した方が楽かも

*2:Nginx 初心者なので間違っているかもしれません

Azure Search に追加された Data Source と Indexer を使って SQL Database から自動的に Index を作成する

ちょっと前に Azure Search について書いた時に、適当に流した Indexer 周りについて実際にコード書いていろいろ遊んでみたのでまとめます。

http://azure.microsoft.com/en-us/documentation/articles/search-api-indexers-2014-10-20-preview/

一応、作成したプロジェクトは GitHub に上げておきました。

間違って Azure Search の管理者キーを含んだまま上げてしまったんですが、ポータルから無効化しておいたので動きません。各自差し換えてください。

基本的な処理の流れとしては以下のようになります。

  1. 同名のカラムを持つ Index を作成
  2. 接続文字列から Data Source を作成
  3. Data Source と Index を指定して Indexer を作成
  4. 作成した Indexer を実行

順を追って解説していきます。

Index を作成

残念ながら Azure Search 側に SQL Database のスキーマを読み取って、自動的にいい感じにインデックスを作ってくれる機能が無かったので自分で作りました。

SQL Database でテーブルのスキーマを使ったことなかったんですが、割とあっさりできました。方法はいろいろあるっぽいですが、今回は INFORMATION_SCHEMA を叩いてみました。

SELECT
    COLUMN_NAME, DATA_TYPE
FROM
    INFORMATION_SCHEMA.COLUMNS
WHERE
    TABLE_SCHEMA = 'SalesLT' AND TABLE_NAME = 'Product'
ORDER BY ORDINAL_POSITION

実際にはパラメータ化クエリにしてコードに埋め込んでますが、実行してみると以下のような感じに。

いい感じにカラム名とデータ型が取れました。カラム以外に Azure Search ではキーが必要なので、プライマリキーを取っていたりしますが、コード見てもらう形で。

後はこの情報を使って Index を作成するのですが、SQL Database の型と Azure Search の型を合わせてあげる必要があるので注意。つまり対応してない型があるってことです。

Data Source を作成

本来なら RedDog.Search が対応していてもらいたい部分なんですが、Data Source と Indexer に関しては未実装なので手動で API を叩きます。

ただし RedDog.Search に便利なメソッドがあったので、それをそのまま使います。

connection.Execute(new ApiRequest("datasources", HttpMethod.Post)
{
    Body = new
    {
        name = "sampledb",
        type = "azuresql or docdb",
        credentials = new
        {
            connectionString = "SQL Database の接続文字列"
        },
        container = new
        {
            name = "[SalesLT].[Product]",
        }
    }
}).Wait();

特に説明するところのないコードなんですが、SQL Database の他にも DocumentDB をソースとして使えるみたいです。ただし接続文字列という概念は無さそうなので、どうやって使うのかは謎。

container に指定しているのは SQL Database の場合はテーブル名です。スキーマ名も省略せずに書いた方が良い気がしました。

Indexer を作成

ここまでで Index と Data Source が用意できたので、やっと Indexer を作成する準備が出来ました。と言っても、API を 1 つ叩くだけで終わります。

connection.Execute(new ApiRequest("indexers", HttpMethod.Post)
{
    Body = new
    {
        name = "sampleindexer",
        dataSourceName = "sampledb",
        targetIndexName = "product"
    }
}).Wait();

これもまた特に説明するところが無いコードですが、dataSourceName に作成済みの Data Source 名を、targetIndexName に作成済みの Index 名を指定して実行しているだけです。

Indexer の作成タイミングで実行スケジュールを指定できたりしますが、今回は簡単にするためにオンデマンド実行にします。

作成した Indexer を実行

これで Indexer が作成できたので、実際に動かしてみます。API も名前指定するだけで、とてもシンプル。

connection.Execute(new ApiRequest("indexers/sampleindexer/run", HttpMethod.Post)).Wait();

実行すると Azure Search が SQL Database に接続して、いい感じにデータを追加してくれます。

ポータルから確認するとデータが追加されていることが確認出来ました。Indexer を Azure Search 側で持ってくれていて、しかも Azure のサービスとの親和性の高さは今後が期待出来そうな感じです。

そろそろ Elasticsearch の勉強会とかに参加して知識を増やしたい感あります。