はじめに
Azure App Service の Web App for Containers は、Windows コンテナをサポートしているみたい。.NET Framework + WCF のサービスを動かせるかも。
試してみた。
WCF サービスとクライアント
サービスコントラクトとデータコントラクト
using System; using System.Runtime.Serialization; using System.ServiceModel; namespace NetHttpSample.Shared { [ServiceContract] public interface ISampleService { [OperationContract] ServerInfo GetServerInfo(); } [DataContract] public class ServerInfo { [DataMember] public string MachineName { get; set; } [DataMember] public string HostName { get; set; } [DataMember] public string[] IPAddresses { get; set; } } }
WCF サービス
サービス側は NetHttpBinding を使う。Web App for Containers だとフロントで TLS が終端されるみたいなので、コンテナ側では HTTP で待ち受ける。
using System; using System.Linq; using System.Net; using System.ServiceModel; using System.Threading; using NetHttpSample.Shared; namespace NetHttpSample.Server { internal class Program { static void Main(string[] args) { var host = new ServiceHost( typeof(SampleService), new Uri("http://localhost:80")); try { var binding = new NetHttpBinding(); host.AddServiceEndpoint( typeof(ISampleService), binding, "/SampleService"); host.Open(); foreach (var endpoint in host.Description.Endpoints) { Console.WriteLine(endpoint.ListenUri); } Thread.Sleep(-1); } catch { host.Close(); } } } [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)] public class SampleService : ISampleService { public ServerInfo GetServerInfo() { var hostName = Dns.GetHostName(); var ipAddresses = Dns.GetHostAddresses(hostName); return new ServerInfo { MachineName = Environment.MachineName, HostName = hostName, IPAddresses = ipAddresses.Select(x => x.ToString()).ToArray(), }; } } }
Dockerfile
# ベースイメージとして公式の .NET Framework ランタイムイメージを使用 FROM mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2019 # アプリケーションのディレクトリを作成 WORKDIR /app # WCF サービスのバイナリをコンテナにコピー COPY ./bin/Debug/ /app # 必要なポートを公開 EXPOSE 80 # サービスを起動するためのエントリーポイント ENTRYPOINT ["/app/NetHttpSample.Server.exe"]
WCF クライアント
クライアント側は NetHttpsBinding を使う。アドレス違うし、バインディングも違うから通信できるか心配だったけど、特に問題なかった。
using System; using System.Net; using System.Net.Security; using System.ServiceModel; using System.ServiceModel.Channels; using NetHttpSample.Shared; namespace NetHttpSample.Client { internal class Program { static void Main(string[] args) { string host; if (args.Length > 0) { host = args[0]; } else { Console.Write("ホスト[localhost]:"); host = Console.ReadLine(); if (string.IsNullOrEmpty(host)) { host = "localhost"; } } ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((a, b, c, d) => true); for (var z = 0; z < 3; z++) { var binding = new NetHttpsBinding { AllowCookies = true, UseDefaultWebProxy = false, }; var factory = new ChannelFactory<ISampleService>( binding, new EndpointAddress( new Uri($"https://{host}:443/SampleService"), EndpointIdentity.CreateDnsIdentity("azurewebsites.net"))); factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None; for (var y = 0; y < 3; y++) { var channel = factory.CreateChannel(); for (var x = 0; x < 10; x++) { var result = channel.GetServerInfo(); Console.WriteLine($"{nameof(result.MachineName)}: {result.MachineName}"); } ((IChannel)channel).Close(); Console.WriteLine("----------"); } factory.Close(); Console.WriteLine("===================="); } Console.WriteLine("Enter で終了"); Console.ReadLine(); } } }
デプロイ
- Azure ポータルでリソースグループ作成
- Azure ポータルでコンテナレジストリ作成
- アクセスキーの管理者ユーザーを ON にする
- コンテナイメージ作成
sh docker build -t nethttpsample:1.0.0 .
- コンテナイメージをコンテナレジストリにプッシュ
sh az acr login -n <コンテナレジストリ名> docker tag nethttpsample:1.0.0 <コンテナレジストリ名>.azurecr.io/nethttpsample:1.0.0 docker push <コンテナレジストリ名>.azurecr.io/nethttpsample:1.0.0
- App Service の Web App for Containers を作成
- 基本
- 公開:コンテナー
- オペレーティングシステム:Windows
- Docker
- セッションアフィニティ:有効
- チャンネルがオープンしている間は同じインスタンスにアクセスして欲しいので
- 基本
実行
Web App for Containers のインスタンスを 3 つに増やしてクライアントを実行したら、channel はオープンしている間、同じサーバーにアクセスし続けた。 channel を作り直した場合でも、作成元の ChannelFactory が同じなら、同じサーバーにアクセスし続けた。 ChannelFactory を作り直したら、違うサーバーにアクセスした。
NetTcpBinding と同じで、ChannelFactory が同じなら、channel は同じサーバーに接続し続けることを確認。期待通りの結果だ。
おわりに
レガシーな WCF サービスをどうにかしてモダンなインフラで動かしたいので、Web App for Containers というのはかなりアリだと思った。.NET Framework から .NET に移行するのがベストなのは理解している。ただ、あまりにも工数がかかり過ぎるのでね。