Skip to content

aciceri/nixos-devops-talk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NixOS per DevOps

Contenuti

  • Cosa sono Nix e NixOS
  • Perche’ NixOS si presta bene per fare DevOps
  • CI/CD con NixOS
  • Un caso d’uso

Disclaimer

  • Ma x lo posso fare anche con y!
  • Il mio approccio con Nix e’ ortodosso, ma nulla vieta un’integrazione solo parziale
  • Molte opinioni personali
  • Sacrifichero’ un po’ precisione a favore di una migliore digeribilita’
  • Certe parti sono scarne, questa presentazione e’ open source, aiutatemi a migliorarla!

Nix

./pics/the-declarative-trinity.webp

Nix come linguaggio

Nix e’ un DSL funzionale lazy pensato per restituire una struttura chiamata **derivazione**

Derivazione

stdenv.mkDerivation {
  name = "hello";

  src = ./src;

  buildInputs = [ coreutils gcc ];

  buildPhase = ''
    gcc "$src/hello.c" -o ./hello
  '';
  installPhase = ''
    mkdir -p "$out/bin"
    cp ./hello "$out/bin/"
  '';
}
  • la sintassi ricorda JSON, ma e’ Turing completo! (condizioni, funzioni, etc…)
  • stdenv.mkDerivation wrappa builtins.derivation

Quando l’evaluator Nix valuta una derivazione crea un file .drv

{
  "/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv": {
    "outputs": {
      "out": {
        "path": "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
      }
    },
    "inputSrcs": [],
    "inputDrvs": {},
    "platform": "mysystem",
    "builder": "mybuilder",
    "args": [],
    "env": {
      "builder": "mybuilder",
      "name": "myname",
      "out": "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname",
      "system": "mysystem"
    }
  }
}

Build della derivazione

Il file drv contiene tutte le informazioni necessarie per costruire l’output della derivazione.

Se la derivazione dipende da altre derivazioni prima vengono buildate queste.

Input addressed vs content addressed

Input addresssed: l’output della derivazione finira’ in un path che contiene un hash ottenuto partendo alcune informazioni tra cui il nome, la versione, il builder e i nomi degli input (quindi dai loro hash).

In questo modo e’ possibile sapere l’output di una derivazione prima di buildarla.

Le build avvengono in ambienti sandboxed senza accesso al sistema (in particolare a internet), in sostanza dipendono solo dalle derivazioni input.

Come possiamo buildare qualcosa se non possiamo scaricarne i sorgenti?

Fixed output: se siamo in grado di esplicitare l’hash del contenuto nel file drv (prima che avvenga la build) allora la sandbox dove avverra’ la build avra’ accesso a internet (git, curl, etc…)

Corollario: risalendo il grafo delle dipendenze di una derivazione alla fine troveremo sempre derivazioni fixed output.

Sono dette anche content addressed.

Demo

https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/games/umoria/default.nix

nix derivation show nixpkgs#umoria
nix build nixpkgs#umoria -L --rebuild

Nix store

Dove finiscono gli output delle derivazioni?

./pics/store.webp

Dipendenze e closure

Nix quindi e’ in grado di gestire contemporaneamente diverse versioni delle stesse dipendenze.

Potremo per esempio avere sullo stesso sistema versioni diverse di uno stesso software che pero’ richiedono dipendenze differenti (dependency hell).

Data una derivazione, e’ possibile considerare la chiusura rispetto alla relazione di dipendenza di tale derivazione, ovvero la closure.

Pertanto possiamo copiare un programma con tutte le sue dipendenze da una macchina all’altra semplicemente copiando la closure.

Cache

./pics/caches-are-fast.png

Nix prima di buildare una derivazione (un file drv) interroga un substituter (detto anche cache) ed eventualmente scarica l’output della derivazione.

Cio’ e’ possibile poiche’ gli output sono (generalmente) input addressed.

NixOS

E se l’intero sistema operativo fosse l’output di una derivazione?

Caratteristiche

In ordine sparso:

  • dichiarativo
  • Linux
  • usa systemd
  • puo’ essere rolling release (in realta’ fanno release)

Module system

Nix non e’ strong typed, i moduli aggiungono un type system dentro il linguaggio stesso.

Sono estremamente componibili.

Sostanzialmente un modulo fa due cose:

  • dichiara opzioni (nomi, tipi, valori di esempio, valori di default, documentazione, etc…)
  • imposta altre opzioni (di altri moduli) partendo dai valori delle sue opzioni

Flakes

./pics/fleyks.png

Il linguaggio diventa veramente puro (e.g. non puo’ leggere path fuori dallo store).

Forniscono un modo unificato di dichiarare le dipendenze dei propri progetti ed interagire con essi da CLI.

Schema di un flake:

