Nixos.org

Aus lugvswiki
Version vom 26. Mai 2022, 17:00 Uhr von Marc (Diskussion | Beiträge) (Beispiel Haskell development mit NixOS)
Zur Navigation springenZur Suche springen

Einführung in nixos.org

Paketliste: https://search.nixos.org/packages oder Overlays auf Github (unterschiedliche Qualität) Webseite: https://nixos.org Dokumentation: https://nixos.org/manual/nixos/stable/

Zusammenfassung: Nixos basiert auf dem Nix Paket manager welcher es erlaubt deterministisch also immer gleich Abhängigkeiten zu definieren. Wenn sich eine Abhängigkeit ändert ändert sich der Paktetpfad (ähnlich einem Git-Hash) und alle darauf basierenden Abhängigkeiten bekommen neue Pfade (Versionen). Vorteil: Gleicher Pfad gleiches Verhalten, dh.h auch Bisecting z.B mit git möglich ! Nachteil: Bei kleinen Änderungen muss viel neu heruntergeladen werden.

Pakete werden durch Garbage Collector Roots (Fäden) vor dem Garbage Collector (Müllschlucker) bewart. D.h. solange ein Paket über einen Symlink über eine User Installation oder über eine System-Konfiguration noch erreicht werden kann wird das Paket nicht gelöscht.

PATH etc werden über spezielle User Profiles gemanaged und ersetzen /user/local/bin usw.

Damit sind atomic Upgrades möglich durch switchen von Symlinks.

Nixos erlaubt es ein Paket mit Abhängigkeiten zu archivieren (Nar Archive) und offline woanders zu importieren/installieren.

Auch können Pakete mit Abhängkeiten über nix-store --copy-closure differentiell kopiert werden.

Pakete und Systeme können also ohne das laufende System zu stören auf mehreren PCs verteilt je nach Architektur auf Build-Slaves vorbereitet werden. Jedes Paket wird in einer Sandbox (chroot) installiert, so dass jedes Paket nur seine eigenen Abhängigkeiten sieht. Damit wird erreicht dass jede Installation soweit möglich bis auf Kleinigkeiten wie Zeit bzw CPU Architektur oder RAM Größe das gleiche reproduzierbare Verhalten zeigt.

Zum Beispiel wenn man was unter Ubuntu kompiliert kann das configure Ergebnis von anderen installierten aber nicht relevanten Paketen und Header Dateien abhängen. Dh. 2 Ubuntu Installationen können verschiedenes Verhalten produzieren.

Dadurch dass jedes Paket mit jeder Version seinen eigenen /nix/store/... Namen mit Hash hat kann man mehrere Generationen gleichzeitig auf der gleichen Partition installiert haben und beim Boot-Prozess auswählen welche Version man will. Dann wird /etc/ gemäß des im Store vorbereiteten Systems angepasst. Bootet man eine alte Version mit altem MySQL kann diese allerdings mit einem aktualisierten /var/db/.. mysql Verzeichnis nichts mehr anfangen. Aber deswegen gibt es ja z.B BTRFS.

Am besten erklährt man die Vorteile der configuration.nix welche Hardware Profile laden kann an einem Beispeil, weswegen auch der Installationsprozess eine Hardware Datei und eine Sytem-Configurations-Datei erstellt.

Es demonstriert deutlich wie einfach es ist

  • Dokmuentation
  • Verhalten
  • Software (environment.systemPackages)
  • Cron-Jobs z.B. Backups

in wenigen Dateien übersichtlich zu konfigurieren und zu duplizieren aber auch an den PC anzupassen. Leider muss man bei Thunderbird z.B. immer noch manuell Sprachpakete nachintsallieren, weil jene in ~/.thunderbird gespeichert werden.

z.B. können so auch BTRFS Snapshots einmal pro Tag automatisch konfiguriert werden.

Die Installation kann theoretisch vereinfacht werden indem man ein Shell Script mit curl läd welches /etc/nixos/configuration.nix oder module runterläd bzw die Festplatte formatiert.

