Create an RPM Package

As of December 2, 2022, there were no rpm packages for NGINX Prometheus Exporter which could be easily installed in Alma Linux 9 (or other RHEL clones). I built a package for my needs and thanks to Open Build Service (OBS) made it available to others.

RPM Package

There is lots and lots of documentation on what an rpm package is and how to create one. You can follow the links at the end of this post for further reading. I will only talk about the steps I needed to take to get this package built.

  1. Download the latest release of NGINX Prometheus Exporter. I chose amd64 and arm64 because those are the only ones I was interested in.
  2. Create a spec file.
  3. Create a systemd service file.
  4. Create a default configuration file.

You will find all these files in my OBS subproject: nginx-prometheus-exporter.

I am posting these files here, too, to explain what they mean and where I learned how to create them.

nginx-prometheus-exporter.spec

Name: nginx-prometheus-exporter
Version: 0.11.0
Release: 1
License: Apache-2.0
Group: System/Management
Summary: NGINX Prometheus exporter
Url: https://github.com/nginxinc/nginx-prometheus-exporter
Source0: nginx-prometheus-exporter_%{version}_linux_amd64.tar.gz
Source1: nginx-prometheus-exporter_%{version}_linux_arm64.tar.gz
Source2: nginx-prometheus-exporter.service
Source3: nginx-prometheus-exporter.default

BuildRequires: systemd-rpm-macros
BuildRoot: %{_tmppath}/%{name}-%{version}-build
Requires: systemd-rpm-macros

%description
NGINX Prometheus exporter makes it possible to monitor NGINX or NGINX Plus using Prometheus.

%ifarch aarch64
%prep
%setup -a 1 -b 1 -c -n %{name}_%{version}
%endif

%ifarch x86_64
%prep
%setup -a 0 -b 0 -c -n %{name}_%{version}
%endif

%build

%install
rm -rf $RPM_BUILD_ROOT

install -dp %{buildroot}%{_bindir}
install -dp %{buildroot}/etc/default
install -dp %{buildroot}%{_unitdir}

install -p -m 755 %{name} %{buildroot}%{_bindir}
install -p -m 644 %{SOURCE2} %{buildroot}%{_unitdir}/%{name}.service
install -p -m 644 %{SOURCE3} %{buildroot}/etc/default/%{name}

%pre
%systemd_pre nginx-prometheus-exporter.service
getent group prometheus >/dev/null || %{_sbindir}/groupadd -r prometheus
getent passwd prometheus >/dev/null || %{_sbindir}/useradd -r -g prometheus -d %{_localstatedir}/lib/prometheus -M -s /sbin/nologin prometheus

%post
%systemd_post nginx-prometheus-exporter.service

%preun
%systemd_preun nginx-prometheus-exporter.service

%postun
%systemd_postun nginx-prometheus-exporter.service

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root,-)
%{_bindir}/%{name}
%{_unitdir}/%{name}.service
/etc/default/%{name}

%changelog

I took direct help from the sources below to write this spec file.

Multiple Architectures

I was interested in two architectures and so downloaded the release tarballs for both. Thankfully the releases are pre-built binaries so I didn't have to build them from scratch. I just needed to wrap them in an RPM package.

Notice Source0 and Source1 in the spec file. They are the two release tarballs. I downloaded them with curl.

$ curl -LO https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v0.11.0/nginx-prometheus-exporter_0.11.0_darwin_amd64.tar.gz
$ curl -LO https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v0.11.0/nginx-prometheus-exporter_0.11.0_darwin_arm64.tar.gz

In the %pre section I used ifarch to determine which architecture was used to build the package and used the corresponding tarball. OBS spins up the appropriate architecture to build the package so it works beautifully there.

I learned that rpm --eval %{arm}, when run in a newer version of Alma Linux (or other RHEL clones), will show all supported ARM architectures. Unfortunately, instead of armv8, aarch64 is the right name. I found that in OBS. If I run rpm --eval %{_arch} on my Raspberry Pi 3, its output is aarch64. The output on a Linode VM is x86_64 as well as on OBS. These are the values I used.

Since both tarballs have the same files, after unpacking them in %setup, I didn't need to use %ifarch in other sections.

%systemd_*

Since the package was meant for a systemd-enabled OS I had to use %systemd_ macros. These are provided by the systemd-rpm-macros package.

User prometheus

I had to run the exporter service as some user. So I created the prometheus group and user.

nginx-prometheus-exporter.service

[Unit]
Description=NGINX Prometheus Exporter
Documentation=https://github.com/nginxinc/nginx-prometheus-exporter
After=network-online.target nginx.service
Wants=network-online.target

[Service]
User=prometheus
EnvironmentFile=/etc/default/nginx-prometheus-exporter
ExecStart=/usr/bin/nginx-prometheus-exporter $ARGS
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

I took direct help from the sources below to write this service file.

nginx-prometheus-exporter.default

ARGS=""

