Journaux liées à cette note :

Faut-il encore configurer du swap en 2025, même sur des serveurs avec beaucoup de RAM ? #OnMePoseLaQuestion, #DevOps, #admin-sys, #linux, #JaiDécouvert, #JaiLu

Aujourd'hui, j'ai implémenté des tests de montée en charge à l'aide de Grafana k6. En ciblant un site web hébergé sur un petit serveur Scaleway DEV1-M, j'ai constaté que le serveur est devenu inaccessible à la fin des tests. Aucun swap n'était configuré sur cette Virtual machine de 4Go de RAM.

Je me suis souvenu qu'en 2019, j'ai rencontré aussi des problèmes de freeze sur une VM AWS EC2 que j'ai corrigés en ajoutant un peu de swap au serveur. Après cela, je n'ai constaté plus aucun freeze de VM pendant 4 ans.

Ce sujet de swap m'a fait penser à la question qu'un ami m'a posée en octobre 2024 :

Désactiver le swap sur une Debian, recommandé ou pas ?

Alors que j'ai 29Go utilisé sur 64, le swap était plein (3,5Go occupé à 100%), les 12 cœurs du serveur partaient dans les tours. J'ai désactivé le swap et me voilà gentiment avec un load average raisonnable, pour les tâches de cette machine.

C'est une très bonne question que je me pose depuis longtemps. J'ai enfin pris un peu de temps pour creuser ce sujet.

Sept mois plus tard, voici ma réponse dans cette note 😉.

#JaiDécouvert le paramètre kernel nommé Swappiness.

swappiness

This control is used to define how aggressive the kernel will swap memory pages. Higher values will increase aggressiveness, lower values decrease the amount of swap. A value of 0 instructs the kernel not to initiate swap until the amount of free and file-backed pages is less than the high water mark in a zone.

The default value is 60.

documentation du kernel

Dans la documentation SwapFaq d'Ubuntu j'ai lu :

The swappiness parameter controls the tendency of the kernel to move processes out of physical memory and onto the swap disk. Because disks are much slower than RAM, this can lead to slower response times for system and applications if processes are too aggressively moved out of memory.

  • swappiness can have a value of between 0 and 100
  • swappiness=0 tells the kernel to avoid swapping processes out of physical memory for as long as possible
  • swappiness=100 tells the kernel to aggressively swap processes out of physical memory and move them to swap cache

The default setting in Ubuntu is swappiness=60. Reducing the default value of swappiness will probably improve overall performance for a typical Ubuntu desktop installation. A value of swappiness=10 is recommended, but feel free to experiment. Note: Ubuntu server installations have different performance requirements to desktop systems, and the default value of 60 is likely more suitable.

source

D'après ce que j'ai compris, plus swappiness tend vers zéro, moins le swap est utilisé.

J'ai lu ici :

vm.swappiness = 60 : Valeur par défaut de Linux : à partir de 40% d’occupation de Ram, le noyau écrit sur le disque.

source

Cependant, je n'ai pas trouvé d'autres sources qui confirment cette correspondance entre la valeur de swappiness et un pourcentage précis d'utilisation de la RAM.

J'ai ensuite cherché à savoir si c'était encore pertinent de configurer du swap en 2025, sur des serveurs qui disposent de beaucoup de RAM.

#JaiLu ce thread : "Do I need swap space if I have more than enough amount of RAM?", et voici un extrait qui peut servir de conclusion :

In other words, by disabling swap you gain nothing, but you limit the operation system's number of useful options in dealing with a memory request. Which might not be, but very possibly may be a disadvantage (and will never be an advantage).

source

Je pense que ceci est d'autant plus vrai si le paramètre swappiness est bien configuré.

