Введение

В России все больше пользователей и организаций используют российские Linux системы. Но при подготовке пакетов для комплексных решений на этих системах могут возникнуть сложности, связанные с несовместимостью ПО, необходимостью настройки пакетов и т.д. В этой статье рассмотрю основные проблемы, с которыми наша команда столкнулась при развертывании проектного решения на российских Linux системах: Astra Linux, Alt Linux, Red OS. Этот опыт будет полезен для команд разработки, которые переходят на Linux и ранее не работали с ними.

Цель этой статьи не заключается в том, чтобы создать универсальную пошаговую инструкцию (поскольку она будет различаться в зависимости от конкретного случая), но помочь понять, с какими трудностями можно столкнуться при работе с системами, которые требуют доставки приложения через пакеты. Также расскажу, насколько трудоемким может быть проект, который включает развертывание на российских ОС.

О проекте

Сегодня в России активно развиваются ОС на базе Linux и создаются экосистемы для управления и мониторинга рабочих мест и серверов. Ранее, до 2022 года, альтернатив не было, и компании должны были прорабатывать такие решения самостоятельно. Так в прошлом году наша компания собрала ПО для централизованного управления оборудованием, мониторинга и инвентаризации под конкретного заказчика. Это ПО было разработано для включения в один из ПАК, который мы разрабатываем.

Проект состоял из системы мониторинга Zabbix, системы управления конфигурациями Ansible и веб-интерфейса Ansible Semaphore для запуска сценариев. Кроссплатформенное ASP.NET-приложение, разработанное нами, дополняло функционал Ansible Semaphore и являлось центром композиции. Для хранения данных мы выбрали MySQL, так как она была официально поддерживаема всеми вышеупомянутыми компонентами. Все эти компоненты были нужны для установки на ОС Astra Linux, Alt Linux и Red OS.

Далее рассмотрю, с какими задачами мы столкнулись и как их решали. Рассмотрю два аспекта: специфику нашего приложения, а именно тот факт, что оно написано .Net, а также некоторые аспекты подготовки пакетов.

Немного о технологических трендах

Сразу хочется сделать акцент на том, что все более популярным становится мнение, что серверная ОС представляет собой платформу для запуска докер-контейнеров и должна содержать в себе минимальный набор необходимых пакетов. Такой подход имеет право на жизнь, но наш заказчик исповедовал другую религию – классическую, поэтому в нашем случае способ установки должен был быть реализован строго через пакеты.

Что нужно учитывать для .NET?

На момент реализации проекта, исходя из официальной документации, Astra Linux и Alt Linux поддерживали .NET включительно до 7.0, за исключением некоторых .NET Core прошлых версий.  

Общий процесс установки среды выполнения для .NET в Astra Linux сводится к следующему:

1. Установить дополнительные пакеты для использования протокола HTTPS ca-certificates и apt-transport-https вызовом команды:

sudo apt install ca-certificates apt-transport-https

2. Добавить ключи пакетов Microsoft в список доверенных ключей:

wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg > /dev/null

3. Загрузить параметры репозитория Microsoft (параметры сохраняются в файле /etc/apt/sources.list.d/microsoft-prod.list)

a. Для Astra Linux Special Edition РУСБ.10015-01 (очередное обновление 1.7):

sudo wget https://packages.microsoft.com/config/debian/10/prod.list -O /etc/apt/sources.list.d/microsoft-prod.list

b. Для Astra Linux Special Edition РУСБ.10015-01 (очередное обновление 1.6), Astra Linux Special Edition РУСБ.10015-16 исп. 1, Astra Linux Common Edition:

sudo wget https://packages.microsoft.com/config/debian/9/prod.list -O /etc/apt/sources.list.d/microsoft-prod.list

4. Обновить кэш пакетов:

sudo apt update

5. Установить пакеты:

sudo apt install aspnetcore-runtime-<номер_версии>

где номер версии — 2.1, 2.2, 3.0, 3.1, 5.0, 6.0, 7.0.

Alt Linux уже содержит dotnet в списке доступных пакетов и устанавливается через менеджер пакетов apt-get вызовом команды:

apt-get install dotnet

apt-get install dotnet-aspnetcore

Что касается Red OS, официальный сайт не содержит дополнительной информации о работе.net. Тем не менеe, в перечне ПО репозитория РЕД ОС содержится упоминание о пакете dotnet. В результате удалось установить и запустить приложение.

После установки среды выполнения можно запускать приложение. Порт доступен, web api сервер на Kestrel выполняет запросы. Разницы в работе между представленными для нашего приложения системами, Windows и Ubuntu, Debian не обнаружены. В процессе работы, для упрощения поставки программного комплекса было принято решение публиковать приложение dotnet, включая среду выполнения. Подробнее тут и тут.

