
Crear una aplicación de respaldo que se ejecute en cualquier distribución no es una tarea fácil. Para garantizar que Veeam Agent para Linux se ejecute en distribuciones de RHEL 6 y Debian 6, openSUSE Leap 15.1 y Ubuntu 19.04 tienen que resolver una serie de problemas, especialmente cuando considera que el módulo del núcleo es parte del producto de software.
Este artículo se basa en una presentación en la conferencia
LinuxPiter 2019 .
Linux no es solo uno de los sistemas operativos más populares. De hecho, esta es una plataforma sobre la cual puedes hacer algo único, algo tuyo. Debido a esto, Linux tiene muchas distribuciones que difieren en un conjunto de componentes de software. Y aquí surge el problema: para que el producto de software funcione en cualquier distribución, debe tener en cuenta las características de cada uno.
Gerentes de paquetes. .deb vs .rpm
Comencemos con el problema obvio de distribuir el producto para diferentes distribuciones.
La forma más típica de distribuir productos de software es colocar el paquete en el repositorio para que el administrador de paquetes integrado en el sistema pueda instalarlo desde allí.
Sin embargo, tenemos dos formatos de paquete populares:
rpm y
deb . Entonces, todos tendrán que apoyar.
En el mundo de los paquetes deb, el nivel de compatibilidad es sorprendente. El mismo paquete se instala igualmente bien y funciona tanto en Debian 6 como en Ubuntu 19.04. Los estándares para el proceso de creación de paquetes y trabajo con ellos, establecidos en distribuciones antiguas de Debian, siguen siendo relevantes en el nuevo sistema operativo Linux Mint y elemental. Por lo tanto, en el caso de Veeam Agent para Linux, un paquete de deb para cada plataforma de hardware es suficiente.
Pero en el mundo de los paquetes rpm, las diferencias son grandes. En primer lugar, debido al hecho de que hay dos distribuidores completamente independientes de Red Hat y SUSE, para los cuales la compatibilidad no es absolutamente necesaria. En segundo lugar, estos distribuidores tienen distribuciones de esos. soporte y experimental. Entre ellos, tampoco es necesaria la compatibilidad. Resultó que para el6, el7 y el8 sus propios paquetes. Paquete separado para Fedora. Paquetes para SLES11 y 12 y separados para openSUSE. El principal problema son las dependencias y los nombres de los paquetes.
Problema de dependencia
Por desgracia, los mismos paquetes a menudo terminan bajo diferentes nombres en diferentes distribuciones. A continuación se muestra una lista parcial de las dependencias del paquete veeam.
Como resultado, la lista de dependencias es exclusiva de la distribución.
Empeora cuando una versión actualizada comienza a ocultarse bajo el nombre del paquete anterior.
Un ejemplo:Fedora 24 ha actualizado el paquete
ncurses de la versión 5 a la versión 6. Nuestro producto fue construido con la versión 5 para garantizar la compatibilidad con distribuciones anteriores. Para usar la quinta versión anterior de la biblioteca en Fedora 24, tuve que usar el paquete
ncurses-compat-libs .
Como resultado, aparecen dos paquetes para Fedora, con diferentes dependencias.
Más interesante Después de la próxima actualización del paquete de distribución, el paquete
ncurses-compat-libs con la quinta versión de la biblioteca no está disponible. No es rentable para un distribuidor incorporar bibliotecas antiguas a una nueva versión de distribución. Después de un tiempo, el problema se repitió en las distribuciones de SUSE.
Como resultado, para algunas distribuciones, tuve que abandonar la dependencia explícita de
ncurses-libs y arreglar el producto para que pudiera funcionar con cualquier versión de la biblioteca.
Por cierto, en la octava versión de Red Hat ya no hay un
metapaquete de Python que haga referencia al buen y antiguo
Python 2.7 . Hay
python2 y
python 3.
Alternativa a los gestores de paquetes
El problema con las dependencias es antiguo y obvio desde hace mucho tiempo. Solo recuerda el infierno de la dependencia.
Combine una variedad de bibliotecas y aplicaciones para que todas funcionen de manera estable y no entren en conflicto; de hecho, cualquier distribuidor de Linux está tratando de resolver este problema.
El administrador de paquetes canónicos
Snappy está tratando de resolver este problema de manera muy diferente. La idea principal: la aplicación se ejecuta en una caja de arena aislada y protegida del sistema principal. Si la aplicación necesita bibliotecas, se entregan con la aplicación misma.
Flatpak también le permite ejecutar aplicaciones en el sandbox usando Contenedores de Linux. También existe
AppImage , que le permite crear imágenes portátiles de programas.
Estas soluciones le permiten crear un paquete para cualquier distribución. En el caso de
Flatpak y
AppImage, la instalación y el lanzamiento de la aplicación es posible incluso sin el conocimiento del administrador.
El principal problema es que no todas las aplicaciones pueden ejecutarse en el sandbox y sin privilegios de
root . Algunos necesitan acceso directo a la plataforma. No estoy hablando de módulos de kernel, que dependen mucho del kernel y no se ajustan al concepto de sandbox.
El segundo problema es que las distribuciones populares de Red Hat y SUSE en el entorno empresarial aún no son compatibles con Snappy y Flatpak.
En este sentido, Veeam Agent para Linux no está en
snapcraft.io ni en
flathub.org .
Al final de la pregunta sobre los administradores de paquetes, noto que hay una opción para abandonar completamente los administradores de paquetes combinando archivos binarios y un script para instalarlos en un paquete.
Tal paquete le permite crear un paquete común para diferentes distribuciones y plataformas, para llevar a cabo un proceso de instalación interactivo, llevando a cabo la personalización necesaria. Encontré tales paquetes para Linux solo de VMware.
Problema de actualización

