1 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • NixOS facilita crear VMs o ISOs solo con configuración, pero incluso una imagen live casi mínima se generó desde el inicio con 458 MiB, una gran diferencia frente a la ISO de VM de Alpine de unos 66 MiB
  • La mayor parte del tamaño la ocupaba nix-store.squashfs, que incluía Python 3.13.13, módulos de Linux, systemd, Perl, GRUB, documentación y dependencias relacionadas con Nix
  • Tras pasar por nix.enable = false, documentation.enable = false y eliminar register-nix-paths, la ISO se redujo de 458 MiB → 384 MiB → 360 MiB, y también desapareció la dependencia de Boost
  • Al quitar el cliente de OpenSSH, los paquetes por defecto, las herramientas de instalación de GRUB, los módulos del kernel en tiempo de ejecución y la ruta de activación basada en Perl, el tamaño final bajó hasta 183 MiB
  • Puede servir como referencia para imágenes de arranque pequeñas de prueba, pero como elimina muchas funciones necesarias, es difícil usarla tal cual en un escritorio o en entornos importantes

Crear una ISO desde la configuración de NixOS

  • NixOS permite crear fácilmente una VM a partir de una configuración
    • nixos-rebuild build-vm genera una VM con la configuración actual del sistema
    • Con pkgs.nixos también se puede crear una VM a partir de una configuración arbitraria, aunque no sea la del sistema
  • El ejemplo básico crea una VM mínima dejando solo system.stateVersion = "26.05" y services.getty.autologinUser = "root"
  • Esta VM funciona como una thin VM
    • La imagen de disco solo contiene los archivos creados directamente dentro de la VM
    • El resto, como /nix/store, se monta desde el sistema operativo anfitrión
  • Si el host no tiene Nix, o se quiere ejecutar en un host remoto o en un hipervisor común, hace falta una ISO autocontenida
  • Se puede construir una ISO importando el módulo iso-image.nix de NixOS
    • image.baseName = lib.mkForce "nixos" define el nombre de la ISO de salida
    • Un ejemplo de ejecución sería qemu-system-x86_64 --cdrom .../nixos.iso -m 1G --accel kvm
    • Si no se está en un entorno Linux moderno amd64, puede ser necesario cambiar la arquitectura o el método de aceleración

Punto de partida: ISO de 458 MiB

  • El resultado de la compilación de la ISO base fue de 458 MiB
  • Esta imagen todavía ni siquiera incluía vim
    • Al arrancar, ejecutar vim devolvía command not found
  • Como comparación, la ISO de VM de Alpine usada como referencia pesa unos 66 MiB
  • También se menciona a Damn Small Linux como un caso que ofrecía un entorno de escritorio bastante completo con un tamaño mucho menor
  • El objetivo no era llegar al nivel de Damn Small Linux, sino comprobar si era posible reducir aunque fuera un poco una ISO de NixOS

Análisis del tamaño dentro de la ISO

  • Al montar la ISO y revisarla con du, el tamaño se repartía así
    • nix-store.squashfs: 416 MiB
    • initrd: 26 MiB
    • kernel: 13 MiB
    • ISO completa: 458 MiB
  • El principal factor de tamaño era nix-store.squashfs, es decir, el espacio de usuario principal
  • Al montar el squashfs aparecían rutas similares a las del Nix store
    • python3-3.13.13: 128 MiB
    • linux-6.18.35-modules: 144 MiB
    • systemd-260.1: 60 MiB
    • perl-5.42.0: 56 MiB
    • grub-2.12: unos 62 MiB sumando varios elementos
    • También se incluían documentos como nix-manual-2.34.7 y nixos-manual-html
  • Como la ISO se compila en el host, las rutas del store dentro de la ISO también se pueden rastrear desde el /nix/store del host
  • Con nix why-depends se identificó de dónde venían las dependencias
    • Boost entraba por la ruta del daemon de Nix
    • La cadena llevaba por nix-daemon.conf, nix, libnixutil.so hasta boost-1.89.0