Concernant la taille du swap recommandée par rapport à la RAM du serveur, la documentation de Ubuntu conseille les ratios suivants :

       RAM      Swap    Maximum Swap
      256MB    256MB           512MB 
      512MB    512MB          1024MB
     1024MB   1024MB          2048MB
        1GB      1GB             2GB
        2GB      1GB             4GB
        3GB      2GB             6GB
        4GB      2GB             8GB
        5GB      2GB            10GB
        6GB      2GB            12GB
        8GB      3GB            16GB
       12GB      3GB            24GB
       16GB      4GB            32GB
       24GB      5GB            48GB
       32GB      6GB            64GB
       64GB      8GB           128GB
      128GB     11GB           256GB
      256GB     16GB           512GB
      512GB     23GB             1TB
        1TB     32GB             2TB
        2TB     46GB             4TB
        4TB     64GB             8TB
        8TB     91GB            16TB

source

#JaiDécouvert aussi que depuis le kernel 2.6, les fichiers de swap sont aussi rapides que les partitions de swap :

Definitely not. With the 2.6 kernel, "a swap file is just as fast as a swap partition."

source

Suite à ces apprentissages, j'ai configuré et activé un swap de 2G sur la VM Scaleway DEV1-L équipée de 4G de RAM, avec le paramètre swappiness réglé à 10.

J'ai relancé mon test Grafana k6 et je n'ai constaté plus aucun freeze, je n'ai pas perdu l'accès au serveur.

De plus, probablement grâce au paramètre swappiness fixé à 10, j'ai observé que le swap n'a pas été utilisé pendant le test.

Suite à ces lectures et à cette expérience concluante, j'ai décidé de désormais configurer systématiquement du swap sur tous mes serveurs de la manière suivante :

if swapon --show | grep -q "^/swapfile"; then
    echo "Swap is already configured"
else
    get_swap_size() {
        local ram_gb=$(free -g | awk '/^Mem:/ {print $2}')
        
        # Why this values? See https://help.ubuntu.com/community/SwapFaq#How_much_swap_do_I_need.3F
        if [ $ram_gb -le 1 ]; then
            echo "1G"
        elif [ $ram_gb -le 2 ]; then
            echo "1G"
        elif [ $ram_gb -le 6 ]; then
            echo "2G"
        elif [ $ram_gb -le 12 ]; then
            echo "3G"
        elif [ $ram_gb -le 16 ]; then
            echo "4G"
        elif [ $ram_gb -le 24 ]; then
            echo "5G"
        elif [ $ram_gb -le 32 ]; then
            echo "6G"
        elif [ $ram_gb -le 64 ]; then
            echo "8G"
        elif [ $ram_gb -le 128 ]; then
            echo "11G"
        else
            echo "11G"
        fi
    }

    SWAP_SIZE=$(get_swap_size)
    fallocate -l $SWAP_SIZE /swapfile
    chmod 600 /swapfile
    mkswap /swapfile
    swapon /swapfile

    if ! grep -q "^/swapfile.*swap" /etc/fstab; then
        echo "/swapfile none swap sw 0 0" >> /etc/fstab
    fi
fi

# Why 10 instead default 60? see https://help.ubuntu.com/community/SwapFaq#:~:text=a%20value%20of%20swappiness%3D10%20is%20recommended
echo 10 | tee /proc/sys/vm/swappiness
echo "vm.swappiness=10" | tee -a /etc/sysctl.conf

Playground qui présente comment je setup un projet Python Flask en 2025 #dev-kit, #python, #mise, #docker, #WSL, #playground, #software-engineering

Je pense que cela doit faire depuis 2015 que je n'ai pas développé une application en Python Flask !

Entre 2008 et 2015, j'ai beaucoup itéré dans mes méthodes d'installation et de setup de mes environnements de développement Python.