# Usage of /usr/bin/nginx-prometheus-exporter:
#   -nginx.plus
#       Start the exporter for NGINX Plus. By default, the exporter is started for NGINX. The default value can be overwritten by NGINX_PLUS environment variable.
#   -nginx.retries uint
#       A number of retries the exporter will make on start to connect to the NGINX stub_status page/NGINX Plus API before exiting with an error. The default value can be overwritten by NGINX_RETRIES environment variable.
#   -nginx.retry-interval value
#       An interval between retries to connect to the NGINX stub_status page/NGINX Plus API on start. The default value can be overwritten by NGINX_RETRY_INTERVAL environment variable. (default 5s)
#   -nginx.scrape-uri string
#       A URI or unix domain socket path for scraping NGINX or NGINX Plus metrics.
#       For NGINX, the stub_status page must be available through the URI. For NGINX Plus -- the API. The default value can be overwritten by SCRAPE_URI environment variable. (default "http://127.0.0.1:8080/stub_status")
#   -nginx.ssl-ca-cert string
#       Path to the PEM encoded CA certificate file used to validate the servers SSL certificate. The default value can be overwritten by SSL_CA_CERT environment variable.
#   -nginx.ssl-client-cert string
#       Path to the PEM encoded client certificate file to use when connecting to the server. The default value can be overwritten by SSL_CLIENT_CERT environment variable.
#   -nginx.ssl-client-key string
#       Path to the PEM encoded client certificate key file to use when connecting to the server. The default value can be overwritten by SSL_CLIENT_KEY environment variable.
#   -nginx.ssl-verify
#       Perform SSL certificate verification. The default value can be overwritten by SSL_VERIFY environment variable. (default true)
#   -nginx.timeout value
#       A timeout for scraping metrics from NGINX or NGINX Plus. The default value can be overwritten by TIMEOUT environment variable. (default 5s)
#   -prometheus.const-labels value
#       A comma separated list of constant labels that will be used in every metric. Format is label1=value1,label2=value2... The default value can be overwritten by CONST_LABELS environment variable.
#   -version
#       Display the NGINX exporter version.
#   -web.listen-address string
#       An address or unix domain socket path to listen on for web interface and telemetry. The default value can be overwritten by LISTEN_ADDRESS environment variable. (default ":9113")
#   -web.secured-metrics
#       Expose metrics using https. The default value can be overwritten by SECURED_METRICS variable.
#   -web.ssl-server-cert string
#       Path to the PEM encoded certificate for the nginx-exporter metrics server(when web.secured-metrics=true). The default value can be overwritten by SSL_SERVER_CERT variable.
#   -web.ssl-server-key string
#       Path to the PEM encoded key for the nginx-exporter metrics server (when web.secured-metrics=true). The default value can be overwritten by SSL_SERVER_KEY variable.
#   -web.telemetry-path string
#       A path under which to expose metrics. The default value can be overwritten by TELEMETRY_PATH environment variable. (default "/metrics")
#

This file is used by nginx-prometheus-exporter.service (see EnvironmentFile) to provide the default arguments to the service. It's placed under /etc/default directory and admins can modify it to suit their needs. I had to use the -nginx.scrape-uri flag because my nginx stub site was configured differently than what the exporter expects by default.

Open Build Service

I used Open Build Service (OBS) to create this package and make it available in a repository. It works differently from if you built an RPM package locally. For example, I didn't have to organize the files in a specific directory structure that rpmbuild expects. All files lived in the same directory.

Create an account on OBS.

Install osc, e.g. on macOS with Homebrew.

$ brew install osc

Configure osc.

Create a subproject in the OBS web UI.

Configure repositories in the web UI. There was no RHEL 9 repository (environment) available, only RHEL 7 and older. I chose Fedora 37 because it was the newest available. Since I'm not building the binaries and only wrapping pre-built binaries in a package, this was good enough. It feels weird to build on Fedora 37 and have that as part of the repository name but it works fine. I also only included x86_64 and aarch64 architectures as discussed above. Get a list of all supported distributions here: https://api.opensuse.org/distributions?format=json (Build Service supported build targets). Use API docs for more information.

# https://build.opensuse.org/projects/home:aikchar:nginx-prometheus-exporter/meta
<project name="home:aikchar:nginx-prometheus-exporter">
<title>nginx-prometheus-exporter</title>
<description>NGINX Prometheus exporter makes it possible to monitor NGINX or NGINX Plus using Prometheus.</description>
<person userid="aikchar" role="maintainer"/>
<repository name="Fedora_37">
    <path project="Fedora:37" repository="standard"/>
    <arch>x86_64</arch>
    <arch>aarch64</arch>
</repository>
</project>

Check out the project,

$ osc checkout home:aikchar:nginx-prometheus-exporter

Create new package in the checked out project workspace,

$ cd home:aikchar:nginx-prometheus-exporter
$ osc mkpac nginx-prometheus-exporter

Add all files to the package,

$ osc add nginx-prometheus-exporter.default nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz nginx-prometheus-exporter.service nginx-prometheus-exporter_0.11.0_linux_arm64.tar.gz nginx-prometheus-exporter.spec

Commit (push) the files and trigger a build,

$ osc commit -m 'My commit'

Use the web UI to get the repository information. For example, this package is available from nginx-prometheus-exporter.

On my Alma Linux 9 (x86_64) and Oracle Linux 9 (aarch64) machines I ran these commands to install nginx-prometheus-exporter,

$ sudo dnf config-manager --add-repo https://download.opensuse.org/repositories/home:aikchar:nginx-prometheus-exporter/Fedora_37/home:aikchar:nginx-prometheus-exporter.repo
$ sudo dnf install -y nginx-prometheus-exporter

More Reading