1 điểm bởi GN⁺ 4 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • NixOS giúp tạo VM hoặc ISO dễ dàng chỉ bằng cấu hình, nhưng ngay cả image live gần mức tối thiểu cũng được tạo ra ở mức 458MiB ngay từ đầu, chênh lệch lớn so với ISO Alpine VM khoảng 66MiB
  • Phần lớn dung lượng nằm ở nix-store.squashfs, bên trong có Python 3.13.13, Linux modules, systemd, Perl, GRUB, tài liệu và các phụ thuộc liên quan đến Nix
  • Qua các bước nix.enable = false, documentation.enable = false, và loại bỏ register-nix-paths, ISO đã giảm từ 458MiB → 384MiB → 360MiB, đồng thời phụ thuộc Boost cũng biến mất
  • Sau khi loại bỏ OpenSSH client, các gói mặc định, công cụ cài đặt GRUB, kernel module runtime và cả đường kích hoạt dựa trên Perl, kích thước cuối cùng giảm xuống còn 183MiB
  • Đây là tài liệu tham khảo hữu ích cho image khởi động nhỏ phục vụ thử nghiệm, nhưng vì đã loại bỏ quá nhiều chức năng nên khó dùng nguyên trạng cho desktop hoặc môi trường quan trọng

Tạo ISO từ cấu hình NixOS

  • NixOS cho phép tạo VM dễ dàng dựa trên cấu hình
    • nixos-rebuild build-vm tạo VM từ cấu hình hệ thống hiện tại
    • Dùng pkgs.nixos có thể tạo VM từ cấu hình tùy ý ngay cả khi đó không phải cấu hình hệ thống
  • Ví dụ cơ bản tạo một VM tối thiểu chỉ với system.stateVersion = "26.05"services.getty.autologinUser = "root"
  • VM này hoạt động theo kiểu thin VM
    • Ảnh đĩa chỉ chứa các tệp được tạo trực tiếp bên trong VM
    • Phần còn lại như /nix/store được mount từ hệ điều hành host
  • Nếu host không có Nix, là host từ xa, hoặc chạy trên hypervisor thông thường, thì cần một ISO tự chứa
  • Có thể build ISO bằng cách import module iso-image.nix của NixOS
    • image.baseName = lib.mkForce "nixos" dùng để đặt tên file ISO đầu ra
    • Ví dụ chạy là qemu-system-x86_64 --cdrom .../nixos.iso -m 1G --accel kvm
    • Nếu không phải môi trường Linux hiện đại trên amd64 thì có thể cần đổi kiến trúc hoặc cách tăng tốc

Điểm khởi đầu: ISO 458MiB

  • Kết quả build ISO mặc định là 458MiB
  • Image này thậm chí còn chưa có vim
    • Sau khi boot, chạy vim sẽ nhận command not found
  • ISO VM của Alpine được dùng để so sánh có dung lượng khoảng 66MiB
  • Damn Small Linux được nhắc đến như một ví dụ từng cung cấp môi trường desktop hoàn chỉnh với kích thước nhỏ hơn rất nhiều
  • Mục tiêu không phải đạt đến mức của Damn Small Linux mà là kiểm tra xem có thể giảm ISO NixOS thêm được bao nhiêu

Phân tích dung lượng bên trong ISO

  • Sau khi mount ISO và kiểm tra bằng du, dung lượng được chia như sau
    • nix-store.squashfs: 416MiB
    • initrd: 26MiB
    • kernel: 13MiB
    • toàn bộ ISO: 458MiB
  • Yếu tố chiếm dung lượng chính là nix-store.squashfs, tức không gian user space chính
  • Khi mount squashfs sẽ thấy các đường dẫn trông như 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: tổng nhiều mục khoảng 62MiB
    • Cũng có cả tài liệu như nix-manual-2.34.7, nixos-manual-html
  • Vì ISO được build trên host, nên các đường dẫn store trong ISO cũng có thể lần theo trong /nix/store của host
  • nix why-depends được dùng để tìm nguồn gốc phụ thuộc
    • Boost được kéo vào qua đường dẫn Nix daemon
    • Từ nix-daemon.conf, nix, libnixutil.so đi đến boost-1.89.0

Loại bỏ Nix và tài liệu

  • Thử dùng nix.enable = false để loại bỏ chính Nix khỏi image
  • Dùng documentation.enable = false để tắt tài liệu
  • Kết quả đầu tiên là 458MiB → 384MiB
  • Tuy vậy Boost vẫn còn tồn tại
    • register-nix-paths.service cố đăng ký nội dung store của ISO khi boot
    • Đường này lại kéo Nix và Boost quay trở lại
  • Dùng systemd.services.register-nix-paths = lib.mkForce {} để xóa rỗng service đó
  • Kết quả là ISO còn 360MiB, và nix why-depends xác nhận không còn phụ thuộc Boost

