Linux tiene muchas caras: cómo trabajar en cualquier distribución

Linux tiene muchas caras: cómo trabajar en cualquier distribución

Crear una aplicación de respaldo que funcione en cualquier distribución es una tarea complicada. Para asegurar el funcionamiento de Veeam Agent for Linux en distribuciones desde Red Hat 6 y Debian 6 hasta OpenSUSE 15.1 y Ubuntu 19.04, es necesario resolver una serie de problemas, especialmente considerando que el producto incluye un módulo del núcleo.

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, es una plataforma sobre la cual se puede construir algo único, algo propio. Como resultado, existen numerosas distribuciones de Linux que se diferencian por su conjunto de componentes de software. Y aquí surge un problema: para que el producto funcione en cualquier distribución, es necesario tener en cuenta las particularidades de cada una.

Gestores de paquetes. .deb vs .rpm

Empezaremos con el problema evidente de la distribución del producto para diferentes distribuciones.
La forma más típica de distribuir productos de software es subir un paquete a un repositorio, de modo que el gestor de paquetes integrado en el sistema pueda instalarlo desde ahí.
Sin embargo, hay dos formatos de paquetes populares: rpm y deb. Por lo tanto, será necesario mantenerlos todos.

En el mundo de los paquetes deb, el nivel de compatibilidad es impresionante. Un mismo paquete se instala y funciona igual de bien tanto en Debian 6 como en Ubuntu 19.04. Los estándares en el proceso de creación de paquetes y su manejo, establecidos en antiguas distribuciones de Debian, siguen siendo relevantes en las modernas Linux Mint y elementary OS. Por lo tanto, en el caso de Veeam Agent for Linux, se requiere un único paquete deb para cada plataforma de hardware.

En cambio, en el mundo de los paquetes rpm, las diferencias son grandes. Primero, porque hay dos distribuidores completamente independientes, Red Hat y SUSE, que no requieren compatibilidad. En segundo lugar, estos distribuidores tienen distribuciones con soporte técnico y experimentales. Entre ellas, la compatibilidad tampoco es necesaria. Resulta que tenemos paquetes diferentes para el el6, el7 y el8. Un paquete separado para Fedora. Paquetes para SLES11 y 12 y otro para openSUSE. El principal problema son las dependencias y los nombres de los paquetes.

Problema de dependencias

Lamentablemente, los mismos paquetes a menudo tienen nombres diferentes en distintas distribuciones. A continuación, se presenta una lista no exhaustiva de las dependencias del paquete veeam.

Para EL7:
Para SLES 12:

  • libblkid
  • libgcc
  • libstdc++
  • ncurses-libs
  • fuse-libs
  • file-libs
  • veeamsnap = 3.0.2.1185
  • libblkid1
  • libgcc_s1
  • libstdc++6
  • libmagic1
  • libfuse2
  • veeamsnap-kmp = 3.0.2.1185

Como resultado, la lista de dependencias resulta ser única para la distribución.

Es peor cuando una versión actualizada se oculta bajo el antiguo nombre del paquete.

Ejemplo:

En Fedora 24, se ha actualizado el paquete ncurses de la versión 5 a la versión 6. Nuestro producto fue compilado precisamente con la versión 5, para garantizar la compatibilidad con las distribuciones antiguas. Para utilizar la antigua versión 5 de la biblioteca en Fedora 24, fue necesario usar el paquete ncurses-compat-libs.

Como resultado, aparecen dos paquetes para Fedora, con diferentes dependencias.

La situación se vuelve más interesante. Después de otra actualización de la distribución, el paquete ncurses-compat-libs con la versión 5 de la biblioteca resulta ser inaccesible. Para el distribuidor, es costoso cargar bibliotecas antiguas en la nueva versión de la distribución. Pasado un tiempo, el problema se repitió también en las distribuciones de SUSE.

Como resultado, para algunas distribuciones fue necesario renunciar a la dependencia explícita de ncurses-libs, y ajustar el producto para que pueda funcionar con cualquier versión de la biblioteca.

Por cierto, en la versión 8 de Red Hat ya no hay el metapaquete python, que hacía referencia a la buena y antigua. python 2.7. Hay python2 y python3.

La alternativa a los gestores de paquetes

El problema de las dependencias es antiguo y bien conocido. Recordemos, por ejemplo, el Dependency hell.
Combinar diversas bibliotecas y aplicaciones de manera que todas funcionen de forma estable y no entren en conflicto es precisamente la tarea que intenta resolver cualquier distribuidor de Linux.

