
マイクロ サービス アーキテクチャに基づいてソリューションを設計する場合、個々のコンポーネントを必要に応じて調整することなく、システム全体を迅速かつ簡単に管理する、可能な限り高度な自動化を実現するという要件に遭遇することがよくあります。
これは本当に難しいことであり、そのため、最も簡単な方法でマイクロ サービス アーキテクチャを確立する方法を説明するチュートリアルを準備することにしました。マイクロ サービス アーキテクチャは、迅速かつ簡単にスケールし、クライアントの要件に適合させることができます。
個々のサービスのコードや設定には干渉したくなく、Docker 内のコンテナをオーケストレーションすることによってのみシステムを制御したいと考えていました。
その結果、コンテナ設定をいくつか変更するだけで簡単に拡張できるシンプルなマイクロ サービス アーキテクチャが実現します。その他のすべては、ゲートウェイ/ロード バランサーとして Ocelot によって提供され、サービス ディスカバリ エージェントとして Consul によって提供されます。
このようなアーキテクチャにより、他のサービス内での展開を調整することなく、単一のサービスを再展開できます。再デプロイされたサービスはサービス検出時に自動的に登録され、ゲートウェイ経由ですぐに利用できるようになります。これがすべての開発チームにとってどれほど大きな後押しとなるか想像できるでしょう。
確かに、単一のゲートウェイ サービスを使用すると、アーキテクチャにとって単一障害点になるため、高可用性を実現するには、そのゲートウェイ サービスのインスタンスを少なくとも 2 つデプロイする必要があります。しかし、その問題はあなたに任せておきます。
前回のデモでは、サービス ディスカバリーのために Eureka とともにサービス ゲートウェイおよびロード バランサーとして Ocelot を実装する方法を示しました。 Eureka の代わりに、このデモではサービス検出に Consul を使用します。
Consul は、サービス検出、構成、セグメンテーション機能を備えたフル機能のコントロール プレーンを提供するサービス メッシュ ソリューションです。これらの各機能は、必要に応じて個別に使用することも、組み合わせて使用して完全なサービス メッシュを構築することもできます。 Consul にはデータ プレーンが必要で、プロキシとネイティブの両方の統合モデルをサポートします。 Consul にはシンプルな組み込みプロキシが同梱されているため、何も設定しなくてもすぐに動作しますが、Envoy などのサードパーティのプロキシ統合もサポートしています。
Consul の主な機能は次のとおりです。
サービス検出: Consul のクライアントは API や mysql などのサービスを登録でき、他のクライアントは Consul を使用して特定のサービスのプロバイダーを検出できます。 DNS または HTTP を使用すると、アプリケーションは依存するサービスを簡単に見つけることができます。
ヘルス チェック: Consul クライアントは、特定のサービス (「Web サーバーが 200 OK を返しているか」) またはローカル ノード (「メモリ使用率が 90% を下回っているか」) に関連付けられたヘルス チェックを、任意の数で提供できます。この情報は、オペレーターがクラスターの健全性を監視するために使用でき、サービス検出コンポーネントが異常なホストからトラフィックをルーティングするために使用されます。
KV ストア: アプリケーションは、動的な構成、機能のフラグ付け、調整、リーダーの選出などを含む、さまざまな目的で Consul の階層型キー/値ストアを利用できます。シンプルな HTTP API により使いやすくなります。
安全なサービス通信: Consul は、相互 TLS 接続を確立するためにサービスの TLS 証明書を生成および配布できます。インテンションを使用して、通信を許可するサービスを定義できます。サービスのセグメンテーションは、複雑なネットワーク トポロジや静的なファイアウォール ルールを使用する代わりに、リアルタイムで変更できる意図を使用して簡単に管理できます。
マルチ データセンター: Consul は、追加設定なしで複数のデータセンターをサポートします。これは、Consul のユーザーが、複数のリージョンに拡張するために追加の抽象化レイヤーを構築することを心配する必要がないことを意味します。
Consul は、DevOps コミュニティとアプリケーション開発者の両方にとって使いやすいように設計されており、最新の弾力性のあるインフラストラクチャに最適です。
出典: 領事紹介
このチュートリアルの重要な部分は、Consul を使用してサービス エンドポイントを動的に検出することです。サービスが Consul に登録されると、一般的な DNS またはカスタム API を使用してサービスを検出できます。
Consul は、これらのサービス インスタンスのヘルスチェックを提供します。サービス インスタンスまたはサービス自体の 1 つが異常であるか、ヘルスチェックに失敗した場合、レジストリはそれを認識し、サービスのアドレスを返すことを回避します。この場合、ロード バランサーが実行する作業はレジストリによって処理されます。
同じサービスの複数のインスタンスを使用しているため、Consul はトラフィックを異なるインスタンスにランダムに送信します。したがって、サービスのインスタンス間の負荷のバランスがとれます。
Consul は、集中型ロード バランサーを導入することなく、障害検出とサービスの複数のインスタンスにわたる負荷分散の課題に対処します。
レジストリは自動的に管理され、サービスの新しいインスタンスが登録され、トラフィックを受信できるようになると更新されます。これは、サービスを簡単に拡張するのに役立ちます。
Consul への自己登録の実装方法の詳細を説明する前に、自己登録を使用したサービス検出が実際にどのように機能するかを見てみましょう。
最初のステップでは、サービス インスタンスは、その名前、ID、およびアドレスを指定して、サービス ディスカバリ サービスに自身を登録します。その後、このゲートウェイは、名前/ID で Consul サービス検出をクエリすることにより、このサービスのアドレスを取得できるようになります。
ここで注意すべき重要な点は、同じ Consul サービス エージェント上で実行されているサービスのインスタンス間を明確にするために、サービス インスタンスが固有のサービス IDで登録されていることです。すべてのサービスがノードごとに一意の ID を持つ必要があるため、名前が競合する可能性がある場合 (今回の場合)、一意の ID を指定する必要があります。


