0x01 前言

最近我又在计划我博客新一代的架构,加入了监控联动的部分。监控主要依赖zabbix,而zabbix agent则是很重要的一个组件。但官方的安装包不能很好适配我的环境,所以我决定自行打包。

自行打包还有一个好处,可以预先配置好agent的配置文件。当rpm被安装时自动填充配置文件内的hostname等信息,而后利用zabbix的自动注册功能在server注册,以此实现自动化监控。

最终通过外部程序调用zabbix api获取数据,最终实现高可用。

想的是比较美好。

0x02 准备

我是用的系统是centos7,只有64位的版本,所以不用纠结32位的;而zabbix源码使用最新的版本,在我写这篇文章的时候,最新版本为4.2.0,下载地址如下:

首先准备一台纯净的虚拟机:

[root[email protected] ~]# lsb_release -a
LSB Version:	:core-4.1-amd64:core-4.1-noarch
Distributor ID:	CentOS
Description:	CentOS Linux release 7.6.1810 (Core) 
Release:	7.6.1810
Codename:	Core

因为打包的过程极有可能需要多次调试,每一次调试都需要编译,为了能最大程度减少编译时间,建议使用SSD替代HDD。

准备好打包环境后即可安装依赖,因为我环境的需要,我需要安装以下软件包:

rpm-build gcc gcc-c++ curl-devel libssh2-devel OpenIPMI-devel

最后两个请根据实际情况选择,一般情况下可以忽略:

完成依赖安装后建立一个临时文件夹,而我的习惯是建立一个codex的文件夹:

[[email protected] ~]# mkdir /root/codex

默认情况下,rpmbuild的topdir在当前用户的home目录下,但我们可以作出修改:

# 打开或建立文件
vim ~/.rpmmacros

# 填入内容并保存
%_topdir /root/codex/

紧接着执行一次rpmbuild命令即可完成相关文件夹的创建:

# 执行命令
[[email protected] ~]# rpmbuild 1
错误:stat /root/1 失败:没有那个文件或目录

# 检查目录
[[email protected] ~]# ll codex/
总用量 0
drwxr-xr-x 2 root root 6 4月  11 23:21 BUILD
drwxr-xr-x 2 root root 6 4月  11 23:21 BUILDROOT
drwxr-xr-x 2 root root 6 4月  11 23:21 RPMS
drwxr-xr-x 2 root root 6 4月  11 23:21 SOURCES
drwxr-xr-x 2 root root 6 4月  11 23:21 SPECS
drwxr-xr-x 2 root root 6 4月  11 23:21 SRPMS

部分目录的用途如下:

  • BUILD:存放打包过程中的源文件
  • SOURCE:存放源文件
  • SPEC:存放SPEC文件
  • SRPM:存放RPM格式的源文件
  • RPM:存放二进制文件

最后到SOURCES目录下下载zabbix源码即可:

# 进入文件夹
[[email protected] ~]# cd /root/codex/SOURCES/

# 下载文件
[[email protected] SOURCES]# wget 'https://sourceforge.net/projects/zabbix/files/ZABBIX%20Latest%20Stable/4.2.0/zabbix-4.2.0.tar.gz/download' -O zabbix-4.2.0.tar.gz

下载好的源码压缩包不需要解压。

0x03 SPEC文件

SPEC文件是RPM包的描述文件,通过这个文件可以定义如软件名、版本、类别、打包人等信息,最重要的是包含创建、安装和卸载时的操作等。

一个SPEC文件包含几个区段,每个区段对应不同的部署阶段,以下是本文用到的内容:

0x03.1 文件头

文件头主要包含软件包的一些信息,诸如软件名、版本、授权等信息,具体内容如下:

%define             client_hostname %(hostname)
%define             build_timestamp %(date +%s)

Name:               zabbix-agent
Version:            4.2.0
Release:            1%{?dist}
Summary:            Zabbix Agent
Group:              Applications/Internet
License:            GPL
Url:                https://ngx.hk
Source0:            zabbix-4.2.0.tar.gz
Buildroot:          %{_tmppath}/zabbix-%{version}-%{release}-root-%(%{__id_u} -n)
Vendor:             TerenceChuen
BuildArch:          x86_64
BuildRequires:      gcc, gcc-c++, libssh2-devel, OpenIPMI-devel, curl-devel
Requires:           gcc, gcc-c++, libssh2, OpenIPMI

%description
zabbix agent for NGX.HK

第1和第2行中有个%define,它为自定义宏,它可以调用shell脚本等,并可在后面的区段中引用:

# 定义宏,获取hostname
%define             client_hostname %(hostname)

# 引用
%{client_hostname}

BuildRequires与Requires则表示打包时需要的依赖包和安装时需要的依赖包。如果没在系统中检测到相关软件包,则抛出错误。

0x03.2 构建预处理

预处理段以%prep开头,并在下一行编写命令。

预处理段支持shell命令,一般用来进行解包操作,如tar,unzip等。

而一般情况下,我会使用&setup解压SOURCES目录中的tar.gz压缩包。如果压缩包的名称与软件包的名称一致,那么可以直接使用:

