Menu

Service Fabric testing inspired by AZ-202 certification study

I studied to AZ-202 Azure Developer transition certification during March. I was earlier completed the 70-532 certification so I was able to go AZ-202 (transition) certification exam. AZ-202 covered the following topics:

Develop for cloud storage

  • Develop solutions that use file storage
  • Develop solutions that use a relational database

Create Platform as a Service (PaaS) Solutions

  • Create an app service Logic App
  • Create app or service that runs on Service Fabric
  • Schedule bulk operations
  • Design and develop applications that run in containers

Secure cloud solutions

  • Implement access control

Develop for an Azure cloud model

  • Develop for autoscaling

Implement cloud integration solutions

  • Configure a message-based integration architecture
  • Develop an application message model

Topics were quite familiar to me except Service Fabric. It was a new technology what I haven't used in any work or personal projects. The best study method for me is to do hands on practices while browsing materials and documents. So I decided to refactor my Blog application while learning Service Fabric. This blog is now running top of the Service Fabric.

This blog post is not a deep dive to the Service Fabric. I describe this Blog application changes mostly from the architectural point of view. I had some difficulties with the SSL configuration show I described that more detailed. 

Basic information about Azure Service Fabric

A few words about Service Fabric. What is a Azure Service Fabric? Microsoft has described Azure Service Fabric: "Azure Service Fabric is a distributed systems platform that makes it easy to package, deploy, and manage scalable and reliable micro services. You can host Service Fabric clusters anywhere: Azure, in an on-premises data center, or on any cloud provider. Basically Service Fabric is a micro service backbone which enables you to build scalable and reliable applications. Microsoft has built multiple Azure services top of Service Fabric like Azure SQL Database, Cosmos DB, PowerBI etc.

Service Fabric has Stateless and Statefull services. Stateless service is a logical choice when service's persistent state is stored in an external service (ex. Database). Statefull Service allows Service Fabric to maintain the state via Reliable Collections or Reliable Actors.

Pluralsight cources

Pluralsight have good cources about Service Fabric which I recommend to go through while learning:

Understanding the Programming Models of Azure Service Fabric by Ivan Gavryliuk
Using Azure Service Fabric in Production by Ivan Gavryliuk
Building an Application with Azure Service Fabric Mesh by Ivan Gavryliuk

Creating a Service Fabric Cluster

You can create Service Fabric Cluster via Azure CLI by using az sf cluster create command. More details about the command you can find from here. And of course you can create cluster in Azure Portal. Notice that provisioning the Service Fabric Cluster might take some time. Nilay Parikh has written a good step-by-step guide to setup Service Fabric Cluster.

Service Fabric Blog Application architecture

I currently have three micro service components in my Service Fabric Cluster:

1) Blog Web Application (Stateless Service)
2) Backend Content Service (Stateless Service)
3) Analytics Service (Statefull Service)

Communication between services is handled via Service Fabric service remoting. Other possibility is of course to use HTTP endpoints. I wanted to use service remoting because it's a Service Fabric's unique way to do communication between services.

undefined

Blog Web Application (Stateless Service)

Blog Web is the application which shows the Blog content for end users (front-end). Application is stateless because it doesn't have any state. This application is the same .NET Core MVC application which I created a year ago.

Content Service (Stateless Service)

Content Service is a stateless component which delivers all blog contents for Blog Web Application. Basically during the refactoring I moved all ButterCMS API call logic from earlier version of my Blog Application to this new Service Fabric Stateless Service project. Project has no nothing specially. It executes calls to the ButterCMS API which is outside of the Service Fabric Cluster. Mae by later I change this project to Statefull and I'll use Reliable Collections as a caching purposes.

Analytics Service (Statefull Service)

I wanted also learn more about Reliable Collections so I created one Statefull Service. Analytics Statefull service stores all pageview data to the Reliable Collection. Data in the Reliable Collections are replicated across the Service Fabric Cluster.

Basically I have ASP.NET Core Middleware component in the Blog application which gets page and user agent information and sends it to the Analytics service via service remoting.

Configure SSL certificate for your site

I found several instructions from internet, how to do this but didn't find a complete guide which works. This section shows how I managed to get this working.

Upload your site SSL certificate to the KeyVault

Before uploading your site certificate to KeyVault convert your CRT-file certificate to PFX using ex. Openssl:

openssl pkcs12 -export -out "d:\certificates\mysite.pfx" -inkey "d:\certificates\mysite.key" -in "d:\certificates\mysite.crt"

undefined

undefined

