1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • O NixOS facilita criar VMs ou ISOs apenas com configuração, mas até uma imagem live quase mínima já é gerada com 458MiB desde o início, uma diferença grande em relação à ISO de VM do Alpine com cerca de 66MiB
  • A maior parte do tamanho vinha de nix-store.squashfs, que incluía Python 3.13.13, módulos do Linux, systemd, Perl, GRUB, documentação e dependências relacionadas ao Nix
  • Ao passar por nix.enable = false, documentation.enable = false e remover register-nix-paths, a ISO caiu de 458MiB → 384MiB → 360MiB, e a dependência de Boost também saiu
  • Removendo também o cliente OpenSSH, pacotes padrão, ferramentas de instalação do GRUB, módulos de kernel em tempo de execução e o caminho de ativação baseado em Perl, o tamanho final caiu para 183MiB
  • Pode servir como referência para imagens de boot pequenas e experimentais, mas como remove muitos recursos necessários, é difícil usar isso como está em desktops ou ambientes importantes

Criando uma ISO a partir da configuração do NixOS

  • O NixOS permite criar VMs facilmente com base em configuração
    • nixos-rebuild build-vm gera uma VM da configuração atual do sistema
    • Usando pkgs.nixos, também é possível criar uma VM a partir de uma configuração arbitrária, mesmo que não seja a do sistema
  • O exemplo básico gera uma VM mínima deixando apenas system.stateVersion = "26.05" e services.getty.autologinUser = "root"
  • Essa VM funciona como uma thin VM
    • A imagem de disco inclui apenas os arquivos criados diretamente dentro da VM
    • O restante, como /nix/store, é montado a partir do sistema operacional hospedeiro
  • Para executar em um host sem Nix, em um host remoto ou em um hipervisor comum, é necessária uma ISO autossuficiente
  • É possível construir uma ISO importando o módulo iso-image.nix do NixOS
    • image.baseName = lib.mkForce "nixos" define o nome da ISO gerada
    • Um exemplo de execução tem a forma qemu-system-x86_64 --cdrom .../nixos.iso -m 1G --accel kvm
    • Se o ambiente não for um Linux moderno em amd64, talvez seja necessário mudar a arquitetura ou a forma de aceleração

Ponto de partida: ISO de 458MiB

  • O resultado da build da ISO padrão foi de 458MiB
  • Essa imagem ainda nem incluía vim
    • Depois do boot, ao executar vim, aparecia command not found
  • Como comparação, a ISO de VM do Alpine usada como referência tinha cerca de 66MiB
  • O Damn Small Linux aparece como exemplo de um sistema que oferecia um ambiente de desktop bem acabado em um tamanho muito menor
  • O objetivo não era chegar ao nível do Damn Small Linux, e sim verificar se dava para reduzir a ISO do NixOS ao menos um pouco

Análise do tamanho dentro da ISO

  • Ao montar a ISO e verificar com du, o tamanho estava dividido assim
    • nix-store.squashfs: 416MiB
    • initrd: 26MiB
    • kernel: 13MiB
    • ISO inteira: 458MiB
  • O principal fator de tamanho era o espaço de usuário principal, nix-store.squashfs
  • Ao montar o squashfs, apareciam caminhos parecidos com o Nix store
    • python3-3.13.13: 128MiB
    • linux-6.18.35-modules: 144MiB
    • systemd-260.1: 60MiB
    • perl-5.42.0: 56MiB
    • grub-2.12: cerca de 62MiB somando vários itens
    • Também havia documentação como nix-manual-2.34.7 e nixos-manual-html
  • Como a ISO é construída no host, os caminhos do store dentro da ISO também podem ser rastreados no /nix/store do host
  • nix why-depends foi usado para descobrir de onde vinham as dependências
    • O Boost entrava pelo caminho do daemon do Nix
    • Passando por nix-daemon.conf, nix e libnixutil.so, chegava-se a boost-1.89.0