Loại bỏ OpenSSH và các gói mặc định

  • Theo cách tương tự, environment.defaultPackages cũng có thể bị xóa rỗng
  • Việc loại bỏ ssh phức tạp hơn
    • modules/programs/ssh.nix thêm OpenSSH vào environment.corePackages
    • Không tìm thấy tùy chọn kiểu programs.ssh.enable để kiểm soát việc này
    • services.openssh.enable là cấu hình cho server chứ không phải tùy chọn xóa client
  • Có thể loại programs/ssh.nix bằng disabledModules, nhưng các module khác lại giả định rằng tùy chọn programs.ssh tồn tại, gây lỗi dây chuyền
  • Cách giải quyết là cung cấp một stub option riêng không sử dụng các tùy chọn programs.ssh
    • options.programs.ssh = lib.mkOption {};
    • Sau đó loại module SSH thật bằng disabledModules = [ "programs/ssh.nix" ];
  • Trong quá trình này cũng áp dụng thêm các cấu hình sau
    • documentation.man.enable = false
    • networking.firewall.enable = false
    • environment.defaultPackages = lib.mkForce []

Ghi chú về cấu trúc module của NixOS

  • Module NixOS về cơ bản có ba phần lớn
    • Mục ở cấp module: imports, disabledModules
    • Định nghĩa tùy chọn: options.*
    • Phần triển khai: config.*
  • Những module không định nghĩa tùy chọn có thể dùng dạng rút gọn với các thuộc tính triển khai mà không cần tiền tố config.
  • Để giữ dạng rút gọn trong phần cấu hình còn lại, stub option cho programs.ssh được tách ra thành một module import riêng

Loại bỏ công cụ cài đặt GRUB

  • Một trong những mục lớn còn lại là các tệp liên quan đến GRUB, khoảng 62MiB
  • Bản thân bootloader là cần thiết, nhưng không cần phải mang theo toàn bộ công cụ cài đặt
  • Preset ISO của NixOS đóng gói cả GRUB bản UEFI lẫn BIOS
  • Không có tùy chọn rõ ràng để tắt chúng, nên dùng cách mạnh tay hơn để đặt lại các giá trị sau
    • system.extraDependencies = lib.mkForce []
    • environment.systemPackages = lib.mkForce config.environment.corePackages
  • Không xóa hoàn toàn environment.systemPackages
    • Nếu không có bash, getty có thể rơi vào crashloop liên tục, nên vẫn giữ corePackages để shell còn hoạt động ở mức tối thiểu

Loại bỏ kernel module

  • linux-6.18.35-modules có dung lượng 144MiB, tương đương khoảng một phần tư toàn bộ
  • NixOS dường như không có hook phù hợp để giới hạn kernel module dùng ở runtime
  • Thay vào đó, thư mục kernel-modules bị xóa khỏi đầu ra hệ thống
    • system.systemBuilderCommands = lib.mkAfter "rm $out/kernel-modules";
  • Cách này về thực chất vô hiệu hóa việc nạp module runtime
    • Các module cần thiết phải được đưa vào boot.initrd.kernelModules hoặc availableKernelModules
  • Sau thay đổi này, hệ thống mất khả năng chuyển sang độ phân giải hiển thị thuận tiện hơn, nhưng vẫn boot được
  • Kích thước ISO giảm xuống 197MiB

Loại bỏ Perl và các tính năng thay thế mang tính thử nghiệm

  • Vẫn còn Perl chiếm 56MiB
  • Kiểm tra bằng nix why-depends cho thấy Perl được dùng trong quá trình kích hoạt hệ thống để cấu hình user và /etc
  • Không thể bỏ hoàn toàn phần cấu hình user và /etc
  • Thay vào đó, dùng các tính năng thử nghiệm để thay thế đường đi hiện có
    • Quản lý /etc bằng cơ chế overlay
    • Quản lý user bằng userborn native
  • Cấu hình được áp dụng như sau
    • system.etc.overlay.enable = true
    • system.etc.overlay.mutable = false
    • services.userborn.enable = true
  • Kích thước ISO cuối cùng là 183MiB

Trạng thái cuối cùng và giới hạn

  • Từ điểm xuất phát 458MiB xuống 183MiB, tức gần bằng một phần ba bản gốc
  • Dù vậy, kết quả này vẫn khó có thể gọi là “tốt”
  • Nó không phù hợp để dùng nguyên trạng cho desktop hoặc môi trường quan trọng thực sự
    • Mọi chức năng bị loại bỏ đều tồn tại vì có lý do
  • Nếu cần một image boot nhỏ cho mục đích thử nghiệm và chỉ thực hiện các tác vụ rất đơn giản, đây có thể là tài liệu tham khảo hữu ích
  • Nếu sao chép nguyên cấu hình cuối cùng để dùng, rất có thể sẽ thiếu những chức năng cần cho mục đích thực tế