%setup -c

如果不一致或希望提高SPEC文件的可用性,可以用-n指定压缩包名称:

%setup -q -n zabbix-%{version}

而%{version}这个宏是在文件头中定义的Version,上面这个%setup命令最终会将SOURCES目录下的zabbix-4.2.0.tar.gz文件解压到BUILD目录中。

0x03.3 构建

构建段以%build开头,这部分内容和平平常编译软件的部分一致,主要通过configure文件与相关参数定制化软件并使用make编译。

如果是其他软件,构建部分的内容可能较为复杂。而zabbix agent的构建内容如下:

%build
./configure \
--prefix=/usr/local/zabbix \
--enable-agent \
--with-net-snmp \
--with-libcurl \
--with-libxml2 \
--with-ssh2 \
--with-openipmi \
--with-openssl \
--with-libcurl
make

0x3.4 安装段

安装段以%install开头,主要执行的是make install命令,而安装的路径为文件头中定义的目录:Buildroot。

%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install

0x03.5 清理临时文件

保持目录整洁是个好习惯。

%clean
rm -rf $RPM_BUILD_ROOT

0x03.6 部署前预处理

该部分以%pre开头。

这个部署是指将打包好的RPM包在目标系统中安装的过程,而这个安装可能会有前置准备工作。诸如用户创建、目录创建与权限修改等。

%pre
getent group zabbix || groupadd zabbix
getent passwd zabbix || useradd -M -s /sbin/nologin -g zabbix zabbix
[ -d /usr/local/zabbix ] && rm -rf /usr/local/zabbix
[ -d /var/run/zabbix ] || mkdir /var/run/zabbix && chown zabbix:zabbix /var/run/zabbix
[ -f /var/log/zabbix_agentd.log ] || touch /var/log/zabbix_agentd.log && chown zabbix:zabbix /var/log/zabbix_agentd.log

因为我打包的zabbix agent RPM安装包主要用于系统安装完成后的自动化部署工作,所以没有旧版本zabbix agent配置文件备份等顾虑,因此我在部署前预处理部分中选择直接删除相关文件夹。

如果你是打包用于升级或大规模分发的RPM包,则建议根据实际情况修改该部分内容。

0x03.7 安装后执行

该部分以%post开头。

该部分主要是RPM包安装后执行,可以进行RPM解包之后的配置文件修改等操作。

%post
sed -i "/^# PidFile=/a\PidFile=/var/run/zabbix/zabbix_agentd.pid" /usr/local/zabbix/etc/zabbix_agentd.conf
sed -i "/^LogFile=/c\LogFile=/var/log/zabbix_agentd.log" /usr/local/zabbix/etc/zabbix_agentd.conf
sed -i "/^Server=/c\Server=zbx.ngx.hk" /usr/local/zabbix/etc/zabbix_agentd.conf
sed -i "/^ServerActive=/c\ServerActive=zbx.ngx.hk" /usr/local/zabbix/etc/zabbix_agentd.conf
sed -i "/^Hostname=/c\Hostname=%{client_hostname}" /usr/local/zabbix/etc/zabbix_agentd.conf
cat > /usr/lib/systemd/system/zabbix-agent.service << "EOF"
[Unit]
Description=Zabbix Agent
After=syslog.target
After=network.target

[Service]
Environment="CONFFILE=/usr/local/zabbix/etc/zabbix_agentd.conf"
EnvironmentFile=-/etc/sysconfig/zabbix-agent
Type=forking
Restart=on-failure
PIDFile=/run/zabbix/zabbix_agentd.pid
KillMode=control-group
ExecStart=/usr/local/zabbix/sbin/zabbix_agentd -c $CONFFILE
ExecStop=/bin/kill -SIGTERM $MAINPID
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable zabbix-agent.service

而在我的项目中,我还建立了启动文件zabbix-agent.service,并将其设为自动启动。

0x3.8 卸载前执行

卸载前需要停止服务,而我在停止服务之后还禁用了该服务的自动启动以删除相关软链接,而这部分内容以%preun:

%preun
systemctl stop zabbix-agent.service
systemctl disable zabbix-agent.service

0x03.9 卸载后执行

如果大家卸载过zabbix agent会发现,它会将配置文件重命名并保留在系统中,以便后续查看或修改。而这个工作正是由%preun这个区段内容执行的脚本。

# rpm卸载后执行
%postun
userdel zabbix
rm -rf /usr/local/zabbix
rm -rf /var/run/zabbix
rm -f /usr/lib/systemd/system/zabbix-agent.service

但在我的环境中就比较粗暴,我选择直接删除文件与目录。

0x03.10 打包的文件

以%files开头的内容表示需要打包到RPM包中的内容,因为我编译的路径为:/usr/local/zabbix,所以我的配置信息为:

%files
%defattr(-,root,root)
/usr/local/zabbix

这里有个%defattr,代表默认权限。

-为权限默认值,即:文本文件0644、可执行文件0755,而后面两个值分别为owner和group。

当然,还可以单独配置权限,如:

%attr(0755,root,root) /usr/local/zabbix/bin/zabbix_agentd

0x03.11 完整文件

%define             client_hostname %(hostname)
%define             build_timestamp %(date +%s)

Name:               zabbix-agent
Version:            4.2.0
Release:            1%{?dist}
Summary:            Zabbix Agent
Group:              Applications/Internet
License:            GPL
Url:                https://ngx.hk
Source0:            zabbix-4.2.0.tar.gz
Buildroot:          %{_tmppath}/zabbix-%{version}-%{release}-root-%(%{__id_u} -n)
Vendor:             TerenceChuen
BuildArch:          x86_64
BuildRequires:      gcc, gcc-c++, libssh2-devel, OpenIPMI-devel, curl-devel
Requires:           gcc, gcc-c++, libssh2, OpenIPMI

%description
zabbix agent for NGX.HK

# 预处理
%prep

# 解压源码压缩包
%setup -q -n zabbix-%{version}

# 编译
%build
./configure \
--prefix=/usr/local/zabbix \
--enable-agent \
--with-net-snmp \
--with-libcurl \
--with-libxml2 \
--with-ssh2 \
--with-openipmi \
--with-openssl \
--with-libcurl
make

# 安装
%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install

# 清理临时文件
%clean
rm -rf $RPM_BUILD_ROOT

# 安装前执行
%pre
getent group zabbix || groupadd zabbix
getent passwd zabbix || useradd -M -s /sbin/nologin -g zabbix zabbix
[ -d /usr/local/zabbix ] && rm -rf /usr/local/zabbix
[ -d /var/run/zabbix ] || mkdir /var/run/zabbix && chown zabbix:zabbix /var/run/zabbix
[ -f /var/log/zabbix_agentd.log ] || touch /var/log/zabbix_agentd.log && chown zabbix:zabbix /var/log/zabbix_agentd.log

# rpm安装后执行
%post
sed -i "/^# PidFile=/a\PidFile=/var/run/zabbix/zabbix_agentd.pid" /usr/local/zabbix/etc/zabbix_agentd.conf
sed -i "/^LogFile=/c\LogFile=/var/log/zabbix_agentd.log" /usr/local/zabbix/etc/zabbix_agentd.conf
sed -i "/^Server=/c\Server=zbx.ngx.hk" /usr/local/zabbix/etc/zabbix_agentd.conf
sed -i "/^ServerActive=/c\ServerActive=zbx.ngx.hk" /usr/local/zabbix/etc/zabbix_agentd.conf
sed -i "/^Hostname=/c\Hostname=%{client_hostname}" /usr/local/zabbix/etc/zabbix_agentd.conf
cat > /usr/lib/systemd/system/zabbix-agent.service << "EOF"
[Unit]
Description=Zabbix Agent
After=syslog.target
After=network.target

[Service]
Environment="CONFFILE=/usr/local/zabbix/etc/zabbix_agentd.conf"
EnvironmentFile=-/etc/sysconfig/zabbix-agent
Type=forking
Restart=on-failure
PIDFile=/run/zabbix/zabbix_agentd.pid
KillMode=control-group
ExecStart=/usr/local/zabbix/sbin/zabbix_agentd -c $CONFFILE
ExecStop=/bin/kill -SIGTERM $MAINPID
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable zabbix-agent.service

# rpm卸载前执行
%preun
systemctl stop zabbix-agent.service
systemctl disable zabbix-agent.service

# 放入rpm中的文件
%files
# 文件属性,mode,owner,group,-为默认值(文本文件0644,可执行文件0755)
%defattr(-,root,root)
/usr/local/zabbix

# rpm卸载后执行
%postun
userdel zabbix
rm -rf /usr/local/zabbix
rm -rf /var/run/zabbix
rm -f /usr/lib/systemd/system/zabbix-agent.service

0x04 打包

通过help参数查看rpmbuild的相关信息可以发现又多种打包参数:

在这里,我使用-ba参数从specfile构建源代码和二进制软件包:

[[email protected] SPECS]# rpmbuild -ba zabbix-agent.spec

完成编译和打包后即可在RPMS文件夹中找到打包好的RPM包:

[[email protected] SPECS]# ll -h /root/codex/RPMS/x86_64/
总用量 996K
-rw-r--r-- 1 root root 234K 4月  18 11:50 zabbix-agent-4.2.0-1.el7.x86_64.rpm
-rw-r--r-- 1 root root 757K 4月  18 11:50 zabbix-agent-debuginfo-4.2.0-1.el7.x86_64.rpm

0x05 应用

将在0x04中打包好的RPM包复制到目标机器,即可用rpm或yum命令安装:

[[email protected] x86_64]# yum install ./zabbix-agent-4.2.0-1.el7.x86_64.rpm

卸载可以通过rpm和yum进行:

[[email protected] x86_64]# yum remove zabbix-agent.x86_64

0x06 结语

RPM包可以将打包的时间戳添加到文件名中,然后将PRM包放置到内部源,再通过自动部署手段即可实现自动更新、下发配置等操作。