Eliminar Nix y la documentación

  • Se intentó quitar Nix de la imagen con nix.enable = false
  • También se desactivó la documentación con documentation.enable = false
  • El primer resultado fue 458 MiB → 384 MiB
  • Pero Boost seguía presente
    • register-nix-paths.service intenta registrar al arrancar el contenido del store de la ISO
    • Esa ruta volvía a arrastrar Nix y Boost
  • Se vació y eliminó ese servicio con systemd.services.register-nix-paths = lib.mkForce {}
  • Con eso la ISO bajó a 360 MiB y nix why-depends confirmó que ya no existía la dependencia de Boost

Eliminar OpenSSH y los paquetes por defecto

  • De forma similar, también se pudo vaciar environment.defaultPackages
  • Quitar ssh fue más complicado
    • modules/programs/ssh.nix agrega OpenSSH a environment.corePackages
    • No se encontró una opción como programs.ssh.enable para controlarlo
    • services.openssh.enable es una opción del servidor, no para eliminar el cliente
  • Era posible excluir programs/ssh.nix usando disabledModules, pero otros módulos esperaban que existieran las opciones de programs.ssh, lo que generaba errores en cadena
  • La solución fue aportar en otro módulo una opción stub que no usara las opciones de programs.ssh
    • options.programs.ssh = lib.mkOption {};
    • Después se excluyó el módulo SSH real con disabledModules = [ "programs/ssh.nix" ];
  • En este proceso también se aplicaron estas configuraciones
    • documentation.man.enable = false
    • networking.firewall.enable = false
    • environment.defaultPackages = lib.mkForce []

Nota sobre la estructura de módulos en NixOS

  • Los módulos de NixOS tienen, en general, tres partes
    • Elementos a nivel de módulo: imports, disabledModules
    • Definición de opciones: options.*
    • Implementación: config.*
  • Los módulos que no definen opciones pueden usar una forma abreviada escribiendo propiedades de implementación sin el prefijo config.
  • Para mantener esa forma abreviada en el resto de la configuración, la opción stub de programs.ssh se separó en un módulo importado aparte

Eliminar herramientas de instalación de GRUB

  • Uno de los elementos grandes que quedaban eran unos 62 MiB de archivos relacionados con GRUB
  • Se concluyó que el bootloader en sí era necesario, pero no hacía falta incluir todas las herramientas de instalación
  • El preset de ISO de NixOS empaqueta versiones de GRUB tanto para UEFI como para BIOS
  • Como no había una opción clara para desactivarlo, se recurrió a un método más brusco restableciendo estos valores
    • system.extraDependencies = lib.mkForce []
    • environment.systemPackages = lib.mkForce config.environment.corePackages
  • environment.systemPackages no se vació por completo
    • Sin bash, getty puede entrar en un crashloop, así que se mantuvieron corePackages para que el shell siguiera funcionando al menos de forma básica

Eliminar módulos del kernel

  • linux-6.18.35-modules ocupaba 144 MiB, aproximadamente una cuarta parte del tamaño total
  • No parecía haber en NixOS un buen gancho para limitar los módulos del kernel usados en tiempo de ejecución
  • En su lugar, se eliminó la carpeta kernel-modules de la salida del sistema
    • system.systemBuilderCommands = lib.mkAfter "rm $out/kernel-modules";
  • En la práctica, este método desactiva casi por completo la carga de módulos en tiempo de ejecución
    • Los módulos necesarios deben incluirse en boot.initrd.kernelModules o en availableKernelModules
  • Después de este cambio se perdió la capacidad de pasar a una resolución de pantalla más cómoda, pero el sistema seguía arrancando
  • El tamaño de la ISO bajó hasta 197 MiB

Eliminar Perl y funciones experimentales alternativas

  • Todavía seguían presentes 56 MiB de Perl
  • Con nix why-depends se comprobó que Perl se usaba durante la activación del sistema para configurar usuarios y /etc
  • No era posible descartar por completo la configuración de usuarios ni de /etc
  • En su lugar, se sustituyó la ruta existente por funciones experimentales
    • La gestión de /etc usa un método basado en overlay
    • La gestión de usuarios usa el userborn nativo
  • Las configuraciones aplicadas fueron estas
    • system.etc.overlay.enable = true
    • system.etc.overlay.mutable = false
    • services.userborn.enable = true
  • El tamaño final de la ISO quedó en 183 MiB