D'après mes souvenirs, si je devais dresser la liste des différentes étapes, ça donnerai ceci :

  • 2006 : aucune méthode, j'installe Python 🙂
  • 2007 : je me bats avec setuptools et distutils (mais ça va, c'était plus mature que ce que je pouvais trouver dans le monde PHP qui n'avait pas encore imaginé composer)
  • 2008 : je trouve la paie avec virtualenv
  • 2010 : j'ai peur d'écrire des scripts en Bash alors à la place, j'écris un script bootstrap.py dans lequel j'essaie d'automatiser au maximum l'installation du projet
  • 2012 : je me bats avec buildout pour essayer d'automatiser des éléments d'installation. Avec le recul, je réalise que je n'ai jamais rien compris à buildout
  • 2012 : j'utilise Vagrant pour fixer les éléments d'installation, je suis plutôt satisfait
  • 2015 : je suis radicale, j'enferme tout l'environnement de dev Python dans un container de développement, je monte un path volume pour exposer le code source du projet dans le container. Je bricole en entrypoint avec la commande "sleep".

Des choses ont changé depuis 2015.

Mais, une chose que je n'ai pas changée, c'est que je continue à suivre le modèle The Twelve-Factors App et je continue à déployer tous mes projets packagé dans des images Docker. Généralement avec un simple docker-compose.yml sur le serveur, ou alors Kubernetes pour des projets de plus grande envergure… mais cela ne m'arrive jamais en pratique, je travaille toujours sur des petits projets.

Choses qui ont changé : depuis fin 2018, j'ai décidé de ne plus utiliser Docker dans mes environnements de développement pour les projets codés en NodeJS, Golang, Python

Au départ, cela a commencé par uniquement les projets en NodeJS pour des raisons de performance.

J'ai ensuite découvert Asdf et plus récemment Mise. À partir de cela, tout est devenu plus facilement pour moi.
Avec Asdf, je n'ai plus besoin "d'enfermer" mes projets dans des containers Docker pour fixer l'environnement de développement, les versions…

Cette introduction est un peu longue, je n'ai pas abordé le sujet principal de cette note 🙂.

Je viens de publier un playground d'un exemple de projet minimaliste Python Flask suivant mes pratiques de 2025.

Voici son repository : mise-python-flask-playground

Ce playground est "propulsé" par Docker et Mise.

J'ai documenté la méthode d'installation pour :

Je précise que je n'ai pas eu l'occasion de tester l'installation sous Windows, hier j'ai essayé, mais je n'ai pas réussi à installer WSL2 sous Windows dans un Virtualbox lancé sous Fedora. Je suis à la recherche d'une personne pour tester si mes instructions d'installation sont valides ou non.

Briques technologiques présentes dans le playground :

Voici quelques petites subtilités.

Dans le fichier alembic.ini j'ai modifié le paramètre file_template parce que j'aime que les fichiers de migration soient classés par ordre chronologique :

[alembic]
# template used to generate migration files
file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d%%(second).2d_%%(slug)s

Ce qui donne par exemple :

20250205_124639_users.py
20250205_125437_add_user_lastname.py

Ici le port de PostgreSQL est généré dynamiquement par docker compose :

  postgres:
    image: postgres:17
	...
	ports:
      - 5432 # <= ici

Avec cela, fini les conflits de port quand je lance plusieurs projets en même temps sur ma workstation.

L'URL vers le serveur PostgreSQL est générée dynamiquement par le script get_postgres_url.sh qui est appelé par le fichier .envrc. Tout cela se passe de manière transparente.

J'initialise ici les extensions PostgreSQL :

def init_db():
    db.drop_all()
    db.session.execute(db.text('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'))
    db.session.execute(db.text('CREATE EXTENSION IF NOT EXISTS "unaccent"'))
    db.session.commit()
    db.create_all()

et ici dans la première migration :

def upgrade():
    op.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";')
    op.execute('CREATE EXTENSION IF NOT EXISTS "unaccent";')
    op.create_table('users',
        sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
        sa.Column('firstname', sa.String(), nullable=False),
        sa.PrimaryKeyConstraint('id')
    )