Removendo Nix e documentação

  • Foi feita uma tentativa de remover o próprio Nix da imagem com nix.enable = false
  • A documentação também foi desativada com documentation.enable = false
  • O primeiro resultado foi 458MiB → 384MiB
  • Mas o Boost ainda continuava presente
    • register-nix-paths.service tentava registrar no boot o conteúdo do store da ISO
    • Esse caminho acabava puxando Nix e Boost de volta
  • Com systemd.services.register-nix-paths = lib.mkForce {} o serviço foi esvaziado e removido
  • Com isso, a ISO passou a 360MiB, e nix why-depends confirmou que a dependência de Boost havia sumido

Removendo OpenSSH e pacotes padrão

  • De forma parecida, também foi possível esvaziar environment.defaultPackages
  • Remover ssh foi mais complicado
    • modules/programs/ssh.nix adiciona o OpenSSH a environment.corePackages
    • Não foi encontrada uma opção como programs.ssh.enable para controlar isso
    • services.openssh.enable é configuração de servidor, não uma opção para remover o cliente
  • Era possível excluir programs/ssh.nix com disabledModules, mas outros módulos esperavam a existência das opções programs.ssh, o que gerava erros em cascata
  • A solução foi fornecer em um módulo separado uma opção stub que não usa programs.ssh
    • options.programs.ssh = lib.mkOption {};
    • Depois disso, o módulo real de SSH foi excluído com disabledModules = [ "programs/ssh.nix" ];
  • Nesse processo, também foram aplicadas as configurações abaixo
    • documentation.man.enable = false
    • networking.firewall.enable = false
    • environment.defaultPackages = lib.mkForce []

Nota sobre a estrutura de módulos do NixOS

  • Os módulos do NixOS têm, em geral, três partes
    • Itens no nível do módulo: imports, disabledModules
    • Definição de opções: options.*
    • Implementação: config.*
  • Módulos que não definem opções podem usar uma forma abreviada e escrever propriedades de implementação sem o prefixo config.
  • Para manter a forma abreviada no restante da configuração, a opção stub programs.ssh foi separada em um módulo importado à parte

Removendo ferramentas de instalação do GRUB

  • Um dos grandes itens restantes era o conjunto de arquivos do GRUB, com cerca de 62MiB
  • A conclusão foi que o bootloader em si é necessário, mas não é preciso incluir todas as ferramentas de instalação
  • O preset de ISO do NixOS empacota tanto a versão UEFI quanto a versão BIOS do GRUB
  • Como não havia uma opção clara para desligar isso, foi usado um método mais bruto para redefinir os seguintes valores
    • system.extraDependencies = lib.mkForce []
    • environment.systemPackages = lib.mkForce config.environment.corePackages
  • environment.systemPackages não foi totalmente esvaziado
    • Sem bash, o getty pode entrar em crashloop constante, então corePackages foi mantido para garantir que o shell continue funcionando minimamente

Removendo módulos do kernel

  • linux-6.18.35-modules tinha 144MiB, cerca de um quarto do tamanho total
  • Não parecia existir no NixOS um bom hook para limitar os módulos de kernel usados em tempo de execução
  • Em vez disso, a pasta kernel-modules foi removida do output do sistema
    • system.systemBuilderCommands = lib.mkAfter "rm $out/kernel-modules";
  • Na prática, isso desativa quase totalmente o carregamento de módulos em tempo de execução
    • Os módulos necessários precisam estar em boot.initrd.kernelModules ou availableKernelModules
  • Depois dessa mudança, perdeu-se a capacidade de trocar para uma resolução de tela mais confortável, mas o sistema ainda inicializava
  • O tamanho da ISO caiu para 197MiB

Removendo Perl e recursos alternativos experimentais

  • Ainda restavam 56MiB de Perl
  • Com nix why-depends, foi verificado que o Perl era usado durante a ativação do sistema para configurar usuários e /etc
  • Não era possível simplesmente abandonar por completo a configuração de usuários e /etc
  • Em vez disso, o caminho existente foi substituído por recursos experimentais
    • O gerenciamento de /etc passou a usar o modo overlay
    • O gerenciamento de usuários passou a usar o userborn nativo
  • As configurações aplicadas foram as seguintes
    • system.etc.overlay.enable = true
    • system.etc.overlay.mutable = false
    • services.userborn.enable = true
  • O tamanho final da ISO chegou a 183MiB