De una manera muy distinta, el gestor de paquetes Snappy de Canonical. La idea principal es: la aplicación se ejecuta en una sandbox aislada y protegida del sistema principal. Si la aplicación necesita bibliotecas, se proporcionan junto con la propia aplicación.

Flatpak también permite ejecutar aplicaciones en una sandbox, utilizando Linux Containers. La idea de la sandbox es utilizada también por AppImage.

Estas soluciones permiten crear un solo paquete para cualquier distribución. En el caso de Flatpak la instalación y ejecución de la aplicación es posible incluso sin el conocimiento del administrador.

El problema principal es que no todas las aplicaciones pueden funcionar en una sandbox. Algunas requieren acceso directo a la plataforma. Ya ni hablar de los módulos del núcleo que dependen estrictamente del núcleo y que no encajan de ninguna manera en el concepto de sandbox.

El segundo problema es que las distribuciones populares en el entorno empresarial de Red Hat y SUSE aún no contienen soporte para Snappy y Flatpak.

Debido a esto, Veeam Agent for Linux no está disponible ni en snapcraft.io ni en flathub.org.

Para concluir la cuestión de los gestores de paquetes, mencionaré que existe la opción de prescindir completamente de los gestores de paquetes, combinando en un solo paquete los archivos binarios y un script para su instalación.

Este paquete permite crear un solo instalador común para diferentes distribuciones y plataformas, llevar a cabo un proceso de instalación interactivo y realizar la personalización necesaria. He encontrado tales paquetes para Linux solo de VMware.

Problema de actualizaciones

Linux tiene muchas caras: cómo trabajar en cualquier distribución
Incluso si todos los problemas de dependencias están resueltos, el programa puede funcionar de manera diferente en la misma distribución. Se debe a las actualizaciones.

Existen 3 estrategias de actualización:

  • La más sencilla es nunca actualizar. Configuraste el servidor y te olvidaste. ¿Para qué actualizaciones si todo funciona? Los problemas comienzan en la primera llamada al soporte técnico. El creador de la distribución solo da soporte a la versión más actualizada.
  • Se puede confiar en el distribuidor y configurar la actualización automática. En este caso, la llamada al soporte técnico es probable justo después de una actualización fallida.
  • La opción de actualización manual solo después de probarla en la infraestructura de pruebas es la más segura, pero costosa y laboriosa. No todos pueden permitírselo.

Dado que diferentes usuarios adoptan diferentes estrategias de actualización, es necesario soportar tanto la versión más reciente como todas las versiones anteriores. Esto complica tanto el proceso de desarrollo como el de prueba, y añade problemas al servicio de soporte.

Diversidad de plataformas de hardware

Las diversas plataformas de hardware son un problema que, en gran medida, es específico del código nativo. Como mínimo, se debe compilar archivos binarios para cada plataforma soportada.

En el proyecto Veeam Agent for Linux, no hemos podido soportar nada que sea RISC.

No profundizaré en este asunto. Solo señalaré los problemas principales: tipos dependientes de la plataforma, como size_t, alineación de estructuras y orden de bytes.

Enlazado estático y/o dinámico

Linux tiene muchas caras: cómo trabajar en cualquier distribución
Sin embargo, la cuestión de "¿Cómo enlazar con bibliotecas: dinámicamente o estáticamente?" merece ser discutida.

Por lo general, las aplicaciones en C/C++ en Linux utilizan enlace dinámico. Esto funciona muy bien si la aplicación está compilada específicamente para una distribución concreta.

Sin embargo, si el objetivo es abarcar diversas distribuciones con un solo archivo binario, se debe orientar hacia la distribución más antigua que se soporte. Para nosotros, esto es Red Hat 6. Contiene gcc 4.4, que ni siquiera soporta completamente el estándar C++11. completamente.

Compilamos nuestro proyecto con gcc 6.3, que soporta completamente C++14. Naturalmente, en tal caso, en Red Hat 6 se necesita llevar la biblioteca libstdc++ y boost con nosotros. Lo más sencillo es enlazarlas de manera estática.

Pero, lamentablemente, no todas las bibliotecas se pueden enlazar estáticamente.

En primer lugar, bibliotecas del sistema como libfuse, libblkid deben enlazarse dinámicamente para asegurar su compatibilidad con el núcleo y sus módulos.

En segundo lugar, hay un matiz con las licencias.

La licencia GPL en principio permite enlazar bibliotecas solo con código opensource. MIT y BSD permiten el enlace estático y permiten incluir bibliotecas en el proyecto. Pero LGPL, aunque parece no contradecir el enlace estático, exige proporcionar al público los archivos necesarios para el enlace.