Je découvre la compression Zstandard #OnMaPartagé, #JaiDécouvert, #WebDev, #DevOps, #compression, #brotli, #zstandard, #JeMeDemande, #JaimeraisUnJour

Un ami m'a partagé Zstandard (zstd), un algorithme de compression.

Il y a 2 ans, j'ai étudié et activé Brotli dans mes containers nginx, voir la note : Mise en œuvre du module Nginx Brotli.

Je viens de trouver un module zstd pour nginx : https://github.com/tokers/zstd-nginx-module

Mon ami m'a partagé cet excellent article : Choosing Between gzip, Brotli and zStandard Compression. Très complet, il explique tout, contient des benchmarks…

Voici ce que je retiens.

Brotli a été créé par Google, Zstandard par Facebook :

Zstandard is a newer compression method developed by Facebook.

source

Je lis sur canIuse, le support Zstandard a été ajouté à Chrome en mars 2024 et à Firefox en mai 2024, c'est donc une technologie très jeune coté browser.

Benchmark sur le dépôt officiel de Zstandard :

J'ai trouvé ces threads Hacker News :

Zstandard semble être fortement adopté au niveau de l'écosystème des OS Linux :

The Linux kernel has included Zstandard since November 2017 (version 4.14) as a compression method for the btrfs and squashfs filesystems.

source

Packages Ubuntu et Debian :

In March 2018, Canonical tested the use of zstd as a deb package compression method by default for the Ubuntu Linux distribution. Compared with xz compression of deb packages, zstd at level 19 decompresses significantly faster, but at the cost of 6% larger package files. Support was added to Debian in April 2018

source

Packages Fedora :

Fedora added ZStandard support to rpm in May 2018 (Fedora release 28) and used it for packaging the release in October 2019 (Fedora 31). In Fedora 33, the filesystem is compressed by default with zstd.

source

#JeMeDemande si dans mes projets de doit utiliser Zstandard plutôt que Brotli 🤔.

Je pense avoir trouver une réponse ici :

The research I’ve shared in this article also shows that for many sites Brotli will provide better compression for static content. Zstandard could potentially provide some benefits for dynamic content due to its faster compression speeds. Additionally:

  • ...
  • For dynamic content
    • Brotli level 5 usually result in smaller payloads, at similar or slightly slower compression times.
    • zStandard level 12 often produces similar payloads to Brotli level 5, with compression times faster than gzip and Brotli.
  • For static content
    • Brotli level 11 produces the smallest payloads
    • zStandard is able to apply their highest compression levels much faster than Brotli, but the payloads are still smaller with Brotli.

source

#JaimeraisUnJour prendre le temps d'installer zstd-nginx-module à mon image Docker nginx-brotli-docker (ou alors d'en trouver une déjà existante).

Journal du vendredi 11 octobre 2024 à 20:28

J'ai mis à jour mon repository vagrant-virtualbox-fedora vers Ubuntu 24.04 LTS.

Qu'est-ce que RPM Fusion et pourquoi ce nom ? #fedora, #distribution-linux

Jusqu'à ce soir, j'installais sous Fedora des packages qui vennait du dépôt RPM Fusion sans vraiment avoir étudié ce qu'était-ce repository. Je viens de me renseigner et voici ce que j'ai appris.

"RPM Fusion" est l'équivalent des dépôts "contrib et non-free" chez Debian ou "universe et multiverse" chez Ubuntu.

Le terme "Fusion" dans "RPM Fusion" fait référence à l'origine du dépôt. RPM Fusion est né de la fusion de plusieurs dépôts tiers qui existaient avant sa création en 2007-2008, tels que : Livna, Freshrpms et Dribble.

Journal du lundi 09 septembre 2024 à 15:59 #admin-sys, #DevOps, #Doctrine, #selfhosting

Dans cette note, je souhaite présenter ma doctrine de mise à jour d'OS de serveurs.

