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

Posted

技术标签:

【中文标题】将 C# Stateful Service Fabric 应用程序从 Visual Studio 部署到 Linux【英文标题】:Deploy a C# Stateful Service Fabric application from Visual Studio to Linux 【发布时间】:2018-06-18 14:54:37 【问题描述】:

EDIT 04/06/18 => 更新了最后状态的问题


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

从 09/2017 开始,我应该可以迁移到 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 服务。

看起来有一个警告(虽然输出窗口上什么都没有),但如果我尝试通过 Service Fabric Tools 2.0(测试版)提供的模板使用 .Net core 2.0 创建一个新的空 Statful 服务,它仍然在这里:

所以我要忍受它。

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

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

    然后我稍微更改了以前用于 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
    然后,我已成功从 Visual Studio 部署到我的安全 SF Linux 集群。不幸的是,我的两个有状态服务都出现以下错误:

错误事件:SourceId='System.Hosting', 属性='代码包激活:代码:入口点'。有一个错误 在 CodePackage 激活期间。服务主机退出并终止 代码:134

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

从 Visual Studio 在 Linux 上部署 C# .Net Core SF 有状态服务的方法是否正确?

编辑:查看 LinuxsyslogVer2v0 表,我收到以下错误:

starthost.sh[100041]:未处理的异常: System.IO.FileLoadException:无法加载文件或程序集 'System.Threading.Thread,版本=4.1.0.0,文化=中性, PublicKeyToken=b03f5f7f11d50a3a'。定位程序集的清单 定义与程序集引用不匹配。 (例外来自 HRESULT: 0x80131040)

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

编辑:进一步澄清:

我的老板希望我在 Linux 上运行,因为从 D1v2 机器开始,价格是 Windows 机器的一半(无许可证等) 我的 .NET Core 2.0 服务在 Windows 上成功运行。所以 .NET Core 端口应该没问题。

【问题讨论】:

您可以尝试使用 shell 脚本来运行 dotnet,例如此处的示例:github.com/Azure-Samples/… 您可能有错误的运行时标识符。你的 Linux 集群是 Ubuntu 16.04,对吧?它的运行时标识符是 ubuntu.16.04-x64。完整列表在这里:docs.microsoft.com/en-us/dotnet/core/rid-catalog @VaclavTurecek 确实 RID 是错误的。 ubuntu.16.04-x64 也没有运气 为什么在 linux 机器上运行它很重要? @Mardoxx 一个很简单的原因:从 D1v2 开始,Linux 机器的价格是 Windows 的一半(与 Windows 相比)。所以我的老板希望我们从长远来看为我们的客户在 Linux 上进行部署。 【参考方案1】:

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


首先,Reliable Services 在 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。 编辑您的 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 上,它是一个可执行文件 (*.exe)。在 Linux 上不是。我最终得到了 Linux C# 示例并复制/粘贴了入口点。 https://docs.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”。

4。不要使用 MSBuild 构建!!

是的,它也应该构建 Linux 包,或者至少看起来如此,因为当您右键单击项目并单击“构建”时,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 : 集群连接实例为空

见https://***.com/a/38104087/870604

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

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

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

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

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

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

您终于使用 .NET Core 2.0 从 Windows 上的 Visual Studio(有点错误,我使用了 PowerShell)将 C# Reliable Services 部署到 Linux SF 集群。

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


结论:TL;DR

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

【讨论】:

【参考方案2】:

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

在此版本中,仅适用于 Windows 的 Service Fabric 支持 .NET Core 2.0 服务。即将全面支持 Windows 和 Linux 上的 .NET Core 2.0 服务。

来自Service Fabric 6.1 Release Notes 因此,只要您的目标是 .net core 2.0,就没有 Linux。

【讨论】:

这是我最初的想法,但从 github 上他们说它应该已经可以预览了。也就是说,我在我的问题上取得了很大进展,我现在几乎有一些东西正在运行。当它 100% OK 时,我会发布一些信息。【参考方案3】:

借助此帮助,我已成功部署到 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

【讨论】:

以上是关于将 C# Stateful Service Fabric 应用程序从 Visual Studio 部署到 Linux的主要内容,如果未能解决你的问题,请参考以下文章

Azure Service Fabric 错误:访问被拒绝。部署失败

从 Flutter 中的 Stateful Widget 返回数据

Flutter控件篇(Stateful widget)——ListView

Flutter Stateful Widget 重新创建 State

[转帖]理解k8s 的 Ingress

如何在 FAB 按钮单击时将数据从多个片段发送到单个活动