.NET Core 程序的打包与分发 (Linux 篇 Part. I)

.NET Core 从 runtime 发布 1.0 RTM 版本至今已经过去一年多了,由于其主要针对跨平台 Console App 设计(ASP.NET Core 的实际部署方式也是 Console App),在 Windows 和 macOS 上的应用都比较有限:对于 Windows 平台,由于有兼容性较好的完整 .NET Framework,几乎不需要使用 .NET Core;而 macOS 程序主要以桌面 GUI 为主,.NET Core 除了用在服务器程序开发中,也很难有什么别的用途,而 macOS 上 mono 程序开发/调试的体验也还说得过去。

但 Linux 的情况就不同。首先 Linux 的应用主要以 Console/Web Server 为主,而 .NET Core 主要就是针对这些;其次在很多发行版上,mono runtime 的版本非常旧,API 实现不全,并且调试不方便。这种情况下,合理使用 .NET Core 能显著简化 .NET 应用程序的部署。

本文主要针对将 Console App 使用 Self-contained 方式(自带完整 runtime)部署到 .NET Core 支持的发行版的应用场景。这些发行版的特征是存在版本号(非滚动更新),且同一版本内二进制 ABI 稳定,如 Ubuntu, Debian, CentOS/RHEL, Fedora 等;而对于滚动更新的发行版如 Arch Linux 等,我将会在下一篇文章中讲述如何分发 .NET Core 程序。

依赖

  1. .NET Core SDK for Linux
    本文使用 .NET Core runtime 1.1, .NET Core SDK 1.0,因此示例中会出现很多 netcoreapp1.1,如果你需要不同版本的 .NET Core runtime,请将其替换为你需要的版本,如 netcoreapp1.0, netcoreapp2.0
  2. FPM
  3. rpm-build (如果需要构建 rpm 包)

个人推荐使用 CentOS 7.x,主要是因为以下理由:

  1. 据我观察,CentOS 上的 FPM 遇到的疑难杂症最少,因为其 Ruby 版本最旧(但对 FPM 又足够新),并且一直不更新大版本。
  2. CentOS 制作 RPM 和 DEB 包都比较方便,而很多不使用 RPM 的发行版可能不方便安装 rpm-build

示例程序

本文的分发对象是一个简单的 Hello World 程序,最终的效果是用户安装 deb/rpm 包之后,使用终端执行 helloworld 输出 “Hello World!”。

生成 Hello World 程序

mkdir ~/helloworld
cd ~/helloworld
dotnet new console

添加 Runtime Identifier (RID)

修改 helloworld.csproj,在 PropertyGroup 中添加 RuntimeIdentifiers,在其中填入你的目标发行版,用 “;” 分隔。

比如我的目标发行版是 CentOS 7 和 Debian 8,那么我的 helloworld.csproj 内容如下

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
    <RuntimeIdentifiers>centos.7-x64;debian.8-x64</RuntimeIdentifiers>
  </PropertyGroup>
</Project>

.NET Core 2.0 为了简化生成与发布流程,新增了 linux-x64 这个 Runtime identifier,其生成的二进制可以在多数 .NET Core 支持的发行版上通用。如果你使用的 TargetFramework 是 netcoreapp2.0,可以考虑使用 linux-x64。

runtime identifier 参考

生成二进制

.NET Core 的 SDK 支持 publish 命令,可以指定 configuration, runtime identifierframework 选项,生成目标平台可直接运行的二进制文件。

以 CentOS 7 为例,使用以下命令:

dotnet restore -r centos.7-x64
dotnet publish -c Release -f netcoreapp1.1 -r centos.7-x64 --output pack_root/opt/helloworld

可编译项目,并将 CentOS 7 平台的二进制可执行文件 publish 至 pack_root/opt/helloworld 中。

如果需要为其它受 .NET Core 支持的 Linux 发行版生成二进制,可将 centos.7-x64 替换为对应发行版的 Runtime Identifier。

使用 FPM 打包

在打包之前,首先要将二进制软链接至 /usr/bin 目录下:

mkdir -p pack_root/usr/bin
ln -s /opt/helloworld/helloworld pack_root/usr/bin

设置权限

find pack_root/ -type d | xargs chmod 755
find pack_root/ -type f | xargs chmod 644
chmod +x pack_root/opt/helloworld/helloworld
chmod +x pack_root/usr/bin/helloworld

调用 FPM

指定包信息,从 pack_root 目录制作 RPM 包,输出到当前目录

fpm -s dir -t rpm --name "helloworld" -C pack_root/ --version 1.0.0 --iteration 1 --description "Hello World" \
--depends libunwind --depends libicu \
--package ./

输出 Created package {:path=>"./helloworld-1.0.0-1.x86_64.rpm"}则为打包成功。

其中 --depends libunwind --depends libicu 为 .NET Core 在 CentOS 上依赖的包。如果需要为其它发行版打包,需要将 -t rpm 改为目标发行版的包管理器(如 deb/pacman 等),并且将 dependency 替换为该发行版上 .NET Core 的依赖(参考 .NET Core 安装步骤里的依赖安装)。

如果你的程序依赖了发行版包管理器的其它的包,也要在这里写上。

使用 sudo yum install helloworld-1.0.0-1.x86_64.rpm 安装这个 helloworld 包,并尝试使用 helloworld命令,检查是否正确安装。

总结

使用这种方式制作并分发 .NET 程序的包相对较容易,并且在目标环境下无需安装 .NET 环境,只需要安装几个基本的依赖,不同 .NET 程序之间的环境互不影响;缺点是一个 Hello World 的包就有 50MB,并且只支持一部分发行版。

事实上, .NET Core 除了支持自带 Runtime,还可使用共享 Runtime。在下一篇文章中,我将详细讲解如何使用发行版提供的共享 Runtime (包括 Mono runtime)部署 .NET Core 程序。

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注