先日は瞬殺で作るMesos + Chronos + Marathon + Dockerクラスタ環境で書いた通り、Mesosの環境を作ったのですが、今回はマイクロソフトが発表したService Fabricを触ってみました。実際触ってみるとかなり面白い感じなので、皆さんにも共有してみたいと思います。
Service Fabricとは
Azure Service Fabricはマイクロサービスベースのアプリケーションを素早く開発するためのPaaSです。
マイクロサービスとは
その前にそもそもマイクロサービスとは何でしょうか?マイクロサービスはマーチンファウラー氏が提唱した用語です。具体的なイメージはこの記事がわかりやすいです。モノリシックなRubyからGoによるマイクロサービスへ
上記マーチンファウラー氏のMicroservicesのページより
今までのアプリケーションは例えば私も大好きなRuby on Railsを想像すればわかりやすいですが、モノリシックといわれる構造になっています。一つのサービスの中にさまざまな機能が盛り込まれています。例えばPDF作成だとか、業務的な個別の機能だったり、アラート機能かもしれません。もちろんサービス内部はオブジェクト指向等の考え方を使ってモジュール化されていますが、サービス自体はたくさんの機能を含んで一つにまとまっています。一方、マイクロサービスは、「それぞれのプロセスで動作する単一の問題を解決するサービス」のことを指します。
マイクロサービスアーキテクチャは、複数のマイクロサービスを組み合わせて、大きなサービスを提供するようなアーキテクチャを指します。
上記マーチンファウラー氏のMicroservicesのページより
マイクロサービスが解決する問題
モノリシックな構造のアプリケーションはもちろんうまくいったのですが、クラウドが活用されるようになったときに、何かをちょっとでも変更すると、サービス全体をデプロイしないといけませんし、サービス自体の複雑さが増しているので、メンテナンスも難しくなってきています。
また、大量のトランザクションに耐えるためにはスケールすることが求められますが、複雑なサービス全体をスケールさせるようにする手間はとても面倒くさいものです。
これらの問題を解決する方法の一つがマイクロサービスのアーキテクチャを採用することです。
マイクロサービスを組み合わせた構成を採用すると、それぞれのサービスが小さく、分割されているので、デプロイも容易ですし、必要な個別のサービスだけをデプロイすることができるので、それぞれのサービスを保守することはそこまで難しくなくなります。またスケールに関しても、大きく複雑なモノリシックのアプリケーションをスケールさせるのは大変ですが、小さなサービスをスケール指させることはそんなに難しくありません。Docker等のコンテナ技術はこのような背景で生まれてきたと思います。
マイクロサービスを実装する難しさ
さて、この良さそうな雰囲気のマイクロサービスですが、実際に開発すると次のような問題が出てきそうです。
- 並列処理を意識したコードを書かないといけない
- サービスを組み合わせが大変そう
- どうやってテストするの?
マイクロサービスはマイクロサービス化されるような設計でコードを書く必要があります。また、大量のトランザクションをこなすということを考えると、マルチコア時代なので、並列処理を意識したコーディングも必要でしょう。そこで近年では、並列処理がしやすく、副作用の無い関数型プログラミングが脚光を浴びています。
さらに、マイクロサービスができたとして、きっとそれを組み合わせて動かしたり、テストしたりするのも相当大変かと思われます。場合によっては、クラスタの中のサーバーが落ちる場合もありますが、その時はどうしましょうか?
実際に私もDockerを利用していますが、それぞれのサービスを作るのは本当に楽だな!と感じますが、今の課題は組み合わせやそれを作ったテストをどうやって行くかに移行しています。
Azure Service Fabricの面白いところ
日本ではあまり注目されていないみたいですが、その点がAzure Service Fabricのとても面白いところです。Azure Service Fabricは、アプリケーション開発者がマイクロサービスを開発するときに、サービス自体の開発以外のめんどくさいことに頭を悩ませなくても開発できる基盤がそろっている感じです。マジでこれは楽そう。
Microsoft Announces Azure Service Fabric, A New Framework For Building Highly Scalable Cloud Servicesより
実際に触ってみると、ローカルにガチにクラスタ環境を簡単に作れたり、マイクロサービスを作るもの本当にちゃっちゃとできる感じでした。デプロイやオーケストレーションも簡単です。そして、また、クラスタにデプロイしたマイクロサービスのテストなんかも簡単にできる基盤がそろっていて、ご丁寧に様々な障害を発生させる機能までありますw
並列処理に関しても、Scalaでも採用しているActorという仕組みを使ってちゃっちゃとコーディングができるようになっています。
さらに、お気に入りの部分がこの、Service Fabricは結構枯れた技術であるということです。なんでここまで実際の運用考えてすごくよくできているのかな?と思ったのですが、その理由がここでも述べられているとおり、この技術はマイクロソフトがAzure SQL Databases や Azure Document DBやSkype for Business等のガチサービスで使っている基盤だからとのことです。つまり、自分でガチスケールする、そういう基盤が自分で作れちゃうサービスなわけです。これは熱いっですね。あーなんか作りたい!
Service Fabricの環境づくり
もう説明はそろそろいいでしょう。実際に触ってみましょう。
英語に抵抗ない人はSet up your Service Fabric development environmentを読んでみてください。
現在サポートしている環境は下記の通りのようです。将来はLinuxもサポートするらしいです。
OS
- Windows 8/8.1
- Windows Server 2012 R2
- Windows 10 Techinical Preview
Visual Studio 2015
Visual Studio 2015 RC が必要のようです。ここ
からダウンロードできました。
NOTE: 日本語環境ではまだ動作しなさそうです
あと、手順には載ってませんでしたが、まだPreviewということもあり、言語が日本語の場合、うまく動作しませんでした。Visual Studio 2015 RCをダウンロードすると日本語環境のOSの場合、日本語になってしまうので、その場合、Language Packをダウンロードするとうまくいきました。Language Packをインストールしたら、Tools(ツール) > Options (オプション) > International Settings(国際化設定)で英語モードを有効にしてトライしましょう。
Microsoft Visual Studio 2015 RC Language Pack
SDK
ここからダウンロードできます。
ダウンロードしてら、実行して、インストールしてください。
PowerShellの設定
Service FabricはPowerShellを使って、開発環境を作ったり、デプロイするようなのですが、デフォルトでVisual Studioからできないようです。次のコマンドを管理者権限で実行されたPowerShellで実行しましょう。
尚、管理者権限でPowerShellを実行する方法は、PowerShellを起動させてから
Start-Process powershell -Verb runAs
を実行すると、管理者権限で実行されたPowerShellのウインドウが立ち上がります。
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force -Scope CurrentUser
クラスタのインストールと設定
Service Fabricのクラスタを作ります。同じく、管理者権限で実行されたPowerShellで以下のコマンドを実行します。
cd "$env:ProgramFiles\Microsoft SDKs\Service Fabric\ClusterSetup"
.\DevClusterSetup.ps1
スクリプトが実行されて、私の環境だとC:\SfDevCluster
にクラスターのファイルが生成されました。
###クラスタの確認
次のコマンドを管理者権限のPowerShellで実行すると、ローカルに作ったクラスタ用のエクスプローラを実行することができます。
cd "$env:ProgramFiles\Microsoft SDKs\ServiceFabric\Tools\ServiceFabricExplorer"
.\ServiceFabricExplorer.exe
次のようなService Fabricエクスプローラが起動したら準備完了です!スクリーンショットのように、Nodeが緑色になってるのを確認してくださいね。
Service Fabricで開発してみる
プログラミングモデルを選ぶ
Service Fabricの開発では、次の中からプログラミングモデルを選びます。
- Reliable Actors API (Stateless)
- Reliable Actors API (Stateful)
- Reilable Service API (Stateless)
- Reliable Service API (Stateful)
おおざっぱなイメージを説明しておきますと、Actors APIのほうは、独立した小さなロジックをクラスタで実行したいようなケースで用います。並行性とか、どのクラスタで実行するかとかは、Service Fabricがやってくれます。
一方、Service APIのほうはそれよりももう少し大きな粒度です。サンプルで出てくる例としては、Service APIでStatelessのサービスを使って、WebApiサービスを作成する例が出てきました。
両方のパターンにおいて、Stateless/Statelfulサービスがあります。Statelessだとステートを共有できませんが、Statefulだと、クラスタ間でデータを共有してくれて、なおかつ、ノードが死んだ場合も自動的に引き継がれます。
Buildの動画では、この4種類以外にも、Node.js のアプリをデプロイとかもできたりするよと説明がありましたが、その手順は今のところ私は見つけられていません。
Stateful Actorでクラスタと戯れてみる
じゃあ、早速Actorを作ってみましょう。Templates > Visual C# > Cloud > Service FabricのApplication with Stateful Actor Serviceを選択します。プロジェクト名は何でもいいですが、私はHelloStatefulActorという名前にしてみました。
たくさんのテンプレートファイルが生成されました。まずはマイクロサービス側のActorを一作ってみます。
Stateful Actorを作成する
実はこの時点で、Statefulなサンプルが出来上がっていますが、Actorのイメージをつかむためにも自分でもコーディングしてみましょう。
まずはIHelloStatefulActor.csを開いてください。これはアクターのインターフェイスです。SayHelloというメソッドを追加してみました。
HelloStatefulActor.cs
インターフェイスを追加したので、実装のほうにもSayHelloを追加しましょう。ほかの部分はもともとテンプレートから作成されています。単純にわたってきた引数に対して、こんにちわを返しているだけですね。この部分はステートレスですが、参考のため書いています。
namespace HelloStatefulActor.Interfaces
{
public interface IHelloStatefulActor : IActor
{
Task<int> GetCountAsync();
Task SetCountAsync(int count);
Task<string> SayHello(string greeting); // 追加した
}
}
HelloStatefulActor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using HelloStatefulActor.Interfaces;
using Microsoft.ServiceFabric;
using Microsoft.ServiceFabric.Actors;
namespace HelloStatefulActor
{
public class HelloStatefulActor : Actor<HelloStatefulActorState>, IHelloStatefulActor
{
public override Task OnActivateAsync()
{
if (this.State == null)
{
this.State = new HelloStatefulActorState() { Count = 0 };
}
ActorEventSource.Current.ActorMessage(this, "State initialized to {0}", this.State);
return Task.FromResult(true);
}
public Task<int> GetCountAsync()
{
ActorEventSource.Current.ActorMessage(this, "Getting current count value as {0}", this.State.Count);
return Task.FromResult(this.State.Count);
}
public Task SetCountAsync(int count)
{
ActorEventSource.Current.ActorMessage(this, "Setting current count of value to {0}", count);
this.State.Count = count;
return Task.FromResult(true);
}
public Task<string> SayHello(string greeting) // 追加した
{
return Task.FromResult("You said: '" + greeting + "', I say: Hello Stateful Actors!");
}
}
}
はい、これでサービス側終わり。むっちゃ簡単ですね。
Program.cs
次にクライアント側です。複雑そうですが、大したことはしていません。最初にActorをクライアントから使うためには、クラスタに接続する必要がありますが、次のようなActorProxyを使うことで簡単に接続することができます。
var proxy = ActorProxy.Create<IHelloStatefulActor>(ActorId.NewId(), "fabric:/HelloStatefulActorApplication");
次にSayHelloを実際にクライアントから読んでるところを見てみます。むっちゃ簡単ですね。proxyに対してそのままSayHelloを呼ぶだけです。これで実際にはクラスタにアクセスされて、現在actorが実行されているノードで処理が実施されます。
Console.WriteLine("From Actor {0}: {1}", proxy.GetActorId(), proxy.SayHello("よろしく").Result);
その後は、既存のメソッドを使って、自動的に割り当てられるActorのIDと、カウンタの数字を無限ループでインクリメントするだけの作りになっています。
using HelloStatefulActor.Interfaces;
using Microsoft.ServiceFabric.Actors;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace HelloStatefulActor.Client
{
public class Program
{
public static void Main(string[] args)
{
// Actorプロキシの取得
var proxy = ActorProxy.Create<IHelloStatefulActor>(ActorId.NewId(), "fabric:/HelloStatefulActorApplication");
// SayHelloを使ってみる
Console.WriteLine("From Actor {0}: {1}", proxy.GetActorId(), proxy.SayHello("よろしく").Result);
// Stateful Actorを使ってみる
int count = 10;
while(true)
{
count = proxy.GetCountAsync().Result;
Console.WriteLine("Count from Actor {1}: {0}", proxy.GetActorId(), count);
count++;
Console.WriteLine("Setting Count to in Actor {0}: {1}", proxy.GetActorId(), count);
proxy.SetCountAsync(count).Wait();
Thread.Sleep(100);
}
}
}
}
クラスタにデプロイしてみる
実際に、デバッグ実行してみましょう。スクリーンショットの通りStartボタンを押します。
作成したActorがデプロイされている感じです。Diagnostic Eventのウインドウを見るとActorがクラスタにデプロイされている感じです。
実際に、Service Fabric Explorerを見てみると、ガッツリデプロイされています。しかもクラスタ構成になっていて、プライマリとかセカンダリなどのレプリカセットが組まれてはります。
これは楽ちんですね。
クライアントからActorを動かしてみる
最後に、さくっとクライアントからActorを動かしてみましょう。一旦デバッグ実行を止めて、クライアントをビルドしてみます。HelloStatefulActor.Clientを右クリックしてBuildを選択してみてください。
クライアントの実行
さて、ビルドされたクライアントの場所は環境によって異なりますが、上記でビルドするとOutput タブにクライアントのexeの場所が出力されています。私の環境だと、PowerShellで下記のようにすると実行されました。
cd "C:\Users\ushio\Source\HelloFabric\HelloStatefulActor\HelloStatefulActor.Client\bin\Debug"
.\HelloStatefulActor.Client.exe
実行していると、次のように、最初のSayHelloが実行されたのちに、whileループにより、Actorが定期的に呼ばれて、1づつ数値がインクリメントされます。
クラスタの障害を再現してみる
さて、これだけだと面白くないので、クラスタに障害を起こしてみましょう。Fabric Explorerを開いて、Application View > HelloStatefulActorApplicationType > fabric:/HelloStatefulActorApplication > fabric:/HelloStatefulActorApplication/HelloStatefulActorService
をみると、HelloStatefulActorのレプリカセットが複数デプロイされていることがわかります。それぞれ、Primary, Secondaryを持ったレプリカ構成になっています
現在、PowerShellで実行しているクライアントを見ると数字が出ています。これは、自動で割り振られているActorのIDです。
From Actor 3878874698898263532: You said: 'よろしく', I say: Hello Stateful Actors!
Count from Actor 0: 3878874698898263532
Setting Count to in Actor 3878874698898263532: 1
Count from Actor 1: 3878874698898263532
Setting Count to in Actor 3878874698898263532: 2
Count from Actor 2: 3878874698898263532
Setting Count to in Actor 3878874698898263532: 3
Count from Actor 3: 3878874698898263532
このPartitionInformation のLowKey とHighKeyの数字の間にこのアクターのIDが含まれているレプリカセットを探します。
私の例では下記のレプリカセットがそれに該当します。そのレプリカセットのPrimaryのノードが障害を起こしたと仮定して実際にノードを停止させてみます。上記のスクリーンショットの例だと、PrimaryはNode.5ですね。
LowKey : 3074457345618258604
HighKey : 5124095576030431005
Node View > Node.x (停止させるノードは先ほどの手順で発見した、いま実行中のアクターが使っているレプリカセットのプライマリノードを停止させてください)を右クリックして停止させています。
尚、Fabric Explorerの表示は、リロードボタンをクリックしないと変わりませんのであしからず。
リロードボタンを押すと、Nodeが停止されて、レプリカセットのノードのセカンダリがプライマリに自動的に昇格(この例ではNode.1がプライマリに昇格しています)しているのがわかります。
そしてさらにかっこいいことに、PowerShellを見ていると、ノードを落とした後一瞬動作が止まるのですが、数値のインクリメント自体は番号がかけることなく実行されます。
つまり、レプリカ間でしっかりステートが共有されていて、ノードの障害時にも他のノードがその数値を引き継いで処理を実行しているのがわかります。うーん。かっこいい!これがこれだけのプログラミングでできてしまうService Fabricは相当素敵です!
まとめ
さて、いかがでしたでしょうか?最初はピンと来なかったのですが、実際に触ってみるとこれは相当おしゃれ感が漂っています。
こんなにも簡単にクラスタ用のアプリが作れて、簡単に障害に強いような環境ができちゃうのは末恐ろしいですね。マイクロソフトが言っている「ビジネスロジックに集中できるようにする」というのは本当ですね。
自分はオープンソース系なので、マイクロソフト基盤に触ってこなかったですが、さすがの完成度です。次回はServiceの方を深掘りして実際何か作ってみたり、Azureでクラスタ組んだりしてみたいですね!
ちなみにアーキテクチャをよく理解したいかたは次の動画とか見るとおすすめです!Service Fabric documentation
では、よいマイクロサービスを!