Deklarativa byggen med Nix/NixOS

Edward Tjörnhammar

License: CC-BY-SA | Baserad på shwr

Published: June 4, 2015

Deklarativa byggen med Nix/NixOS

Av Edward Tjörnhammar

Med deklarativ åsyftas att resultatet av tilldelningen av ett värde inte påverkas av vart det görs. Deklarativt kan slappt översättas med "inga överraskningar"

Intro

  • Vem är jag
  • Antaganden
  • Angående frågor

Översikt

  1. Övergripande mål & syfte
  2. Nix
    • Språket
    • Byggsystemet
    • Koncepten
    • Användning
  3. NixOS
    • nixpkgs
    • Systemet
    • Moduler
  4. Exempeldeklarationer

Övergripande Mål

  1. Isolering mellan produkter
    • m.a.p. bygg- och exekveringsmiljö
  2. Referentiell transparens m.a.p. bygginstruktioner
  3. Att vara en experimentell platform
Referentiell transparens m.a.p. bygginstruktioner åsyftar att man vill producera samma binär för samma bygginstruktion. Intuitionen säger oss att detta kan åstadkommas enklare om vi använder ett språk med denna egenskap. Märk att detta dock inte är ett måste; Anmärkningsvärt är att Debian har ett liknande initiativ.

Syfte

  • Repeterbara deterministiska byggen leder till lättare igenkännbara fel
    • Källkoden: ändrade signatur, är borta!?
    • Kompileringsfel, dålig bygginstruktion?
    • Testerna gick inte längre igenom!
    • Kanske ovanstående för något, eller introducerat, av beroenderna?
  • Transaktionella uppgraderingar
  • Kan ersätta konfigurationshanteringsverktyg
  • Närmre mellan utveckling, kontinuerlig integration, provisionering
Att ett bygge är repeterbart innebär att dessa fel blir tydligare eftersom vi kan vara säkra på att det inte är något lokalt och orent som introducerat felet. Du kommer se samma fel på en annan dator, e.g. byggservern.

Nix

Språket Nix

  • Funktionellt formaliserat med en vänsterrekursiv grammatik
  • Rent (referentiellt transparent), en funktion ger alltså alltid samma svar för samma input
  • Lat evaluerat, utifrån-in istället för innifrån-ut
  • Otypat, med några primitiva undantag:
    • pather, spather, urlar, boolar, intar, listor, funktioner och attributset
  • Inte homoikoniskt, alltså ingen LISP, se Guix/GuixSD
Ett annat sätt att beskriva lat evaluering är att vi bara utvärderar det vi behöver. Homoikonisitet betyder att det finns ett värde i språket som kan uttrycka sin egen syntax. Gillar man metaprogrammering och tycker det är viktigt att systemet verkligen är fritt; Testa då GuixSD. Primitiva typer är sådana som fångas av den reguljära grammatiken direkt.

Fixpunktkombinatorn

	nix-repl> fix = f: let x = f x; in x
  

Ger mig ett x där x är utvärderat på sig själv med f.

Exempel
nix-repl> (fix (x: { a="Hejsan"; b="Världen"; c=x.a+" "+x.b+"!"; })).c
nix-repl> "Hejsan Världen!"
Intentionen med denna slide är inte att förklara fixpunktkombinatorn utan att visa på':'
  • Hur man binder ett attribut
  • Hur lambdafunktioner ser ut
  • Hur ett funktionsanrop ser ut
  • Hur ett attributset ser ut
Fix och unfix är dock intressanta i sig själva eftersom de tillåter att man både skickar, manipulerar och färdigställer paketdeklarationer i samma uttryck.

Byggsystemet Nix

  1. Byggs med C++, bison, flex, coreutils, perl, libxml , curl!
  2. Installeras under din användare med:
    curl https://nixos.org/nix/install | sh
  3. Kan anropas över RPC
  4. Nix skall fungera med GNU-system, OS X och under Cygwin