En general, usar enlace dinámico evitará la necesidad de proporcionar algo.

Construcción de aplicaciones en C/C++

Para construir aplicaciones en C/C++ para diferentes plataformas y distribuciones, basta con elegir o compilar una versión adecuada de gcc y usar compiladores cruzados para arquitecturas específicas, y compilar todo el conjunto de bibliotecas. Esta tarea es totalmente factible, pero bastante complicada. No hay garantías de que el compilador y las bibliotecas elegidas aseguren un funcionamiento correcto.

Una ventaja evidente: la infraestructura se simplifica mucho, ya que todo el proceso de construcción se puede realizar en una sola máquina. Además, solo es necesario compilar un conjunto de archivos binarios para una arquitectura y se pueden empaquetar en paquetes para diferentes distribuciones. Así es como se construyen los paquetes veeam para Veeam Agent for Linux.

En lugar de esta opción, se puede simplemente preparar una granja de compilación, es decir, varias máquinas para la construcción. Cada una de estas máquinas se encargará de compilar la aplicación y empaquetar para una distribución específica y una arquitectura determinada. En este caso, la compilación se lleva a cabo con las herramientas que ha preparado el distribuidor. Es decir, la etapa de preparación del compilador y la selección de bibliotecas queda eliminada. Además, el proceso de construcción puede ser fácilmente paralelizado.

Sin embargo, hay una desventaja en este enfoque: para cada distribución dentro de una misma arquitectura, será necesario compilar su propio conjunto de archivos binarios. Además, es un inconveniente que se necesita mantener tantas máquinas, asignar una gran cantidad de espacio en disco y memoria RAM.

Así se construyen los paquetes KMOD del módulo del núcleo veeamsnap para las distribuciones de Red Hat.

Open Build Service

Los colegas de SUSE intentaron implementar una especie de término medio en forma de un servicio especial para compilar aplicaciones y construir paquetes — openbuildservice.

En esencia, es un hipervisor que crea una máquina virtual, instala todos los paquetes necesarios, realiza la compilación de la aplicación y construye el paquete en este entorno aislado, después de lo cual la máquina virtual es liberada.

Linux tiene muchas caras: cómo trabajar en cualquier distribución

El programador implementado en OpenBuildService determinará por sí mismo cuántas máquinas virtuales puede iniciar para una velocidad óptima de construcción de paquetes. El mecanismo de firma integrado firmará los paquetes automáticamente y los publicará en el repositorio integrado. El sistema de control de versiones incorporado guardará el historial de cambios y compilaciones. Solo queda agregar sus fuentes en este sistema. Incluso no es necesario elevar el servidor uno mismo, se puede utilizar el de código abierto.

Aquí, sin embargo, hay un problema: tal cosechadora es difícil de integrar en la infraestructura existente. Por ejemplo, el control de versiones no es necesario, ya que tenemos el nuestro para las fuentes. El mecanismo de firma es diferente: se utiliza un servidor especial. El repositorio también es innecesario.

Además, el soporte para otras distribuciones — por ejemplo, Red Hat — está implementado de manera bastante limitada, lo cual es bastante comprensible.

Una de las ventajas de este servicio es el rápido soporte para la próxima versión de la distribución SUSE. Antes del anuncio oficial del lanzamiento, los paquetes necesarios para la compilación se cargan en el repositorio público. Aparece una nueva distribución en la lista de distribuciones disponibles en OpenBuildService. Marcamos la casilla y se añade al plan de compilación. De esta manera, la adición de una nueva versión de la distribución se realiza prácticamente con un clic.

En nuestra infraestructura, se compila toda la variedad de paquetes KMP del módulo de núcleo veeamsnap para las distribuciones SUSE utilizando OpenBuildService.

A continuación, me gustaría detenerme en temas específicos para los módulos de núcleo.

kernel ABI

Los módulos del núcleo de Linux se han distribuido históricamente en forma de código fuente. Esto se debe a que los creadores del núcleo no se preocupan por mantener una API estable para los módulos del núcleo, y mucho menos a nivel binario, es decir, el kABI.

Para compilar un módulo para el núcleo vanilla, son necesarios los headers de ese núcleo en concreto, y solo funcionará en ese núcleo.

DKMS permite automatizar el proceso de compilación de módulos al actualizar el núcleo. Como resultado, los usuarios del repositorio Debian (y sus numerosos parientes) utilizan módulos del núcleo ya sea del repositorio del distribuidor o compilados a partir de código fuente con DKMS.

