从Visual Studio部署一个C#有状态的Service Fabric应用程序到Linux

6

编辑 04/06/18 => 更新问题,附上最新状态


我有一个运行在我的Windows Service Fabric集群上的工作.Net 4.6状态服务。从2017年9月开始,我应该能够转移到Linux:https://blogs.msdn.microsoft.com/azureservicefabric/2017/09/25/service-fabric-6-0-release/。所以我正在尝试在Linux上部署它以节省成本。
首先,我将所有代码从.Net 4.6迁移到了.Net Core 2.0。现在我可以编译我的二进制文件而没有问题。我基本上创建了新的.Net Core项目,然后将所有源代码从.Net 4.6项目移动到新的.Net Core项目中。然后我更新了我的Service Fabric应用程序。我从sfproj中删除了以前的SF服务,然后添加了我的新的.Net Core服务。

enter image description here

看起来有一个警告(但输出窗口上没有任何信息),如果我尝试使用Service Fabric Tools 2.0(beta)提供的模板通过.Net Core 2.0创建一个新的空Statful服务,那么它就在这里:

enter image description here

所以我要接受它。

  1. 在我的开发机上,我修改了包含我的有状态服务的2个csproj项目,以便它们可以作为Windows可执行文件在本地运行。我使用了 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

5. 接下来,我已经成功从Visual Studio将应用程序部署到了我的安全的SF Linux集群上。不幸的是,我的两个有状态服务都出现了以下错误:

enter image description here

错误事件:SourceId='System.Hosting',Property='CodePackageActivation:Code:EntryPoint'。在 CodePackage 激活期间发生错误。服务主机以退出代码 134 终止。

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

  • 使用 Visual Studio 从 Linux 部署 C# .Net Core SF 有状态服务的方法正确吗?

编辑:查看 LinuxsyslogVer2v0 表中的内容,我得到了以下错误:

starthost.sh[100041]: 未经处理的异常:System.IO.FileLoadException: 无法加载文件或程序集“System.Threading.Thread, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”。所定位的程序集清单定义与程序集引用不匹配。(来自 HRESULT 的异常:0x80131040)

我找到了以下 bug 报告:https://github.com/dotnet/sdk/issues/1502。不幸的是,即使使用 dotnet deploy,我仍然会遇到此错误。

编辑:进一步澄清:

  • 我的老板希望我在Linux上运行,因为从D1v2机器开始,与Windows机器相比,价格降低了一半(没有许可证等)。
  • 我的.NET Core 2.0服务在Windows上成功运行。因此,.NET Core移植应该没问题。

你可以尝试使用一个shell脚本来运行dotnet,就像这个例子中所示:https://github.com/Azure-Samples/service-fabric-dotnet-core-getting-started/blob/master/Services/CounterService/CounterServiceApplication/CounterServiceWebServicePkg/ServiceManifest-Linux.xml - LoekD
1
你可能选择了错误的运行时标识符。你的 Linux 集群是 Ubuntu 16.04,对吗?该版本的运行时标识符为 ubuntu.16.04-x64。完整列表请参见此处:https://learn.microsoft.com/en-us/dotnet/core/rid-catalog - Vaclav Turecek
@VaclavTurecek 确实 RID 是错误的。在 ubuntu.16.04-x64 上也没有运气。 - ken2k
为什么在Linux机器上运行这个很重要? - Mardoxx
1
@Mardoxx,一个非常简单的原因:从D1v2开始,Linux机器的价格只有Windows的一半。因此,我的老板希望我们长期为客户部署在Linux上。 - ken2k
3个回答

5

所以,让它正常工作真是让人头痛。但它能够工作。嗯,有点勉强。


首先,在Linux上可靠的服务仍处于预览阶段:https://github.com/Microsoft/service-fabric/issues/71

全面支持Linux应该很快就会到来(根据之前的链接,实际上它已经可以使用了...)。

现在关于如何进行的详细信息,这里有一些信息来帮助其他人,因为Microsoft文档中没有任何关于此的内容,我花费了整整3天才设法让它工作。

1. 使用.NET Core 2.0进行项目开发。

它支持Linux。目前仍处于预览阶段,但它可以工作。

2. 对于您的项目,请使用正确的RID。

截至今天(2018年4月),使用的正确RID是ubuntu.16.04-x64。 编辑您的可靠服务项目的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上,它是一个可执行文件(*.exe)。在Linux上不是。最终,我得到了Linux C#示例,并复制/粘贴了入口点。 https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-create-your-first-linux-application-with-csharp

因此,基本上我现在在每个可靠服务的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 [ "${1}" -ne "0" ]; then
    # make our script exit with the right error code.
    exit ${1}
  fi
}

DIR=`dirname $0`
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"。

enter image description here

4. 不要使用MSBuild进行构建!!

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

enter image description here

不要相信操作的表面成功,当部署时,它将惨败于无法正确执行的问题,一些*.so文件丢失和其他问题。MSBuild非常有bug,并且在依赖方面表现不良。
例如,请参阅此错误报告: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

它重复利用了 Service Fabric 项目模板中 sfproj 项目内提供的 Deploy-FabricApplication.ps1 脚本。该脚本解析 Cloud.xmlPublishProfile 并部署到您的服务群集。
因此,您需要在 PublishProfiles/Cloud.xmlApplicationParameters/Cloud.xml 上分别指定正确的值,然后执行脚本。
当然,这只有在您安装了用于保护群集的证书时才有效。请注意,第一个点 '.' 很重要,因为如果您不使用它,则会遇到以下错误:

Get-ServiceFabricClusterManifest:群集连接实例为空

请参阅 https://dev59.com/S1oT5IYBdhLWcg3w2SUS#38104087 哦,由于 Service Fabric SDK 中也存在错误,您可能还想关闭本地 SF 群集…… https://github.com/Azure/service-fabric-issues/issues/821

7. 现在是时候进行另一个欺骗了。

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

具体来说,Nuget 包 Microsoft.Azure.Devices 在版本 1.6.0 上无法工作。出现了 dll 引用未找到或其他问题。我回滚到以前的版本,即 1.5.1,问题得以解决。

此时,我已经没有精力再为此创建另一个 Github 问题。抱歉 MS,我不是你们的 QA 团队,我感到疲惫不堪。

8. 使用第一个 PowerShell 脚本重新构建,使用第二个 PowerShell 脚本部署,完成。

您终于将 C# 可靠服务使用 .NET Core 2.0 从 Visual Studio(有点故障,我使用了 PowerShell)部署到 Linux SF 集群上。

现在我仍然有我的 ASP.NET Core 服务问题,但这将成为另一天的故事。


结论:简而言之

整个事情都很混乱。到处都是漏洞。在SDK、工具和一些Microsoft Nuget包中都有。非常糟糕的体验。但它得到了支持(目前是预览版),你可以让它工作。希望这篇文章能够帮助...


1
我已经成功地部署到 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>

请参见在Azure上的Linux Service Fabric群集中运行.NET Core 2.0应用程序

* Visual Studio 15.7.3


1

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

在这个版本中,仅支持在 Service Fabric for Windows 上使用 .NET Core 2.0 服务。全面的跨平台支持 .NET Core 2.0 服务在 Windows 和 Linux 上即将推出。

来自Service Fabric 6.1 发布说明。因此,只要你的目标是 .net core 2.0,就不能在 Linux 上运行。


这是我最初的想法,但从Github上看,他们说它应该已经在预览中可用了。 话虽如此,关于我的问题,我取得了很大的进展,现在几乎有些东西可以运行了。当一切100% OK时,我会发布一些信息。 - ken2k

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接