Sökvägar

  1. Används för att namnge slutgiltiga attributset
  2. Refereras i språket med vinkelparenteser:
    <tjonix/moduler/smartahemmet.nix> <kneget/paket/tidsredovisning.nix>
  3. Söks efter med $NIX_PATH:
    export NIX_PATH=“tjonix=$HOME/nix/tjonix:kneget=$HOME/nix/kneget:$NIX_PATH”
  4. Kan överskridas individuellt med -I växeln:
    -I tjonix=https://tjornhammar.com/bygg/funkis/tjonix-0.1.2.tar.gz
Anmärkningsvärt för sökvägar är att varje vägsegment kan, på filsystemet, innehålla en egen default.nix vilken introducerar nya attribut för just det attributsettet för den delsökvägen.

Nomenklatur

  1. Utprodukter (outputs): grunda prefixinstallationer av ett paket
  2. Deriverare (derivations): listar andra beroende deriverare samt källfiler
  3. Slutningar (closures): aggregeringen av alla beroende utprodukt(er)
  4. Profiler (profiles): aggregerar en bunt slutningar
  5. Miljöer (environments): är profiler med miljövariabler, mjuklänkar
Profiler är något du använder för att namnge specifika slutningar. Miljöer används oftast för att paketera binära LSB antagande saker som steam.

Utprodukter

find $(nix-build ‘<nixpkgs>’ -A nylon) -type f

/nix/store/my4xkz7v1m7amvcy2r69p9s5gab8n60b-nylon-1.21/bin/nylon
/nix/store/my4xkz7v1m7amvcy2r69p9s5gab8n60b-nylon-1.21/share/man/man1/nylon.1.gz

Utprodukter (forts.)

ldd $(nix-build ‘<nixpkgs>’ -A nylon)/bin/nylon

  linux-vdso.so.1 (0x00007ffd0b2b2000)
  libevent-2.0.so.5 => /nix/store/l73jvl87dbys0iv1cfwnvkikf6rya23r-libevent-2.0.22/lib/libevent-2.0.so.5 (0x00007f506cd36000)
  libresolv.so.2 => /nix/store/i0l0jjkk82wsqz9z5yhg35iy78bjq684-glibc-2.21/lib/libresolv.so.2 (0x00007f506cb1f000)
  libnsl.so.1 => /nix/store/i0l0jjkk82wsqz9z5yhg35iy78bjq684-glibc-2.21/lib/libnsl.so.1 (0x00007f506c907000)
  libc.so.6 => /nix/store/i0l0jjkk82wsqz9z5yhg35iy78bjq684-glibc-2.21/lib/libc.so.6 (0x00007f506c567000)
  libpthread.so.0 => /nix/store/i0l0jjkk82wsqz9z5yhg35iy78bjq684-glibc-2.21/lib/libpthread.so.0 (0x00007f506c34a000)
  /nix/store/i0l0jjkk82wsqz9z5yhg35iy78bjq684-glibc-2.21/lib/ld-linux-x86-64.so.2 (0x00007f506cf7d000)

Deriverare - Nix

oberoende.nix build.sh
    derivation {
      name = "oberoende-paket";
      builder = ./build.sh;
      system = "x86_64-linux";
      finurlig = "trasig";
    }
    #!/bin/sh
    echo "Jag är så $finurlig!!"
nix-build oberoende.nix
falerar!

Deriverare - Rå

nix-instantiate oberoende.nix -> /nix/store/ngcpv5nx6q0sxj138ab02jds4wykybf0-oberoende-paket.drv
 Derive([("out","/nix/store/6qcdj32sdpkpvgii08p3kf5zis29dcsm-oberoende-paket","","") ]
   , []
   , ["/nix/store/pnvdaqw27appm87d589v012mriggndms-build.sh"]
   , "x86_64-linux"
   , "/nix/store/pnvdaqw27appm87d589v012mriggndms-build.sh"
   , [] 
   , [ ("builder","/nix/store/pnvdaqw27appm87d589v012mriggndms-build.sh")
     , ("finurlig","trasig")
     , ("name","oberoende-paket")
     , ("out","/nix/store/6qcdj32sdpkpvgii08p3kf5zis29dcsm-oberoende-paket")
     , ("system","x86_64-linux")
   ])