Sin embargo, esta situación no satisface particularmente al segmento empresarial. Los distribuidores de código propietario desean distribuir el producto en forma de binarios compilados.

Los administradores no quieren tener herramientas de desarrollo en servidores de producción por razones de seguridad. Los distribuidores de Enterprise Linux — como Red Hat y SUSE — han decidido que podrán mantener un kABI estable para sus usuarios. Como resultado, surgieron paquetes KMOD para Red Hat y paquetes KMP para SUSE.

La esencia de esta solución es bastante simple. Para una versión específica de la distribución, la API del núcleo se 'congela'. El distribuidor declara que utiliza exactamente el núcleo, por ejemplo, 3.10, e introduce solo correcciones y mejoras que no afectan a las interfaces del núcleo, y los módulos compilados para el primer núcleo pueden ser utilizados para todos los posteriores sin necesidad de recompilación.

Red Hat declara que existe compatibilidad kABI para su distribución a lo largo de todo su ciclo de vida. Es decir, un módulo compilado para RHEL 6.0 (lanzado en noviembre de 2010) también debería funcionar en la versión 6.10 (lanzada en junio de 2018). Esto representa casi 8 años. Naturalmente, esta tarea es bastante compleja.
Hemos registrado varios casos donde, debido a problemas de compatibilidad 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 núcleo de RHEL 7.5 y, sin embargo, se cargaba y garantizaba que el servidor se caía, decidimos no utilizar la compatibilidad kABI para RHEL 7 en absoluto.

En la actualidad, el paquete KMOD para RHEL 7 contiene una compilación para cada versión de lanzamiento y un script que asegura la carga del módulo.

SUSE adoptó un enfoque más cauteloso hacia la compatibilidad kABI. Solo garantizan la compatibilidad kABI dentro de un solo service pack.

Por ejemplo, el lanzamiento de SLES 12 fue en septiembre de 2014. Y SLES 12 SP1 en diciembre de 2015, es decir, pasó poco más de un año. A pesar de que ambas versiones utilizan el núcleo 3.12, son incompatibles con kABI. Es evidente que mantener la compatibilidad kABI durante solo un año es mucho más fácil. Un ciclo anual de actualización del módulo del núcleo no debería causar problemas a los creadores de módulos.

Como resultado de esta política de SUSE, no hemos registrado ningún problema de compatibilidad kABI con nuestro módulo veeamsnap. Sin embargo, el número de paquetes para SUSE es casi un orden de magnitud mayor.

Parches y backports

A pesar de que los distribuidores intentan garantizar la compatibilidad kABI y la estabilidad del núcleo, también buscan mejorar el rendimiento y corregir los defectos de este núcleo estable.

Al mismo tiempo, además de su propio "trabajo en errores", los desarrolladores del núcleo enterprise linux monitorean los cambios en el núcleo vanilla y los trasladan a su propio "estable".

A veces esto lleva a nuevos errores.

En el último lanzamiento de Red Hat 6, se cometió un error en una de las actualizaciones menores. Esto provocó que el módulo veeamsnap garantizara que el sistema se colapsara al liberar un snapshot. Al comparar las fuentes del núcleo antes y después de la actualización, descubrimos que todo era culpa de un backport. Se realizó una corrección similar en el núcleo vanilla versión 4.19. Sin embargo, en el núcleo vanilla esta corrección funcionaba correctamente, mientras que al trasladarla al "estable" 2.6.32 surgió un problema de bloqueo en spin.

Por supuesto, todos cometemos errores en algún momento, pero ¿valía la pena trasladar el código de 4.19 a 2.6.32, arriesgando la estabilidad? No estoy seguro...

Lo peor es cuando el marketing se involucra en la tensión entre "estabilidad" y "modernización". Al departamento de marketing le gustaría que el núcleo de la distribución actualizada fuera estable, y al mismo tiempo fuese mejor en rendimiento y contar con nuevas funciones. Esto lleva a compromisos extraños.

Cuando intenté compilar un módulo en el núcleo 4.4 de SLES 12 SP3, me sorprendió descubrir que tenía funcionalidades del núcleo 4.8 vanilla. En mi opinión, la implementación de la entrada/salida en bloque del núcleo 4.4 de SLES 12 SP3 se asemeja más al núcleo 4.8 que al lanzamiento anterior del núcleo 4.4 estable de SLES12 SP2. No me atrevo a juzgar qué porcentaje del código del núcleo 4.8 se trasladó al 4.4 de SLES para SP3, pero no puedo llamarlo simplemente un núcleo estable 4.4.

