5
votes

I'm working on a Service Fabric application that is deployed to Azure. It currently consists of only 5 stateless services. The zipped archive weighs in at ~200MB, which is already becoming problematic.

By inspecting the contents of the archive, I can see the primary problem is that many files are required by all services. An exact duplicate of those files is therefore present in each service's folder. However, the zip compression format does not do anything clever with respect to duplicate files within the archive.

As an experiment, I wrote a little script to find all duplicate files in the deployment and delete all but one of each files. Then I tried zipping the results and it comes in at a much more practical 38MB.

I also noticed that system libraries are bundled, including:

  • System.Private.CoreLib.dll (12MB)
  • System.Private.Xml.dll (8MB)
  • coreclr.dll (5MB)

These are all big files, so I'd be interested to know if there was a way for me to only bundle them once. I've tried removing them altogether but then Service Fabric fails to start the application.

Can anyone offer any advice as to how I can drastically reduce my deployment package size?

NOTE: I've already read the docs on compressing packages, but I am very confused as to why their compression method would help. Indeed, I tried it and it didn't. All they do is zip each subfolder inside the primary zip, but there is no de-duplication of files involved.

3
Have you considered doing a Framework-dependent deployment (installing dotnet core in the server and deploy just the binaries for your application)? docs.microsoft.com/en-us/dotnet/core/deployingDiego Mendes
@Diego Mendes If I remove those files, the package deploys but fails to start the application.me--

3 Answers

5
votes

There is a way to reduce the size of the package but I would say it isn't a good way or the way things should be done but still I think it can be of use in some cases.

Please note: This approach requires target machines to have all prerequisites installed (including .NET Core Runtime etc.)

When building .NET Core app there are two deployment models: self-contained and framework-dependent.

In the self-contained mode all required framework binaries are published with the application binaries while in the framework-dependent only application binaries are published.

By default if the project has runtime specified: <RuntimeIdentifier>win7-x64</RuntimeIdentifier> in .csproj then publish operation is self-contained - that is why all of your services do copy all the things.

In order to turn this off you can simply add SelfContained=false property to every service project you have.

Here is an example of new .NET Core stateless service project:

<PropertyGroup>
  <TargetFramework>netcoreapp2.2</TargetFramework>
  <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
  <IsServiceFabricServiceProject>True</IsServiceFabricServiceProject>
  <ServerGarbageCollection>True</ServerGarbageCollection>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  <TargetLatestRuntimePatch>False</TargetLatestRuntimePatch>
  <SelfContained>false</SelfContained>
</PropertyGroup>

I did a small test and created new Service Fabric application with five services. The uncompressed package size in Debug was around ~500 MB. After I have modified all the projects the package size dropped to ~30MB.

The application deployed worked well on the Local Cluster so it demonstrates that this concept is a working way to reduce package size.

In the end I will highlight the warning one more time:

Please note: This approach requires target machines to have all prerequisites installed (including .NET Core Runtime etc.)

0
votes

You usually don't want to know which node runs which service and you want to deploy service versions independently of each other, so sharing binaries between otherwise independent services creates a very unnatural run-time dependency. I'd advise against that, except for platform binaries like AspNet and DotNet of course.

However, did you read about creating differential packages? https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-application-upgrade-advanced#upgrade-with-a-diff-package that would reduce the size of upgrade packages after the initial 200MB hit.

0
votes

Here's another option: https://devblogs.microsoft.com/dotnet/app-trimming-in-net-5/

<SelfContained>True</SelfContained>
<PublishTrimmed>True</PublishTrimmed>

From a quick test just now, trimming one app reduced the package size from ~110m MB to ~70MB (compared to ~25MB for selfcontained=false).

The trimming process took several minutes for a single application though, and the project I work on has 10-20 apps per Service Fabric project. Also I suspect that this process isn't safe when you have a heavy reliance on dependency injection model in your code.

For debug builds we use SelfContained=False though because developers will have the required runtimes on their machines. Not for release deployments though.

As a final note, since the OP mentioned file upload being a particular bottleneck:

A large proportion of the deployment time is just zipping and uploading the package

I noticed recently that we were using the deprecated Publish Build Artifacts task when uploading artifacts during our build pipeline. It was taking 20 minutes to upload 2GB of files. I switched over the suggested Publish Pipeline Artifact task and it took our publish step down to 10-20 seconds. From what I can tell, it's using all kinds of tricks under the hood for this newer task to speed up uploads (and downloads) including file deduplication. I suspect that zipping up build artifacts yourself at that point would actually hurt your upload times.