Första raden beskriver deriverarens egna utprodukter, standard är dock bara en "out". Rad 2 listar alla beroende deriverande outprodukter. Rad 6 listar alla beroende deriverande utprodukter som skall skickas till "builder". Rad 7-11 listar alla miljövariabler som byggaren skall få tilldelad.

Slutningar

nix-store -q –graph $(nix-build ‘<nixpkgs>’ -A nylon) | awk -F’->‘’/->/{print $1}’ | sort -u

  "/nix/store/5hkwn27l77b7c37z7812acdf4p9ldr6m-linux-headers-3.12.32"
  "/nix/store/5jnfr7xzg0prz618l3nayibi51jpj9ym-perl-5.20.2"
  "/nix/store/60nvlmj63b87xxfw20sg7ww3g84ijcq5-zlib-1.2.8"
  "/nix/store/6qh72zn2431fgdzlvzzdgnm8alvl2y02-bzip2-1.0.6"
  "/nix/store/90j6b3cacg3ak0760dkda2rfi2lwp6cq-attr-2.4.47"
  "/nix/store/bnlcsbw7nhd21vw4xssnxwi9y7h2asjj-coreutils-8.23"
  "/nix/store/cpv8pyc772cx0spzz76sa6dvsf6555dh-gcc-4.8.4"
  "/nix/store/dbxpkswwc7rh6g1iy6dwqklzw39hihb1-bash-4.3-p33"
  "/nix/store/i0l0jjkk82wsqz9z5yhg35iy78bjq684-glibc-2.21"
  "/nix/store/jk37x39wbkban1yzxgxik0pdlgvp0c9p-acl-2.2.52"
  "/nix/store/l73jvl87dbys0iv1cfwnvkikf6rya23r-libevent-2.0.22"
  "/nix/store/z6vp5aix4ks1zdjdry7v7dahg8dd02sy-python-2.7.10"
  "/nix/store/zzjmzg5xpns5mcnj01ss6qsd4yj706zh-openssl-1.0.1m"

Filerna i Nix (byggen)

  1. *.nix: nixdeklarationer kompileras ofta direkt till 3. med nix-build
  2. *.drv: deriverarebeskrivningar skapas från *.nix genom nix-instantiate
  3. $out: utprodukt, byggs från *.drv med nix-store --realise

.nix ≻ .drv ≻ $out

Filerna i Nix (cache)

  1. *.nar: nixarkiv skapas från utprodukten med nix-store --dump, används för cache:ar
  2. *.narinfo: signaturfil för nixarkiven, m.a.p. en cache skapas tillsammans med nixarkiv med nix-push

$out ≻ .nar ≻ .narinfo

narinfo innehåller typiskt':' hash, deriverare, systemarkitektur, osv.

Typisk användning

  1. Installera cmake i profilen dev: nix-env -i cmake -p dev
  2. Installera steam i profilen lek: nix-env -i steam -p lek
  3. Ta bort cmake från dev: nix-env -e cmake -p dev
  4. Aktivera lek: nix-env --switch-profile lek
  5. Se historiken för dev: nix-env --list-generations -p dev
  6. Återställ dev med cmake: nix-env --switch-generation 1 -p dev
  7. Uppdatera pakeddefinitioner: nix-channel --update

Typisk avlusning

  1. Starta ett skal med curl som beroende:
    nix-shell ‘<nixpkgs>’ -p curl
  2. Starta ett skal för att manuellt köra byggfaserna för curl:
    nix-shell ‘<nixpkgs>’ -A curl
  3. Inspektera miljön för curl
    nix-store --print-env $(nix-instantiate ‘<nixpkgs>’ -A curl)

NixOS

nixpkgs

  1. Standardmiljön stdenv{Linux, Darwin, Native}
    • Bootstrap, Miljövariabler, byggfaser, pre och post faser, …
    • {unpack, patch, configure, build, install}Phase
    • Hjälpfunktionerna stdenv.mkDerivation pkgs.callPackage
    • Drar in ett system och platform den är avsedd att fungera på
    • Adaptrar för korskompilering(crossSystem, crossDrv), statiska byggen
    • Känt system?nix-build --argstr system armv7l-linux
  2. Paketdefinitioner pkgs
stdenvLinux är den enda rena, vilken bara refererar till /nix/store. stdenvNative använder externa sökvägar

Utveckla med Nix/nixpkgs

Paketdefinition, default.nix Utgåva, release.nix
{ stdenv, lib, cmake, catch }:
stdenv.mkDerivation rec {
  name = "screamd-${version}";
  version = "0.0.1";
  src = ./.;
  buildInputs = [ cmake catch ];
}
with (import <nixpkgs> {}).pkgs;
callPackage ./. {}
nix-build release.nix

Överskridningar (användare)

~/.nixpkgs/config.nix

{
  packageOverrides = pkgs: with pkgs; {
    screamd = import /home/edwtjo/repos/egna/screamd/default.nix {
      inherit stdenv catch cmake;
    };
  };
}

Överskridningar (system)

/etc/nixos/configuration.nix

{ config, lib, pkgs, ... }: {
  nixpkgs.config.packageOverrides = self: with self; {
    screamd = import /home/edwtjo/repos/egna/screamd/default.nix {
      inherit stdenv catch cmake;
    };
    xfce = xfce // {
      libxfce4ui = lib.overrideDerivation (xfce.libxfce4ui) (attrs:  {
        postInstall = ''
          sed -i -e "/Super/d" $out/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-keyboard-shortcuts.xml
        '';
      });
    };
  }

Användningsflaggor

packageOverrides = self: with self: {
  useUnfreeCodecs = true;
  unisonX11 = unison.override{ enableX11 = true; };
  mplayerUF = mplayer.override{ useUnfreeCodecs = true; };
  idea8 = idea.idea-community.override{ jdk = openjdk8; };
}

NixOS

  1. systemdbaserat
  2. Logiskt strukturerat kring moduler
  3. Systemets baslayout följer i inte FHS/LSB
  4. Bor numera i nixpkgs/nixos
  5. Realiseras med en systemprofil och miljö
  6. Kan byggas med nix-build ‘<nixpkgs/nixos>’ -A system
    • För att byta till det nybyggda systemet nixos-rebuild switch

Tjänsteexempel

{ config, lib, pkgs, ... }: {
  systemd.services.screamd = {
    description = "MCast to DSP Screamer";
    after = [ "network.target" ];
    wantedBy = [ "multi-user.target" ];
    serviceConfig.ExecStart = "${pkgs.screamd}/bin/screamd";
  };
}

Moduler

{ config, lib, pkgs, ... }: with lib; {
  options.tjonix.mosh.enable = mkEnableOption "Enable mosh usage on this node";
  config = mkIf config.tjonix.mosh.enable {
    environment.systemPackages = with pkgs; [ mosh ];
    networking.firewall.allowedUDPPortRanges = [{ from = 60000; to = 61000; }];
  };
}

Exempel

SSH rootåtkomst

/etc/nixos/ssh-rootaccess.nix

{ config, pkgs, ...}: {
  services.openssh.enable = true;
  users.extraUsers.root.openssh.authorizedKeys.keyFiles = [ ./id_edwtjo.pub ];
}

HTPC = Kodi + Genesis + SVTPlay

{ config, pkgs, ... }: {
  imports = [ ./ssh-rootaccess.nix ];
  services.xserver.enable = true;
  services.xserver.autorun = true;
  services.xserver.displayManager.slim.enable = true;
  services.xserver.displayManager.slim.defaultUser = "htpc";
  services.xserver.displayManager.slim.autoLogin = true;
  services.xserver.desktopManager.kodi.enable = true;
  nixpkgs.config.kodi.enableSVTPlay = true;
  nixpkgs.config.kodi.enableGenesis = true;
  users.extraUsers.htpc = { uid = 1001; createHome = true; group = "htpc";
  home = "/home/htpc"; shell = pkgs.zsh + "/bin/zsh"; };
  users.extraGroups.htpc.gid = 1001;
}
Ja, denna slajd är för bedrövlig och borde inte visas upp