Lo más molesto de esto es que al escribir un módulo que funcione igual de bien en diferentes núcleos, ya no se puede confiar en la versión del núcleo. También hay que tener en cuenta la distribución. Es bueno que a veces se pueda basar en una definición que aparece junto con la nueva funcionalidad, pero esta oportunidad no siempre se presenta.

Como resultado, el código se llena de directivas peculiares de compilación condicional.

También hay parches que cambian la API documentada del núcleo.
Me encontré con una distribución KDE neon 5.16 y me sorprendió al ver que la llamada a lookup_bdev en esta versión del núcleo cambió la lista de parámetros de entrada.

Para compilar, tuve que añadir un script en el makefile que verifica si el parámetro mask está presente en la función lookup_bdev.

Firma de los módulos del núcleo

Pero volvamos a la cuestión de la distribución de paquetes.

Una de las ventajas del kABI estable es que los módulos del núcleo en forma de archivo binario se pueden firmar. En este caso, el desarrollador puede estar seguro de que el módulo no ha sido dañado accidentalmente o modificado intencionadamente. Esto se puede verificar con el comando modinfo.

Las distribuciones de Red Hat y SUSE permiten verificar la firma de un módulo y cargarlo solo si el certificado correspondiente está registrado en el sistema. El certificado es una clave pública con la que se firma el módulo. Lo distribuimos en forma de paquete separado.

El problema aquí es que los certificados pueden estar integrados en el núcleo (los utilizan los distribuidores) o deben ser escritos en la memoria no volátil de EFI mediante una utilidad. mokutil. Utilidad mokutil al instalar un certificado se requiere reiniciar el sistema y antes de cargar el núcleo del sistema operativo, se le pide al administrador que permita la carga de un nuevo certificado.

Por lo tanto, agregar un certificado requiere acceso físico del administrador al sistema. Si la máquina está en la nube o simplemente en un servidor remoto y solo se tiene acceso a través de la red (por ejemplo, por ssh), será imposible agregar el certificado.

EFI en máquinas virtuales

A pesar de que EFI ha sido admitido por prácticamente todos los fabricantes de placas madre desde hace mucho tiempo, al instalar el sistema, el administrador puede no pensar en la necesidad de EFI, y este puede estar desactivado.

No todos los hipervisores soportan EFI. VMWare vSphere lo soporta desde la versión 5.
Microsoft Hyper-V también ha agregado soporte para EFI, comenzando con Hyper-V para Windows Server 2012R2.

Sin embargo, en la configuración predeterminada, esta funcionalidad para máquinas Linux está desactivada, lo que significa que no se puede instalar el certificado.

En vSphere 6.5, se puede habilitar la opción Secure Boot solo en la versión anterior de la interfaz web que funciona a través de Flash. La interfaz web en HTML-5 aún está bastante rezagada.

Distribuciones experimentales

Y por último, consideremos la cuestión de las distribuciones experimentales y aquellas sin soporte oficial. Por un lado, es poco probable que tales distribuciones se encuentren en los servidores de organizaciones serias. No hay soporte oficial para estas distribuciones. Por lo tanto, no se puede proporcionar soporte técnico del producto en tales distribuciones.

Sin embargo, estas distribuciones se convierten en una plataforma útil para probar nuevas soluciones experimentales. Por ejemplo, Fedora, OpenSUSE Tumbleweed o versiones inestables de Debian. Son bastante estables. Siempre tienen las versiones más recientes de los programas y siempre un núcleo nuevo. Dentro de un año, esta funcionalidad experimental puede aparecer en una nueva versión de RHEL, SLES o Ubuntu.

Así que si algo no funciona en una distribución experimental, es una razón para investigar el problema y resolverlo. Se debe estar preparado para que esta funcionalidad aparezca pronto en los servidores de producción de los usuarios.

La lista actual de distribuciones oficialmente compatibles con la versión 3.0 se puede consultar. aquíSin embargo, la lista real de distribuciones en las que nuestro producto puede funcionar es mucho más amplia.

Personalmente, me interesó el experimento con el sistema operativo 'Elbrus'. Después de modificar el paquete veeam, nuestro producto se instaló y funcionó. Escribí sobre este experimento en Habr en el artículo.

La compatibilidad con nuevas distribuciones continúa. Esperamos el lanzamiento de la versión 4.0. La beta debería salir pronto, así que estén atentos a whats-new!

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster