.NET Framework应用程序的Linux容器(当很难使用.Net Core时)

哈Ha

我想与世界分享至少对我来说是一项非典型的任务及其解决方案,在我看来这是可以接受的。下面描述的方法可能不是解决问题的理想方法,但是它可以正常工作,并且可以按预期工作。

设置和背景


有一项工作要做:您需要在现场制作各种设备,材料和物体的BIM模型的3D预览。需要轻巧,简单的东西。

这些对象的模型以各种CAD系统的专有格式以及3D模型的开放格式的形式存储在网站上,并可供下载其中包括IFC格式我将其用作解决此任务的资源。

选项之一及其功能


形式上,人们可能会限制自己编写某种* .ifc转换器,以便将某些内容显示在网页上。这是我开始的地方。

选择了一个很棒的工具箱xBIM工具箱进行了这种转换

使用此工具的示例简单明了地描述了如何使用IFC和专门用于Web格式的* .wexBIM。

首先,将* .ifc转换为* .wexBIM:
using System.IO;
using Xbim.Ifc;
using Xbim.ModelGeometry.Scene;

namespace CreateWexBIM
{
    class Program
    {
        public static void Main()
        {
            const string fileName = "SampleHouse.ifc";
            using (var model = IfcStore.Open(fileName))
            {
                var context = new Xbim3DModelContext(model);
                context.CreateContext();

                var wexBimFilename = Path.ChangeExtension(fileName, "wexBIM");
                using (var wexBiMfile = File.Create(wexBimFilename))
                {
                    using (var wexBimBinaryWriter = new BinaryWriter(wexBiMfile))
                    {
                        model.SaveAsWexBim(wexBimBinaryWriter);
                        wexBimBinaryWriter.Close();
                    }
                    wexBiMfile.Close();
                }
            }
        }
    }
}


接下来,在“播放器” xBIM WeXplorer中使用生成的文件

在页面中嵌入* .wexBIM的示例:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Hello building!</title>
    <script src="js/xbim-viewer.debug.bundle.js"></script>
</head>
<body>
    <div id="content">
        <canvas id="viewer" width="500" height="300"></canvas>
        <script type="text/javascript">
            var viewer = new xViewer('viewer');
            viewer.load('data/SampleHouse.wexbim');
            viewer.start();
        </script>
    </div>    
</body>
</html>


好吧,走吧。我从xBIM中获取细节。我正在编写一个控制台应用程序,该应用程序接收一堆* .ifc文件的路径作为输入,并且在它们旁边添加了一堆* .wexBIM文件。一切都可以上传到站点。

但是以某种方式它很简单...我希望该程序成为一种服务,它在向门户网站发送* .ifc事件后,立即创建必要的* .wexBIM,并立即出现在准备好的容器中。

好的,我提出了新的要求:

  1. 让转换任务来自我们的RabbitMQ ;
  2. 我想以二进制消息的形式查看任务本身,实际上它已准备好由protobuf文件中描述的类进行反序列
  3. 该任务将包含一个链接,可从我们的Minio下载源* .ifc文件;
  4. 该任务还将告诉我在Minio的哪个存储桶中添加结果;
  5. 让应用程序本身在.net core 3.1下构建,并在“ docker farm”上的Linux docker容器内工作;

最初的困难和约定


我不会详细描述实施的前四点。也许以后。

使应用程序侦听作业队列,并将包含结果的消息从作业消息CorrelationId发送到队列拧紧了protobuf生成的请求/响应类。教用minio下载/上传文件

我在控制台应用程序项目中所做的所有工作。在项目设置中:

<TargetFramework>netcoreapp3.1</TargetFramework>

在装有Windows 10的计算机上,所有内容均已完全调试并正常工作。但是,当我尝试在WSL中运行应用程序时,出现System.IO.FileLoadException错误

完整的错误信息:
{
  "Type": "System.IO.FileLoadException",
  "Message": "Failed to load Xbim.Geometry.Engine64.dll",
  "TargetSite": "Void .ctor(Microsoft.Extensions.Logging.ILogger`1[Xbim.Geometry.Engine.Interop.XbimGeometryEngine])",
  "StackTrace": " at Xbim.Geometry.Engine.Interop.XbimGeometryEngine..ctor(ILogger`1 logger)\r\n at Xbim.Geometry.Engine.Interop.XbimGeometryEngine..ctor()\r\n at Xbim.ModelGeometry.Scene.Xbim3DModelContext.get_Engine()\r\n at Xbim.ModelGeometry.Scene.Xbim3DModelContext.CreateContext(ReportProgressDelegate progDelegate, Boolean adjustWcs)\r\n at My.Converter.ConvertIfc.CreateWebIfc(String ifcFileFullPath, String wexBIMFolder)",
  "Data": {},
  "InnerException": {
    "Type": "System.IO.FileNotFoundException",
    "Message": "Could not load file or assembly 'Xbim.Geometry.Engine.dll, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.",
    "FileName": "Xbim.Geometry.Engine.dll, Culture=neutral, PublicKeyToken=null",
    "FusionLog": "",
    "TargetSite": "System.Reflection.RuntimeAssembly nLoad(System.Reflection.AssemblyName, System.String, System.Reflection.RuntimeAssembly, System.Threading.StackCrawlMark ByRef, Boolean, System.Runtime.Loader.AssemblyLoadContext)",
    "StackTrace": " at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, RuntimeAssembly assemblyContext, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, AssemblyLoadContext assemblyLoadContext)\r\n at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, StackCrawlMark& stackMark, AssemblyLoadContext assemblyLoadContext)\r\n at System.Reflection.Assembly.Load(String assemblyString)\r\n at Xbim.Geometry.Engine.Interop.XbimGeometryEngine..ctor(ILogger`1 logger)",
    "Data": {},
    "Source": "System.Private.CoreLib",
    "HResult": -2147024894
  },
  "Source": "Xbim.Geometry.Engine.Interop",
  "HResult": -2146232799
}

一段积极的谷歌搜索和深思熟虑的阅读告诉我,我非常专心:
Recently at work, we were evaluating a few options to render building models in the browser. Building Information Modeling (BIM) in interoperability scenarios is done via Industry Foundation Classes, mostly in the STEP Physical File format. The schema is quite huge and complex with all the things you have to consider, so we were glad to find the xBim open source project on GitHub. They've got both projects to visualize building models in the browser with WebGL as well as conversion tools to create the binary-formatted geometry mesh. To achieve that, native C++ libraries are dynamically loaded (so no .Net Core compatibility) which must be present in the bin folder. The C++ libraries are expected either in the same folder as the application binaries or in a x64 (or x86, respectively) sub folder (See here for more details). In regular projects, the xBim.Geometry NuGet package adds a build task to copy the dlls into the build output folder, but this doesn't work with the new tooling. You can, however, get it to work in Visual Studio 2015 by taking care of supplying the interop dlls yourself.

类似的困难并不仅仅针对我。很多人都希望在.Net Core下使用xBIM
并不是很关键,但是它发生了很大的变化……这都归结为无法正常加载Xbim.Geometry.Engine64.dll您需要在计算机上安装vc_redist.x64.exe我有什么选择?
我想到的第一件事是:“可以使用具有完整.Net框架的Windows容器吗?
将适用于Visual Studio 2015、2017和2019的Microsoft Visual C ++可再发行组件交付到此容器,一切都会好吗?” 我尝试了这个:

为Docker测试Windows映像:
.Net Core :

<TargetFramework>net47</TargetFramework>

Dockerfile:

FROM microsoft/dotnet-framework:4.7
WORKDIR /bimlibconverter
COPY lib/VC_redist.x64.exe /VC_redist.x64.exe
RUN C:\VC_redist.x64.exe /quiet /install
COPY bin/Release .
ENTRYPOINT ["MyConverter.exe"]

好吧,它奏效了...还活着!但。但是带docker的主机Linux机器呢?使用Windows Server Core上的映像来驱动容器将不起作用需要下车...

妥协与掩饰


网上的另一次搜索带给我一篇文章在其中,作者需要类似的实现:
更糟的是:
所有二进制文件都是32位(x86)。
有些需要可视化的C ++可再发行运行时组件。
有些需要.NET运行时。
即使我们仅使用命令行界面(CLI),有些也需要开窗系统。
这篇文章描述了在Linux容器中的wine中运行Windows应用程序的潜力。很好奇,我决定。

经过一些测试,错误和补充后,收到了Dockerfile:

带有Wine,.Net Framework和vcredist的基于Ubuntu的Docker镜像:

FROM ubuntu:latest
#  x86
RUN dpkg --add-architecture i386 \
    && apt-get update \
    #   
    && apt-get install -qfy --install-recommends \
        software-properties-common \
        gnupg2 \
        wget \
        xvfb \
        cabextract \
    #  Wine
    && wget -nv https://dl.winehq.org/wine-builds/winehq.key \
    && apt-key add winehq.key \
    && apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ bionic main' \
    #     Wine
    && add-apt-repository ppa:cybermax-dexter/sdl2-backport \
    #  Wine
    && apt-get install -qfy --install-recommends \
        winehq-staging \
        winbind \
    # 
    && apt-get -y clean \
    && rm -rf \
      /var/lib/apt/lists/* \
      /usr/share/doc \
      /usr/share/doc-base \
      /usr/share/man \
      /usr/share/locale \
      /usr/share/zoneinfo
#    Wine
ENV WINEDEBUG=fixme-all
ENV WINEPREFIX=/root/.net
ENV WINEARCH=win64
#  Wine
RUN winecfg \
    # winetricks,   .Net Framework  
    && wget https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks \
    -O /usr/local/bin/winetricks \
    && chmod +x /usr/local/bin/winetricks \
# 
    && apt-get -y clean \
    && rm -rf \
      /var/lib/apt/lists/* \
      /usr/share/doc \
      /usr/share/doc-base \
      /usr/share/man \
      /usr/share/locale \
      /usr/share/zoneinfo \
    # Wine   
    && wineboot -u && winetricks -q dotnet472 && xvfb-run winetricks -q vcrun2015

WORKDIR /root/.net/drive_c/myconverter/

#  
COPY /bin/Release/ /root/.net/drive_c/myconverter/

ENTRYPOINT ["wine", "MyConverter.exe"]

UPD: . rueler

构建不是很快,但是成功结束了。我尝试,检查一下。作品!

结果,结论,想法


有效。输出是docker容器的Linux映像。它是“蓬松的”(〜5.2GB),但是启动非常快,并且在.Net Framework 4.7上运行Windows控制台应用程序,该应用程序侦听RabbitMQ,将日志写入Graylog,将文件下载并上传Minio中。我将通过远程docker API更新应用程序。

解决了功利主义专用任务的解决方案。也许而且很可能不是普遍的。但原则上我感到满意。也许有人也会派上用场。

谢谢阅读。我第一次在Habr上写作。在评论中见。

All Articles