Estado final y límites

  • Se pasó de 458 MiB al inicio a 183 MiB al final, casi un tercio del original
  • Aun así, no se considera que el resultado sea realmente “bueno”
  • No es adecuado para un escritorio de uso real ni para entornos importantes
    • Todas las funciones eliminadas estaban ahí por alguna razón
  • Puede servir como referencia si se necesita una imagen de arranque pequeña para pruebas y solo va a realizar tareas muy limitadas
  • Copiar la configuración final tal cual puede dejar fuera funciones necesarias para el objetivo real

Margen para reducir más

  • Este trabajo se centró en elementos que simplemente podían eliminarse o que tenían sustitutos relativamente claros
  • Aún quedan áreas que requerirían un trabajo más profundo
    • Actualmente se empaquetan tanto systemdMinimal como systemd
    • Al intentar quitar uno de los dos, se rompían otras rutas de compilación
  • También quedan elementos pequeños que todavía podrían retirarse y que, sumados, podrían representar un tamaño relevante
  • Seguir optimizando requeriría más investigación y experimentación

1 comentarios

 
GN⁺ 4 시간 전
Comentarios en Lobste.rs
  • Hay un módulo hecho justo para esto. Requiere bastante compilación, pero puede crear un initrd completamente autónomo con todo el espacio de usuario de NixOS en unos 80 MiB con compresión zstd
    Esto no se limita solo al initrd autónomo; también puede servir para reducir el tamaño de cualquier NixOS. Probablemente también se pueda aplicar a una ISO de instalación
    https://github.com/wucke13/minimal-nixos/

  • El sistema base de TinyCore Linux es Core, de 17 MB
    Si además quieres X y FLTK/FLWM, está TinyCore, de 23 MB, y si quieres más gestores de ventanas y aplicaciones, está CorePlus, de 248 MB
    http://www.tinycorelinux.net/downloads.html

    • No veo qué tiene que ver eso con la configuración declarativa o las VM reproducibles
  • Recomiendo una charla de NixCon sobre cómo reducir NixOS para usarlo como alternativa a Yocto: https://youtu.be/AsXY61laNb8
    No fue tan detallada como esperaba, pero lo que escuché directamente de Óli y Matthew en la conferencia fue impresionante. Me pregunto si habrá algún texto de resumen

  • En NixOS, siempre resulta un poco frustrante crear una instalación pequeña
    Parece que el tamaño de la parte de SSH se puede reducir con la siguiente configuración

    programs.ssh.setXAuthLocation = false;  
    security.pam.services.su.forwardXAuth = lib.mkForce false;  
    fonts.fontconfig.enable = false;  
    

    También puedes importar "${nixpkgs}/nixos/modules/profiles/minimal.nix". Ahí ya vienen algunas de las optimizaciones del artículo

    • En el caso de uso que motivó el artículo original, en realidad casi no hacía falta ssh
      Aun así, en la mayoría de los casos este enfoque probablemente sea más razonable
      Ya había visto antes "${nixpkgs}/nixos/modules/profiles/minimal.nix" y me había parecido menos útil de lo esperado, así que no pensé en incluirlo cuando empecé a investigar. Cuando me acordé después, ya iba por la mitad, y meterlo a la fuerza en una etapa temprana donde debió haber estado originalmente me pareció un poco poco honesto
  • Es extraño la frecuencia con la que Perl termina metiéndose en sistemas actuales. Incluso en una ISO pequeña aparece Perl, y si intentas compilar algo serio desde cero, terminas en openssl -> Perl

  • Antes de leerlo, ya suponía que sería por algún script de Perl tonto que nadie reescribió en C
    Edit: sí, así era

  • Desde la versión 26.05, NixOS usa systemd en el initrd predeterminado. Esto se debe a la gran cantidad de casos de uso de initrd que un sistema operativo moderno tiene que soportar
    systemdMinimal es un binario de systemd compilado con menos flags y dependencias, lo que ayuda a mantener más pequeño el initrd
    Pero si el objetivo es una ISO mínima, parece posible hacer que ambos dependan del mismo binario