Le Blog Utux

HTTP 200 GET /

NixOS : la distribution déclarative

Rédigé par uTux Aucun commentaire

Si vous êtes familier avec l'écosystème des distributions Linux, vous avez probablement levé un sourcil (comme Teal'c) en lisant ce titre car vous les connaissez toutes, vous avez touché à tous les gestionnaires de paquet, vous avez utilisé debian et gentoo, en bref vous avez fait le tour et plus rien ne nous surprend.

Et pourtant, bien que NixOS soit une distribution assez ancienne (2003) elle dispose de nombreux atouts inédits passés plutôt inaperçus jusqu'à présent.

NixOS logo

La configuration déclarative centralisée

Ce que je trouve le plus intéressant dans NixOS, c'est la configuration centralisée dans un unique fichier. En effet si vous avez déjà travaillé sur des routeurs ou diverses appliances, vous avez remarqué que l'on peut souvent importer et exporter la configuration sous forme de texte assez facilement, cela rend la maintenance très facile. Sous NixOS c'est le même principe, mais en plus puissant puisqu'on peut rollback voire booter sur une ancienne configuration depuis grub. Exemple de configuration d'un serveur MariaDB :

/etc/nixos/configuration.nix

{ config, pkgs, ... }:

{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
    ];

  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  networking = {
    hostName = "mariadb";
    nameservers = [ "192.168.0.31" ];
    defaultGateway = "192.168.0.254";
    interfaces.enp0s3.ip4 = [
      {
        address = "192.168.0.41";
        prefixLength = 24;
      }
    ];
  };

  # Select internationalisation properties.
  i18n = {
    consoleFont = "Lat2-Terminus16";
    consoleKeyMap = "fr";
    defaultLocale = "fr_FR.UTF-8";
  };

  # Set your time zone.
  time.timeZone = "Europe/Paris";

  # List packages installed in system profile. To search by name, run:
  # $ nix-env -qaP | grep wget
  environment.systemPackages = with pkgs; [
    git
    htop
    sudo
    tree
    vim
  ];

  # Services
  services = {
    openssh = {
      enable = true;
      permitRootLogin = "yes";
    };
    mysql = {
      enable = true;
      package = pkgs.mysql;
      extraOptions = ''bind-address=0.0.0.0'';
    };
  };

  # Open ports in the firewall.
  networking.firewall.allowedTCPPorts = [ 22 3306 ];
  # networking.firewall.allowedUDPPorts = [ ... ];

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.extraUsers = {
    utux = {
      isNormalUser = true;
      extraGroups = [ "wheel" ];
    };
  };

  # The NixOS release to be compatible with for stateful data such as databases.
  system.stateVersion = "17.03";

}

/etc/nixos/hardware-configuration.nix

# Do not modify this file!  It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations.  Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:

{
  imports = [ ];

  boot.initrd.availableKernelModules = [ "virtio_pci" "ahci" "xhci_pci" "sr_mod" "virtio_blk" ];
  boot.kernelModules = [ ];
  boot.extraModulePackages = [ ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/78634ba0-11d1-4f91-85ae-ac2ee247c387";
      fsType = "xfs";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/019A-1A05";
      fsType = "vfat";
    };

  swapDevices =
    [ { device = "/dev/disk/by-uuid/075a27eb-5656-4b57-b186-73a6d86e5e5c"; }
    ];

  nix.maxJobs = lib.mkDefault 1;
}

Comme vous le voyez, le fichier configuration.nix contient toute la configuration, incluant les services tiers tels que mariadb dans notre cas. Cela va donc encore plus loin que le /etc/rc.conf sur FreeBSD/NetBSD/OpenBSD qui centralise déjà pas mal de choses. Le fichier hardware-configuration.nix lui est généré automatiquement il n'y a pas besoin d'y toucher et il est plus ou moins unique par serveur.

Pour générer et appliquer la configuration :

nixos-rebuild switch

Pour mettre à jour le système :

nixos-rebuild switch --upgrade

Puis un petit mysql_secure_installation la première fois pour préparer notre SGBD.

Ce qu'il reste à faire en dehors du configuration.nix, c'est la définition des mots de passe, avec passwd et bien sûr la gestion des données persistantes (les bases de données pour mariadb par exemple).

Nix, gestionnaire de paquets fonctionnel

Je vais être un peu plus prudent sur ce point, car étant encore en phase de découverte de NixOS, je ne connais pas encore très bien le gestionnaire de paquets Nix. Cependant, à la différence des gestionnaires classiques tels que apt, yum ou pacman, il ne se contente pas d'aller chercher des paquets pour les décompresser. Chaque version de chaque paquet est installé dans une arborescence /nix/store/{identifiant unique}, du coup plusieurs versions peuvent cohabiter ensemble et les mises à jour n'écrasent rien. Il est possible également pour les utilisateurs d'installer des paquets pour leur environnement (non-root) uniquement.

Mon avis

J'ai mis un serveur NixOS en test et il est trop tôt pour en tirer des conclusions. Mais j'aime l'idée de configuration centralisée déclarative, car la configuration classique des systèmes Linux n'est pas toujours simple à maintenir : Docker, Ansible, NixOS : le savoir (re)faire.

Si je dois citer deux inconvénients à NixOS : elle prend de la place (1,6GB à l'installation avec MariaDB) et elle nécessite au moins 1GB de RAM pour s'installer sous peine de voir oomkiller tuer nixos-install... elle peut cependant tourner avec 256MB par la suite.

NixOS nous montre qu'une distribution Linux ce n'est pas seulement un éinième fork de Ubuntu avec un wallpaper personalisé, ou encore une guerre de gestionnaire de paquets (dnf/apt), il existe encore de l'innovation et rien que pour cela elle mérite le coup d’œil.

Le problème de KDE et des partages smb avec mot de passe

Rédigé par uTux 5 commentaires

Un problème existant depuis des années m'a toujours gêné sous KDE : lors de l'ouverture d'un partage samba dans dolphin, l'utilisateur est invité à saisir un identifiant et mot de passe, jusqu'ici tout va bien, mais si je décide d'ouvrir un des fichiers, par exemple une vidéo avec VLC, ce dernier ne parvient pas à y accéder car l'information d'authentification n'est pas conservée.

Ce problème n'est pas présent sur GNOME / MATE / Xfce / Lxde puisque l'authentification se fait à travers le composant gvfs sur lequel les logiciels vont ensuite se baser, c'est à dire qu'une fois le partage samba monté, il est ouvert et accessible comme n'importe quel répertoire local. Sous KDE il semble que ce rôle soit confié à Kio mais cela ne fonctionne visiblement pas et ce depuis très longtemps (j'ai constaté cela à l'époque de Fedora 17). Notez que Windows implémente la même chose puisqu'une fois connecté à un partage, les applications peuvent y accéder sans devoir s'authentifier systématiquement.

Autre point : avec gvfs l'ouverture d'un fichier sur le réseau semble se faire en temps réel alors qu'avec kio une copie en répertoire temporaire local est effectuée ce qui ajoute une latence à l'ouverture.

Ce billet est une question ouverte, si vous êtes utilisateur de KDE et que vous avez résolu ces soucis, je suis preneur de vos solutions par curiosité.

VirtualBox : Installer les Guest Additions sur Debian Stretch

Rédigé par uTux 3 commentaires

Depuis que virtualbox-guest-x11 et virtualbox-guest-utils ont été retirés, il faut faire à l'ancienne :

Hôte :

Périphériques > Insérer l'image CD des Additions Invité...

Machine virtuelle :

A faire avec l'environnement graphique lancé :

$ sudo apt-get update
$ sudo apt-get install build-essential dkms
$ cd /media/cdrom
$ sudo bash VBoxLinuxAdditions.sh

Note : si /media/cdrom est vide, essayez d'ouvrir le CD-ROM VBOXADDITIONS apparu sur votre bureau ou dans votre gestionnaire de fichiers (selon l'environnement). Vous pouvez aussi essayer la commande suivante :

$ sudo mount cdrom

Pensez à redémarrer la machine virtuelle.

Résumé :

  • build-essential : Méta-paquet qui installe les outils nécessaires à la compilation, c'est la boîte à outils de base pour Debian.
  • dkms : Framework qui permet de compiler des modules pour le noyau. Non seulement c'est plus propre qu'un make install (automatisation et suivi des versions) mais aussi et surtout il recompilera automatiquement les modules en cas de mise à jour du kernel.

Debian Stretch : mais où est VirtualBox ?

Rédigé par uTux 5 commentaires

A l'heure où j'écris ce billet, Debian Stretch est disponible en version RC3, on approche donc de la sortie finale. En faisant des tests en machine virtuelle, je me suis aperçu que le paquet virtualbox-guest-x11 n'était plus disponible, virtualbox non plus d'ailleurs. Pourquoi ce logiciel s'est-il fait éjecter ?

En faisant quelques recherches je suis tombé sur ce thread :

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=794466

Même si ce n'est pas une communication officielle, voici ce que je comprends :

  • Oracle ne veut plus fournir de patches ni même d'assistance pour les CVE (failles de sécurité), ils préfèrent que les utilisateurs migrent vers une version plus récente.
  • Le mainteneur n'a pas la capacité de patcher lui-même VirtualBox, le code étant beaucoup trop complexe.
  • Virtualbox sort donc des dépôts debian jusqu'à nouvel ordre. Le freeze pour Stretch étant déjà passé, il est peu probable qu'il revienne.

Pour installer VirtualBox sous Debian Stretch la seule solution est donc d'utiliser le dépôt additionnel VirtualBox, fourni par Oracle.

Configuration et déploiement avec Ansible

Rédigé par uTux 5 commentaires

Ansible est un logiciel d'automatisation et de déploiement, il permet de créer des listes de tâches qui peuvent ensuite être jouées sur un ou plusieurs serveurs.

Attention cet article n'est pas un tutoriel (pour cela je vous renvoie vers la documentation officielle), mon but est de faire un petit retour d'expérience et montrer un exemple de projet Ansible.

Pourquoi Ansible ?

Il existe de nombreuses solutions de ce type mais il y a selon moi deux points qui distinguent Ansible : il est simple à prendre en main (excellente documentation et syntaxe yaml humainement lisible) et il ne nécessite pas d'agent pour fonctionner. En effet sur vos cibles, vous avez uniquement besoin de Python et SSH, présents en standard sur quasiment toutes les distributions Linux. Ansible marche aussi avec Windows et FreeBSD.

Pourquoi pas un script Bash / Perl / Python ?

Ben oui, il y a beaucoup de gens qui se sont fait leur propre script afin de configurer rapidement leur serveur. Mais en vrai les scripts c'est pas idéal :

  • C'est long à développer et ce n'est pas forcément votre métier.
  • Plus le script est complexe moins il sera lisible.
  • Le script doit être copié et exécuté à la main sur chaque serveur.
  • Il se contente d'exécuter une série de commandes sans se soucier du résultat.
  • Bon courage pour gérer un inventaire centralisé et des variables dynamiques.
  • Même en documentant votre script, vous serez le seul à comprendre réellement comment il marche.
  • Qui n'a jamais eu à déboguer un script obscur vieux de 10 ans ?

Ansible est beaucoup plus propre car depuis une unique machine vous gérez votre inventaire de serveurs et vos playbooks dans un même projet. Vous ne vous souciez que du résultat, vous ne demandez pas à ansible de faire un apt-get install curl, vous lui dites juste que le paquet curl doit être présent, à lui de faire en sorte de l'installer si besoin .Et c'est important non seulement car Ansible sait ce qu'il fait, mais il sait aussi le faire plusieurs fois, c'est ce qu'on appelle l'idempotence (sous réserve de ne pas faire n'importe quoi avec les playbooks, bien entendu). Enfin, il y a une énorme communauté d'utilisateurs et beaucoup de modules tiers, on trouve donc toujours des solutions aux problèmes

Exemple d'utilisation

Avertissement : Il y a plusieurs moyens de structurer un projet Ansible, consultez la page Best Practises. L'organisation que j'ai choisi n'engage que moi, vous êtes libre de faire autrement si ça colle mieux à vos besoins !

Voici un exemple de projet Ansible qui utilise un inventaire, des playbooks et des roles. Il sera utilisé pour configurer les nouveaux serveurs fraîchement installés. Objectifs :

  • Installer une liste de paquets de base
  • Configurer le /etc/hostname avec le nom du serveur
  • Configurer la locale fr_FR.UTF-8.
  • Configurer la timezone Europe/Paris.
  • Installer Nginx et Php-fpm sur les serveurs web
  • Tout en étant compatible Debian 8 & Ubuntu 16.04 (les paquets php n'ont pas le même nom !)

Arborescence du projet :

├── deploy-newserver.yml
├── group_vars
│   └── all
├── host_vars
│   ├── server01.example.org
│   └── web01.example.org
├── production.ini
└── roles
    ├── configure-newserver
    │   └── tasks
    │       └── main.yml
    │   └── templates
    │       └── hostname.j2
    ├── install-nginx
    |   └── tasks
    |       └── main.yml
    └── install-php-fpm
        └── tasks
            └── main.yml

Détail des fichiers :

---
# deploy-newserver.yml

- hosts: all
  roles:
    - configure-newserver

- hosts: webservers
  roles:
    - install-nginx
    - install-php-fpm
---
# group_vars/all
ansible_user: root
---
# host_vars/server01.example.org

ansible_host: 172.16.42.190
---
# host_vars/web01.example.org

ansible_host: 172.16.42.180
# production.ini
server01.example.org

[webservers]
web01.example.org
---
# roles/configure-newserver/tasks/main.yml

  - name: set hostname (volatile)
    hostname:
      name: '{{ inventory_hostname }}'

  - name: set hostname (permanent)
    template:
      src: hostname.j2
      dest: /etc/hostname
      force: yes

  - name: generate locales
    locale_gen:
      name: '{{ locale }}'
      state: present
    with_items:
      - en_EN.UTF-8
      - fr_FR.UTF-8

  - name: set default locale
    lineinfile:
      dest: /etc/default/locale
      regexp: '^LANG=.*$'
      line: 'LANG=fr_FR.UTF-8'
      create: yes
      state: present

  - name: set timezone
    file:
      src: /usr/share/zoneinfo/Europe/Paris
      dest: /etc/localtime
      force: yes
      state: link

  - name: common packages
    apt:
      name: "{{ item }}"
      state: present
    with_items:
      - apt-transport-https
      - bsd-mailx
      - ca-certificates
      - htop
      - manpages
      - net-tools
      - openssl
      - pciutils
      - postfix
      - ntp
      - ntpdate
      - rsync
      - tree
      - tzdata
      - ufw
      - unzip
      - vim
# roles/configure-newserver/templates/hostname.j2
{{ inventory_hostname }}
---
# roles/install-nginx/tasks/main.yml

  - name: install
    apt:
      name: nginx
      state: present
---
# roles/install-php-fpm/tasks/main.yml

  - name: php5-fpm if Debian <= 8
    apt:
      name: '{{ item }}'
      state: present
    with_items:
      - php5-fpm
      - php5-gd
    when:
      - ansible_distribution == "Debian" and ansible_distribution_major_version <= '8'

  - name: php7.0-fpm if Ubuntu => 16
    apt:
      name: '{{ item }}'
      state: present
    with_items:
      - php7.0-fpm
      - php7.0-xml
      - php7.0-gd
    when:
      - ansible_distribution == "Ubuntu" and ansible_distribution_major_version >= '16'

Lancer en dry-run (ne modifie rien) :

$ ansible-playbook -i production.ini deploy-newserver.yml --check

Lancer le déploiement :

$ ansible-playbook -i production.ini deploy-newserver.yml

Lancer le déploiement sur web01.example.org uniquement :

$ ansible-playbook -i production.ini deploy-newserver.yml --limit web01.example.org

Et voilà.

Exécution du playbook.

Ansible et moi

J'utilise régulièrement Ansible et maintient des playbooks pour des projets persos et pro, et j'en suis très satisfait. La documentation m'a permis d'être autonome et à l'aise assez vite et m'a même poussé à automatiser plus de choses que nécessaire, comme le déploiement de sites web alors que je pensais me limiter à la stack nginx / php-fpm / letsencrypt.

Même si je suis pleinement convaincu et satisfait par Ansible, je rencontre quand même certaines limitations. Par exemple l'inventaire n'est pas idéal quand on a beaucoup de serveurs et de groupes, ça peut rapidement devenir illisible. Il y a aussi certains modules tels que authorized_key pour lesquels j'aimerai plus de fonctionnalités, en l’occurrence pouvoir utiliser le paramètre 'exclusive' avec plusieurs clés.

Petits conseils en vrac

  • Ansible évolue vite, utilisez une version récente, disponible dans les backports debian ou via pip.
  • Le state: present est souvent implicite, vous n'avez donc pas besoin de le mettre, cependant c'est mieux de le faire, pour rendre le playbook plus évident à comprendre.
  • Pensez à utiliser les variables, pour les URLs ou les numéros de version.
  • Ne mettez pas de mot de passe ou de credential dans les playbooks, utilisez des variables passées au moment de l'exécution, ou dans un fichier que vous prendrez soin d'exclure si vous utilisez un repo public.
  • Faites un dry-run pour tester vos playbooks, on ne sait jamais.
  • Attention au module file et plus particulièrement aux permissions, surtout quand vous travaillez sur des fichiers existants (tels que le resolv.conf).
  • La commande ansible hostname -m setup vous permet de récupérer tous les 'facts' (OS, version, ip...) d'une cible, ils peuvent être utilisés dans les playbooks.
  • Testez vos playbooks jusqu'au bout : redémarrez le serveur pour vérifier que les modifications ne sont pas volatiles.
  • Faites des choses propres et simples. Si vos besoins sont trop variés ou spécifiques, n'utilisez pas Ansible, ou limitez vous au minimum.
  • La machine sur laquelle vous exécutez Ansible est critique, car en général vous aurez l'accès root without-password à tous vos serveurs, c'est donc un point d'entrée que vous devez sécuriser au maximum.

Conclusion

Si vous souhaitez unifier, accélérer et automatiser vos déploiements ou tout simplement vous initier au devops, Ansible est un outil que je recommande fortement, l'essayer c'est l'adopter.

Fil RSS des articles de cette catégorie