使用 GitLab CI/CD 为 Linux 构建 RPM 包(二)

大家好!我是大聪明-PLUS

上一篇文章介绍了构建 RPM 包和自动化该过程的基础知识。

本出版物是该系列的最后一篇,展示了现成解决方案的实际实施,包括:

  • Web 服务开发。

  • 安装软件包时,将该服务注册为具有自动加载(systemd)的系统服务。

  • 通过 journald 实现集中日志记录。

因此,创建了一个功能齐全的模板,用于快速部署服务,随后将其发布在 RPM 包中。

Web 服务开发

计数器程序非常适合用来演示该解决方案的功能。让我们将上一节中的简单控制台程序转换为 Web 应用程序。该程序应执行以下操作:

  • 是一个 Web 应用程序。在端口 5432 上运行。

  • 在后台运行时,运行计数器

  • 实现 GetValue 端点,当访问时,该端点将返回当前计数器值作为响应。

  • 提供基本操作的日志记录:

    • Web 服务启动

    • 启动计数器

    • 记录访问端点的事实

如果程序是从头创建的,那么您需要运行以下命令

复制代码
`dotnet new webapp -n <Название проекта>`

您可以通过对 csproj 文件进行以下更改来修改原始应用程序

复制代码
`<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>

    <ItemGroup>

        <PackageReference Include="Microsoft.AspNetCore" Version="2.3.0" />
        <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.3.0" />
        <PackageReference Include="NLog" Version="6.0.5" />
        <PackageReference Include="NLog.Web.AspNetCore" Version="6.0.5" />
    </ItemGroup>

</Project>`

关键的变化是改变Sdk类型(第1行)

另外,请注意所需的 NuGet 包。示例中只有四个。Microsoft.AspNetCore 和 Microsoft.AspNetCore.Hosting 是 Web 服务正常运行所必需的。另外两个是通过 NLog 库进行日志记录的包。

设置日志记录

在我的示例中,选择 Nlog 库进行日志记录的原因如下:

  • 这是大多数应用程序开发中公司团队登录的标准解决方案。

  • 配置方便,设置灵活。

  • 一个充满活力的社区。如果有任何问题,一定能找到解决方案。

要操作该库,需要创建一个配置文件 nlog.config

它现在包括创建 Web 应用程序本身、检索值的端点、初始化日志记录以及优雅地终止进程。

所以就保留它们吧。这个程序仍然非常简单,一眼就能看懂。不过,这是一个功能齐全的原型。如果它能正常工作,那么更复杂的解决方案也同样有效。

SPEC文件修改

最重要且最有趣的问题是如何修改 SPEC 文件,使程序成为服务?在提供修改后文件的示例之前,让我们先了解一下关键的变化:

  • 依赖关系已正确指定。

  • 除了程序的二进制文件之外,还会复制并安装 nlog.config。这看似一个小细节,但它很好地说明了如果有多个文件(通常情况下)该如何处理。

  • 生成服务文件并安装在所需路径中。

  • 添加负责初始安装和删除服务的脚本,这将注册我们的服务,反之亦然 - 删除它。

指定依赖项

复制代码
`BuildRequires:  dotnet-sdk-8.0
Requires:       systemd dotnet-8.0`

在将原型最终确定为可运行状态时,依赖项已正确配置。SDK 并非应用程序运行所必需的,但它是构建所必需的。因此,如果 Docker 镜像中未安装 SDK,则会出现更容易分析的错误。

第二个依赖项更有趣。大多数现代发行版使用 systemd init 系统。然而,有些发行版使用传统的 SysV Init。不同系统之间存在差异,包括命令语法。当前的 spec 文件是专门为 systemd 配置的,这在依赖项中有所说明。

**请注意!**如果您想了解(而且绝对有必要)服务注册、自动启动和启动顺序的工作原理,我推荐 Brian Ward 的《Linux 内部原理》。这本书在网上很容易找到,各种格式的版本都有。它提供了关于这个主题以及许多其他主题的相当全面的信息。如果您还没有读过,一定要读一读。它会让事情变得更加清晰易懂。

复制附加程序文件

上一篇文章没有关注构建器以及安装程序如何了解将哪些文件复制到哪里。

这分为两个阶段完成。第一阶段,在安装部分,负责在虚拟根系统中创建必要的目录并将文件夹复制到这些目录。

复制代码
`%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/opt/%{name}
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_unitdir}

install -m 755 %{name}/bin/Release/net8.0/linux-x64/publish/%{name} %{buildroot}/opt/%{name}/%{name}
install -m 644 %{name}/nlog.config %{buildroot}/opt/%{name}/nlog.config`