After upload open certificate from Keyvault and copy the secret identifier value to the clipboard

undefined

Add certificate to the Service Fabric nodes

Use Add-AzureRmServiceFabricApplicationCertificate to install a certificate to all nodes in the cluster. You can specify a certificate you already have or have the system generate a new one for you, and upload it to a new or existing Azure key vault. Source: Add-AzureRmServiceFabricApplicationCertificate

$secret = "https://[MyKeyvault].vault.azure.net/secrets/[MyCertificate]/[MyCertificateId]"
$groupname="[MyResourceGroupName]"
$clustername = "[MyServiceFabricClusterName]"

Add-AzureRmServiceFabricApplicationCertificate -ResourceGroupName $groupname -Name $clustername -SecretIdentifier $secret -Verbose

Open 443 Port in Load Balancer

I found this script from here.

$probename = "AppPortProbe"
$rulename="AppPortLBRule"
$RGname="[MyResourceGroupName]"
$port=443

# Get the load balancer resource
$resource = Get-AzResource | Where {$_.ResourceGroupName –eq $RGname -and $_.ResourceType -eq "Microsoft.Network/loadBalancers"}
$slb = Get-AzLoadBalancer -Name $resource.Name -ResourceGroupName $RGname

# Add a new probe configuration to the load balancer
$slb | Add-AzLoadBalancerProbeConfig -Name $probename -Protocol Tcp -Port $port -IntervalInSeconds 15 -ProbeCount 2

# Add rule configuration to the load balancer
$probe = Get-AzLoadBalancerProbeConfig -Name $probename -LoadBalancer $slb
$slb | Add-AzLoadBalancerRuleConfig -Name $rulename -BackendAddressPool $slb.BackendAddressPools[0] -FrontendIpConfiguration $slb.FrontendIpConfigurations[0] -Probe $probe -Protocol Tcp -FrontendPort $port -BackendPort $port

# Set the goal state for the load balancer
$slb | Set-AzLoadBalancer

Service Fabric ApplicationManifest changes

This rule allows Network service to get access to the certificate.

 <Principals>
    <Users>
      <User Name="NETWORK SERVICE" AccountType="NetworkService" />
    </Users>
  </Principals>
  <Policies>
    <SecurityAccessPolicies>
      <SecurityAccessPolicy ResourceRef="HttpsCert" PrincipalRef="NETWORK SERVICE" ResourceType="Certificate" />
    </SecurityAccessPolicies>
  </Policies>
  <Certificates>
    <SecretsCertificate X509FindValue="[MyCertificateThumbprint]" Name="HttpsCert" />
  </Certificates>

ServiceManifest changes in the web project

Add new 443 HTTPS endpoint to the service manifest.

  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="80" />      
      <Endpoint Protocol="https" Name="ServiceHttpsEndpoint" Type="Input" Port="443" />
    </Endpoints>
  </Resources>

Endpoint changes (web project)

Service Listener changed to use new HTTPS endpoint. Certificate will be fetched from the VM nodes certificate store.

 /// <summary>
        /// Optional override to create listeners (like tcp, http) for this service instance.
        /// </summary>
        /// <returns>The collection of listeners.</returns>
        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        {
            return new ServiceInstanceListener[]
              {
                      new ServiceInstanceListener(serviceContext =>
                          new KestrelCommunicationListener(serviceContext, "ServiceHttpsEndpoint", (url, listener) =>
                          {
                              ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

                              return new WebHostBuilder()
                                          .UseKestrel(opt =>
                                          {
                                              int port = serviceContext.CodePackageActivationContext.GetEndpoint("ServiceHttpsEndpoint").Port;
                                              opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
                                              {
                                                  listenOptions.UseHttps(GetCertificateFromStore());
                                              });
                                          })
                                          .ConfigureServices(
                                              services => services
                                                  .AddSingleton<StatelessServiceContext>(serviceContext))
                                          .UseContentRoot(Directory.GetCurrentDirectory())
                                          .UseStartup<Startup>()
                                          .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                                          .UseUrls(url)
                                          .Build();
                          }))
              };
        }

 public static X509Certificate2 GetCertificateFromStore()
        {
            var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            try
            {
                store.Open(OpenFlags.ReadOnly);
                var certCollection = store.Certificates;
                var currentCerts = certCollection.Find(X509FindType.FindByThumbprint, "[MyCertificateThumbprintFromConfiguration]", false);
                return currentCerts.Count == 0 ? null : currentCerts[0];
            }
            finally
            {
                store.Close();
            }
        }

That's it now it's working.

Comments