Incluso si se resuelven todos los problemas de dependencia, el programa puede funcionar de manera diferente en la misma distribución. El punto está en las actualizaciones.
Hay 3 estrategias de actualización:
- Lo más fácil es nunca actualizar. Configurado el servidor y olvidado. ¿Por qué actualizaciones si todo funciona? Los problemas comienzan la primera vez que contacta a soporte. El creador de la distribución solo admite una versión actualizada.
- Puede confiar en el distribuidor y configurar actualizaciones automáticas. En este caso, es probable que haya una llamada de soporte inmediatamente después de una actualización fallida.
- La opción de actualización manual solo después de ejecutarla en la infraestructura de prueba es la más fiel, pero costosa y requiere mucho tiempo. No todos pueden permitírselo.
Dado que diferentes usuarios usan diferentes estrategias de actualización, debe admitir tanto la última versión como todas las versiones anteriores. Esto complica el proceso de desarrollo, y el proceso de prueba, agrega un dolor de cabeza al servicio de soporte.
Variedad de plataformas de hardware.
Varias plataformas de hardware son un problema que es en gran medida específico del código nativo. Como mínimo, debe recopilar binarios para cada plataforma compatible.
En el proyecto Veeam Agent para Linux, todavía no podemos admitir al menos algo similar a RISC.
No me detendré en este tema en detalle. Solo describiré los problemas principales: tipos dependientes de la plataforma, como
size_t
, alineación de estructuras y orden de bytes.
Enlace estático y / o dinámico