.NET アプリケーションで自己登録を実装する方法を見てみましょう。まず、サービス検出に必要な構成を、 docker-compose.override.ymlファイルを通じて渡された環境変数から読み取る必要があります。
public static class ServiceConfigExtensions
{
public static ServiceConfig GetServiceConfig ( this IConfiguration configuration )
{
ArgumentNullException . ThrowIfNull ( configuration ) ;
ServiceConfig serviceConfig = new ( )
{
Id = configuration . GetValue < string > ( "ServiceConfig:Id" ) ,
Name = configuration . GetValue < string > ( "ServiceConfig:Name" ) ,
ApiUrl = configuration . GetValue < string > ( "ServiceConfig:ApiUrl" ) ,
Port = configuration . GetValue < int > ( "ServiceConfig:Port" ) ,
ConsulUrl = configuration . GetValue < Uri > ( "ServiceConfig:ConsulUrl" ) ,
HealthCheckEndPoint = configuration . GetValue < string > ( "ServiceConfig:HealthCheckEndPoint" ) ,
} ;
return serviceConfig ;
}
}サービス検出サービスに到達するために必要な構成を読み取った後、それを使用してサービスを登録できます。以下のコードはバックグラウンド タスク(ホストされたサービス) として実装されており、サービスに関する以前の情報が存在する場合はそれをオーバーライドすることによってConsulにサービスを登録します。サービスがシャットダウンしている場合、サービスは Consul レジストリから自動的に登録解除されます。
public class ServiceDiscoveryHostedService (
ILogger < ServiceDiscoveryHostedService > logger ,
IConsulClient client ,
ServiceConfig config )
: IHostedService
{
private AgentServiceRegistration _serviceRegistration ;
/// <summary>
/// Registers service to Consul registry
/// </summary>
public async Task StartAsync ( CancellationToken cancellationToken )
{
_serviceRegistration = new AgentServiceRegistration
{
ID = config . Id ,
Name = config . Name ,
Address = config . ApiUrl ,
Port = config . Port ,
Check = new AgentServiceCheck ( )
{
DeregisterCriticalServiceAfter = TimeSpan . FromSeconds ( 5 ) ,
Interval = TimeSpan . FromSeconds ( 15 ) ,
HTTP = $ "http:// { config . ApiUrl } : { config . Port } /api/values/ { config . HealthCheckEndPoint } " ,
Timeout = TimeSpan . FromSeconds ( 5 )
}
} ;
try
{
await client . Agent . ServiceDeregister ( _serviceRegistration . ID , cancellationToken ) . ConfigureAwait ( false ) ;
await client . Agent . ServiceRegister ( _serviceRegistration , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( Exception ex )
{
logger . LogError ( ex , $ "Error while trying to deregister in { nameof ( StartAsync ) } " ) ;
}
}
/// <summary>
/// If the service is shutting down it deregisters service from Consul registry
/// </summary>
public async Task StopAsync ( CancellationToken cancellationToken )
{
try
{
await client . Agent . ServiceDeregister ( _serviceRegistration . ID , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( Exception ex )
{
logger . LogError ( ex , $ "Error while trying to deregister in { nameof ( StopAsync ) } " ) ;
}
}
}サービスをサービス検出サービスに登録したら、ゲートウェイ API の実装を開始できます。
Ocelot では、ルート(アップストリーム要求のマッピングに使用される構成) とGlobalConfiguration (QoS、レート制限などのその他の構成) のリストを含む構成ファイルを提供する必要があります。以下の ocelot.json ファイルで、HTTP リクエストを転送する方法を確認できます。使用するロード バランサーのタイプを指定する必要があります。この場合、これは利用可能なサービスをループし、利用可能なサービスにリクエストを送信する「ラウンドロビン」です。 ServiceDiscoveryProviderのGlobalConfigurationで Consul をサービス検出サービスとして設定することが重要です。
{
"Routes" : [
{
"Servicename" : " ValueService " ,
"DownstreamPathTemplate" : " /{everything} " ,
"DownstreamScheme" : " http " ,
"UpstreamPathTemplate" : " /{everything} " ,
"UpstreamHttpMethod" : [ " GET " ],
"UseServiceDiscovery" : true ,
"RouteIsCaseSensitive" : false ,
"LoadBalancerOptions" : {
"Type" : " RoundRobin "
},
"QoSOptions" : {
"ExceptionsAllowedBeforeBreaking" : 3 ,
"DurationOfBreak" : 5000 ,
"TimeoutValue" : 2000
}
}
],
"GlobalConfiguration" : {
"RequestIdKey" : " OcelotRequestId " ,
"UseServiceDiscovery" : true ,
"ServiceDiscoveryProvider" : {
"Host" : " consul " ,
"Port" : 8500 ,
"Type" : " PollConsul " ,
"PollingInterval" : 100
}
}
}GlobalConfigurationセクションのServiceDiscoveryProvider設定について必要な説明をいくつか示します。
構成を定義したら、API ゲートウェイの実装を開始できます。以下に、 ocelot.json構成ファイルとサービス レジストリとしてConsul を使用する Ocelot API Gateway サービスの実装を示します。
IHostBuilder hostBuilder = Host . CreateDefaultBuilder ( args )
. UseContentRoot ( Directory . GetCurrentDirectory ( ) )
. ConfigureWebHostDefaults ( webBuilder =>
{
webBuilder . ConfigureServices ( services =>
services
. AddOcelot ( )
. AddConsul < MyConsulServiceBuilder > ( )
. AddCacheManager ( x =>
{
x . WithDictionaryHandle ( ) ;
} )
. AddPolly ( ) ) ;
webBuilder . Configure ( app =>
app . UseOcelot ( ) . Wait ( ) )
. ConfigureAppConfiguration ( ( hostingContext , config ) =>
{
config
. SetBasePath ( hostingContext . HostingEnvironment . ContentRootPath )
. AddJsonFile ( "appsettings.json" , false , true )
. AddJsonFile ( $ "appsettings. { hostingContext . HostingEnvironment . EnvironmentName } .json" , true , true )
. AddJsonFile ( "ocelot.json" , false , true )
. AddEnvironmentVariables ( ) ;
} )
. ConfigureLogging ( ( builderContext , logging ) =>
{
logging . ClearProviders ( ) ;
logging . AddConsole ( ) ;
logging . AddDebug ( ) ;
} ) ;
} ) ;
IHost host = hostBuilder . Build ( ) ;
await host . RunAsync ( ) ; 前に述べたように、コンテナ用の軽量 GNU/Linux ディストリビューションを使用して、 Consulを含むすべてのサービスを Docker でコンテナ化します。
すべてのコンテナーのセットアップを含むdocker-compose.ymlファイルは次のようになります。
services :
services :
consul :
image : hashicorp/consul
container_name : consul
command : consul agent -dev -log-level=warn -ui -client=0.0.0.0
hostname : consul
networks :
- common_network
valueservice1.openapi :
image : valueservice.openapi:latest
container_name : valueservice1.openapi
restart : on-failure
hostname : valueservice1.openapi
build :
context : .
dockerfile : src/ValueService.OpenApi/Dockerfile
networks :
- common_network
valueservice2.openapi :
image : valueservice.openapi:latest
container_name : valueservice2.openapi
restart : on-failure
hostname : valueservice2.openapi
build :
context : .
dockerfile : src/ValueService.OpenApi/Dockerfile
networks :
- common_network
valueservice3.openapi :
image : valueservice.openapi:latest
container_name : valueservice3.openapi
restart : on-failure
hostname : valueservice3.openapi
build :
context : .
dockerfile : src/ValueService.OpenApi/Dockerfile
networks :
- common_network
services.gateway :
image : services.gateway:latest
container_name : services.gateway
restart : on-failure
hostname : services.gateway
build :
context : .
dockerfile : src/Services.Gateway/Dockerfile
networks :
- common_network
networks :
common_network :
driver : bridge
私たちのサービスには構成ファイルが含まれていないことに注意してください。そのために、Docker-compose.override.ymlファイルを使用します。
services :
consul :
ports :
- " 8500:8500 "
valueservice1.openapi :
# Swagger UI: http://localhost:9100/index.html
# http://localhost:9100/api/values
environment :
- ASPNETCORE_ENVIRONMENT=Development
- ServiceConfig__ApiUrl=valueservice1.openapi
- ServiceConfig__ConsulUrl=http://consul:8500
- ServiceConfig__HealthCheckEndPoint=healthcheck
- ServiceConfig__Id=ValueService.OpenApi-9100
- ServiceConfig__Name=ValueService
- ServiceConfig__Port=8080
ports :
- 9100:8080
depends_on :
- consul
valueservice2.openapi :
# Swagger UI: http://localhost:9200/index.html
# http://localhost:9200/api/values
environment :
- ASPNETCORE_ENVIRONMENT=Development
- ServiceConfig__ApiUrl=valueservice2.openapi
- ServiceConfig__ConsulUrl=http://consul:8500
- ServiceConfig__HealthCheckEndPoint=healthcheck
- ServiceConfig__Id=ValueService.OpenApi-9200
- ServiceConfig__Name=ValueService
- ServiceConfig__Port=8080
ports :
- 9200:8080
depends_on :
- consul
valueservice3.openapi :
# Swagger UI: http://localhost:9300/index.html
# http://localhost:9300/api/values
environment :
- ASPNETCORE_ENVIRONMENT=Development
- ServiceConfig__ApiUrl=valueservice3.openapi
- ServiceConfig__ConsulUrl=http://consul:8500
- ServiceConfig__HealthCheckEndPoint=healthcheck
- ServiceConfig__Id=ValueService.OpenApi-9300
- ServiceConfig__Name=ValueService
- ServiceConfig__Port=8080
ports :
- 9300:8080
depends_on :
- consul
services.gateway :
# Call first available service: http://localhost:9500/api/values
environment :
- ASPNETCORE_ENVIRONMENT=Development
ports :
- 9500:8080
depends_on :
- consul
- valueservice1.openapi
- valueservice2.openapi
- valueservice3.openapi 構成ファイルを実行するには、Powershell を開き、ルート フォルダー内の構成ファイルに移動します。次に、コマンドdocker-compose up -d --build --remove-orphansを実行し、すべてのサービスを開始して実行します。 -dパラメータは、切り離されたコマンドを実行します。これは、コンテナーがバックグラウンドで実行され、Powershell ウィンドウをブロックしないことを意味します。実行中のすべてのコンテナを確認するには、コマンドdocker ps を使用します。

Consul は、すぐに使える優れた Web ユーザー インターフェイスを提供します。ポート8500 : http://localhost:8500 でアクセスできます。いくつかの画面を見てみましょう。
Consul UI サービスのホームページ。Consul エージェントおよび Web サービス チェックに関連するすべての情報が含まれます。 



API Gateway 経由でいくつかの呼び出しを行ってみましょう: http://localhost:9500/api/values。ロード バランサーは、利用可能なサービスをループしてリクエストを送信し、応答を返します。



マイクロ サービス システムの構築と保守は簡単ではありません。しかし、このチュートリアルでは、マイクロ サービス アーキテクチャを使用したアプリケーションの開発とデプロイがいかに簡単であるかを示しました。 HashiCorp Consul は、サービス検出、ヘルスチェック、Key-Value ストレージ、およびマルチデータセンターに対する一流のサポートを備えています。 Ocelot はゲートウェイとして Consul サービス レジストリと正常に通信し、サービス登録を取得し、ロード バランサは利用可能なサービスをループしてリクエストを送信します。両方を使用すると、このような課題に直面している開発者の作業が大幅に楽になります。同意しますか?
楽しむ!
MIT の下でライセンスを取得しています。 LinkedIn で私に連絡してください。