在上面的清单中,您可以看到创建了三个目录并复制了两个文件。

files 部分的第二步指定了哪些文件最终对我们的程序是有价值的。在安装和配置过程中,可能会出现不必要的文件。

复制代码
`%files

/opt/%{name}/%{name}

%{_bindir}/%{name}


%{_unitdir}/%{name}.service


/opt/%{name}/nlog.config`

生成服务文件

这是新的东西。要将程序注册为服务,我们需要创建一个有效的服务文件,将其放在所需的目录中,然后执行注册和启动命令。

该文件可以单独创建,但在构建过程中生成会更方便。这样你就可以使用 %{name} 之类的宏了。该文件的这一部分负责生成服务文件:

复制代码
`cat > %{buildroot}%{_unitdir}/%{name}.service << EOF
[Unit]
Description=%{name} Service
After=network.target

[Service]
Type=exec
ExecStart=/opt/%{name}/%{name}
WorkingDirectory=/opt/%{name}
User=root
Restart=always
RestartSec=5

KillSignal=SIGTERM
TimeoutStopSec=30
SyslogIdentifier=%{name}
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF`

我自己可能没想到这一点。我之前在某个地方看到过。这是一个很棒的解决方案。我们将一个流读入一个文件,然后创建该文件。如上所述,许多行都使用了 %{name} 宏,它可以避免在程序名称更改时出现错误并浪费时间。

如果您之前了解过 systemd 的工作原理,那么这个清单本身就很简单。让我们简单浏览一下,以便更好地理解。

  • 仅当操作系统中的网络启动时,该服务才会启动。

  • 二进制文件将在相应工作目录的 /opt/%{name}/%{name} 启动。

  • 以 root 身份

  • 如果发生什么情况,5秒后重新启动。

  • 指定日志记录的标识符。

服务的初始安装和删除脚本

我非常希望该服务能够在安装软件包时自动启动,而无需任何额外的操作。%post %preun %postun 部分就是为此目的而设计的。

为了确定这是第一次安装(之前没有包)、删除还是更新,使用条件 $1 -eq <我们正在检查的号码>。

在 rpm-build 脚本中,$1 将返回安装时系统中该软件包的编号。结果如下:

  • 0 - 首次安装。未安装任何软件包。

  • 1 和 2 - 软件包已安装。删除或更新将执行不同的步骤。

在当前示例中,未检查包的更新。这样做是为了简单起见。

复制代码
`post

if [ $1 -eq 1 ]; then

    systemctl daemon-reload >/dev/null 2>&1 || :
    systemctl enable %{name}.service >/dev/null 2>&1 || :
    systemctl start %{name}.service >/dev/null 2>&1 || :
fi

%preun

if [ $1 -eq 0 ]; then

    systemctl stop %{name}.service >/dev/null 2>&1 || :
    systemctl disable %{name}.service >/dev/null 2>&1 || :
fi

%postun

if [ $1 -ge 1 ]; then

    systemctl daemon-reload >/dev/null 2>&1 || :
    systemctl try-restart %{name}.service >/dev/null 2>&1 || :
fi`

我确信,如果之前不清楚它的工作原理,那么现在它已经变得更加清晰了。

该服务在首次安装时注册。卸载时,它会被停止并删除。

结语

我想附加程序的源代码,但在编辑器里找不到。算了,它太简单了,比如说,发到 GitHub 上。我需要的都在这两篇文章里了。如果它们对大家有用,我会很高兴。谢谢你的关注。

相关推荐
SundayBear8 小时前
Linux驱动开发指南
linux·驱动开发·嵌入式
yugi9878389 小时前
C语言多进程创建和回收
linux·c语言·算法
鸠摩智首席音效师9 小时前
如何在 Bash 命令中执行命令 (嵌套命令) ?
linux·bash
Bella的成长园地9 小时前
Linux 中sudo bash -i 和 su root 有什么区别?
linux·运维·bash
vortex510 小时前
Linux 用户管理详解:从古老Unix到现代集成
linux·运维·unix
无敌最俊朗@10 小时前
C++ Opencv 蓝图01(进阶版)
linux·windows
omnibots11 小时前
瑞萨SDK编译linux时,make menuconfig报错
linux·服务器·前端·嵌入式硬件
Java 码农11 小时前
linux shell 数组
linux·运维·服务器
大梦谁先觉i11 小时前
Linux 磁盘空间“消失”之谜:文件已删,空间却不释放?
linux·运维·服务器