Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC22, RFC78] NixOS a la carte (proof of concept) #148456

Closed
wants to merge 33 commits into from

Conversation

roberth
Copy link
Member

@roberth roberth commented Dec 3, 2021

Motivation for this change
  • RFC22: a minimal NixOS module list to improve performance. A 3× eval performance improvement is realistic based on earlier experimentation on the postgresql test.
  • RFC78: independent modules that can be composed (almost) like functions, for use outside of conventional NixOS.
  • Allow alternative implementations to coexist

These are big goals that this PR does not achieve by itself, but works towards. The actual goal for this PR is to provide an example of the kinds of refactoring that can be done to make NixOS more modular.

What it does already achieve, is an example of how to build a dockerTools image using the NixOS /etc code:

    let
      nixosCore = (lib.nixos.core ({ config, modules, ... }: {
        imports = [ pkgs.nixosModule modules.etc ];
        environment.etc."hosts" = {
          text = ''
            127.0.0.1 localhost
            ::1 localhost
          '';
          mode = "0444";
        };
      }));
    in pkgs.dockerTools.streamLayeredImage {
      name = "poc";
      tag = "latest";
      fakeRootCommands = ''
        mkdir -p /etc
        ${nixosCore.config.system.build.etcActivationCommands}
      '';
      config.Cmd = pkgs.writeScript "poc-cmd" ''
        #!${pkgs.busybox}/bin/sh
        ${pkgs.busybox}/bin/cat /etc/hosts
      '';
    }

So there you have it, reusing a bit of NixOS with great evaluation performance. When more modules are refactored this way, it becomes actually useful, solving a real problem in practical dockerTools use such as #94636) and replacing incomplete solutions like #105685.

I've found that the best workflow for this refactoring is test driven development.

Things done
  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 22.05 Release Notes (or backporting 21.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
    • (Release notes changes) Ran nixos/doc/manual/md-to-db.sh to update generated release notes
  • Fits CONTRIBUTING.md.

@github-actions github-actions bot added 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: changelog 8.has: documentation 8.has: module (update) This PR changes an existing module in `nixos/` labels Dec 3, 2021
@roberth
Copy link
Member Author

roberth commented Dec 3, 2021

@ofborg test docker-tools etc nixpkgs openssh postgresql.postgresql_12 postgresql-container

@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10 labels Dec 3, 2021
@pennae
Copy link
Contributor

pennae commented Dec 3, 2021

interesting! we've been trying to get to a faster nixos another way, by marking most modules as "optional" and having the module system not evaluate them after the initial import of their nix file unless a configurable path in config has values (in our plan this path would usually be a module enable option). the docs work we've been doing is part of this, since docs currently evaluate all modules anyway and users probably will not be very happy if most of the configuration.nix manpage disappears because they've selected only a subset of modules for their system :<

@roberth
Copy link
Member Author

roberth commented Dec 3, 2021

@pennae That's also a good approach; a bit more pragmatic, but seems to need a more complicated module system and does not necessarily stimulate improvements towards RFC78 for example. Could you show what changes you had to make?

@pennae
Copy link
Contributor

pennae commented Dec 3, 2021

@roberth we don't have anything for this yet since we're concentrating on doing the docs bit first (which in itself yields about a 40% improvement in system build time on this machine). this being a highly experimental endeavour we just haven't yet had the energy to spend on doing things that might end up rejected.

the idea was to tag each module that isn't essential with an extra toplevel key that describes properties of the modules, leading to eg

{ lib, config, ... }:

{
  module.enabledBy = [ "services" "theThing" "enable" ];

  options.services.theThing.enable = mkEnableOption "the thing";

  # ...
}

the module system imports all modules as before, but does not evaluate such optional modules except for their enabledBy. it then checks the resulting options (not config!) for enabler paths and adds those to the evaluation. repeat until fixed point is reached. for modules that enable other modules we'd go ahead and allow dependency declarations, eg module.requires to avoid unnecessary iterations. our testing has shown that module parse time is about 5% of the total instantiation time for our configuration, and if nixos modules declare their dependencies correctly we'd only have to do a single iteration to enable all necessary modules. as a test we had minimized module-list.nix for our config and (with the current docs build enabled) also arrived at a ~40% build time reduction.

@roberth
Copy link
Member Author

roberth commented Dec 3, 2021

~40% build time reduction.

You should be able to get about 3× faster, which is your reduction applied twice. That discrepancy can be explained by the choice of test and I was quite radical with my pruning, even going into some modules to set values to sensible defaults in order to remove more modules. It doubt whether those wins are feasible without refactoring those modules.

@pennae
Copy link
Contributor

pennae commented Dec 3, 2021

oh yes, a lot of the remaining time was most likely due to a large environment.systemPackages from our config. we had also pruned the config itself for a different test and got a 4x improvement with the pruned config. there's clearly a lot of improvement still possible!

@ofborg ofborg bot added 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux and removed 10.rebuild-linux: 1-10 labels Dec 4, 2021
@aanderse
Copy link
Member

aanderse commented Dec 7, 2021

@roberth awesome! I'm looking forward to seeing more examples so I can better wrap my mind around this.
@pennae also awesome! I hope to hear more about that coming up.

I thought I would include this link here as well because it is relevant.

@ofborg ofborg bot added the 2.status: merge conflict This PR has merge conflicts with the target branch label Dec 8, 2021
@ofborg ofborg bot removed the 2.status: merge conflict This PR has merge conflicts with the target branch label Dec 8, 2021
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/a-faster-dockertools-buildimage-prototype/16922/4

@roberth
Copy link
Member Author

roberth commented Jan 10, 2022

Idea: migrate the implementations of nixos/modules/profiles to new nixos/modules/archetypes, so we have a better name and a new namespace to rethink the profiles.

@roberth
Copy link
Member Author

roberth commented Jan 27, 2022

I'm closing this because the work now happens in other PRs, which tend to be linked here.

@roberth
Copy link
Member Author

roberth commented May 16, 2022

Tracking which changes have been cleaned up and merged

Note to self: might want to bundle up some of the similar and simple changes, like module extractions and adding imports.

Things not to do:

  • Create an attribute set of NixOS modules. This would be premature as the set of relevant modules is yet to be determined.
  • Create more modules than necessary.
  • Blindly rebase. Make sure the refactors are clean.

The commits of this PR and their relation to derived PRs or vice versa:


related changes

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/genodepkgs-extending-nixpkgs-nixos-to-genode/8779/7

@roberth
Copy link
Member Author

roberth commented Jul 1, 2023

Latest PR is for having a separate activation script, that can run during the build of an image

EDIT: review appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants