将 C# Stateful Service Fabric 应用程序从 Visual Studio 部署到 Linux

Deploy a C# Stateful Service Fabric application from Visual Studio to Linux

编辑 2018 年 4 月 6 日 => 更新问题的最后状态


所以我在 Azure 上部署的 Windows Service Fabric 集群上有这个工作的 .Net 4.6 有状态服务,目前 运行。

从09/2017开始,我应该可以搬到Linux:https://blogs.msdn.microsoft.com/azureservicefabric/2017/09/25/service-fabric-6-0-release/

所以我试图将它部署在 Linux 上,这样我就可以节省成本。

  1. 首先,我已将所有代码从 .Net 4.6 迁移到 .Net Core 2.0。现在我可以毫无问题地编译我的二进制文件。我基本上创建了新的 .Net Core 项目,然后将所有源代码从 .Net 4.6 项目移至新的 .Net Core 项目。

  2. 然后我更新了我的 Service Fabric 应用程序。我从 sfproj 中删除了以前的 SF 服务,然后添加了新的 .Net Core 服务。

看起来有一个警告(尽管输出 window 没有任何内容),但如果我尝试通过 Service Fabric 提供的模板使用 .Net core 2.0 创建一个新的空 Statful 服务,它就会出现工具 2.0(测试版):

所以我要忍受它。

  1. 在我的开发机器上,我修改了包含状态服务的 2 个 csproj 项目,以便它们可以 运行 在本地作为 Windows executables。我用过 win7-x64 runtimeIdentifier.

运行 我的 Windows 机器上的本地 SF 集群很好。

  1. 然后我为 Linux 稍微更改了以前的 csproj 文件。我用了 ubuntu.16.10-x64 runtimeIdentifier.

我还更改了 ServiceManifest.xml 文件以针对 linux 兼容的二进制文件:

  <!-- Code package is your service executable. -->
  <CodePackage Name="Code" Version="1.9.6">
    <EntryPoint>
      <ExeHost>
        <Program>entryPoint.sh</Program>
      </ExeHost>
    </EntryPoint>
  </CodePackage>

entryPoint.sh是最终执行的基本脚本:

dotnet $DIR/MyService.dll
  1. 然后我已经从 Visual Studio 成功部署到我的安全 SF Linux 集群。不幸的是,我的两个有状态服务都出现以下错误:

Error event: SourceId='System.Hosting', Property='CodePackageActivation:Code:EntryPoint'. There was an error during CodePackage activation.The service host terminated with exit code:134

看起来我的二进制文件在启动时崩溃了。所以这是我的问题:

编辑:查看 LinuxsyslogVer2v0 table,我得到以下错误:

starthost.sh[100041]: Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'System.Threading.Thread, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

我发现了以下错误报告:https://github.com/dotnet/sdk/issues/1502 不幸的是,我在不使用 MSBuild(使用 dotnet deploy)的情况下仍然遇到错误。

编辑:进一步说明:

我遇到了类似的问题,但我认为这是问题所在:

In this release, .NET Core 2.0 services are only supported on Service Fabric for Windows. Full cross-platform support for .NET Core 2.0 services on Windows and Linux is coming soon.

来自Service Fabric 6.1 Release Notes 所以没有 Linux 只要你的目标是 .net core 2.0.

所以,要让它正常工作真的很麻烦。但它有效。嗯,有点。


首先,Reliable Services 在 Linux 上仍处于预览状态: https://github.com/Microsoft/service-fabric/issues/71

完整的 Linux 支持应该很快就会到来(实际上它应该可用 已经 根据之前的 link...)。

现在关于如何进行的详细信息,这里有一些信息可以帮助其他人,因为 Microsoft 文档中没有任何关于它的内容,我真的浪费了 3 天时间试图让它工作。

1。请为您的项目使用 .NET Core 2.0。

Linux 支持它。目前处于预览状态,但它有效。

2。请为您的项目使用正确的 RID。

截至今天(2018 年 4 月),正确使用的 RID 是 ubuntu.16.04-x64。 编辑 Reliable Service 项目的 csproj 文件并像这样设置 RID:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <IsServiceFabricServiceProject>True</IsServiceFabricServiceProject>
    <RuntimeIdentifier>ubuntu.16.04-x64</RuntimeIdentifier>
    <Platforms>AnyCPU;x64</Platforms>
  </PropertyGroup>

有趣的是,您应该能够使用RuntimeIdentifiers参数(使用S提供多个RID最后)像那样:

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <IsServiceFabricServiceProject>True</IsServiceFabricServiceProject>
    <RuntimeIdentifiers>win7x64;ubuntu.16.04-x64</RuntimeIdentifiers>
    <Platforms>AnyCPU;x64</Platforms>
  </PropertyGroup>

