.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 程序。
依赖
- .NET Core SDK for Linux
本文使用 .NET Core runtime 1.1, .NET Core SDK 1.0,因此示例中会出现很多 netcoreapp1.1,如果你需要不同版本的 .NET Core runtime,请将其替换为你需要的版本,如 netcoreapp1.0, netcoreapp2.0 - FPM
- rpm-build (如果需要构建 rpm 包)
个人推荐使用 CentOS 7.x,主要是因为以下理由:
- 据我观察,CentOS 上的 FPM 遇到的疑难杂症最少,因为其 Ruby 版本最旧(但对 FPM 又足够新),并且一直不更新大版本。
- 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。
生成二进制
.NET Core 的 SDK 支持 publish
命令,可以指定 configuration
, runtime identifier
与 framework
选项,生成目标平台可直接运行的二进制文件。
以 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 程序。