Y aquí está la pregunta "¿Cómo vincular a las bibliotecas, de forma dinámica o estática?" Vale la pena discutirlo.
Por lo general, las aplicaciones Linux C / C ++ usan enlaces dinámicos. Esto funciona muy bien si la aplicación está construida específicamente para una distribución específica.
Si la tarea es cubrir una variedad de distribuciones con un archivo binario, debe concentrarse en la distribución admitida más antigua. Para nosotros, esto es Red Hat 6. Contiene gcc 4.4, que incluso el estándar C ++ 11 no es
totalmente compatible.
Estamos construyendo nuestro proyecto usando gcc 6.3, que es totalmente compatible con C ++ 14. Naturalmente, en este caso en Red Hat 6, la biblioteca libstdc ++ y boost debe arrastrarse. La forma más fácil de vincularlos es estáticamente.
Pero, por desgracia, no todas las bibliotecas se pueden vincular estáticamente.
Primero, las bibliotecas del sistema, como
libfuse ,
libblkid, deben vincularse dinámicamente para asegurarse de que sean compatibles con el núcleo y sus módulos.
En segundo lugar, hay una sutileza con las licencias.
La licencia GPL básicamente permite vincular bibliotecas solo con código de código abierto. MIT y BSD permiten la vinculación estática y permiten incluir bibliotecas en el proyecto. Pero LGPL no parece contradecir la vinculación estática, sino que requiere compartir los archivos necesarios para la vinculación.
En general, el uso de enlaces dinámicos protegerá contra la necesidad de proporcionar algo.
Creación de aplicaciones C / C ++
Para crear aplicaciones C / C ++ para diferentes plataformas y distribuciones, es suficiente seleccionar o compilar una versión adecuada de gcc y usar compiladores cruzados para arquitecturas específicas, para recopilar todo el conjunto de bibliotecas. Este trabajo es bastante factible, pero bastante problemático. Y no hay garantías de que el compilador y las bibliotecas seleccionados proporcionen una opción viable.
Una ventaja obvia: la infraestructura se simplifica enormemente, ya que todo el proceso de ensamblaje se puede realizar en una máquina. Además, es suficiente recopilar un conjunto de archivos binarios para una arquitectura y puede empaquetarlos en paquetes para diferentes distribuciones. Así es como se crean los paquetes veeam para Veeam Agent para Linux.
En contraste con esta opción, simplemente puede preparar la granja de compilación, es decir, varias máquinas para ensamblar. Cada máquina proporcionará la compilación de la aplicación y el ensamblaje del paquete para una distribución particular y una arquitectura específica. En este caso, la compilación se realiza por los medios que el distribuidor ha preparado. Es decir, la etapa de preparación del compilador y la selección de bibliotecas ya no son necesarias. Además, el proceso de ensamblaje se puede paralelizar fácilmente.
Sin embargo, hay un inconveniente en este enfoque: para cada distribución dentro de la misma arquitectura, tendrá que ensamblar su propio conjunto de archivos binarios. También una desventaja es que se deben mantener tantas máquinas para asignar una gran cantidad de espacio en disco y RAM.
De esta manera, se ensamblan los paquetes KMOD del módulo del núcleo veeamsnap para distribuciones de Red Hat.
Servicio de compilación abierta
Los colegas de SUSE intentaron implementar un término medio como un servicio especial para compilar aplicaciones y crear paquetes:
openbuildservice .
De hecho, es un hipervisor que crea una máquina virtual, instala todos los paquetes necesarios en ella, compila la aplicación y compila el paquete en este entorno aislado, después de lo cual se libera dicha máquina virtual.