Durch den inkrementellen Aufbau der Pakete + einrichten von /etc/* beim Booten können auch abgebrochene Installationen fortgesetzt werden. Es werden nur die noch fehlenden noch nicht im /nix/store vorhandenen Pakete identifiziert und installiert.

Gleichzeitig kann man durch sogenannte derivations eigene Software z.B. von Github konfigurieren und installieren.


# beispiel eigenes Software Paket

name = mkDerivation {
  name = "mein-paket-von-github"
  src = fetchGithub { # z.B. mit nix-prefetch-git holen
    owner = "...";
    name = "...";
    hash = ".."
  };
  buildDepends = [ readline ];
  meta = { .. }
}
# fertig details bitte Orignial-Dokumentation nachschauen.

Wenn man also wie hier gezeigt /home und / auf unterschiedlichen BTRFS subvolumes hat kann man System/Home unabhängig bei Bedarf zurücksetzen *EGAL* was der Benutzer gemacht hat. Ausfallzeiten länger als 5min eigentlich kaum möglich ausser man Hardware-Probleme.

Allerdings muss man regelmäßig alte Snapshots löschen (und PC bischen Zeit geben). Das ist aber planbar.

Deswegn ist das alles ein ziemlich idiotensicheres Setup.

Wenn wirklich Interesse besteht müsste man das alles nochmal testen. Aber als Idee / Vorstellung ist dies schonmal gut.

# shell script zum formatieren und installieren von Nixos nach booten von CD

DEV=nbd0
ENCRYPT=none
SWAP_SIZE=30GiB
BOOT_SIZE=512MiB
# partition prefix
P=p
# BOOT_SIZE=1GiB
MOUNT_POINT=/mnt


set -x
parted /dev/${DEV} -- mklabel gpt
parted /dev/${DEV} -- mkpart primary $BOOT_SIZE -$SWAP_SIZE
parted /dev/${DEV} -- mkpart primary linux-swap -$SWAP_SIZE 100%
parted /dev/${DEV} -- mkpart primary 1MiB $BOOT_SIZE -$SWAP_SIZE 100%
parted /dev/${DEV} -- mkpart ESP fat32 1MiB $BOOT_SIZE
parted /dev/${DEV} -- set 3 boot on

case $ENCRYPT in
  none)
    mkfs.btrfs /dev/${DEV}${P}1
    ROOT_DEVICE=/dev/${DEV}${P}1
    ;;
  luks)
    MAPPER_NAME=${DEV}${P}1
    cryptsetup --verbose --verify-passphrase luksFormat /dev/${DEV}${P}1
    cryptsetup luksOpen /dev/${DEV}${P}1 ${MAPPER_NAME}
    mkfs.btrfs /dev/mapper/${MAPPER_NAME}
    ROOT_DEVICE=/dev/mapper/${MAPPER_NAME}
    ;;
esac
mkswap -L swap /dev/${DEV}${P}2
mkfs.fat -F 32 -n boot /dev/${DEV}${P}3
mount $ROOT_DEVICE $MOUNT_POINT
mkdir /mnt/boot
mount /dev/${DEV}${P}3 $MOUNT_POINT/boot
nixos-generate-config --root $MOUNT_POINT
# sed -i 's/# \(boot.loader.grub.device\)/\1/' $MOUNT_POINT/etc/nixos/configuration.nix
nixos-install --root $MOUNT_POINT

#configuration.nix
{pkgs , config, ...}:
let
inherit (pkgs) lib;
inherit (lib) mkMerge mkOverride;
in
{

  imports =
    [
         ./disk-configuration.nix
         ./shared-configuration.nix
         ./hardware-configuration.nix
    ];
  config = {
    boot.loader.grub.configurationName = "x68_64";
  };

}

shared-configuration.nix
{pkgs , config, ...}:

let

  inherit (pkgs) lib;
  inherit (lib) mkMerge mkOverride;

in

{

  imports =
    [
    ];


  config = {
        nixpkgs.config.allowBroken = true;

        hardware.pulseaudio.enable = true;
        networking.networkmanager.enable = true;
        networking.firewall.enable = true;
        networking.firewall.allowedTCPPorts = [];

      # networking.useDHCP = true;

        programs.bash.promptInit = "";
        environment.pathsToLink = ["/"];

        environment.sessionVariables = { TERMINFO_DIRS = "/run/current-system/sw/share/terminfo"; };

        i18n.defaultLocale = "de_DE.UTF-8";
        time.timeZone=  "Europe/Berlin";

#	services.blueman.enable = true;
        environment.systemPackages = [
          pkgs.iotop
          pkgs.pciutils
          pkgs.gwenview
pkgs.evince
pkgs.okular
# pkgs.acroread
         # ] ++ (attrValues pkgs.plasma5) ++ [
          #  pkgs.plasma-nm
          pkgs.bluedevil

          pkgs.vim
          pkgs.kate
          pkgs.firefox




          # /backup/mount-disk kann durch /volumes ersetzt werden
          # /backup/references eventuell auf /volumes verschieben ?
          ( pkgs.writeScriptBin "erstelle-backup-externe-festplatte" ''
          #!/bin/sh -e
          set -x
          mkdir -p /backup/mount-disk
          mkdir -p /backup/mount-backup
          mkdir -p /backup/references

          mount /dev/sda3 /backup/mount-disk
          mount /dev/disk/by-uuid/1b6a7b15-f6bd-4c0c-b735-aaf928ee79db /backup/mount-backup -o compress=zstd:1

          for subvol in home system; do
            echo backup hup $subvol
            btrfs-backup-nach.sh "benutzer-backup-$subvol" "/backup/mount-disk/$subvol" "/backup/references/benutzer-backup-$subvol" /backup/mount-backup
          done

          umount /backup/mount-disk
          umount /backup/mount-backup

          echo "BACKUP FERTIG"
          '')

          ( pkgs.writeScriptBin "btrfs-backup-nach.sh" ''
#!/bin/sh -e
set -o pipefail

# mount: mount point
# target: the name of the external disk
# for each name a reference is kept so that next backup is differential and fast

target=$1
mount=''${2:-/backup}

# identify that the target destination is the expected one.
# this is done by expecting backup-dummy$target filename to be there
dummy="$mount/backup-dummy$target"

[ -n "target" ] || { echo "usage: ./backup.sh external-disk /backup "; exit 1; }

[ -f "$dummy" ] || {
        echo "$dummy file to identify backup location not found. Shall I touch it? [y/N]"
        read reply
        if [ "$reply" == "y" ]; then
                touch "$dummy"
        else
                echo 'failing'; exit 1
        fi
}

backup(){
  local name="$1"
  local from="$2"
  # where to store subvolumes and keep last for incremental update
  local dir_in_from="$3"
  local to="$4"

  now=$(date '+%Y-%m-%d_%H%M%S')

  mkdir -p "$dir_in_from"

  if [ -f "$dir_in_from/last-backup-''${name}" ]; then
    local last=$(cat "$dir_in_from/last-backup-''${name}" 2>/dev/null || true)
    echo 'snapshotting from'
    btrfs subvolume snapshot -r "$from" "$dir_in_from/$name-$now"
    echo 'updating target'
    btrfs send -p "$dir_in_from/$last" "$dir_in_from/$name-$now" | btrfs receive "$to"
    echo 'deleting old reference'
    btrfs subvolume delete "$dir_in_from/$last"
    echo "new snapshot is is $name-$now"
    echo "$name-$now" > "$dir_in_from/last-backup-''${name}"
  else
    echo "creating initial snapshot as reference"
    btrfs subvolume snapshot -r "$from" "$dir_in_from/$name-$now"
    echo "sending initial data .. might take long"
    btrfs send "$dir_in_from/$name-$now" | btrfs receive "$to"
    echo "$name-$now" > "$dir_in_from/last-backup-''${name}"
  fi
}

backup "$@"
          ''
          )

          ( pkgs.writeScriptBin "nixos-aktualisieren" ''
             #!/bin/sh
             set -e
             set -x
             sudo erstelle-snapshots
             sudo nix-channel --update
             sudo nixos-rebuild boot
             echo 'Aktualisierung fertig bitte neu starten. Thunderbird Extensios -> Deutsche Übersetzungen neu installieren'
          ''
          )

          ( pkgs.writeScriptBin "erstelle-snapshots" ''
             #!/bin/sh
             set -x
              echo "system"
              ${pkgs.btrfsProgs}/bin/btrfs subvolume snapshot -r / /subvolumes/system-$(${pkgs.coreutils}/bin/date +'%Y-%m-%d_%H%M%S')
              echo "home"
              ${pkgs.btrfsProgs}/bin/btrfs subvolume snapshot -r /home /subvolumes/home-$(${pkgs.coreutils}/bin/date +'%Y-%m-%d_%H%M%S')
              echo "fertig"
          ''
          )

          (
            pkgs.writeScriptBin "hilfe" ''
            #!/bin/sh
            ${pkgs.utillinux}/bin/more <<EOF
            Bei Problemen -> LUGVS Mailinglist

            User Linux Group Villingen Schwenningen einmal pro Monat
                Siehe https://lug-vs.org/lugvswiki/index.php/Hauptseite,
                Die kennen sich jedoch mit Nixos.org Linux nicht aus

                Haben auch Mailingliste

            Chat Hilfe zu Nixos (English)
                Terminal eingaben irssi
                Dann eingeben /connect irc.liberachact.net
                Dann eingeben /join #nixos

            Remote Desktop sharing (Support)
                anydesk nutzen

            System aktualisieren (kann nichts passieren, weil alte Versionen bleiben bestehen)
                sudo nixos-aktualisieren
                Eventuell muss bei Thunderbird hinterher Deutsche Sprachpakete
                neu installiert werden.


            Backups (gleiche Festplatte mit BTRFS)
                sudo erstelle-snapshot eingeben

                Backups liegen unter /subvolumes
                Schützt nicht vor Festplatten-Crash / versagen

            Backup externe Festplatte (btrfs send) empfohlen:
                sudo erstelle-backup-externe-festplatte


            System aufräumen (alte Versionen löschen - also nur wenn neue Version geht)
                sudo nix-collect-garbage -d
                Wenn danach Anwendungen nicht mehr korrekt funktionieren eventuell in
                ~/.config/* Dateien löschen und erneut probieren
                (vorher sudo erstelle-snapshot ausführen)

            iphone mounten (wegen Bildern)
                google ifuse. z.B. 
                sudo ifuse /mnt

            Android mounten 
                sudo mtpfs /mnt

            Drucken -> Cups kann eingerichtet werden. Wenns aber nur 1-2 Seiten
                sind kurz zu Frau Burger gehen oder zu meiner Mutter, PDF mitbringen.

                  Drucken -> "Print To File" oder "In Datei Drucken"
                  PDF Speicherort angeben.

              Zum Enrichten langfristig:
                  sudo nano /etc/nixos/shared-configuration.nix
                  dort siehe printing (Treiber müssen hinzugefügt werden)



            Manuell Backups externes Medium:
                Externe Festplatte ist besser weil Festplatte kaputt gehen kann
                (einfach manuell kopieren, gibt auch viele Tools)

                Beispiel
                mount /dev/sdb1 /mnt
                rsync -ra --delete /home/benutzer/ /mnt/backup-home-benutzer/
                umount /mnt

            System-Check
                smartmontools ist installiert

                Dateisystem:
                  sudo btrfs scrub start /dev/sda
                  # bischen warten..
                  # dann immer wieder bis fertig ist:
                  sudo btrfs scrub status /dev/sda

                Paket-Check
                  sudo nix-store --verify --check-contents

                Festplatte Eigenauskunft S.M.A.R.T auslesen
                  sudo smartctl -a /dev/sda

                  Die werte sollten über den TRESH werten liegen.

                Memory (beim booten memtest wählen)

            Habe-Ich-Internet-Test
                ping www.gmx.de

            root werden:
                sudo anwendung
                sudo su -> wieder shell

            USB Stick (oder Kamera)
                mount /dev/sdb1 /mnt
                .. Daten kopieren
                umount /mnt

                Statt sda1 eventuell sdb2 (zweite Partition) oder sdbX für zweiten USB Stick etc

                Partitionen (USB Stick) finden:
                sudo fdisk -l
                auf Grösse achten

                Können das auch als Script automatisieren oder udev-rule

            Programme:
                audacity: Audio
                scalc (Tabellen)
                simpress (Präsentationen)
                gimp: Bilder
                Krita: Malen
                Firefox: Internet
                Thunderbird: E-Mails
                Blender: 3d test und 3d Malen, 3d Animation und mehr
                mplayer/vlc: videos und Mediadateien aufmachen + Streaming
            EOF
            ''
            )

          pkgs.irssi
          pkgs.utillinux
          pkgs.nettools
          # pkgs.firefox
          pkgs.chromium
          pkgs.libreoffice
          pkgs.audacity
          pkgs.krita
          pkgs.inkscape
          pkgs.gimp
          pkgs.thunderbird
          pkgs.gnome3.nautilus
          pkgs.ifuse # mount iphone
          pkgs.mtpfs # mountandroid
          pkgs.anydesk # Es funktioniert einfach...
          pkgs.smartmontools
          pkgs.blender
          pkgs.mplayer
          pkgs.vlc
        ];

        services.printing = {
          enable = true;
          # drivers = [ pkgs.hlip ];
        };

        programs.zsh.enable = true;
        programs.zsh.promptInit = ''
          if [ "$TERM" != dumb ]; then
            # prompt walters
            autoload -U promptinit && promptinit 
          fi
        '';

        users.users.benutzer = {
          isNormalUser = true;
          home = "/home/benutzer";
          description = "benutzer";
          extraGroups = [ "wheel" "networkmanager" ];
          # openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ];
        };


      environment.etc."nix/machines".target = "nix/machines.sample";

      fileSystems."/tmp" = { device = "tmpfs"; fsType = "tmpfs"; options = ["size=20000m" "mode=1777"]; };

      fonts.fontconfig.enable = true;
      fonts.enableFontDir = true;
      fonts.fonts = [ ];

      nixpkgs.config.permittedInsecurePackages = [
        "openssl-1.0.2u"
      ];
      nixpkgs.config.allowUnfree = true;
      nixpkgs.config.android_sdk.accept_license = true;
      nixpkgs.config.ruby.tags = true;
      # nixpkgs.config.packageOverrides = p: {



       nix = {
          package = pkgs.nixUnstable;
          daemonNiceLevel = 20;
          useSandbox = true;
       };

      programs.ssh.startAgent = false;
      programs.gnupg.agent.enable = true;

      # services.cron.systemCronJobs =
      #   let
      #     backupscript = pkgs.writeScriptBin "backupscript" ''
      #     #!/bin/sh
      #     ${pkgs.btrfsProgs}/bin/btrfs subvolume snapshot / /root/root-of-fs-$(${pkgs.coreutils}/bin/date +'%Y-%m-%d_%H%M%S')
      #     '';
      #   in
      #     [
      #       "0 10,15 * * * root ${backupscript}"
      #     ];


      services.nfs.server.enable = true;

      services.ntp.enable = true;

      # services.openssh = {
      #   enable    = true;
      #   allowSFTP = true;
      #   # startOn  = "never";
      #   passwordAuthentication = true;
      # };


      security.sudo = {
        enable = true; 
        configFile = mkOverride 20 ''
          root        ALL=(ALL) SETENV: ALL
          benutzer    ALL=(ALL) NOPASSWD: ALL

          # Users in the \"wheel\" group can do anything.
          # %wheel      ALL=(ALL) SETENV: ALL
          ''; 
      };

      documentation.nixos.enable = false;  # takes too much time to build

      services.xserver.enable = true;
      services.xserver.autorun = true;
      services.xserver.layout = "de";

      services.xserver.wacom.enable = true;
      services.xserver.enableTCP = true;
      services.xserver.resolutions = [];
      # services.xserver.windowManager.default = "wmii";

      services.xserver.desktopManager.plasma5.enable = true;
      services.xserver.desktopManager.lxqt.enable = true;

      systemd.services.nix-daemon.serviceConfig.IOSchedulingClass = "idle";
      systemd.services.nix-daemon.serviceConfig.CPUShares = 123;
      systemd.services.nix-daemon.serviceConfig.MemoryLimit = "10G";
      # users.defaultUserShell = "/var/run/current-system/sw/bin/zsh";
  };

}


# hardware-configuration.nix
# vom Installationscript erstellt
{ config, lib, pkgs, ... }:

{
  imports =
    [ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
    ];

  boot.initrd.availableKernelModules = [ "btrfs" "ehci_pci" "ahci" "usb_storage" "sd_mod" "sr_mod" "rtsx_usb_sdmmc" ];
  boot.kernelParams = [ ];
  # boot.kernelPackages = pkgs.linuxPackages_testing; # oder 20
  boot.blacklistedKernelModules = [ ];

  powerManagement = {
    enable = true;
    cpuFreqGovernor = "ondemand";
  };

  # services.logind.lidSwitch = "ignore";
  # services.xserver.videoDrivers = [ "ati_unfree" ];
  # boot.kernelPackages = pkgs.linuxPackages_latest;
  # boot.kernelParams = [ "amd_iommu=pt" "ivrs_ioapic[32]=00:14.0" "iommu=soft" ];
  # services.xserver.videoDrivers = [ "amdgpu" ];
  # services.xserver.videoDrivers = [ "ati_unfree" ];
  hardware.firmware = [pkgs.firmwareLinuxNonfree pkgs.sof-firmware];
  hardware.cpu.amd.updateMicrocode = true;
  hardware.enableAllFirmware = true;
  # hardware.enableRedistributableFirmware = true;
  # hardware.opengl.enable = true;
  # hardware.opengl.driSupport = true;
  hardware.opengl.driSupport = true;
  # programs.light.enable = true;
}


Zusammenfassung:

Eine fast automatische Installation wäre so möglich:

# iso runterladen
# booten
# sudo su

# automatisch formatieren und configuration.nix Template anlegen
DEV=/dev/sda
curl https://script-noch-zu-erstellen-was-foramtiert-dann-mounted-dann-configuration.nix-anlegt > /tmp/script
. /tmp/script
# nixos-hardware-scan
# nixos-install ..
# shutdown -r now

Das Sytsem hätte dann - lugvs dokumentation mit hilfe-lugvs aufrufbar oder ähnlich auch was vieles weitere erinnert - regelmäßige Snapshots - ein Backup Script für externe Festplatte inkrementell mit BTRFS - einfache Update Scripte um sich selbst solange zu aktualisieren bis sich die

 Wörter in configuration.nix verändern. Manchmal kommt was dazu oder wird
 umbenannt.

Aber ein Crash oder halbfertiges System beim Benutzer (Update-Probleme) sind passieren nicht mehr oder sind zurückstellbar in kalkulierbaren 5 Minuten.


Wie geht nutzt man z.B. Python oder Haskell mit NixOS?

cabal2nix_init(){
  local dir=$(basename `pwd`)
  local defaultGHC=${1:-ghc921}

$(nix-build -A cabal2nix $NIXPKGS_ALL)/bin/cabal2nix . > $dir.nix

cat > default.nix << EOF
{ nixpkgs ? import <nixpkgs> {}, compiler ? "${defaultGHC}" }:
nixpkgs.haskell.packages.\${compiler}.callPackage ./$dir.nix { }
EOF

cat > shell.nix << EOF
{ nixpkgs ? import <nixpkgs> {}, compiler ? "${defaultGHC}" }:
(import ./default.nix { inherit nixpkgs compiler; }).env
EOF

# nix-shell -A env ./default.nix

das cabal2nix_init Script erstellt einige .nix Dateien die dann genutzt werden um die Abhängikgeiten im Store bereitzustellen. Dann kann mit nix-shell eine Shell bereitgestellt werden wo ghc ghc-pkg cabal etc vorhanden sind und jene finden. D.h. einem ./Setup configure && ./Setup build steht nichts mehr im Wege.

Das geht meistens gut - manchmal leider auch nicht.

Ähnliche Tools gibts für Ruby, Python ..

Auf die Art und Weise lassen sich auch derivations generieren so dass man Software über environment.systemPackages schnell installieren kann.

Nach meiner Meinung ist das alles einfacher als Pakete für Debian / Suse bereitzustellen (wenns klappt)