Self‑contained публикация позволяет упаковать.NET Runtime вместе с нашим приложением, чтобы оно могло выполняться изолированно и независимо от среды выполнения. В результате получается самодостаточное приложение, способное работать на целевой платформе без необходимости предварительной установки.NET Runtime. При использовании команды dotnet publish, необходимо указать целевую платформу и архитектуру с помощью аргумента ‑runtime и ‑self‑contained.

Публикация релизной сборки приложения для Linux 64, включая среду выполнения:

dotnet publish -c Release -r linux-x64 --self-contained

Основным минусом решения является увеличение размера пакета приложения, что может замедлить процесс скачивания и установки приложения, это необходимо учитывать при распространении приложения в ограниченных сетях. Так же в числе минусов – отсутствие возможности обновления через менеджеров пакетов. Несмотря на эти потенциальные проблемы, self-contained приложения предоставляют удобство использования и независимость от уже установленной среды, в нашем конкретном случае нам не приходится выполнять подготовку на каждой целевой машине.

О пакетах

Российские Linux-системы могут использовать различные пакетные менеджеры и репозитории, которые могут отличаться от стандартных пакетных менеджеров и репозиториев, используемых в других Linux-системах.

Так, например, AstraLinux, базирующийся на дистрибутиве Debian GNU/Linux, использует родной DEB пакет. РЕД ОС – составной программный продукт, построен на пакетной базе RPM-формата, в качестве менеджера пакетов используется YUM.  В ALT Linux в качестве пакетного менеджера используется APT, но при этом вместо *.deb у нее используется rpm. Приходим к пониманию, что требуется поддерживать deb и rpm пакет.

О сборке пакетов

Первым шагом является создание структуры каталога, которая будет содержать необходимые файлы и каталоги для вашего пакета.

https://wiki.freepascal.org/Debian_package_structure

Основные каталоги включают DEBIAN, usr, и другие (например, etc, var, usr/share). В каталоге DEBIAN будет содержаться файл control, включающий метаданные пакета.

В каталоге usr располагаются файлы, которые должны быть установлены в систему при установке вашего пакета. Сюда включаются исполняемые файлы, библиотеки, ресурсы, настройки и т.д. Вы можете выбрать соответствующие каталоги, такие как usr/bin, usr/lib, usr/share, в зависимости от ваших требований.

На практике для .Net-приложения Deb-пакет для Asp.Net-приложения можно собрать следующим образом (небольшой пример):

1. Создание шаблонного ASP.NET веб-приложения.

Откройте командную строку и выполните следующую команду, чтобы создать шаблонное ASP.NET веб-приложение:

Эта команда создаст новый каталог «mywebapp» и скопирует в него все необходимые файлы и каталоги для шаблонного ASP.NET веб-приложения.

$ dotnet new web -o mywebapp

2. Создайте структуру каталогов для проекта:

mywebapp/

── DEBIAN/

│   ├── control

│   ├── preinst

│   └── postinst

└── var/

      └── www/

            └── mywebapp/

$ mkdir -p mywebapp/DEBIAN

$ mkdir -p mywebapp/var/www/mywebapp

3 Создайте файл DEBIAN/control с информацией о пакете. Вот пример содержимого файла:

Package: mywebapp

Version: 1.0

Architecture: all

Maintainer: Your Name <your@email.com>

Description: My ASP.NET Web Application

$ echo "Package: mywebapp" >> mywebapp/DEBIAN/control

$ echo "Version: 1.0" >> mywebapp/DEBIAN/control

$ echo "Architecture: all" >> mywebapp/DEBIAN/control

$ echo "Maintainer: Your Name <your@email.com>" >> mywebapp/DEBIAN/control

$ echo "Description: My ASP.NET Web Application" >> mywebapp/DEBIAN/control

4. Создайте файл preinst в каталоге DEBIAN. Этот скрипт будет выполнен перед установкой пакета:

$ touch mywebapp/DEBIAN/preinst

$ echo "echo \"Hello from preinst script\"" >> mywebapp/DEBIAN/control

5. Создайте файл postinst в каталоге DEBIAN. Этот скрипт будет выполнен после установки пакета:

$ touch mywebapp/DEBIAN/postinst

$ echo "echo \"Hello from postinst script\"" >> mywebapp/DEBIAN/control

6. Копирование файлов.

Скопируйте файлы вашего ASP.NET веб-приложения в каталог var/www/mywebapp:

$ cp -R mywebapp/* mywebapp/var/www/mywebapp/

7. Создайте deb пакет с помощью команды dpkg-deb:

$ dpkg-deb --build mywebapp

После выполнения этой команды, вы получите файл myapp.deb, который содержит ваше приложение.

Теперь вы можете установить этот deb пакет на системе Debian или Ubuntu с помощью команды dpkg -i myapp.deb:

$ dpkg -i mywebapp.deb

Таким образом, вы можете упаковать ваше asp .net веб-приложение в deb-пакет для удобной установки и дистрибуции. RPM-пакет имеет так же определенную структуру, в которой размещаются файлы приложения.

mkdir -p BUILD RPMS SOURCES SPECS SRPMS

  • «BUILD» - в этой директории будут размещены временные файлы и собранные бинарные файлы.

  • «RPMS» - здесь будут размещены готовые RPM файлы после их сборки.

  • «SOURCES» - директория, в которой размещаются исходные файлы вашего приложения.

  • «SPECS» - в этой директории располагается файл спецификации (spec file) RPM пакета.

  • «SRPMS» - здесь будут храниться исходные RPM пакеты.

Файл control в каталоге DEBIAN содержит метаданные вашего пакета, такие как: название, версия, описание, зависимости и другая информация. Формат файла control строго определен стандартом Debian. Вы можете использовать инструменты, такие как dpkg-deb для генерации этого файла автоматически на основе ваших данных.

В rpm-формате – в файле spec. В случае, если вам нужно выполнить дополнительные действия при установке или удалении пакета, вы можете включить соответствующие скрипты в каталог DEBIAN. Эти скрипты (preinst, postinst, prerm, postrm) выполняются на определенных этапах установки и удаления пакета.

Так, например, мы задействовали скрипт postinst, который выполняется после успешной установки пакета. Конкретно в нашем примере, на этом этапе осуществлялась конфигурации appsettings.json (предполагается, что к этому моменту все необходимые зависимости установлены) и создание метаданных для дальнейшего запроса ключа к нашему приложению.

В пакетах rpm, для выполнения дополнительных действий до или после установки, используются скрипты типа "scriptlet". Существует несколько типов скриптлетов, включая %pre, %post, %preun, %postun, как и в случае с Debian пакетами, все эти скриптлеты должны быть указаны в спецификациях пакета rpm.

Таким образом, пример сборки Asp.net приложения для rpm:

1. Создайте структуру каталогов для упаковываемого приложения:

mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}

2. Перейдите в каталог SPECS:

cd ~/rpmbuild/SPECS

3. Создайте файл spec для вашего RPM пакета:

vim myapp.spec

4. Внутри spec файла, укажите информацию о вашем пакете, такую как название, версия, описание, автор и другие метаданные.

Name:           mywebapp

Version:        1.0

Release:        1%{?dist}

Summary:        My ASP.NET Web Application

License:        MIT

URL:            https://example.com/my-app

Source0:        %{name}-%{version}.tar.gz

        
%description

This is an example RPM package for my-app.


%prep

%autosetup


%build

%configure

make %{?_smp_mflags}


%install

make install DESTDIR=%{buildroot}


%files

%{_bindir}/my-app

%{_mandir}/man1/my-app.1.gz

      
%changelog

# 6. Добавьте скриптлеты в spec файл. Например:

%pre
echo "Выполняется предустановочный скрипт..."

%post
echo "Выполняется постустановочный скрипт..."

%preun
echo "Выполняется предудаление скрипта..."

%postun
echo "Выполняется постудаление скрипта..."

7. Сохраните и закройте spec файл.

8. Поместите все необходимые файлы вашего приложения в каталог SOURCES.

9. Соберите RPM пакет:

rpmbuild -bb mywebapp.spec

10. После успешной сборки, проверьте ваш пакет на наличие ошибок с помощью команды

rpmlint

11. Готовый RPM пакет можно найти в каталоге RPMS.

О сложностях

Проблема №1 Одна интересная особенность скриптлетов в rpm пакетах заключается в том, что они не прерываются. Попытка запросить данные у пользователя в скрипте через команду read, приводит к тому, что скрипт игнорирует команду и продолжает выполнение последующей команды. Тем самым, мы лишаемся сохранения введенных данных в качестве переменных.

 #!/bin/bash

echo "Please enter your name: "

read name

echo "Please enter your age: "

read age

echo "Hello, $name! You are $age years old."

Пришлось частично задействовать глобальные переменные и отдельно запускать конфигурационный скрипт после установки в случае rpm пакетов.

Шаг 5: Сборка пакета.

Используя инструмент dpkg-deb, можно собрать структуру каталогов в Debian пакет. Пакеты RPM создаются с помощью утилиты rpmbuild.

Проблема №2 Несмотря на отличия форматов и инструментов, основные принципы сборки пакетов для deb и rpm форматов достаточно схожи. Однако, требуется использовать две различные операционные системы (Ubuntu и CentOS), а также строго придерживаться спецификации сборки пакетов на каждом шаге.

В процессе решения проблемы рассмотрели несколько утилит: такие как alien, deb2rpm, rebuild-src-rpm. Они позволяют конвертировать пакеты программного обеспечения от одного формата в другой за одну команду.  В следующем примере файл libreoffice-writer_7.0.5-0ubuntu0.20.10.1_amd64.deb преобразуется в файл libreoffice-writer-7.0.5-1.x86_64.rpm

alien -r --scripts libreoffice-writer_7.0.5-0ubuntu0.20.10.1_amd64.deb

В процессе поиска решения, обнаружили следующий интересный проект и статью.

FPM (Effing Package Management) – это проект с открытым исходным кодом, предоставляющий утилиту командной строки для создания и управления пакетами программного обеспечения. Основная цель FPM – упростить и автоматизировать процесс создания пакетов для различных операционных систем. FPM позволяет создавать пакеты в различных форматах, включая RPM, DEB, TAR, ZIP, MSI и других. Это означает, что вы можете использовать одну команду для создания пакета, который может быть установлен на разных операционных системах и дистрибутивах. Проект активно поддерживается сообществом и имеет документацию и примеры использования.

Процесс работы сводится к следующему. В рабочей папке с файлами проекта, создаем конфигурационный файл с расширением *.fpm и описываем его.

--- File: .fpm
lang=EN-US style='font-size:11.0pt;mso-bidi-font-size:12.0pt;font-family:Consolas;
color:#404040;mso-ansi-language:EN-US'><o:p> </o:p>
-s dir<o:p></o:p>
--name hello-world<o:p></o:p>
--license agpl3<o:p></o:p>
--version 0.1.0<o:p></o:p>
--architecture all<o:p></o:p>
--depends bash <o:p></o:p>
--depends lolcat<o:p></o:p>
--before-remove prerm.sh<o:p></o:p>
--description "Say hi!"<o:p></o:p>
--url "https://example.com/hello-world"<o:p></o:p>
--maintainer "You the Amazing Person <you are an amazing person at example dot com>"<o:p></o:p>
<o:p> </o:p>
hello-world=/usr/bin/hello-world hello-world.1=/usr/share/man/man1/hello-world.1<o:p></o:p>

Обратите внимание, что конфигурационный файл имеет схожее обобщённое описание основных свойств файлов спецификации для deb и rpm. Так же можно подключить скрипты, выполняемые до или после процессов установки/удаления. Полное описание аргументов для командной строки можно просмотреть тут.

Далее остается вызвать следующие команды для сборки:

fpm -t deb -p hello-world-0.1.0-1-any.deb
lang=EN-US style='font-size:11.0pt;mso-bidi-font-size:12.0pt;font-family:Consolas;
color:#404040;mso-ansi-language:EN-US'><o:p> </o:p>
fpm -t rpm -p hello-world-0.1.0-1-any.rpm<o:p></o:p>
<o:p> </o:p>

На выходе мы имеем .deb и .rpm-пакет. Как видите, данный способ сборки позволяет.

Из интересного, что может вам помочь в ваших проектах: можно использовать утилиту alien для того, чтобы конвертировать deb пакеты в rpm:

alien -r --scripts mywebapp.deb
class=n><o:p> </o:p>

Проблема № 3 Зависимости зависимостей.

В проекте использованы такие программные решения как Zabbix, Ansible. Их правильная работа предполагает установку некоторых дополнительных пакетов и зависимостей. После установки приложений потребуется настройка и конфигурация для их правильного функционирования. Например, для Zabbix требуется настройка базы данных и настройка различных параметров. Для Ansible также может потребоваться настройка файла конфигурации и настройка аутентификации. Также возможны проблемы с сетевыми настройками, которые могут влиять на доступность приложений.

Размер скрипта настроек и конфигурации будет иметь большое количество строк. Для каждой системы придётся писать отдельный скрипт, для каждой версии скрипт будет отличаться ввиду различий пакетных менеджеров и наличия базовых библиотек.

Пример скрипта на bash:

Примерный перечень команд для развертывания зависимостей. Представлены только зависимости, находящиеся в свободном доступе.

 1. Установка репозиторий Zabbix

wget https://repo.zabbix.com/zabbix/6.4/ubuntu/pool/main/z/zabbix-release/zabbix-release_6.4-1+ubuntu22.04_all.deb

dpkg -i zabbix-release_6.4-1+ubuntu22.04_all.deb

apt update

 2. Установка Zabbix сервер, веб-интерфейс и агент

apt install zabbix-server-mysql zabbix-frontend-php zabbix-nginx-conf zabbix-sql-scripts zabbix-agent

3. Создайте базу данных. Установите и запустите сервер базы данных.

mysql -uroot -p

password

mysql> create database zabbix character set utf8mb4 collate utf8mb4_bin;

mysql> create user zabbix@localhost identified by 'password';

mysql> grant all privileges on zabbix.* to zabbix@localhost;

mysql> set global log_bin_trust_function_creators = 1;

mysql> quit;

4. На хосте Zabbix сервера импортируйте начальную схему и данные. Вам будет предложено ввести недавно созданный пароль.

zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql --default-character-set=utf8mb4 -uzabbix -p zabbix

5. Выключение опции log_bin_trust_function_creators после импорта схемы базы данных.

mysql -uroot -p

password

mysql> set global log_bin_trust_function_creators = 0;

mysql> quit;

6. Настройте базу данных для Zabbix сервера Отредактируйте файл /etc/zabbix/zabbix_server.conf

DBPassword=password

 7. Настройте PHP для веб-интерфейса Отредактируйте файл /etc/zabbix/nginx.conf раскомментируйте и настройте директивы 'listen' и 'server_name'.

listen 8080;

server_name example.com

8. Запустите процессы Zabbix сервера и агента. Запустите процессы Zabbix сервера и агента и настройте их запуск при загрузке ОС.

systemctl restart zabbix-server zabbix-agent nginx php8.1-fpm

systemctl enable zabbix-server zabbix-agent nginx php8.1-fpm

 9. Установка ansible semaphore

 wget https://github.com/ansible-semaphore/semaphore/releases/download/v2.8.75/semaphore_2.8.75_linux_amd64.deb

sudo dpkg -i semaphore_2.8.75_linux_amd64.deb

semaphore setup

semaphore service --config=./config.json

 Далее огромное количество настроек и конфигурации проприетарных зависимостей.

В процессе эксплуатации могут возникнуть специфичные ошибки в работе зависимостей с конкретной системой. Все эти сложности могут быть преодолены путем тщательного тестирования приложения на каждой Linux‑системе и адаптации его к специфическим требованиям и настройкам этих систем. Всё это потребует ресурсов. Если есть возможность, то проще сразу использовать Docker для решения данной задачи. Пример для сравнения: как проблему зависимостей можно решить с помощью docker:

echo "Запускаем контейнер с MySQL";

docker run --name=mysql \

           --restart always\

           -p 3306:3306 \

           -e MYSQL_ROOT_PASSWORD="$mysqlRootPassword" \

           -e MYSQL_USER=semaphore \

           -e MYSQL_PASSWORD=semaphore \

           -d mysql:5.7;

 

echo "Инициализация и запуск ansible-semaphore";

docker run --name=semaphore  \

           --restart always \

           --link mysql \

           -p $portAnsible:3000 \

           -e SEMAPHORE_DB_HOST=mysql \

           -e ANSIBLE_HOST_KEY_CHECKING=False \

           -v $volumeAnsible:$volumeAnsible \

           -d ansiblesemaphore/semaphore;

 

echo "Инициализация и запуск Zabbix-сервер";

docker run --name=zabbix-server \

          --restart always\

           -p $portZabbixServer:10051\

           --link mysql -e DB_SERVER_HOST=mysql \

           -e MYSQL_ROOT_PASSWORD="$mysqlRootPassword" \

           -e MYSQL_USER=zabbix \

           -e MYSQL_PASSWORD=zabbix \

           -d zabbix/zabbix-server-mysql:alpine-5.4-latest;

Или завернуть все через docker-compose, что являлось бы лучшим решением.

Вместо выводов

Каждая российская ОС является по-своему уникальной, и задача «подготовки универсального пакета» для вашего кроссплатформенного или платформенного ПО без использования инструментов «абстрагирования», таких как fpm или docker, переводит задачу в разряд «сложных» (как в плане разработки, так и в плане сопровождения и поддержки). 

Поэтому, если нет специальных ограничений и требуется подготовить ПО к развертыванию на все популярные российские ОС, то советуем обратить внимание, в первую очередь, на Docker/Docker-Compose, как основной способ доставки приложений для серверных ОС.