Je ne traiterai pas ici de la stratégie d'upgrade pour un Cluster Kubernetes.

La mise à jour d'un serveur, par exemple, sous un OS Ubuntu LTS, peut être effectuée avec les commandes suivantes :

  • sudo apt upgrade -y
  • ou sudo apt dist-upgrade -y (plus risqué)
  • ou sudo do-release-upgrade (encore plus risqué)

L'exécution d'un sudo apt upgrade -y peut :

  • Installer une mise à jour de docker, entraînant une interruption des services sur ce serveur de quelques secondes à quelques minutes.
  • Installer une mise à jour de sécurité du kernel, nécessitant alors un redémarrage du serveur, ce qui entraînera une coupure de quelques minutes.

Une montée de version de l'OS via sudo do-release-upgrade peut prendre encore plus de temps et impliquer des ajustements supplémentaires.

Bien que ces opérations se déroulent généralement sans encombre, il n'y a jamais de certitude totale, comme l'illustre l'exemple de la Panne informatique mondiale de juillet 2024.

Sachant cela, avant d'effectuer la mise à jour d'un serveur, j'essaie de déterminer quelles seraient les conséquences d'une coupure d'une journée de ce serveur.

Si je considère que ce risque de coupure est inacceptable ou ne serait pas accepté, j'applique alors la méthode suivante pour réaliser mon upgrade.

Je n'effectue pas la mise à jour le serveur existant. À la place, je déploie un nouveau serveur en utilisant mes scripts automatisés d'Infrastructure as code / GitOps.

C'est pourquoi je préfère éviter de nommer les serveurs d'après le service spécifique qu'ils hébergent (voir aussi Pets vs Cattle). Par exemple, au lieu de nommer un serveur gitlab.servers.example.com, je vais le nommer server1.servers.example.com et configurer gitlab.servers.example.com pour pointer vers server1.servers.example.com.

Ainsi, en cas de mise à jour de server1.servers.example.com, je crée un nouveau serveur nommé server(n+1).servers.example.com.

Ensuite, je lance les scripts de déploiement des services qui étaient présents sur server1.servers.example.com.

Idéalement, j'utilise mes scripts de restauration des données depuis les sauvegardes des services de server1.servers.example.com, ce qui me permet de vérifier leur bon fonctionnement. Ensuite, je prépare des scripts rsync pour synchroniser rapidement les volumes entre server1.servers.example.com et server(n+1).servers.example.com.

Je teste que tout fonctionne bien sur server(n+1).servers.example.com.

Si tout fonctionne correctement, alors :

  • J'arrête les services sur server(n+1).servers.example.com ;
  • J'exécute le script de synchronisation rsync de server1.servers.example.com vers server(n+1).servers.example.com ;
  • Je relance les services sur server(n+1).servers.example.com
  • Je modifie la configuration DNS pour faire pointer les services de server1.servers.example.com vers server(n+1).servers.example.com
  • Quelques jours après cette intervention, je décommissionne server1.servers.example.com.

Cette méthode est plus longue et plus complexe qu'une mise à jour directe de l'OS sur le server1.servers.example.com, mais elle présente plusieurs avantages :

  • Une grande sécurité ;
  • L'opération peut être faite tranquillement, sans stress, avec de la qualité ;
  • Une durée de coupure limitée et maîtrisée ;
  • La possibilité de confier la tâche en toute sécurité à un nouveau DevOps ;
  • La garantie du bon fonctionnement des scripts de déploiement automatisé ;
  • La vérification de l'efficacité des scripts de restauration des sauvegardes ;
  • Un test concret des scripts et de la documentation du Plan de reprise d'activité.

Si le serveur à mettre à jour fonctionne sur une Virtual instance, il est également possible de cloner la VM et de tester la mise à niveau. Cependant, je préfère éviter cette méthode, car elle ne permet pas de valider l'efficacité des scripts de déploiement.