Estado final e limitações

  • O tamanho caiu do ponto de partida de 458MiB para 183MiB, ficando em algo próximo de um terço do original
  • Ainda assim, o resultado não foi considerado exatamente “bom”
  • Não é adequado para desktops de uso real nem para ambientes importantes
    • Todos os recursos removidos existiam por algum motivo
  • Pode servir de referência quando for preciso uma imagem de boot pequena e experimental, capaz de executar apenas tarefas bem limitadas
  • Copiar a configuração final tal como está pode deixar de fora recursos necessários para o objetivo desejado

Espaço para reduzir ainda mais

  • Este trabalho focou em itens que podiam ser “simplesmente removidos” ou que tinham alternativas relativamente claras
  • Ainda existem áreas que exigem trabalho mais profundo
    • Atualmente, systemdMinimal e systemd são empacotados juntos
    • Ao tentar remover um dos dois, outros caminhos de build quebravam
  • Também há itens menores que ainda poderiam ser eliminados, e somados podem representar uma redução relevante
  • Otimizações adicionais exigem mais investigação e experimentação

1 comentários

 
GN⁺ 4 시간 전
Opiniões no Lobste.rs
  • Existe um módulo feito exatamente para esse tipo de uso. Exige bastante compilação, mas dá para criar um initrd totalmente autônomo com todo o espaço de usuário do NixOS em cerca de 80 MiB com compressão zstd
    Isso não se limita só a initrds autônomos; também pode ser usado para reduzir o tamanho de qualquer NixOS. Provavelmente também dá para aplicar a ISOs de instalação
    https://github.com/wucke13/minimal-nixos/

  • O sistema base do TinyCore Linux é o Core, com 17 MB
    Se você quiser X e FLTK/FLWM, existe o TinyCore, com 23 MB; e se quiser mais gerenciadores de janelas e aplicativos, existe o CorePlus, com 248 MB
    http://www.tinycorelinux.net/downloads.html

    • Não vejo o que isso tem a ver com configuração declarativa ou VMs reproduzíveis
  • Recomendo a apresentação da NixCon sobre reduzir o NixOS para servir como alternativa ao Yocto: https://youtu.be/AsXY61laNb8
    Não foi tão detalhada quanto eu esperava, mas o que ouvi diretamente do Óli e do Matthew na conferência foi impressionante. Fico curioso se existe algum texto resumindo isso

  • No NixOS, sempre é meio frustrante tentar montar uma instalação pequena
    Acho que dá para reduzir o tamanho da parte de SSH com as configurações abaixo

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

    Você também pode importar "${nixpkgs}/nixos/modules/profiles/minimal.nix". Ele já inclui parte das otimizações feitas no texto

    • No caso de uso que motivou o texto original, na verdade o próprio ssh quase não era necessário
      Mesmo assim, na maioria dos casos essa abordagem provavelmente é mais sensata
      Eu já tinha visto "${nixpkgs}/nixos/modules/profiles/minimal.nix" antes e achado menos útil do que esperava, então nem pensei em incluí-lo quando comecei a investigar. Quando lembrei dele depois, eu já estava com o trabalho pela metade, e enfiá-lo de volta no começo, onde deveria ter entrado, pareceu um pouco desonesto
  • Hoje em dia é estranho como Perl acaba sendo puxado para tantos sistemas. Até em uma ISO pequena tem Perl, e se você tenta compilar algo minimamente sério do zero, acaba caindo em openssl -> Perl

  • Antes mesmo de ler, eu já imaginava que seria culpa de algum script em Perl idiota que ninguém reescreveu em C
    Edit: era isso mesmo

  • A partir do NixOS 26.05, o initrd padrão usa systemd, por causa do grande número de casos de uso de initrd que um sistema operacional moderno precisa suportar
    systemdMinimal é um binário do systemd compilado com menos flags e dependências, o que ajuda a manter o initrd menor
    Mas, se o objetivo for uma ISO mínima, talvez dê para fazer os dois dependerem do mesmo binário