{ self, ... }@inputs:
{
  # Executed by `nix flake check`
  checks."<system>"."<name>" = derivation;
  # Executed by `nix build .#<name>`
  packages."<system>"."<name>" = derivation;
  # Executed by `nix build .`
  packages."<system>".default = derivation;
  # Executed by `nix run .#<name>`
  apps."<system>"."<name>" = {
    type = "app";
    program = "<store-path>";
  };
  # Executed by `nix run . -- <args?>`
  apps."<system>".default = { type = "app"; program = "..."; };

  # Formatter (alejandra, nixfmt or nixpkgs-fmt)
  formatter."<system>" = derivation;
  # Used for nixpkgs packages, also accessible via `nix build .#<name>`
  legacyPackages."<system>"."<name>" = derivation;
  # Overlay, consumed by other flakes
  overlays."<name>" = final: prev: { };
  # Default overlay
  overlays.default = final: prev: { };
  # Nixos module, consumed by other flakes
  nixosModules."<name>" = { config, ... }: { options = {}; config = {}; };
  # Default module
  nixosModules.default = { config, ... }: { options = {}; config = {}; };
  # Used with `nixos-rebuild switch --flake .#<hostname>`
  # nixosConfigurations."<hostname>".config.system.build.toplevel must be a derivation
  nixosConfigurations."<hostname>" = {};
  # Used by `nix develop .#<name>`
  devShells."<system>"."<name>" = derivation;
  # Used by `nix develop`
  devShells."<system>".default = derivation;
  # Hydra build jobs
  hydraJobs."<attr>"."<system>" = derivation;
  # Used by `nix flake init -t <flake>#<name>`
  templates."<name>" = {
    path = "<store-path>";
    description = "template description goes here?";
  };
  # Used by `nix flake init -t <flake>`
  templates.default = { path = "<store-path>"; description = ""; };
}

nixpkgs

Contiene sia pacchetti (derivazioni) che moduli di NixOS.

https://github.com/nixos/nixpkgs

https://repology.org/repositories/graphs

https://search.nixos.org/

Deploy

nixos-rebuild in realta’ permette anche il deploy remoto.

nixos-rebuild switch --flake github:aciceri/nixfleet#kirk --target-host X.X.X.X --build-host Y.Y.Y.Y

Esistono numeri altri tool:

  • nixops
  • colmena
  • deploy-rs
  • altri

E se volessimo generare immagini per provider cloud specifici?

  • AWS
  • Google Cloud
  • Azure
  • molti altri

https://github.com/nix-community/nixos-generators

Cosa non risolve Nix(OS)

Niente.

Dico davvero.

Ok, quasi niente.

NixOS non e’:

  • Terraform
  • Kubernetes
  • ???

CI/CD

Se i progetti su cui lavorano gli sviluppatori sono flake, la CI puo’ semplicmente buildare un sottoinsieme dei suoi output (packages, checks, devShells, etc…). I progetti non richiedono nessuna configurazione ad hoc per abilitare la CI.

Se lo store del sistema di CI persiste avremo build molto veloci. Altrimenti e’ possibile istruire la CI a pushare cio’ che builda su una cache (e interrogarla prima di buildare).

Se esponiamo questa cache agli sviluppatori minimizzeremo di molto il tempo di build in locale.

Esistono diverse soluzioni:

  • GitHub actions (esistono actions per installare Nix e configurarlo per usare una cache)
  • Hydra (CI ufficiale di nixpkgs, compatibile con diverse forgie)
  • Hercules CI
  • molti altri…

Il continuous deployment viene facile, basta eseguire uno dei tool visti prima in CI.

Esistono soluzioni piu’ strutturate per gestire il deploy di sistemi multipli (come Hercules CI).

Alternativamente si puo’ anche istruire il sistema stesso ad aggiornarsi da solo:

system.autoUpgrade = {
  enable = true;
  flake = "github:aciceri/nixfleet#${config.networking.hostName}";
  dates = "daily";
  allowReboot = false;
};

Chicche

Cross-compilazione

Nix permette di “cross-buildare” le derivazioni in due modi diversi, e funzionano entrambi molto bene.

  • vera cross-compilazione
  • binfmt (QEMU)

Docker

Nix e’ probabilmente un migliore build system per Docker di Docker stesso.

  • miglior caching (no “layering lineare”)
  • vera riproducibilita’
  • le immagini Docker non sono altro che derivazioni Nix
  • accesso a tutto nixpkgs (quantita’ spropositata di pacchetti)
  • cross-building migliore (docker buildx 🤮)

Segreti

./pics/segreto.png

Come possiamo avere segreti se le derivazioni nello store sono leggibili da tutti?

Criptiamoli!

  • agenix
  • sops-nix

Mettiamo nello store solo i segreti criptati e installiamo degli script che li decriptino a runtime.

Questo richiede comunque un meccanismo per distrubuire la chiave privata in fase di deploy.

Disko

NixOS fornisce opzioni che definiscono i punti di mount del sistema, ma durante l’installazione queste partizioni devono essere create manualmente.

Disko e’ un modulo NixOS che permette di dichiarare come devono essere create le partizioni (dimensione, file system, encryption, etc…)

Tale modulo fa due cose:

  • setta i punti di mount (settando le opzioni di NixOS che altrimenti sarebbero state settate manualmente i.e. fileSystems)
  • definisce uno script che formatta automaticamente

Impermanence

NixOS e’ stateless?

Purtroppo no…

Ma possiamo domare lo stato!

https://github.com/nix-community/impermanence

  • e’ un modulo NixOS
  • tmpfs come / (tranne /boot e /nix)
  • elenchiamo tutto cio’ che deve persistere (e lo mettamo in una partizione seperata)
  • ci forza a sapere dove si trova lo stato delle nostre applicazioni, quindi sappiamo cosa backuppare

NixOS anywhere

Il mio cloud provider non supporta NixOS… 😭

https://github.com/nix-community/nixos-anywhere

Per gli amanti di terraform esiste un anche un interessante modulo.

Nix pizza

Questa presentazione

  • e’ un flake
    nix build github:aciceri/nixos-devops-talk
        
  • e’ scritta in org-mode
  • buildata con Nix
  • pubblicata su GitHub Pages usando Hercules CI

Repository

https://github.com/aciceri/nixos-devops-talk

./pics/qr.png

Domande

Non troppo difficili per favore!