因此您可以同时构建 Windows 二进制文件和 Linux 二进制文件。 但是根本行不通。从 Visual Studio 构建项目时,我最终只得到以下目录:

bin/Debug/netcoreapp2.0/

只有 DLL,没有有效的入口点。没有 win7-x64 文件夹,没有 ubuntu.16.04-x64,什么都没有。 这是一个错误,应该被修复,但它不是(我使用 Visual Studio 15.6.2 截至今天都是最新的)。参见 https://github.com/dotnet/core/issues/1039

3。您的服务需要一个有效的入口点。

在 Windows 上它是一个 executable 文件 (*.exe)。在 Linux 上不是。我最终得到了 Linux C# 示例和 copied/pasted 入口点。 https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-create-your-first-linux-application-with-csharp

所以基本上我现在在每个 Reliable Service 的 ServiceManifest.xml 文件中都有以下 EntryPoint :

<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="XXXX"
                 Version="1.0.0"
                 xmlns="http://schemas.microsoft.com/2011/01/fabric"
                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ServiceTypes>
    <!-- This is the name of your ServiceType.
         This name must match the string used in RegisterServiceType call in Program.cs. -->
    <StatefulServiceType ServiceTypeName="YYY" HasPersistedState="true" />
  </ServiceTypes>

  <!-- Code package is your service executable. -->
  <CodePackage Name="Code" Version="1.0.0">
    <EntryPoint>
      <ExeHost>
        <Program>entryPoint.sh</Program>
      </ExeHost>
    </EntryPoint>
  </CodePackage>

entryPoint.sh如下:

#!/usr/bin/env bash
check_errs()
{
  # Function. Parameter 1 is the return code
  if [ "" -ne "0" ]; then
    # make our script exit with the right error code.
    exit 
  fi
}

DIR=`dirname [=14=]`
echo 0x3f > /proc/self/coredump_filter
source $DIR/dotnet-include.sh
dotnet $DIR/NAME_OF_YOUR_SERVICE_DLL.dll $@
check_errs $?

dotnet-include.sh如下:

#!/bin/bash
. /etc/os-release
linuxDistrib=$ID
if [ $linuxDistrib = "rhel" ]; then
  source scl_source enable rh-dotnet20
  exitCode=$?
  if [ $exitCode != 0 ]; then
    echo "Failed: source scl_source enable rh-dotnet20 : ExitCode: $exitCode"
    exit $exitCode
  fi
fi

两者都在 PackageRoot 文件夹中。我指定了它们的两个属性,因此 Build Action 是“Content”,Copy to Output Directory 是“Copy always”。

4。不要使用 MSBuild 构建 !!

是的,它也应该构建 Linux 包,或者至少看起来是这样,因为当您右键单击您的项目并单击 "Build" 时,MSBuild 能够生成以下文件:

不要相信操作表面上的成功,它在部署时无法正确执行会很惨。一些 *.so 文件丢失和其他问题。 MSBuild 漏洞百出,并且在依赖项方面行为不端。

例如,请参阅此错误报告:https://github.com/dotnet/sdk/issues/1502 将近一年后仍未修复...

https://github.com/dotnet/core/issues/977(也有这个)。

5。自己编写一些 PowerShell 脚本来构建这些东西。

我最终使用以下脚本重新发明了轮子来构建我的包:

# Creating binaries for service 1
cd DIRECTORY_OF_MY_SERVICE_1
dotnet publish -c Release -r ubuntu.16.04-x64

# Creating binaries for service 2
cd ..\DIRECTORY_OF_MY_SERVICE_2
dotnet publish -c Release -r ubuntu.16.04-x64

# Creating binaries for service 3
cd ..\DIRECTORY_OF_MY_SERVICE_3
dotnet publish -c Release -r ubuntu.16.04-x64

# Copying ApplicationManifest.xml
cd ..
mkdir PKG\ServiceFabricApplication
echo F|xcopy "ServiceFabricApplication\ApplicationPackageRoot\ApplicationManifest.xml" "PKG\ServiceFabricApplication\ApplicationManifest.xml" /sy

# Copying Service1 files
mkdir "PKG\ServiceFabricApplication\Service1Pkg"
mkdir "PKG\ServiceFabricApplication\Service1Pkg\Code"
xcopy "Service1\PackageRoot\*" "PKG\ServiceFabricApplication\Service1Pkg" /sy /D
xcopy "Service1\bin\Release\netcoreapp2.0\ubuntu.16.04-x64\publish\*" "PKG\ServiceFabricApplication\Service1Pkg\Code" /sy