El planificador implementado en OpenBuildService determinará cuántas máquinas virtuales puede ejecutar para obtener la velocidad de compilación óptima del paquete. El propio mecanismo de firma incorporado firmará los paquetes y los colocará en el repositorio incorporado. El sistema de control de versiones incorporado guardará el historial de cambios y ensamblajes. Queda por agregar simplemente su código fuente a este sistema. Incluso el servidor en sí no es necesario para subir, pero puedes usar el abierto.
Aquí, sin embargo, hay un problema: tal combinación es difícil de encajar en la infraestructura existente. Por ejemplo, el control de versiones no es necesario, ya tenemos el nuestro para las fuentes. El mecanismo de firma es diferente: se utiliza un servidor especial. El repositorio tampoco es necesario.
Además, el soporte para otras distribuciones, por ejemplo, Red Hat, se implementa de manera bastante deficiente, lo cual es comprensible.
La ventaja de este servicio es el rápido soporte de la próxima versión de la distribución SUSE. Antes del anuncio oficial de lanzamiento, los paquetes necesarios para el ensamblaje se cargan en el repositorio público. Una nueva aparece en la lista de distribuciones disponibles en OpenBuildService. Ponemos una marca y se agrega al plan de ensamblaje. Por lo tanto, agregar una nueva versión de la distribución se realiza en casi un clic.
En nuestra infraestructura, utilizando OpenBuildService, reunimos toda la variedad de paquetes KMP del módulo del núcleo veeamsnap para distribuciones SUSE.
Además, me gustaría detenerme en cuestiones específicas de los módulos del núcleo.
kernel ABI
Los módulos del kernel de Linux se han distribuido históricamente en forma de origen. El hecho es que los creadores del kernel no se cargan con el cuidado de mantener una API estable para los módulos del kernel, y aún más a nivel binario, luego kABI.
Para construir un módulo para el núcleo de vainilla, se requieren encabezados de este núcleo en particular, y funcionará solo en este núcleo.
DKMS le permite automatizar el proceso de ensamblaje de módulos al actualizar el kernel. Como resultado, los usuarios del repositorio de Debian (y sus muchos parientes) usan módulos del núcleo desde el repositorio del distribuidor o ensamblados desde la fuente usando DKMS.
Sin embargo, esta situación no es particularmente cómoda con el segmento Enterprise. Los distribuidores de código propietario quieren distribuir el producto en forma de archivos binarios compilados.
Los administradores no desean mantener las herramientas de desarrollo en los servidores de producción por razones de seguridad. Los distribuidores de Linux para empresas, como Red Hat y SUSE, han decidido que pueden mantener kABI estable para sus usuarios. Como resultado, aparecieron los paquetes KMOD para Red Hat y KMP para SUSE.
La esencia de esta solución es bastante simple. La API del núcleo se congela para una versión específica de la distribución. El distribuidor declara que usa el kernel, por ejemplo, 3.10, y solo realiza correcciones y mejoras que no afectan las interfaces del kernel de ninguna manera, y los módulos ensamblados para el primer kernel pueden usarse para todos los posteriores sin recompilación.
Red Hat anuncia la compatibilidad de kABI para la distribución a lo largo del ciclo de vida. Es decir, el módulo ensamblado para rhel 6.0 (versión de noviembre de 2010) también debería funcionar en la versión 6.10 (versión de junio de 2018). Y esto es casi 8 años. Naturalmente, la tarea es bastante complicada.
Registramos varios casos cuando, debido a problemas con la compatibilidad de kABI, el módulo veeamsnap dejó de funcionar.
Después de que el módulo veeamsnap compilado para RHEL 7.0 resultó ser incompatible con el kernel de RHEL 7.5, pero se cargó y garantizó la caída del servidor, nos negamos a usar la compatibilidad de kABI para RHEL 7 en general.
Actualmente, el paquete KMOD para RHEL 7 contiene un ensamblaje para cada versión de lanzamiento y un script que proporciona la carga del módulo.
SUSE abordó la tarea de compatibilidad de kABI con más cuidado. Proporcionan compatibilidad kABI en un solo paquete de servicio.
Por ejemplo, el lanzamiento de SLES 12 tuvo lugar en septiembre de 2014. Y SLES 12 SP1 ya está en diciembre de 2015, es decir, ha pasado poco más de un año. Aunque ambas versiones usan el núcleo 3.12, no son compatibles con kABI. Obviamente, mantener la compatibilidad de kABI durante solo un año es mucho más fácil. El ciclo anual de actualización del módulo principal no debería causar problemas a los creadores de los módulos.
Como resultado de esta política de SUSE, no solucionamos ningún problema con la compatibilidad de kABI en nuestro módulo veeamsnap. Es cierto que el número de paquetes para SUSE es casi un orden de magnitud mayor.
Parches y backports
A pesar del hecho de que los distribuidores están tratando de garantizar la compatibilidad de kABI y la estabilidad del kernel, también están tratando de mejorar el rendimiento y eliminar defectos en este kernel estable.
Además, además de su propio "trabajo sobre errores", los desarrolladores del kernel de Linux empresarial realizan un seguimiento de los cambios en el kernel de vainilla y los transfieren a su "estable".
A veces esto lleva a nuevos
errores .
La última versión de Red Hat 6 cometió un error en una de las actualizaciones menores. Condujo al hecho de que el módulo veeamsnap tenía garantizado el bloqueo del sistema cuando se lanzó la instantánea. Al comparar las fuentes del kernel antes y después de la actualización, descubrimos que el backport era el culpable. Se realizó una solución similar en la versión 4.19 del kernel de vainilla. Pero solo en el núcleo de vainilla, esta solución funcionó bien, y al transferirla al "estable" 2.6.32, hubo un problema con el bloqueo de giro.
Por supuesto, todos siempre tienen errores, pero ¿valió la pena arrastrar el código de 4.19 a 2.6.32, arriesgando la estabilidad? ... No estoy seguro ...
Lo peor de todo, cuando el marketing está vinculado al tira y afloja "estabilidad" <-> "modernización". El departamento de marketing necesita que el núcleo de la distribución actualizada sea estable, por un lado, y al mismo tiempo tenga un mejor rendimiento y tenga nuevas características. Esto lleva a compromisos extraños.
Cuando intenté construir un módulo en el kernel 4.4 de SLES 12 SP3, me sorprendió encontrar la funcionalidad de vanilla 4.8 en él. En mi opinión, la implementación del bloque de E / S del kernel 4.4 del SLES 12 SP3 es más como un kernel 4.8 que la versión anterior del kernel 4.4 estable del SLES12 SP2. No puedo juzgar qué porcentaje de código se transfirió desde el kernel 4.8 al SLES 4.4 para SP3, pero aún no tengo la oportunidad de llamar al kernel el mismo 4.4 estable.
Lo más desagradable de esto es que al escribir un módulo que funciona igualmente bien en diferentes núcleos, ya no puede confiar en la versión del núcleo. También tenemos que tener en cuenta la distribución. Es bueno que a veces pueda participar en una definición que aparece junto con una nueva funcionalidad, pero esta característica no siempre aparece.
Como resultado, el código está rodeado de directivas elegantes para la compilación condicional.
También hay parches que cambian la API del núcleo documentada.
Me encontré con un
kit de distribución
KDE neon 5.16 y me sorprendió mucho ver que la llamada lookup_bdev en esta versión del kernel cambió la lista de parámetros de entrada.
Para reunirnos, tuvimos que agregar un script en el archivo MAKE que verifica si la función lookup_bdev tiene un parámetro de máscara.
Firma de los módulos del kernel
Pero volvamos al tema de la distribución de paquetes.
Una de las ventajas de kABI estable es que los módulos del núcleo se pueden firmar como un archivo binario. En este caso, el desarrollador puede estar seguro de que el módulo no fue dañado accidentalmente o cambiado intencionalmente. Puede verificar esto con el comando modinfo.
Las distribuciones de Red Hat y SUSE le permiten verificar la firma de un módulo y descargarlo solo si el certificado apropiado está registrado en el sistema. El certificado es la clave pública mediante la cual se firma el módulo. Lo distribuimos como un paquete separado.
El problema aquí es que los certificados pueden integrarse en el núcleo (los usan los distribuidores) o deben escribirse en la memoria EFI no volátil utilizando la utilidad
mokutil . Al instalar el certificado, la utilidad
mokutil requiere un reinicio del sistema e incluso antes de cargar el núcleo del sistema operativo, ofrece al administrador que permita la descarga del nuevo certificado.
Por lo tanto, agregar un certificado requiere acceso físico del administrador al sistema. Si la máquina se encuentra en algún lugar de la nube o solo en una sala de servidores remota y el acceso es solo a través de la red (por ejemplo, a través de ssh), será imposible agregar un certificado.
EFI en máquinas virtuales
A pesar del hecho de que EFI ha sido respaldado por casi todos los creadores de la placa base, al instalar el sistema, el administrador puede no pensar en la necesidad de EFI y puede deshabilitarse.
No todos los hipervisores son compatibles con EFI. VMWare vSphere admite EFI desde la versión 5.
Microsoft Hyper-V también recibió soporte EFI, comenzando con Hyper-V para Windows Server 2012R2.
Sin embargo, en la configuración predeterminada, esta funcionalidad está deshabilitada para máquinas Linux, lo que significa que el certificado no se puede instalar.
En vSphere 6.5, puede configurar la opción de
Arranque seguro solo en la versión anterior de la interfaz web que funciona a través de Flash. La interfaz de usuario web en HTML-5 está muy por detrás.
Distribuciones Experimentales
Y finalmente, considere el tema de las distribuciones experimentales y las distribuciones sin apoyo oficial. Por un lado, es improbable que tales distribuciones se encuentren en los servidores de organizaciones serias. No hay soporte oficial para tales distribuciones. Por lo tanto, para proporcionar esos. No es posible el soporte del producto en dicha distribución.
Sin embargo, tales distribuciones se convierten en una plataforma conveniente para probar nuevas soluciones experimentales. Por ejemplo, Fedora, OpenSUSE Tumbleweed o la versión inestable de Debian. Son bastante estables. Siempre tienen nuevas versiones de programas y siempre un nuevo núcleo. Después de un año, esta funcionalidad experimental puede estar en RHEL, SLES o Ubuntu actualizados.
Entonces, si algo no funciona en el kit de distribución experimental, esta es una ocasión para resolver el problema y resolverlo. Debe estar preparado para el hecho de que esta funcionalidad pronto aparecerá en los servidores de producción de los usuarios.
La lista actual de distribuciones oficialmente compatibles para la versión 3.0 se puede encontrar
aquí . Pero la lista real de distribuciones en las que nuestro producto puede trabajar es mucho más amplia.
Personalmente, estaba interesado en un experimento con el sistema operativo Elbrus. Después de actualizar el paquete veeam, nuestro producto fue instalado y ganado. Sobre este experimento, escribí sobre Habré en el
artículo .
Bueno, el soporte para nuevas distribuciones continúa. Estamos esperando el lanzamiento de la versión 4.0. Beta está a punto de aparecer, ¡así que estad atentos a
lo que hay de nuevo !