NixOS Gamestation

{ config, pkgs, ... }: {
  imports = [ ./htpc.nix ];
  nixpkgs.config.kodi.enableAdvancedLauncher = true;
  nixpkgs.config.retroarch = {
    enable4do = true; enableBsnesMercury = true; enableDesmume = true;
    enableFBA = true; enableFceumm = true; enableGambatte = true;
    enableGenesisPlusGX = true; enableMupen64Plus = true; enableNestopia = true;
    enablePicodrive = true; enablePrboom = true; enablePPSSPP = true;
    enableQuickNES = true; enableScummVM = true; enableSnes9x = true;
    enableSnes9xNext = true; enableStella = true; enableVbaM = true;
    enableVbaNext = true;
  };
  environment.systemPackages = with pkgs; [ kodi-retroarch-advanced-launchers ];
}
Ja, denna slajd är för bedrövlig och borde inte visas upp

Användarens egen Zotero miljö

{
  packageOverrides = self: with self; rec {
    zoteroEnv = let
      platform = "Linux-x86_64";
      drv = buildEnv {
        name = "zotero-xpdf";
        paths = [ zotero xpdf sqlite ];
        postBuild = ''
          makeWrapper "${xpdf}/bin/pdftotext" "$out/bin/pdftotext-${platform}"
          makeWrapper "${xpdf}/bin/pdfinfo" "$out/bin/pdfinfo-${platform}"
        '';
      };
    in lib.overrideDerivation drv (x : {buildInputs = x.buildInputs ++ [ makeWrapper ];});
  };
}

Ändra scheduler till deadline

{ config, pkgs, ... }: {
  nixpkgs.config.packageOverrides = self: with self; {
    stdenv = stdenv // {
      platform = stdenv.platform // {
        kernelExtraConfig = ''
          IOSCHED_DEADLINE y
          DEFAULT_DEADLINE y
          DEFAULT_IOSCHED "deadline"
        '' ;
      };
    };
  };
}

NixOS + ck-sources

{ config, pkgs, ... }: {
  boot.kernelPackages = pkgs.linuxPackages_ck_4_0;
  nixpkgs.config.packageOverrides = self: with self;
  let
    ck-sources = (import <nixpkgs/pkgs/os-specific/linux/kernel/generic.nix> {
      inherit perl buildLinux; inherit (linux_4_0) src;
      version = linux_4_0.version + "-ck1";
      stdenv = stdenv // { platform = stdenv.platform // {
          name = "ricer";
      };};
      extraConfig = ''
        SCHED_BFS y
      '';
      kernelPatches = [ ({ patch = fetchurl {
        url = "http://ck.kolivas.org/patches/4.0/4.0/4.0-ck1/patch-4.0-ck1.bz2";
        sha256 = "1lkckz8j5dapmdq7nkaxp541hvflw1ky9gvnsk5s0a440hns6g5y";
      };}) ];
    });
  in { linuxPackages_ck_4_0 = recurseIntoAttrs (linuxPackagesFor ck-sources linuxPackages_4_0); };
}
Ja, denna slajd är för bedrövlig och borde inte visas upp

Vad nu?

Dokumentation

Dokumentationen genereras kontinuerligt men kan tyvärr ibland lagga efter nixpkgs repot

Intressanta nixrepon

Gott å blandat

Att ta med

  • Förståelse för varför utprodukter blir isolerade med Nix
  • Vilka olika steg som inbegrips
  • Att det finns en hel del hjälp att få i nixpkgs
  • Förstå att NixOS bara är en drös moduler
  • Kanske förstå hur man bygger en nixos konfiguration

Tack!

Tillbaka till huvudsida