Placement Constraints with Service Fabric
September 11, 2016 2 Comments
I call this blog my notepad for a reason. Its where I can write things down if for no other reason then to help me remember. There’s the added benefit that over the years a few of you have found it and actually come here to read these scribbles and from what I heard, you sometimes even learn something.
That said, I wanted to share my latest discovery, not just for myself, but for the sake of anyone else that has this need.
Placement Constraint Challenges
As I mentioned in my post on Network Isolation with Service Fabric, you can put placement constraints on services to ensure that they are placed on specific node types. However, this can be challenging when you’re moving your application packages between clusters/environments.
The first one you usually run into is that the moment you put a placement constraint on the service, you can’t deploy it to the local development cluster. When this happens, folks starting trying to alter the local cluster’s manifest. Yes, the local cluster does have a manifest and no, you really don’t want to start changing that unless you have to.
So I set about to find a better way. In the process, I found a couple of simple, but IMHO poorly documented features that make this fairy easy to manage. And manage in a way where the “config is code” aspects of the service and fabric manifests are maintained.
Application vs Service Manifest
There are a couple ways to configure placement constraints. One of the ones you’ll find most readily is the use of code at publication. I’ll be honest, i hate this approach. And I suspect many developers do. This approach while entirely valid, requires you to write even more code for something that can easily be managed declarative via the service and package manifests.
If you dig a bit more, you’ll eventually run across the declaration of constraints in the service manifest.
<ServiceTypes> <StatelessServiceType ServiceTypeName="Stateless1"> <PlacementConstraints>(NodeTypeName==BackEnd)</PlacementConstraints> </StatelessServiceType> </ServiceTypes>
This IMHO, is perfectly valid. However, it has the unfortunate side affect I mentioned earlier. Namely that, despite hours of trying, there’s no easy way to alter this constraint via any type of configuration setting at deployment time. But what I recently discovered was that this can also be done via the application manifest!
<DefaultServices> <Service Name="Stateless1"> <StatelessService ServiceTypeName="Stateless1Type" InstanceCount="[Stateless1_InstanceCount]"> <SingletonPartition /> <PlacementConstraints>(isDMZ==true)</PlacementConstraints> </StatelessService> </Service> </DefaultServices>
I never knew this was even possible until a few days ago. And admittedly, I can’t find this documented ANYWHERE. It was by simple lucky I tried this to see if it would even work. Not only did it work, but I found that any placement constraint declared in the application manifest would override whatever was in the service manifest.
But this still doesn’t solve the root problem. But it does put us a step closer to addressing it.
Environment Specific Application Parameters
As I was trying to solve this problem, Aman Bhardwaj of the service fabric team sent me this link. That link discusses options you have for managing environment specific settings for service fabric applications. Most of the article centers around the use of configuration overrides to change the values of service configuration settings (that are in the PackageRoot/Config/Settings.xml) at publication. We don’t need most of that, what we need is actually much simpler.
The article discusses that you can parameterize the application manifest. This will substitute values defined in the ApplicationParameter files (you get two by default when you create a new service fabric application), In fact, the template generated by visual studio already has an example of this as it sets the service instance count. And since, as I just mentioned we can put the placement constraints into the application manifest… well I think you can see where this is going.
We start by going into the application manifest and adding a new parameter.
<Parameters> <Parameter Name="Stateless1_InstanceCount" DefaultValue="-1" /> <Parameter Name="Stateless1_PlacementConstraints" DefaultValue="" /> </Parameters>
Note that I’m leaving the value blank. This is completely acceptable as it tells the the service fabric that you have no constraints. In fact, during my testing any value that could not be properly evaluated as a placement constraints will be ignored. So you could put in “azureisawesome” and it will have the same affect as leaving this blank. However, we’ll just keep it meaningful and leave it blank.
With the parameter declared, we’re next going to update the constraint declaration in the application manifest to use it.
<DefaultServices> <Service Name="Stateless1"> <StatelessService ServiceTypeName="Stateless1Type" InstanceCount="[Stateless1_InstanceCount]"> <SingletonPartition /> <PlacementConstraints>[Stateless1_PlacementConstraints]</PlacementConstraints> </StatelessService> </Service> </DefaultServices>
So “(isDMZ==true)” has become “[Stateless1_PlacementConstraints]”. With this done, now all we need to do is go define environment specific values in the ApplicationParameter files.
You’re most likely going to leave the Local.xml parameter as blank. But the Cloud.xml (or any other environment specific file you provide, is where you’ll specify the environment specific setting.
<?xml version="1.0" encoding="utf-8"?> <Application xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="fabric:/SampleApp1" xmlns="http://schemas.microsoft.com/2011/01/fabric"> <Parameters> <Parameter Name="Stateless1_InstanceCount" Value="-1" /> <Parameter Name="Stateless1_PlacementConstraints" Value="(isDMZ==false)" /> </Parameters> </Application>
With these in place, when you deploy the service, you simply have to ensure you specify the proper configuration file when you deploy.
This is of course the publication dialog for Visual Studio, but that parameter file is also a parameter of the Deploy-FabricAppliation.ps1 file that is part of the project and use to do publications via the command line. So these same parameter files can be used for unattended or automated deployments.
Off to the festival
And there we have it! Which is good because that’s all the time I have for today. My son is heading off to school this week which means my wife and I will officially be “empty nesters”. Today we’re taking him and his girlfriend to the local Renaissance Festival for one last family outing before he leaves. So I hope this helps you all and I bid you a fond “huzzah” until next time!
PS – I have place a sample app with many of these settings already in place on github.