Vẫn còn chỗ để giảm thêm

  • Lần này tác giả tập trung vào những mục có thể “xóa luôn” hoặc có phương án thay thế tương đối rõ ràng
  • Vẫn còn những khu vực đòi hỏi đào sâu hơn
    • Hiện tại cả systemdMinimal lẫn systemd đều được đóng gói cùng nhau
    • Nếu cố loại một trong hai thì các đường build khác sẽ bị hỏng
  • Những mục nhỏ hơn nữa cũng vẫn có thể loại bỏ, và cộng dồn lại có thể tạo ra khác biệt đáng kể
  • Tối ưu thêm sẽ cần nhiều điều tra và thử nghiệm hơn

1 bình luận

 
Ý kiến trên Lobste.rs
  • Có một mô-đun được tạo ra đúng cho mục đích này. Cần biên dịch khá nhiều, nhưng có thể tạo một initrd hoàn toàn độc lập bao gồm toàn bộ không gian người dùng của NixOS với kích thước khoảng 80MiB khi nén bằng zstd
    Việc này không chỉ giới hạn ở initrd độc lập, mà còn có thể dùng để giảm dung lượng của bất kỳ hệ NixOS nào. Có lẽ cũng áp dụng được cho ISO cài đặt
    https://github.com/wucke13/minimal-nixos/

  • Hệ thống cơ bản của TinyCore Linux là Core với dung lượng 17MB
    Nếu muốn cả X và FLTK/FLWM thì có TinyCore 23MB, còn nếu muốn thêm nhiều trình quản lý cửa sổ và ứng dụng hơn thì có CorePlus 248MB
    http://www.tinycorelinux.net/downloads.html

    • Tôi không rõ chuyện đó liên quan gì đến cấu hình khai báo hay các VM có thể tái lập
  • Tôi khuyên nên xem bài trình bày ở NixCon về việc thu nhỏ NixOS để làm phương án thay thế cho Yocto: https://youtu.be/AsXY61laNb8
    Nó không chi tiết như tôi kỳ vọng, nhưng những gì tôi nghe trực tiếp từ Óli và Matthew tại hội nghị thì rất ấn tượng. Không biết có bài viết tổng hợp nào không

  • Trên NixOS, việc tạo một môi trường cài đặt nhỏ luôn hơi gây bực bội
    Có vẻ có thể giảm kích thước phần SSH bằng cấu hình dưới đây

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

    Cũng có thể import "${nixpkgs}/nixos/modules/profiles/minimal.nix". Trong đó có một phần các tối ưu được nhắc trong bài viết

    • Trong trường hợp sử dụng khiến tôi viết bài gốc thì thực ra bản thân ssh gần như không cần thiết
      Dù vậy, trong đa số trường hợp thì cách này có lẽ hợp lý hơn
      Tôi từng xem "${nixpkgs}/nixos/modules/profiles/minimal.nix" nhưng thấy không ấn tượng như kỳ vọng, nên lúc bắt đầu tìm hiểu tôi đã không nghĩ đến việc đưa nó vào. Khi nhớ ra thì tôi đã làm được khoảng một nửa rồi, nên cảm thấy hơi không trung thực nếu chèn nó vào giai đoạn đầu, vốn là chỗ đáng ra nó phải xuất hiện
  • Dạo này có quá nhiều trường hợp Perl bị kéo vào các hệ thống, thật kỳ lạ. Ngay cả với ISO nhỏ, Perl cũng xuất hiện; và nếu thử biên dịch một thứ gì đó cho ra hồn từ đầu thì lại thành openssl -> Perl

  • Ngay từ trước khi đọc, tôi đã đoán nguyên nhân sẽ là một script Perl ngớ ngẩn nào đó chưa được viết lại bằng C
    Sửa: đúng là vậy thật

  • Từ NixOS 26.05, initrd mặc định của NixOS dùng systemd. Lý do là có nhiều trường hợp sử dụng initrd mà hệ điều hành hiện đại cần hỗ trợ
    systemdMinimal là một nhị phân systemd được biên dịch với ít cờ và phụ thuộc hơn, giúp giữ initrd nhỏ hơn
    Tuy vậy, nếu mục tiêu là ISO tối thiểu thì có vẻ vẫn có thể khiến cả hai cùng phụ thuộc vào một nhị phân duy nhất