# Copying Service2 files
mkdir "PKG\ServiceFabricApplication\Service2Pkg"
mkdir "PKG\ServiceFabricApplication\Service2Pkg\Code"
xcopy "Service2\PackageRoot\*" "PKG\ServiceFabricApplication\Service2Pkg" /sy /D
xcopy "Service2\bin\Release\netcoreapp2.0\ubuntu.16.04-x64\publish\*" "PKG\ServiceFabricApplication\Service2Pkg\Code" /sy

# Copying Service3 files
mkdir "PKG\ServiceFabricApplication\Service3Pkg"
mkdir "PKG\ServiceFabricApplication\Service3Pkg\Code"
xcopy "Service3\PackageRoot\*" "PKG\ServiceFabricApplication\Service3Pkg" /sy /D
xcopy "Service3\bin\Release\netcoreapp2.0\ubuntu.16.04-x64\publish\*" "PKG\ServiceFabricApplication\Service3Pkg\Code" /sy

# Compresses the package
Write-host "Compressing package..."
Copy-ServiceFabricApplicationPackage -ApplicationPackagePath .\PKG\ServiceFabricApplication -CompressPackage -SkipCopy

sfproj 文件是一个 Visual Studio / MSBuild 相关项目,因此您需要自己构建所有内容。 上面的脚本生成的内容与使用 Visual Studio 构建 sfproj 时 MSBuild 创建的 pkg 文件夹相同。它会复制解决方案根目录下 PKG 文件夹中的所有内容。

这里有详细的包结构:https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/service-fabric/service-fabric-package-apps.md

6.现在是时候部署了!

此时我不再信任 Visual Studio,所以我构建了自己的 PowerShell 脚本:

. .\ServiceFabricApplication\Scripts\Deploy-FabricApplication.ps1 -ApplicationPackagePath '.\PKG\ServiceFabricApplication' -PublishProfileFile '.\ServiceFabricApplication\PublishProfiles\Cloud.xml' -DeployOnly:$false -ApplicationParameter:@{} -UnregisterUnusedApplicationVersionsAfterUpgrade $false -OverrideUpgradeBehavior 'None' -OverwriteBehavior 'SameAppTypeAndVersion' -SkipPackageValidation:$false -ErrorAction Stop

它重用 sfproj 项目中 Service Fabric 项目模板提供的 Deploy-FabricApplication.ps1 脚本。此脚本解析 Cloud.xml PublishProfile 并部署到您的服务结构集群。

因此您在 PublishProfiles/Cloud.xmlApplicationParameters/Cloud.xml 上都指定了权限值,然后执行脚本。

当然,只有在您的计算机上安装了用于保护集群的证书时,它才有效。 请注意第一个点“.”很重要,因为如果你不使用它,你会出现以下错误:

Get-ServiceFabricClusterManifest : Cluster connection instance is null

哦,由于 Service Fabric SDK 上也存在错误,您可能也想关闭本地 SF 集群... https://github.com/Azure/service-fabric-issues/issues/821

7。现在又是骗局的时候了。

它根本不起作用,服务在启动时崩溃。在 LinuxsyslogVer2v0 Azure 存储 table 中搜索小时后(Linux 的日志 table,位于使用 SF 集群自动创建的两个 Azure 存储帐户之一),我发现微软自己的 Nuget 包也有问题。

具体来说,Nuget 包 Microsoft.Azure.Devices 不适用于 1.6.0 版。未找到 dll 引用或其他问题。我回滚到之前的版本,即1.5.1,修复了。

在这一点上,我没有精力再创建另一个 Github 问题。对不起 MS,我不是你的 QA 团队,我有点累了。

8。使用第一个 PowerShell 脚本再次构建,使用第二个 PowerShell 脚本进行部署,然后就完成了。

您终于在 Windows 上使用 .NET Core 2.0 将 C# Reliable Services 从 Visual Studio 部署到 Linux SF(有点儿问题,因为我使用的是 PowerShell)集群。

现在我的 ASP.NET 核心服务仍然存在问题,但这将是另一天的故事。


结论:TL;DR

整个事情一团糟。到处都是虫子。在 SDK 中,在工具中,在某些 Microsoft Nuget 包中。糟糕的经历。但它是受支持的(目前处于预览状态),您可以让它工作。希望这篇 post 对您有所帮助...

借助此帮助,我已成功部署到 Linux 服务结构

打开所有服务 .csproj 文件并更新 RuntimeIdentifier,如下所示

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsServiceFabricServiceProject>True</IsServiceFabricServiceProject>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>

更新 ServiceManifest.xml 以删除 .exe 扩展名,如下所示

<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>Web1</Program>
</ExeHost>
</EntryPoint>
</CodePackage>

https://blogs.msdn.microsoft.com/premier_developer/2018/05/27/running-net-core-2-0-applications-in-a-linux-service-fabric-cluster-on-azure/

*Visual Studio 15.7.3